diff options
Diffstat (limited to '')
-rw-r--r-- | g4f/gui/client/html/index.html | 2 | ||||
-rw-r--r-- | g4f/gui/client/js/chat.v1.js | 60 | ||||
-rw-r--r-- | g4f/gui/webview.py | 24 |
3 files changed, 68 insertions, 18 deletions
diff --git a/g4f/gui/client/html/index.html b/g4f/gui/client/html/index.html index 85192d23..46a9c541 100644 --- a/g4f/gui/client/html/index.html +++ b/g4f/gui/client/html/index.html @@ -189,11 +189,13 @@ <label for="switch" title="Add the pages of the first 5 search results to the query."></label> <span class="about">Web Access</span> </div> + <!-- <div class="field"> <input type="checkbox" id="patch" /> <label for="patch" title="Enable create images with Bing."></label> <span class="about">Image Generator</span> </div> + --> <div class="field"> <input type="checkbox" id="history" /> <label for="history" title="To improve the reaction time or if you have trouble with large conversations."></label> diff --git a/g4f/gui/client/js/chat.v1.js b/g4f/gui/client/js/chat.v1.js index 8dd17275..8774fbf1 100644 --- a/g4f/gui/client/js/chat.v1.js +++ b/g4f/gui/client/js/chat.v1.js @@ -27,6 +27,13 @@ messageInput.addEventListener("focus", () => { document.documentElement.scrollTop = document.documentElement.scrollHeight; }); +appStorage = window.localStorage || { + setItem: (key, value) => self[key] = value, + getItem: (key) => self[key], + removeItem: (key) => delete self[key], + length: 0 +} + const markdown_render = (content) => { return markdown.render(content .replaceAll(/<!--.+-->/gm, "") @@ -67,10 +74,10 @@ const register_remove_message = async () => { } const delete_conversations = async () => { - for (let i = 0; i < localStorage.length; i++){ - let key = localStorage.key(i); + for (let i = 0; i < appStorage.length; i++){ + let key = appStorage.key(i); if (key.startsWith("conversation:")) { - localStorage.removeItem(key); + appStorage.removeItem(key); } } hide_sidebar(); @@ -238,7 +245,7 @@ const ask_gpt = async () => { jailbreak: jailbreak.options[jailbreak.selectedIndex].value, web_search: document.getElementById(`switch`).checked, provider: provider.options[provider.selectedIndex].value, - patch_provider: document.getElementById('patch').checked, + patch_provider: document.getElementById('patch')?.checked, messages: messages }); const headers = { @@ -261,6 +268,7 @@ const ask_gpt = async () => { body: body }); const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + let buffer = "" while (true) { const { value, done } = await reader.read(); if (done) break; @@ -268,7 +276,14 @@ const ask_gpt = async () => { if (!line) { continue; } - const message = JSON.parse(line); + let message; + try { + message = JSON.parse(buffer + line); + buffer = ""; + } catch { + buffer += line + continue; + } if (message.type == "content") { text += message.content; } else if (message.type == "provider") { @@ -389,7 +404,7 @@ const hide_option = async (conversation_id) => { }; const delete_conversation = async (conversation_id) => { - localStorage.removeItem(`conversation:${conversation_id}`); + appStorage.removeItem(`conversation:${conversation_id}`); const conversation = document.getElementById(`convo-${conversation_id}`); conversation.remove(); @@ -491,13 +506,13 @@ const load_conversation = async (conversation_id, scroll = true) => { async function get_conversation(conversation_id) { let conversation = await JSON.parse( - localStorage.getItem(`conversation:${conversation_id}`) + appStorage.getItem(`conversation:${conversation_id}`) ); return conversation; } async function save_conversation(conversation_id, conversation) { - localStorage.setItem( + appStorage.setItem( `conversation:${conversation_id}`, JSON.stringify(conversation) ); @@ -515,7 +530,7 @@ async function add_conversation(conversation_id, content) { title = content + ' '.repeat(19 - content.length) } - if (localStorage.getItem(`conversation:${conversation_id}`) == null) { + if (appStorage.getItem(`conversation:${conversation_id}`) == null) { await save_conversation(conversation_id, { id: conversation_id, title: title, @@ -577,9 +592,9 @@ const add_message = async (conversation_id, role, content, provider) => { const load_conversations = async () => { let conversations = []; - for (let i = 0; i < localStorage.length; i++) { - if (localStorage.key(i).startsWith("conversation:")) { - let conversation = localStorage.getItem(localStorage.key(i)); + for (let i = 0; i < appStorage.length; i++) { + if (appStorage.key(i).startsWith("conversation:")) { + let conversation = appStorage.getItem(appStorage.key(i)); conversations.push(JSON.parse(conversation)); } } @@ -654,13 +669,16 @@ sidebar_button.addEventListener("click", (event) => { const register_settings_localstorage = async () => { for (id of ["switch", "model", "jailbreak", "patch", "provider", "history"]) { element = document.getElementById(id); + if (!element) { + continue; + } element.addEventListener('change', async (event) => { switch (event.target.type) { case "checkbox": - localStorage.setItem(id, event.target.checked); + appStorage.setItem(id, event.target.checked); break; case "select-one": - localStorage.setItem(id, event.target.selectedIndex); + appStorage.setItem(id, event.target.selectedIndex); break; default: console.warn("Unresolved element type"); @@ -672,7 +690,9 @@ const register_settings_localstorage = async () => { const load_settings_localstorage = async () => { for (id of ["switch", "model", "jailbreak", "patch", "provider", "history"]) { element = document.getElementById(id); - value = localStorage.getItem(element.id); + if (!element || !(value = appStorage.getItem(element.id))) { + continue; + } if (value) { switch (element.type) { case "checkbox": @@ -712,12 +732,12 @@ const say_hello = async () => { // Theme storage for recurring viewers const storeTheme = function (theme) { - localStorage.setItem("theme", theme); + appStorage.setItem("theme", theme); }; // set theme when visitor returns const setTheme = function () { - const activeTheme = localStorage.getItem("theme"); + const activeTheme = appStorage.getItem("theme"); colorThemes.forEach((themeOption) => { if (themeOption.id === activeTheme) { themeOption.checked = true; @@ -751,8 +771,12 @@ function count_words(text) { return text.trim().match(/[\w\u4E00-\u9FA5]+/gu)?.length || 0; } +function count_chars(text) { + return text.match(/[^\s\p{P}]/gu)?.length || 0; +} + function count_words_and_tokens(text, model) { - return `(${count_words(text)} words, ${count_tokens(model, text)} tokens)`; + return `(${count_words(text)} words, ${count_chars(text)} chars, ${count_tokens(model, text)} tokens)`; } let countFocus = messageInput; diff --git a/g4f/gui/webview.py b/g4f/gui/webview.py new file mode 100644 index 00000000..5a4263dc --- /dev/null +++ b/g4f/gui/webview.py @@ -0,0 +1,24 @@ +import webview +from functools import partial +from platformdirs import user_config_dir + +from g4f.gui import run_gui +from g4f.gui.run import gui_parser +import g4f.version +import g4f.debug + +def run_webview(host: str = "0.0.0.0", port: int = 8080, debug: bool = True): + webview.create_window(f"g4f - {g4f.version.utils.current_version}", f"http://{host}:{port}/") + if debug: + g4f.debug.logging = True + webview.start( + partial(run_gui, host, port), + private_mode=False, + storage_path=user_config_dir("g4f-webview"), + debug=debug + ) + +if __name__ == "__main__": + parser = gui_parser() + args = parser.parse_args() + run_webview(args.host, args.port, args.debug)
\ No newline at end of file |