Add background page

This commit is contained in:
hlohaus
2025-03-25 04:32:11 +01:00
parent f723e92a23
commit 142e4876ae
7 changed files with 288 additions and 159 deletions

View File

@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>G4F DEMO</title>
<link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
<link rel="manifest" href="/static/img/site.webmanifest">
<style>
:root {
--colour-1: #000000;
--colour-2: #ccc;
--colour-3: #e4d4ff;
--colour-4: #f0f0f0;
--colour-5: #181818;
--colour-6: #242424;
--accent: #8b3dff;
--gradient: #1a1a1a;
--background: #16101b;
--size: 70vw;
--top: 50%;
--blur: 40px;
--opacity: 0.6;
}
.gradient {
position: absolute;
z-index: -1;
left: 50vw;
border-radius: 50%;
background: radial-gradient(circle at center, var(--accent), var(--gradient));
width: var(--size);
height: var(--size);
top: var(--top);
transform: translate(-50%, -50%);
filter: blur(var(--blur)) opacity(var(--opacity));
animation: zoom_gradient 6s infinite alternate;
display: none;
max-height: 100%;
transition: max-height 0.25s ease-in;
}
.gradient.hidden {
max-height: 0;
transition: max-height 0.15s ease-out;
}
@media only screen and (min-width: 40em) {
body .gradient{
display: block;
}
}
@keyframes zoom_gradient {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.2);
}
}
/* Body and text color */
body {
background: var(--background);
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
}
iframe {
background: transparent;
width: 100%;
border: none;
}
.hidden {
display: none;
}
#background, #image-feed {
height: 100%;
position: absolute;
z-index: -1;
object-fit: cover;
object-position: center;
width: 100%;
background: black;
}
/* Animation for the gradient circle */
@keyframes zoom_gradient {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.5);
}
}
</style>
<link rel="stylesheet" href="/static/css/all.min.css">
<script>
(async () => {
const isIframe = window.self !== window.top;
const backendUrl = "{{backend_url}}";
let url = new URL(window.location.href)
if (isIframe && backendUrl) {
window.location.replace(`${backendUrl}${url.search}`);
return;
}
})();
</script>
<script src="https://unpkg.com/es-module-shims@1.7.0/dist/es-module-shims.js"></script>
<script type="importmap">
{
"imports": {
"@huggingface/hub": "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.21.0/+esm"
}
}
</script>
</head>
<body>
<img id="image-feed" class="hidden" alt="Image Feed">
<!-- Gradient Background Circle -->
<div class="gradient"></div></div>
<script>
(async () => {
const url = "https://image.pollinations.ai/feed";
const imageFeed = document.getElementById("image-feed");
const images = []
let es = null;
function initES() {
if (es == null || es.readyState == EventSource.CLOSED) {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.nsfw || !data.nologo || data.width < 512 || !data.imageURL || data.isChild || data.status != "end_generating") {
return;
}
const lower = data.prompt.toLowerCase();
const tags = ["nsfw", "timeline", "feet", "blood", "soap", "orally", "heel", "latex", "bathroom", "boobs", "charts", "gel", "logo", "infographic", "warts", " bra ", "prostitute", "curvy", "breasts", "written", "bodies", "naked", "classroom", "malone", "dirty", "shoes", "shower", "banner", "fat", "nipples", "couple", "sexual", "sandal", "supplier", "overlord", "succubus", "platinum", "cracy", "crazy", "hemale", "oprah", "lamic", "ropes", "cables", "wires", "dirty", "messy", "cluttered", "chaotic", "disorganized", "disorderly", "untidy", "unorganized", "unorderly", "unsystematic", "disarranged", "disarrayed", "disheveled", "disordered", "jumbled", "muddled", "scattered", "shambolic", "sloppy", "unkept", "unruly"];
for (i in tags) {
if (lower.indexOf(tags[i]) != -1) {
console.log("Skipping image with tag: " + tags[i]);
console.debug("Skipping image:", data.imageURL);
return;
}
}
const landscape = window.innerWidth > window.innerHeight;
if (landscape && data.width > data.height) {
images.push(data.imageURL);
} else if (!landscape && data.width < data.height) {
images.push(data.imageURL);
}
};
eventSource.onerror = (event) => {
eventSource.close();
}
imageFeed.onerror = () => {
imageFeed.classList.add("hidden");
}
}
}
initES();
setInterval(() => {
if (images.length > 0) {
imageFeed.classList.remove("hidden");
imageFeed.src = images.shift();
} else if(imageFeed) {
initES();
}
}, 7000);
})();
</script>
</body>
</html>

