Better translation support, adding dialogs

This commit is contained in:
jeffser 2024-05-19 19:06:11 -06:00
parent 15fdd0ae1a
commit 6fbf6e139b

View File

@ -21,7 +21,7 @@ import gi
gi.require_version('GtkSource', '5') gi.require_version('GtkSource', '5')
gi.require_version('GdkPixbuf', '2.0') gi.require_version('GdkPixbuf', '2.0')
from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf from gi.repository import Adw, Gtk, Gdk, GLib, GtkSource, Gio, GdkPixbuf
import json, requests, threading, os, re, base64, sys import json, requests, threading, os, re, base64, sys, gettext, locale
from io import BytesIO from io import BytesIO
from PIL import Image from PIL import Image
from datetime import datetime from datetime import datetime
@ -32,11 +32,19 @@ from .available_models import available_models
class AlpacaWindow(Adw.ApplicationWindow): class AlpacaWindow(Adw.ApplicationWindow):
config_dir = os.path.join(os.getenv("XDG_CONFIG_HOME"), "/", os.path.expanduser("~/.var/app/com.jeffser.Alpaca/config")) config_dir = os.path.join(os.getenv("XDG_CONFIG_HOME"), "/", os.path.expanduser("~/.var/app/com.jeffser.Alpaca/config"))
__gtype_name__ = 'AlpacaWindow' __gtype_name__ = 'AlpacaWindow'
localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'locale')
locale.setlocale(locale.LC_ALL, '')
gettext.bindtextdomain('com.jeffser.Alpaca', localedir)
gettext.textdomain('com.jeffser.Alpaca')
_ = gettext.gettext
#Variables #Variables
ollama_url = None ollama_url = None
local_models = [] local_models = []
pulling_models = {} pulling_models = {}
chats = {"chats": {"New Chat": {"messages": []}}, "selected_chat": "New Chat"} chats = {"chats": {_("New Chat"): {"messages": []}}, "selected_chat": "New Chat"}
attached_image = {"path": None, "base64": None} attached_image = {"path": None, "base64": None}
first_time_setup = False first_time_setup = False
@ -75,21 +83,21 @@ class AlpacaWindow(Adw.ApplicationWindow):
toast_messages = { toast_messages = {
"error": [ "error": [
"An error occurred", _("An error occurred"),
"Failed to connect to server", _("Failed to connect to server"),
"Could not list local models", _("Could not list local models"),
"Could not delete model", _("Could not delete model"),
"Could not pull model", _("Could not pull model"),
"Cannot open image", _("Cannot open image"),
"Cannot delete chat because it's the only one left" _("Cannot delete chat because it's the only one left")
], ],
"info": [ "info": [
"Please select a model before chatting", _("Please select a model before chatting"),
"Chat cannot be cleared while receiving a message" _("Chat cannot be cleared while receiving a message")
], ],
"good": [ "good": [
"Model deleted successfully", _("Model deleted successfully"),
"Model pulled successfully" _("Model pulled successfully")
] ]
} }
@ -407,13 +415,13 @@ class AlpacaWindow(Adw.ApplicationWindow):
response = stream_post(f"{self.ollama_url}/api/pull", data=json.dumps(data), callback=lambda data, model_name=f"{model_name}:{tag}": self.pull_model_update(data, model_name)) response = stream_post(f"{self.ollama_url}/api/pull", data=json.dumps(data), callback=lambda data, model_name=f"{model_name}:{tag}": self.pull_model_update(data, model_name))
GLib.idle_add(self.update_list_local_models) GLib.idle_add(self.update_list_local_models)
if response['status'] == 'ok': if response['status'] == 'ok':
GLib.idle_add(self.show_notification, "Task Complete", f"Model '{model_name}:{tag}' pulled successfully.", True, Gio.ThemedIcon.new("emblem-ok-symbolic")) GLib.idle_add(self.show_notification, _("Task Complete"), _("Model '{}' pulled successfully.").format(f"{model_name}:{tag}"), True, Gio.ThemedIcon.new("emblem-ok-symbolic"))
GLib.idle_add(self.show_toast, "good", 1, self.manage_models_overlay) GLib.idle_add(self.show_toast, "good", 1, self.manage_models_overlay)
GLib.idle_add(self.pulling_models[f"{model_name}:{tag}"].get_parent().remove, self.pulling_models[f"{model_name}:{tag}"]) GLib.idle_add(self.pulling_models[f"{model_name}:{tag}"].get_parent().remove, self.pulling_models[f"{model_name}:{tag}"])
del self.pulling_models[f"{model_name}:{tag}"] del self.pulling_models[f"{model_name}:{tag}"]
else: else:
GLib.idle_add(self.show_notification, "Pull Model Error", f"Failed to pull model '{model_name}:{tag}' due to network error.", True, Gio.ThemedIcon.new("dialog-error-symbolic")) GLib.idle_add(self.show_notification, _("Pull Model Error"), _("Failed to pull model '{}' due to network error.").format(f"{model_name}:{tag}"), True, Gio.ThemedIcon.new("dialog-error-symbolic"))
GLib.idle_add(self.show_toast, "error", 4, self.connection_overlay) GLib.idle_add(self.show_toast, "error", 4, self.connection_overlay)
GLib.idle_add(self.manage_models_dialog.close) GLib.idle_add(self.manage_models_dialog.close)
GLib.idle_add(self.show_connection_dialog, True) GLib.idle_add(self.show_connection_dialog, True)
@ -425,12 +433,12 @@ class AlpacaWindow(Adw.ApplicationWindow):
def stop_pull_model_dialog(self, model_name): def stop_pull_model_dialog(self, model_name):
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading="Stop Model", heading=_("Stop Model"),
body=f"Are you sure you want to stop pulling '{model_name}'?", body=_("Are you sure you want to stop pulling '{}'?").format(model_name),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("stop", "Stop") dialog.add_response("stop", _("Stop"))
dialog.set_response_appearance("stop", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("stop", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.choose( dialog.choose(
parent = self.manage_models_dialog, parent = self.manage_models_dialog,
@ -463,12 +471,12 @@ class AlpacaWindow(Adw.ApplicationWindow):
def model_delete_button_activate(self, model_name): def model_delete_button_activate(self, model_name):
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading="Delete Model", heading=_("Delete Model"),
body=f"Are you sure you want to delete '{model_name}'?", body=_("Are you sure you want to delete '{}'?").format(model_name),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("delete", "Delete") dialog.add_response("delete", _("Delete"))
dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.choose( dialog.choose(
parent = self.manage_models_dialog, parent = self.manage_models_dialog,
@ -485,13 +493,13 @@ class AlpacaWindow(Adw.ApplicationWindow):
model=tag_list model=tag_list
) )
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading="Pull Model", heading=_("Pull Model"),
body=f"Please select a tag to pull '{model_name}'", body=_("Please select a tag to pull '{}'").format(model_name),
extra_child=tag_drop_down, extra_child=tag_drop_down,
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("pull", "Pull") dialog.add_response("pull", _("Pull"))
dialog.set_response_appearance("pull", Adw.ResponseAppearance.SUGGESTED) dialog.set_response_appearance("pull", Adw.ResponseAppearance.SUGGESTED)
dialog.choose( dialog.choose(
parent = self.manage_models_dialog, parent = self.manage_models_dialog,
@ -551,7 +559,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
self.chats["chats"][self.chats["selected_chat"]]["messages"] = [] self.chats["chats"][self.chats["selected_chat"]]["messages"] = []
def clear_chat_dialog_response(self, dialog, task): def clear_chat_dialog_response(self, dialog, task):
if dialog.choose_finish(task) == "empty": if dialog.choose_finish(task) == "clear":
self.clear_chat() self.clear_chat()
self.save_history() self.save_history()
@ -560,13 +568,13 @@ class AlpacaWindow(Adw.ApplicationWindow):
self.show_toast("info", 1, self.main_overlay) self.show_toast("info", 1, self.main_overlay)
return return
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Clear Chat", heading=_("Clear Chat"),
body=f"Are you sure you want to clear the chat?", body=_("Are you sure you want to clear the chat?"),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("empty", "Empty") dialog.add_response("clear", _("Clear"))
dialog.set_response_appearance("empty", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("clear", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.choose( dialog.choose(
parent = self, parent = self,
cancellable = None, cancellable = None,
@ -621,13 +629,13 @@ class AlpacaWindow(Adw.ApplicationWindow):
else: self.first_time_setup = False else: self.first_time_setup = False
return return
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Save Changes?", heading=_("Save Changes?"),
body=f"Do you want to save the URL change?", body=_("Do you want to save the URL change?"),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("discard", "Discard") dialog.add_response("discard", _("Discard"))
dialog.add_response("save", "Save") dialog.add_response("save", _("Save"))
dialog.set_response_appearance("discard", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("discard", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.set_response_appearance("save", Adw.ResponseAppearance.SUGGESTED) dialog.set_response_appearance("save", Adw.ResponseAppearance.SUGGESTED)
dialog.choose( dialog.choose(
@ -641,8 +649,6 @@ class AlpacaWindow(Adw.ApplicationWindow):
except: return except: return
try: try:
self.attached_image["path"] = file.get_path() self.attached_image["path"] = file.get_path()
'''with open(self.attached_image["path"], "rb") as image_file:
self.attached_image["base64"] = base64.b64encode(image_file.read()).decode("utf-8")'''
with Image.open(self.attached_image["path"]) as img: with Image.open(self.attached_image["path"]) as img:
width, height = img.size width, height = img.size
max_size = 240 max_size = 240
@ -672,12 +678,12 @@ class AlpacaWindow(Adw.ApplicationWindow):
def open_image(self, button): def open_image(self, button):
if "destructive-action" in button.get_css_classes(): if "destructive-action" in button.get_css_classes():
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Remove Image?", heading=_("Remove Image?"),
body=f"Are you sure you want to remove image?", body=_("Are you sure you want to remove image?"),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("remove", "Remove") dialog.add_response("remove", _("Remove"))
dialog.set_response_appearance("remove", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("remove", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.choose( dialog.choose(
parent = self, parent = self,
@ -699,12 +705,12 @@ class AlpacaWindow(Adw.ApplicationWindow):
self.show_toast("error", 6, self.main_overlay) self.show_toast("error", 6, self.main_overlay)
return return
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Delete Chat", heading=_("Delete Chat"),
body=f"Are you sure you want to delete '{chat_name}'?", body=_("Are you sure you want to delete '{}'?").format(chat_name),
close_response="cancel" close_response="cancel"
) )
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("delete", "Delete") dialog.add_response("delete", _("Delete"))
dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE) dialog.set_response_appearance("delete", Adw.ResponseAppearance.DESTRUCTIVE)
dialog.choose( dialog.choose(
parent = self, parent = self,
@ -730,14 +736,14 @@ class AlpacaWindow(Adw.ApplicationWindow):
css_classes = ["error"] if error else None css_classes = ["error"] if error else None
) )
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Rename Chat", heading=_("Rename Chat"),
body=body, body=body,
extra_child=entry, extra_child=entry,
close_response="cancel" close_response="cancel"
) )
entry.connect("activate", lambda entry, dialog=dialog, old_chat_name=chat_name: self.chat_rename(dialog=dialog, old_chat_name=old_chat_name, entry=entry)) entry.connect("activate", lambda entry, dialog=dialog, old_chat_name=chat_name: self.chat_rename(dialog=dialog, old_chat_name=old_chat_name, entry=entry))
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("rename", "Rename") dialog.add_response("rename", _("Rename"))
dialog.set_response_appearance("rename", Adw.ResponseAppearance.SUGGESTED) dialog.set_response_appearance("rename", Adw.ResponseAppearance.SUGGESTED)
dialog.choose( dialog.choose(
parent = self, parent = self,
@ -750,7 +756,7 @@ class AlpacaWindow(Adw.ApplicationWindow):
chat_name = entry.get_text() chat_name = entry.get_text()
if chat_name and (not task or dialog.choose_finish(task) == "create"): if chat_name and (not task or dialog.choose_finish(task) == "create"):
dialog.force_close() dialog.force_close()
if chat_name in self.chats["chats"]: self.chat_new_dialog(f"The name '{chat_name}' is already in use", True) if chat_name in self.chats["chats"]: self.chat_new_dialog(_("The name '{}' is already in use").format(chat_name), True)
else: else:
self.chats["chats"][chat_name] = {"messages": []} self.chats["chats"][chat_name] = {"messages": []}
self.chats["selected_chat"] = chat_name self.chats["selected_chat"] = chat_name
@ -763,14 +769,14 @@ class AlpacaWindow(Adw.ApplicationWindow):
css_classes = ["error"] if error else None css_classes = ["error"] if error else None
) )
dialog = Adw.AlertDialog( dialog = Adw.AlertDialog(
heading=f"Create Chat", heading=_("Create Chat"),
body=body, body=body,
extra_child=entry, extra_child=entry,
close_response="cancel" close_response="cancel"
) )
entry.connect("activate", lambda entry, dialog=dialog: self.chat_new(dialog=dialog, entry=entry)) entry.connect("activate", lambda entry, dialog=dialog: self.chat_new(dialog=dialog, entry=entry))
dialog.add_response("cancel", "Cancel") dialog.add_response("cancel", _("Cancel"))
dialog.add_response("create", "Create") dialog.add_response("create", _("Create"))
dialog.set_response_appearance("rename", Adw.ResponseAppearance.SUGGESTED) dialog.set_response_appearance("rename", Adw.ResponseAppearance.SUGGESTED)
dialog.choose( dialog.choose(
parent = self, parent = self,