summaryrefslogtreecommitdiffstats
path: root/g4f/gui
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--g4f/gui/client/html/index.html2
-rw-r--r--g4f/gui/client/js/chat.v1.js60
-rw-r--r--g4f/gui/webview.py24
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 + '&nbsp;'.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