View File

@@ -28,43 +28,6 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
.gradient {
position: absolute;
z-index: -1;
left: 50vw;
border-radius: 50%;
background: radial-gradient(circle at center, var(--accent), var(--gradient));
width: var(--size);
height: var(--size);
top: var(--top);
transform: translate(-50%, -50%);
filter: blur(var(--blur)) opacity(var(--opacity));
animation: zoom_gradient 6s infinite alternate;
display: none;
max-height: 100%;
transition: max-height 0.25s ease-in;
}
.gradient.hidden {
max-height: 0;
transition: max-height 0.15s ease-out;
}
@media only screen and (min-width: 40em) {
body .gradient{
display: block;
}
}
@keyframes zoom_gradient {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.2);
}
}
/* Body and text color */
body {
background: var(--background);
@@ -188,16 +151,6 @@
.hidden {
display: none;
}
/* Animation for the gradient circle */
@keyframes zoom_gradient {
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(1.5);
}
}
</style>
<link rel="stylesheet" href="/static/css/all.min.css">
<script>
@@ -221,8 +174,7 @@
</script>
</head>
<body>
<iframe id="background"></iframe>
<img id="image-feed" class="hidden" alt="Image Feed">
<iframe id="background" src="/background"></iframe>
<!-- Gradient Background Circle -->
<div class="gradient"></div>
@@ -343,6 +295,7 @@
const today = new Date().toJSON().slice(0, 10);
const max = 6;
const cache_id = Math.floor(Math.random() * max);
if (cache_id > 3) {
let prompt;
if (cache_id % 2 == 0) {
prompt = `Today is ${new Date().toJSON().slice(0, 10)}.
@@ -353,55 +306,12 @@
}
const response = await fetch(`/backend-api/v2/create?prompt=${prompt}&filter_markdown=html&cache=${cache_id}`);
const text = await response.text()
if (text) {
background.src = `data:text/html;charset=utf-8,${encodeURIComponent(text)}`;
const gradient = document.querySelector('.gradient');
gradient.classList.add('hidden');
const url = "https://image.pollinations.ai/feed";
const imageFeed = document.getElementById("image-feed");
const images = []
let es = null;
function initES() {
if (es == null || es.readyState == EventSource.CLOSED) {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.nsfw || !data.nologo || data.width < 512 || !data.imageURL || data.isChild || data.status != "end_generating") {
return;
}
const lower = data.prompt.toLowerCase();
const tags = ["nsfw", "timeline", "feet", "blood", "soap", "orally", "heel", "latex", "bathroom", "boobs", "charts", "gel", "logo", "infographic", "warts", " bra ", "prostitute", "curvy", "breasts", "written", "bodies", "naked", "classroom", "malone", "dirty", "shoes", "shower", "banner", "fat", "nipples", "couple", "sexual", "sandal", "supplier", "overlord", "succubus", "platinum", "cracy", "crazy", "hemale", "oprah", "lamic", "ropes", "cables", "wires", "dirty", "messy", "cluttered", "chaotic", "disorganized", "disorderly", "untidy", "unorganized", "unorderly", "unsystematic", "disarranged", "disarrayed", "disheveled", "disordered", "jumbled", "muddled", "scattered", "shambolic", "sloppy", "unkept", "unruly"];
for (i in tags) {
if (lower.indexOf(tags[i]) != -1) {
console.log("Skipping image with tag: " + tags[i]);
console.debug("Skipping image:", data.imageURL);
return;
}
}
const landscape = window.innerWidth > window.innerHeight;
if (landscape && data.width > data.height) {
images.push(data.imageURL);
} else if (!landscape && data.width < data.height) {
images.push(data.imageURL);
}
};
eventSource.onerror = (event) => {
eventSource.close();
}
imageFeed.onerror = () => {
imageFeed.classList.add("hidden");
}
}
}
initES();
setInterval(() => {
if (images.length > 0) {
imageFeed.classList.remove("hidden");
imageFeed.src = images.shift();
} else if(imageFeed) {
initES();
}
}, 7000);
const container = document.querySelector('.container');
const button = document.querySelector('.slide-button');

View File

@@ -27,9 +27,11 @@
<h2>QR Code</h2>
<div id="qrcode"></div>
<p id="qrcode-status"></p>
<button id="generateQRCode">Generate QR Code</button>
<script type="module">
const conversation_id = "{{conversation_id}}";
import QrScanner from 'https://cdn.jsdelivr.net/npm/qr-scanner/qr-scanner.min.js';
function generate_uuid() {
@@ -64,19 +66,29 @@
}
});
localStorage.getItem('device_id') || localStorage.setItem('device_id', generate_uuid());
document.getElementById('generateQRCode').addEventListener('click', () => {
const chat_id = generate_uuid();
const qrcode = new QRCode(document.getElementById("qrcode"), {
text: JSON.stringify({
date: Math.floor(Date.now() / 1000),
device_id: localStorage.getItem('device_id')
}),
const url = `/backend-api/v2/chat/${chat_id}`;
fetch(url, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: localStorage.getItem(`conversation:${conversation_id}`)
});
const share = `${new URL(window.location.href).origin}/chat/${encodeURI(chat_id)}/${encodeURI(conversation_id)}`;
document.getElementById('qrcode-status').innerText = share;
document.getElementById("qrcode").innerHTML = '';
const qrcode = new QRCode(
document.getElementById("qrcode"),
share,
{
width: 128,
height: 128,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
});
const switchButton = document.getElementById('switchCamera');
let currentStream = null;

View File

@@ -592,8 +592,8 @@ input-count .text {
display: none;
flex-direction: row;
height: 36px;
width: 120px;
margin-right: 130px;
width: 140px;
margin-right: 150px;
z-index: 1005;
padding: 10px;
margin-top: -4px;

View File

@@ -439,6 +439,18 @@ const register_message_buttons = async () => {
});
});
chatBody.querySelectorAll(".message .fa-qrcode").forEach(async (el) => {
if (el.dataset.click) {
return
}
el.dataset.click = true;
const message_el = get_message_el(el);
el.addEventListener("click", async () => {
iframe.src = `/qrcode/${window.conversation_id}#${message_el.dataset.index}`;
iframe_container.classList.remove("hidden");
});
});
chatBody.querySelectorAll(".message .reasoning_title").forEach(async (el) => {
if (el.dataset.click) {
return
@@ -1446,6 +1458,7 @@ const load_conversation = async (conversation, scroll=true) => {
add_buttons.push(`<button class="options_button">
<div>
<span><i class="fa-solid fa-qrcode"></i></span>
<span><i class="fa-brands fa-whatsapp"></i></span>
<span><i class="fa-solid fa-volume-high"></i></i></span>
<span><i class="fa-solid fa-print"></i></span>
@@ -2038,7 +2051,7 @@ chatPrompt.addEventListener("input", function() {
window.addEventListener('load', async function() {
if (!window.chat_id) {
return;
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
}
if (!window.conversation_id) {
window.conversation_id = window.chat_id;
@@ -2046,7 +2059,9 @@ window.addEventListener('load', async function() {
const response = await fetch(`/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
headers: {'accept': 'application/json'},
});
if (response.ok) {
if (!response.ok) {
return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
}
let conversation = await response.json();
if (!window.conversation_id || conversation.id == window.conversation_id) {
window.conversation_id = conversation.id;
@@ -2063,8 +2078,12 @@ window.addEventListener('load', async function() {
refreshOnHide = true;
}
});
return setInterval(async () => {
if (!refreshOnHide || !window.chat_id) {
var refreshIntervalId = setInterval(async () => {
if (!window.chat_id) {
clearInterval(refreshIntervalId);
return;
}
if (!refreshOnHide) {
return;
}
const response = await fetch(`/backend-api/v2/chat/${window.chat_id}`, {
@@ -2082,7 +2101,7 @@ window.addEventListener('load', async function() {
}
}
}, 5000);
}
return;
}
await safe_load_conversation(window.conversation_id, false);
});

View File

@@ -69,9 +69,9 @@ class Backend_Api(Api):
def home():
return render_template('home.html')
@app.route('/qrcode', methods=['GET'])
def qrcode():
return render_template('qrcode.html')
@app.route('/qrcode/<conversation_id>', methods=['GET'])
def qrcode(conversation_id: str):
return render_template('qrcode.html', conversation_id=conversation_id)
@app.route('/backend-api/v2/models', methods=['GET'])
def jsonify_models(**kwargs):

View File

@@ -36,6 +36,10 @@ class Website:
'function': redirect_home,
'methods': ['GET', 'POST']
},
'/background': {
'function': self._background,
'methods': ['GET']
},
}
def _chat(self, conversation_id):
@@ -51,3 +55,6 @@ class Website:
def _settings(self):
return render_template('index.html', conversation_id=str(uuid.uuid4()))
def _background(self):
return render_template('background.html')