XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/gpt4free

Add background page

142e4876
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

7 个文件 +288 -159
Added g4f/gui/client/background.html +181 -0
@@ -0,0 +1,181 @@
1
2 <!DOCTYPE html>
3 <html lang="en">
4 <head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>G4F DEMO</title>
8 <link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
9 <link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
10 <link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
11 <link rel="manifest" href="/static/img/site.webmanifest">
12 <style>
13 :root {
14 --colour-1: #000000;
15 --colour-2: #ccc;
16 --colour-3: #e4d4ff;
17 --colour-4: #f0f0f0;
18 --colour-5: #181818;
19 --colour-6: #242424;
20 --accent: #8b3dff;
21 --gradient: #1a1a1a;
22 --background: #16101b;
23 --size: 70vw;
24 --top: 50%;
25 --blur: 40px;
26 --opacity: 0.6;
27 }
28
29 .gradient {
30 position: absolute;
31 z-index: -1;
32 left: 50vw;
33 border-radius: 50%;
34 background: radial-gradient(circle at center, var(--accent), var(--gradient));
35 width: var(--size);
36 height: var(--size);
37 top: var(--top);
38 transform: translate(-50%, -50%);
39 filter: blur(var(--blur)) opacity(var(--opacity));
40 animation: zoom_gradient 6s infinite alternate;
41 display: none;
42 max-height: 100%;
43 transition: max-height 0.25s ease-in;
44 }
45
46 .gradient.hidden {
47 max-height: 0;
48 transition: max-height 0.15s ease-out;
49 }
50
51 @media only screen and (min-width: 40em) {
52 body .gradient{
53 display: block;
54 }
55 }
56
57 @keyframes zoom_gradient {
58 0% {
59 transform: translate(-50%, -50%) scale(1);
60 }
61 100% {
62 transform: translate(-50%, -50%) scale(1.2);
63 }
64 }
65
66 /* Body and text color */
67 body {
68 background: var(--background);
69 height: 100vh;
70 margin: 0;
71 padding: 0;
72 overflow: hidden;
73 }
74
75 iframe {
76 background: transparent;
77 width: 100%;
78 border: none;
79 }
80
81 .hidden {
82 display: none;
83 }
84
85 #background, #image-feed {
86 height: 100%;
87 position: absolute;
88 z-index: -1;
89 object-fit: cover;
90 object-position: center;
91 width: 100%;
92 background: black;
93 }
94
95 /* Animation for the gradient circle */
96 @keyframes zoom_gradient {
97 0% {
98 transform: translate(-50%, -50%) scale(1);
99 }
100 100% {
101 transform: translate(-50%, -50%) scale(1.5);
102 }
103 }
104 </style>
105 <link rel="stylesheet" href="/static/css/all.min.css">
106 <script>
107 (async () => {
108 const isIframe = window.self !== window.top;
109 const backendUrl = "{{backend_url}}";
110 let url = new URL(window.location.href)
111 if (isIframe && backendUrl) {
112 window.location.replace(`${backendUrl}${url.search}`);
113 return;
114 }
115 })();
116 </script>
117 <script src="https://unpkg.com/es-module-shims@1.7.0/dist/es-module-shims.js"></script>
118 <script type="importmap">
119 {
120 "imports": {
121 "@huggingface/hub": "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.21.0/+esm"
122 }
123 }
124 </script>
125 </head>
126 <body>
127 <img id="image-feed" class="hidden" alt="Image Feed">
128
129 <!-- Gradient Background Circle -->
130 <div class="gradient"></div></div>
131 <script>
132 (async () => {
133 const url = "https://image.pollinations.ai/feed";
134 const imageFeed = document.getElementById("image-feed");
135 const images = []
136 let es = null;
137 function initES() {
138 if (es == null || es.readyState == EventSource.CLOSED) {
139 const eventSource = new EventSource(url);
140 eventSource.onmessage = (event) => {
141 const data = JSON.parse(event.data);
142 if (data.nsfw || !data.nologo || data.width < 512 || !data.imageURL || data.isChild || data.status != "end_generating") {
143 return;
144 }
145 const lower = data.prompt.toLowerCase();
146 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"];
147 for (i in tags) {
148 if (lower.indexOf(tags[i]) != -1) {
149 console.log("Skipping image with tag: " + tags[i]);
150 console.debug("Skipping image:", data.imageURL);
151 return;
152 }
153 }
154 const landscape = window.innerWidth > window.innerHeight;
155 if (landscape && data.width > data.height) {
156 images.push(data.imageURL);
157 } else if (!landscape && data.width < data.height) {
158 images.push(data.imageURL);
159 }
160 };
161 eventSource.onerror = (event) => {
162 eventSource.close();
163 }
164 imageFeed.onerror = () => {
165 imageFeed.classList.add("hidden");
166 }
167 }
168 }
169 initES();
170 setInterval(() => {
171 if (images.length > 0) {
172 imageFeed.classList.remove("hidden");
173 imageFeed.src = images.shift();
174 } else if(imageFeed) {
175 initES();
176 }
177 }, 7000);
178 })();
179 </script>
180 </body>
181 </html>
Modified g4f/gui/client/demo.html +16 -106
@@ -28,43 +28,6 @@
28 28
29 29 @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
30 30
31 .gradient {
32 position: absolute;
33 z-index: -1;
34 left: 50vw;
35 border-radius: 50%;
36 background: radial-gradient(circle at center, var(--accent), var(--gradient));
37 width: var(--size);
38 height: var(--size);
39 top: var(--top);
40 transform: translate(-50%, -50%);
41 filter: blur(var(--blur)) opacity(var(--opacity));
42 animation: zoom_gradient 6s infinite alternate;
43 display: none;
44 max-height: 100%;
45 transition: max-height 0.25s ease-in;
46 }
47
48 .gradient.hidden {
49 max-height: 0;
50 transition: max-height 0.15s ease-out;
51 }
52
53 @media only screen and (min-width: 40em) {
54 body .gradient{
55 display: block;
56 }
57 }
58
59 @keyframes zoom_gradient {
60 0% {
61 transform: translate(-50%, -50%) scale(1);
62 }
63 100% {
64 transform: translate(-50%, -50%) scale(1.2);
65 }
66 }
67
68 31 /* Body and text color */
69 32 body {
70 33 background: var(--background);
@@ -188,16 +151,6 @@
188 151 .hidden {
189 152 display: none;
190 153 }
191
192 /* Animation for the gradient circle */
193 @keyframes zoom_gradient {
194 0% {
195 transform: translate(-50%, -50%) scale(1);
196 }
197 100% {
198 transform: translate(-50%, -50%) scale(1.5);
199 }
200 }
201 154 </style>
202 155 <link rel="stylesheet" href="/static/css/all.min.css">
203 156 <script>
@@ -221,8 +174,7 @@
221 174 </script>
222 175 </head>
223 176 <body>
224 <iframe id="background"></iframe>
225 <img id="image-feed" class="hidden" alt="Image Feed">
177 <iframe id="background" src="/background"></iframe>
226 178
227 179 <!-- Gradient Background Circle -->
228 180 <div class="gradient"></div>
@@ -343,65 +295,23 @@
343 295 const today = new Date().toJSON().slice(0, 10);
344 296 const max = 6;
345 297 const cache_id = Math.floor(Math.random() * max);
346 let prompt;
347 if (cache_id % 2 == 0) {
348 prompt = `Today is ${new Date().toJSON().slice(0, 10)}.
349 Create a single-page HTML screensaver reflecting the current season (based on the date).
350 Avoid using any text.`;
351 } else {
352 prompt = `Create a single-page HTML screensaver. Avoid using any text.`;
353 }
354 const response = await fetch(`/backend-api/v2/create?prompt=${prompt}&filter_markdown=html&cache=${cache_id}`);
355 const text = await response.text()
356 background.src = `data:text/html;charset=utf-8,${encodeURIComponent(text)}`;
357 const gradient = document.querySelector('.gradient');
358 gradient.classList.add('hidden');
359
360 const url = "https://image.pollinations.ai/feed";
361 const imageFeed = document.getElementById("image-feed");
362 const images = []
363 let es = null;
364 function initES() {
365 if (es == null || es.readyState == EventSource.CLOSED) {
366 const eventSource = new EventSource(url);
367 eventSource.onmessage = (event) => {
368 const data = JSON.parse(event.data);
369 if (data.nsfw || !data.nologo || data.width < 512 || !data.imageURL || data.isChild || data.status != "end_generating") {
370 return;
371 }
372 const lower = data.prompt.toLowerCase();
373 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"];
374 for (i in tags) {
375 if (lower.indexOf(tags[i]) != -1) {
376 console.log("Skipping image with tag: " + tags[i]);
377 console.debug("Skipping image:", data.imageURL);
378 return;
379 }
380 }
381 const landscape = window.innerWidth > window.innerHeight;
382 if (landscape && data.width > data.height) {
383 images.push(data.imageURL);
384 } else if (!landscape && data.width < data.height) {
385 images.push(data.imageURL);
386 }
387 };
388 eventSource.onerror = (event) => {
389 eventSource.close();
390 }
391 imageFeed.onerror = () => {
392 imageFeed.classList.add("hidden");
393 }
298 if (cache_id > 3) {
299 let prompt;
300 if (cache_id % 2 == 0) {
301 prompt = `Today is ${new Date().toJSON().slice(0, 10)}.
302 Create a single-page HTML screensaver reflecting the current season (based on the date).
303 Avoid using any text.`;
304 } else {
305 prompt = `Create a single-page HTML screensaver. Avoid using any text.`;
394 306 }
395 }
396 initES();
397 setInterval(() => {
398 if (images.length > 0) {
399 imageFeed.classList.remove("hidden");
400 imageFeed.src = images.shift();
401 } else if(imageFeed) {
402 initES();
307 const response = await fetch(`/backend-api/v2/create?prompt=${prompt}&filter_markdown=html&cache=${cache_id}`);
308 const text = await response.text()
309 if (text) {
310 background.src = `data:text/html;charset=utf-8,${encodeURIComponent(text)}`;
311 const gradient = document.querySelector('.gradient');
312 gradient.classList.add('hidden');
403 313 }
404 }, 7000);
314 }
405 315
406 316 const container = document.querySelector('.container');
407 317 const button = document.querySelector('.slide-button');
Modified g4f/gui/client/qrcode.html +23 -11
@@ -27,9 +27,11 @@
27 27
28 28 <h2>QR Code</h2>
29 29 <div id="qrcode"></div>
30 <p id="qrcode-status"></p>
30 31 <button id="generateQRCode">Generate QR Code</button>
31 32
32 33 <script type="module">
34 const conversation_id = "{{conversation_id}}";
33 35 import QrScanner from 'https://cdn.jsdelivr.net/npm/qr-scanner/qr-scanner.min.js';
34 36
35 37 function generate_uuid() {
@@ -64,18 +66,28 @@
64 66 }
65 67 });
66 68
67 localStorage.getItem('device_id') || localStorage.setItem('device_id', generate_uuid());
69 document.getElementById('generateQRCode').addEventListener('click', () => {
70 const chat_id = generate_uuid();
68 71
69 const qrcode = new QRCode(document.getElementById("qrcode"), {
70 text: JSON.stringify({
71 date: Math.floor(Date.now() / 1000),
72 device_id: localStorage.getItem('device_id')
73 }),
74 width: 128,
75 height: 128,
76 colorDark: "#000000",
77 colorLight: "#ffffff",
78 correctLevel: QRCode.CorrectLevel.H
72 const url = `/backend-api/v2/chat/${chat_id}`;
73 fetch(url, {
74 method: 'POST',
75 headers: {'content-type': 'application/json'},
76 body: localStorage.getItem(`conversation:${conversation_id}`)
77 });
78 const share = `${new URL(window.location.href).origin}/chat/${encodeURI(chat_id)}/${encodeURI(conversation_id)}`;
79 document.getElementById('qrcode-status').innerText = share;
80 document.getElementById("qrcode").innerHTML = '';
81 const qrcode = new QRCode(
82 document.getElementById("qrcode"),
83 share,
84 {
85 width: 128,
86 height: 128,
87 colorDark: "#000000",
88 colorLight: "#ffffff",
89 correctLevel: QRCode.CorrectLevel.H
90 });
79 91 });
80 92
81 93 const switchButton = document.getElementById('switchCamera');
Modified g4f/gui/client/static/css/style.css +2 -2
@@ -592,8 +592,8 @@ input-count .text {
592 592 display: none;
593 593 flex-direction: row;
594 594 height: 36px;
595 width: 120px;
596 margin-right: 130px;
595 width: 140px;
596 margin-right: 150px;
597 597 z-index: 1005;
598 598 padding: 10px;
599 599 margin-top: -4px;
Modified g4f/gui/client/static/js/chat.v1.js +55 -36
@@ -439,6 +439,18 @@ const register_message_buttons = async () => {
439 439 });
440 440 });
441 441
442 chatBody.querySelectorAll(".message .fa-qrcode").forEach(async (el) => {
443 if (el.dataset.click) {
444 return
445 }
446 el.dataset.click = true;
447 const message_el = get_message_el(el);
448 el.addEventListener("click", async () => {
449 iframe.src = `/qrcode/${window.conversation_id}#${message_el.dataset.index}`;
450 iframe_container.classList.remove("hidden");
451 });
452 });
453
442 454 chatBody.querySelectorAll(".message .reasoning_title").forEach(async (el) => {
443 455 if (el.dataset.click) {
444 456 return
@@ -1446,6 +1458,7 @@ const load_conversation = async (conversation, scroll=true) => {
1446 1458
1447 1459 add_buttons.push(`<button class="options_button">
1448 1460 <div>
1461 <span><i class="fa-solid fa-qrcode"></i></span>
1449 1462 <span><i class="fa-brands fa-whatsapp"></i></span>
1450 1463 <span><i class="fa-solid fa-volume-high"></i></i></span>
1451 1464 <span><i class="fa-solid fa-print"></i></span>
@@ -2038,7 +2051,7 @@ chatPrompt.addEventListener("input", function() {
2038 2051
2039 2052 window.addEventListener('load', async function() {
2040 2053 if (!window.chat_id) {
2041 return;
2054 return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
2042 2055 }
2043 2056 if (!window.conversation_id) {
2044 2057 window.conversation_id = window.chat_id;
@@ -2046,43 +2059,49 @@ window.addEventListener('load', async function() {
2046 2059 const response = await fetch(`/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
2047 2060 headers: {'accept': 'application/json'},
2048 2061 });
2049 if (response.ok) {
2050 let conversation = await response.json();
2051 if (!window.conversation_id || conversation.id == window.conversation_id) {
2052 window.conversation_id = conversation.id;
2053 await load_conversation(conversation);
2054 appStorage.setItem(
2055 `conversation:${conversation.id}`,
2056 JSON.stringify(conversation)
2057 );
2058 let refreshOnHide = true;
2059 document.addEventListener("visibilitychange", () => {
2060 if (document.hidden) {
2061 refreshOnHide = false;
2062 } else {
2063 refreshOnHide = true;
2064 }
2062 if (!response.ok) {
2063 return await load_conversation(JSON.parse(appStorage.getItem(`conversation:${window.conversation_id}`)));
2064 }
2065 let conversation = await response.json();
2066 if (!window.conversation_id || conversation.id == window.conversation_id) {
2067 window.conversation_id = conversation.id;
2068 await load_conversation(conversation);
2069 appStorage.setItem(
2070 `conversation:${conversation.id}`,
2071 JSON.stringify(conversation)
2072 );
2073 let refreshOnHide = true;
2074 document.addEventListener("visibilitychange", () => {
2075 if (document.hidden) {
2076 refreshOnHide = false;
2077 } else {
2078 refreshOnHide = true;
2079 }
2080 });
2081 var refreshIntervalId = setInterval(async () => {
2082 if (!window.chat_id) {
2083 clearInterval(refreshIntervalId);
2084 return;
2085 }
2086 if (!refreshOnHide) {
2087 return;
2088 }
2089 const response = await fetch(`/backend-api/v2/chat/${window.chat_id}`, {
2090 headers: {'accept': 'application/json', 'if-none-match': conversation.updated},
2065 2091 });
2066 return setInterval(async () => {
2067 if (!refreshOnHide || !window.chat_id) {
2068 return;
2069 }
2070 const response = await fetch(`/backend-api/v2/chat/${window.chat_id}`, {
2071 headers: {'accept': 'application/json', 'if-none-match': conversation.updated},
2072 });
2073 if (response.status == 200) {
2074 const new_conversation = await response.json();
2075 if (conversation.id == window.conversation_id && new_conversation.updated != conversation.updated) {
2076 conversation = new_conversation;
2077 appStorage.setItem(
2078 `conversation:${conversation.id}`,
2079 JSON.stringify(conversation)
2080 );
2081 await load_conversation(conversation);
2082 }
2092 if (response.status == 200) {
2093 const new_conversation = await response.json();
2094 if (conversation.id == window.conversation_id && new_conversation.updated != conversation.updated) {
2095 conversation = new_conversation;
2096 appStorage.setItem(
2097 `conversation:${conversation.id}`,
2098 JSON.stringify(conversation)
2099 );
2100 await load_conversation(conversation);
2083 2101 }
2084 }, 5000);
2085 }
2102 }
2103 }, 5000);
2104 return;
2086 2105 }
2087 2106 await safe_load_conversation(window.conversation_id, false);
2088 2107 });
Modified g4f/gui/server/backend_api.py +3 -3
@@ -69,9 +69,9 @@ class Backend_Api(Api):
69 69 def home():
70 70 return render_template('home.html')
71 71
72 @app.route('/qrcode', methods=['GET'])
73 def qrcode():
74 return render_template('qrcode.html')
72 @app.route('/qrcode/<conversation_id>', methods=['GET'])
73 def qrcode(conversation_id: str):
74 return render_template('qrcode.html', conversation_id=conversation_id)
75 75
76 76 @app.route('/backend-api/v2/models', methods=['GET'])
77 77 def jsonify_models(**kwargs):
Modified g4f/gui/server/website.py +8 -1
@@ -36,6 +36,10 @@ class Website:
36 36 'function': redirect_home,
37 37 'methods': ['GET', 'POST']
38 38 },
39 '/background': {
40 'function': self._background,
41 'methods': ['GET']
42 },
39 43 }
40 44
41 45 def _chat(self, conversation_id):
@@ -50,4 +54,7 @@ class Website:
50 54 return render_template('index.html', conversation_id=str(uuid.uuid4()))
51 55
52 56 def _settings(self):
53 return render_template('index.html', conversation_id=str(uuid.uuid4()))
57 return render_template('index.html', conversation_id=str(uuid.uuid4()))
58
59 def _background(self):
60 return render_template('background.html')