From c186b71bf52d53ea79abaf0a12a767312d63a102 Mon Sep 17 00:00:00 2001 From: Adriano Amalfi Date: Sun, 26 May 2024 00:55:17 +0200 Subject: [PATCH 01/13] =?UTF-8?q?feat=20=E2=9C=A8:=20Add=20Italian=20Langu?= =?UTF-8?q?age?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/locale/it/common.json | 88 +++++++++ src/assets/locale/it/knowledge.json | 42 ++++ src/assets/locale/it/option.json | 12 ++ src/assets/locale/it/playground.json | 29 +++ src/assets/locale/it/settings.json | 286 +++++++++++++++++++++++++++ src/assets/locale/it/sidepanel.json | 7 + src/i18n/index.ts | 2 + src/i18n/lang/it.ts | 15 ++ src/i18n/support-language.ts | 4 + 9 files changed, 485 insertions(+) create mode 100644 src/assets/locale/it/common.json create mode 100644 src/assets/locale/it/knowledge.json create mode 100644 src/assets/locale/it/option.json create mode 100644 src/assets/locale/it/playground.json create mode 100644 src/assets/locale/it/settings.json create mode 100644 src/assets/locale/it/sidepanel.json create mode 100644 src/i18n/lang/it.ts diff --git a/src/assets/locale/it/common.json b/src/assets/locale/it/common.json new file mode 100644 index 0000000..df4f4c3 --- /dev/null +++ b/src/assets/locale/it/common.json @@ -0,0 +1,88 @@ +{ + "pageAssist": "Page Assist", + "selectAModel": "Seleziona un Modello", + "save": "Salva", + "saved": "Salvato", + "cancel": "Annulla", + "retry": "Riprova", + "share": { + "tooltip": { + "share": "Condividi" + }, + "modal": { + "title": "Condividi Collegamento alla Chat" + }, + "form": { + "defaultValue": { + "name": "Anonimo", + "title": "Chat Senza Titolo" + }, + "title": { + "label": "Titolo della Chat", + "placeholder": "Inserisci il Titolo della Chat", + "required": "Titolo della Chat obbligatorio" + }, + "name": { + "label": "Il tuo Nome", + "placeholder": "Inserisci il tuo Nome", + "required": "Nome obbligatorio" + }, + "btn": { + "save": "Genera Link", + "saving": "Sto generando il Link..." + } + }, + "notification": { + "successGenerate": "Link copiato negli appunti", + "failGenerate": "Impossibile generare il link" + } + }, + "copyToClipboard": "Copia negli Appunti", + "webSearch": "Ricerca nel Web", + "regenerate": "Rigenera", + "edit": "Modifica", + "saveAndSubmit": "Salva e Invia", + "editMessage": { + "placeholder": "Scrivi un messaggio..." + }, + "submit": "Invia", + "noData": "Nessun Dato", + "noHistory": "Nessuna Cronologia Chat", + "chatWithCurrentPage": "Chatta con la Pagina Corrente", + "beta": "Beta", + "tts": "Leggi ad Alta Voce", + "currentChatModelSettings": "Impostazioni del Modello Corrente", + "modelSettings": { + "label": "Impostazioni del Modello", + "description": "Imposta le opzioni del modello globalmente per tutte le chat", + "form": { + "keepAlive": { + "label": "Keep Alive", + "help": "Imposta il tempo per cui il modello deve rimanere caricato in memoria (default: 5m)", + "placeholder": "Inserisci la durata del Keep Alive (e.g. 5m, 10m, 1h)" + }, + "temperature": { + "label": "Temperatura", + "placeholder": "Inserisci la Temperatura (e.g. 0.7, 1.0)" + }, + "numCtx": { + "label": "Dimensione del Contesto", + "placeholder": "Inserisci la Dimensione del Contesto (default: 2048)" + }, + "seed": { + "label": "Seed", + "placeholder": "Inserisci il Valore Seed (e.g. 1234)", + "help": "Riproducibilità dell'output del modello" + }, + "topK": { + "label": "Top K", + "placeholder": "Inserisci il Valore Top K (e.g. 40, 100)" + }, + "topP": { + "label": "Top P", + "placeholder": "Inserisci il Valore Top P (e.g. 0.9, 0.95)" + } + }, + "advanced": "Altre Impostazioni del Modello" + } +} diff --git a/src/assets/locale/it/knowledge.json b/src/assets/locale/it/knowledge.json new file mode 100644 index 0000000..867cd84 --- /dev/null +++ b/src/assets/locale/it/knowledge.json @@ -0,0 +1,42 @@ +{ + "addBtn": "Aggiungi nuova Knowledge Base", + "columns": { + "title": "Titolo", + "status": "Stato", + "embeddings": "Modello di Embedding", + "createdAt": "Creato da", + "action": "Azioni" + }, + "expandedColumns": { + "name": "Nome" + }, + "tooltip": { + "delete": "Elimina" + }, + "confirm": { + "delete": "Sei sicuro di voler eliminare questa Knowledge Base?" + }, + "deleteSuccess": "Knowledge Base eliminata correttamente", + "status": { + "pending": "In attesa", + "finished": "Completato", + "processing": "In corso" + }, + "addKnowledge": "Aggiungi Knowledge Base", + "form": { + "title": { + "label": "Titolo Knowledge Base", + "placeholder": "Inserisci il titolo della Knowledge Base", + "required": "Il Titolo è obbligatorio" + }, + "uploadFile": { + "label": "Carica File", + "uploadText": "Trascina un file qui or scegli upload", + "uploadHint": "Tipi di file supportati: .pdf, .csv, .txt, .md, .docx", + "required": "File è obbligatorio" + }, + "submit": "Invia", + "success": "Knowledge Base aggiunta correttamente" + }, + "noEmbeddingModel": "Aggiungi prima un modello dalla pagina di impostazione di Ollama" +} \ No newline at end of file diff --git a/src/assets/locale/it/option.json b/src/assets/locale/it/option.json new file mode 100644 index 0000000..6fcd098 --- /dev/null +++ b/src/assets/locale/it/option.json @@ -0,0 +1,12 @@ +{ + "newChat": "Nuova Chat", + "selectAPrompt": "Scegli un Prompt", + "githubRepository": "GitHub Repository", + "settings": "Impsotazioni", + "sidebarTitle": "Cronologia Chat", + "error": "Errore", + "somethingWentWrong": "Qualcosa è andato storto", + "validationSelectModel": "Scegliere un modello per continuare", + "deleteHistoryConfirmation": "Sei sicuro che vuoi eliminare la cronologia?", + "editHistoryTitle": "Inserisci un nuovo titolo" +} \ No newline at end of file diff --git a/src/assets/locale/it/playground.json b/src/assets/locale/it/playground.json new file mode 100644 index 0000000..44972eb --- /dev/null +++ b/src/assets/locale/it/playground.json @@ -0,0 +1,29 @@ +{ + "ollamaState": { + "searching": "Sto cercando Ollama 🦙", + "running": "Ollama è attivo 🦙", + "notRunning": "Impossibile connettersi a Ollama 🦙", + "connectionError": "C'è stato un problema di connessione. Controlla la documentazione per investigare." + }, + "formError": { + "noModel": "Seleziona un modello", + "noEmbeddingModel": "Imposta un modello di embedding da Impostazioni > Ollama" + }, + "form": { + "textarea": { + "placeholder": "Scrivi un messaggio..." + }, + "webSearch": { + "on": "Attivo", + "off": "Disattivato" + } + }, + "tooltip": { + "searchInternet": "Cerca su Internet", + "speechToText": "Speech to Text", + "uploadImage": "Carica immagine", + "stopStreaming": "Ferma lo Streaming", + "knowledge": "Conoscenza" + }, + "sendWhenEnter": "Invia subito dopo Enter" +} \ No newline at end of file diff --git a/src/assets/locale/it/settings.json b/src/assets/locale/it/settings.json new file mode 100644 index 0000000..7ab29d8 --- /dev/null +++ b/src/assets/locale/it/settings.json @@ -0,0 +1,286 @@ +{ + "generalSettings": { + "title": "Impostazioni Generali", + "settings": { + "heading": "Impostazioni Web UI", + "speechRecognitionLang": { + "label": "Lingua per il riconoscimento vocale", + "placeholder": "Scegli una lingua" + }, + "language": { + "label": "Lingua", + "placeholder": "Scegli una lingua" + }, + "darkMode": { + "label": "Cambia il Tema", + "options": { + "light": "Chiaro", + "dark": "Scuro" + } + }, + "copilotResumeLastChat": { + "label": "Riprendi l'ultima chat quando apri il Pannello Laterale (Copilot)" + }, + "hideCurrentChatModelSettings": { + "label": "Nascondi le impostazioni correnti del modello Chat" + } + }, + "webSearch": { + "heading": "Gestione ricerca Web", + "searchMode": { + "label": "Effettua ricerca web Internet semplice" + }, + "provider": { + "label": "Motori di ricerca", + "placeholder": "Scegli un motore di ricerca" + }, + "totalSearchResults": { + "label": "Risultati della ricerca", + "placeholder": "Inserisci il totale delle ricerche" + } + }, + "system": { + "heading": "Impostazioni di Sistema", + "deleteChatHistory": { + "label": "Elimina cronologia Chat", + "button": "Elimina", + "confirm": "Sei sicuro che vuoi eliminare la tua cronologia delle chat? Questa azione non può essere annullata." + }, + "export": { + "label": "Esporta la cronologia Chat, Base di Conoscenza, e Prompts", + "button": "Esporta Dati", + "success": "Esportato con Successo" + }, + "import": { + "label": "Imposta la cronologia Chat, Base di Conoscenza, e Prompts", + "button": "Importa Dati", + "success": "Importato con Successo", + "error": "Errore Importazione" + } + }, + "tts": { + "heading": "Impostazioni Text-to-Speech", + "ttsEnabled": { + "label": "Abilita Text-to-Speech" + }, + "ttsProvider": { + "label": "Text-to-Speech Provider", + "placeholder": "Seleziona un provider" + }, + "ttsVoice": { + "label": "Text-to-Speech Voce", + "placeholder": "Seleziona una voce" + }, + "ssmlEnabled": { + "label": "Abilita SSML (Speech Synthesis Markup Language)" + } + } + }, + "manageModels": { + "title": "Gestione Modelli", + "addBtn": "Aggiungi un nuovo Modello", + "columns": { + "name": "Nome", + "digest": "Digest", + "modifiedAt": "Modificato il", + "size": "Dimensioni", + "actions": "Azioni" + }, + "expandedColumns": { + "parentModel": "Modello Padre", + "format": "Formato", + "family": "Famiglia", + "parameterSize": "Numero di Parametri", + "quantizationLevel": "Livello di Quantizzazione" + }, + "tooltip": { + "delete": "Elimina Modello", + "repull": "Ri-Scarica Modello" + }, + "confirm": { + "delete": "Sei sicuro di voler eliminare questo modello?", + "repull": "Se sicuro che vuoi ri-scaricare questo modello?" + }, + "modal": { + "title": "Aggiungi Nuovo Modello", + "placeholder": "Inserisci il Nome Modello", + "pull": "Scarico del Modello" + }, + "notification": { + "pullModel": "Scarico del Modello", + "pullModelDescription": "Scaricando il modello {{modelName}}. Per ulteriori dettagli visualizza l'icona dell'estensione.", + "success": "Completato", + "error": "Errore", + "successDescription": "Scarico del modello completato", + "successDeleteDescription": "Eliminazione del modello completato", + "someError": "Qualcosa è andato storto. Riprova più tardi" + } + }, + "managePrompts": { + "title": "Gestisci Prompts", + "addBtn": "Aggiungi nuovo Prompt", + "option1": "Normale", + "option2": "RAG", + "questionPrompt": "Question Prompt", + "columns": { + "title": "Titolo", + "prompt": "Prompt", + "type": "Tipo di Prompt", + "actions": "Azioni" + }, + "systemPrompt": "Prompt di Sistema", + "quickPrompt": "Prompt Veloce", + "tooltip": { + "delete": "Elimina Prompt", + "edit": "Modifica Prompt" + }, + "confirm": { + "delete": "Sei sicuro di voler eliminare questo prompt? L'azione non può essere annullata." + }, + "modal": { + "addTitle": "Aggiungi Nuovo Prompt", + "editTitle": "Modifica Prompt" + }, + "form": { + "title": { + "label": "Titolo", + "placeholder": "I Miei Prompt", + "required": "Inserisci il Titolo" + }, + "prompt": { + "label": "Prompt", + "placeholder": "Inserisci Prompt", + "required": "Scrivi il prompt", + "help": "Puoi usare {key} come variabile nel tuo prompt." + }, + "isSystem": { + "label": "Prompt di Sistema" + }, + "btnSave": { + "saving": "Aggiungendo Prompt...", + "save": "Aggiungi Prompt" + }, + "btnEdit": { + "saving": "Aggiornando Prompt...", + "save": "Aggiorna Prompt" + } + }, + "notification": { + "addSuccess": "Prompt Aggiunto", + "addSuccessDesc": "Il Prompt è stato aggiunto correttamente", + "error": "Errore", + "someError": "Qualcosa è andato storto. Riprova più tardi", + "updatedSuccess": "Prompt Aggiornato", + "updatedSuccessDesc": "Il Prompt è stato aggiornato correttmante", + "deletedSuccess": "Prompt Eliminato", + "deletedSuccessDesc": "Il Prompt è stato eliminato correttamente" + } + }, + "manageShare": { + "title": "Gestione Condivisioni", + "heading": "Configura l'URL della Pagina di Condivisione", + "form": { + "url": { + "label": "URL Pagina di Condivisione", + "placeholder": "Inserisci URL Pagina di Condivisione", + "required": "Inserisci l'url della pagina di condivisione!", + "help": "Per ragioni di privacy, tu puoi ospitare in self-host la paginacon il seguente URL. Leggi altro." + } + }, + "webshare": { + "heading": "Condivisioni Web", + "columns": { + "title": "Titolo", + "url": "URL", + "actions": "Azioni" + }, + "tooltip": { + "delete": "Elimina Condivisione" + }, + "confirm": { + "delete": "Sei sicuro che vuoi eliminare questa condivisione? L'azione non può essere annullata." + }, + "label": "Gestione Condivisioni", + "description": "Abilita o Disattiva la funzionalità di condivisione" + }, + "notification": { + "pageShareSuccess": " URL di condivisione aggiornato correttamente", + "someError": "Qualcosa è andato storto. Riprova più tardi", + "webShareDeleteSuccess": "Condivisione eliminata correttamente" + } + }, + "ollamaSettings": { + "title": "Impostazioni Ollama", + "heading": "Configura Ollama", + "settings": { + "ollamaUrl": { + "label": "Ollama URL", + "placeholder": "Inserici l'URL di Ollama" + }, + "ragSettings": { + "label": "Impostazioni RAG", + "model": { + "label": "Modello di Embedding", + "required": "Scegliere il modello", + "help": "E' raccomandato l'uso di modelli come `nomic-embed-text`.", + "placeholder": "Seleziona un modello" + }, + "chunkSize": { + "label": "Dimensione del Blocco (Chunk Size)", + "placeholder": "Inserisci la Dimensione del Blocco (Chunk Size)", + "required": "Inserisci la Dimensione del Blocco (chunk size)" + }, + "chunkOverlap": { + "label": "Sovrapposizione del Blocco (Chunk Overlap)", + "placeholder": "Inserisci la Sovrapposizione del Blocco (Chunk Overlap)", + "required": "Inserisci la Sovrapposizione del Blocco" + } + }, + "prompt": { + "label": "Configura il Prompt RAG", + "option1": "Normale", + "option2": "Web", + "alert": "La configurazione del prompt di sistema qui è deprecato. Usa la sezione Gestione Prompt per aggiungere o modificare i prompts.Questa sezione sarà eliminata nelle prossime release", + "systemPrompt": "Prompt di Sistema", + "systemPromptPlaceholder": "Inserisci il Prompt di Sistema", + "webSearchPrompt": "Prompt per la Ricerca Web", + "webSearchPromptHelp": "Non rimuovere `{search_results}` dal prompt.", + "webSearchPromptError": "Inserisci il prompt per la ricerca web", + "webSearchPromptPlaceholder": "Imserosco il Prompt per la Ricerca Web", + "webSearchFollowUpPrompt": "Prompt di Follow Up sulla Ricerca Web", + "webSearchFollowUpPromptHelp": "Non rimuovere `{chat_history}` e `{question}` dal prompt.", + "webSearchFollowUpPromptError": "Inserisci il Prompt di Follow Up della Ricerca Web!", + "webSearchFollowUpPromptPlaceholder": "I tuoi Prompt di Follow Up delle Ricerche Web" + }, + "advanced": { + "label": "Configurazione Avanzata Ollama URL", + "urlRewriteEnabled": { + "label": "Abilita o Disabilita l'URL di Origine Personalizzato" + }, + "rewriteUrl": { + "label": "URL di Origine Personalizzato", + "placeholder": "Inserisci URL di Origine Personalizzato" + }, + "help": "Se hai problemi di connessione con Ollama su Page Assist, puoi configurare un URL di origine personalizzato. Per saperne di più sulla configurazione, clicca qui." + } + } + }, + "manageSearch": { + "title": "Gestisci Ricerca Web", + "heading": "Configura Ricerca Web" + }, + "about": { + "title": "Informazioni", + "heading": "Informazioni", + "chromeVersion": "Versione di Page Assist", + "ollamaVersion": "Versione di Ollama", + "support": "Puoi supportare il progetto Page Assist donando o sponsorizzando attraverso le seguenti piattaforme:", + "koFi": "Supporta su Ko-fi", + "githubSponsor": "Sponsorizza su GitHub", + "githubRepo": "Repository GitHub" + }, + "manageKnowledge": { + "title": "Gestisci Conoscenza", + "heading": "Configura Base di Conoscenza" + } +} diff --git a/src/assets/locale/it/sidepanel.json b/src/assets/locale/it/sidepanel.json new file mode 100644 index 0000000..89c9ac2 --- /dev/null +++ b/src/assets/locale/it/sidepanel.json @@ -0,0 +1,7 @@ +{ + "tooltip": { + "embed": "L'inserimento della pagina potrebbe richiedere alcuni minuti. Attendere prego...", + "clear": "Cancella la cronologia della chat", + "history": "Cronologia della chat" + } +} \ No newline at end of file diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 3a28a6e..d0b7317 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -5,6 +5,7 @@ import { ru } from "./lang/ru"; import { ml } from "./lang/ml"; import { zh } from "./lang/zh"; import { ja } from "./lang/ja"; +import { it } from "./lang/it"; import LanguageDetector from 'i18next-browser-languagedetector'; i18n @@ -13,6 +14,7 @@ i18n .init({ resources: { en: en, + "it": it, ml: ml, "zh-CN": zh, ru: ru, diff --git a/src/i18n/lang/it.ts b/src/i18n/lang/it.ts new file mode 100644 index 0000000..6be87df --- /dev/null +++ b/src/i18n/lang/it.ts @@ -0,0 +1,15 @@ +import option from "@/assets/locale/it/option.json"; +import playground from "@/assets/locale/it/playground.json"; +import common from "@/assets/locale/it/common.json"; +import sidepanel from "@/assets/locale/it/sidepanel.json"; +import settings from "@/assets/locale/it/settings.json"; +import knowledge from "@/assets/locale/it/knowledge.json"; + +export const it = { + option, + playground, + common, + sidepanel, + settings, + knowledge +} \ No newline at end of file diff --git a/src/i18n/support-language.ts b/src/i18n/support-language.ts index af7db40..a6e3f87 100644 --- a/src/i18n/support-language.ts +++ b/src/i18n/support-language.ts @@ -4,6 +4,10 @@ export const supportLanguage = [ label: "English", value: "en" }, + { + label: "Italiano", + value: "it" + }, { label: "Russian", value: "ru" From 533e38c287a866a7b8713a89f0629b491020675b Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Mon, 27 May 2024 21:09:16 +0530 Subject: [PATCH 02/13] chore: Update Layout component to truncate long model names --- src/components/Layouts/Layout.tsx | 4 ++-- src/libs/process-knowledge.ts | 16 +++++----------- wxt.config.ts | 2 +- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index 0fe494f..2a4f5e4 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -141,9 +141,9 @@ export default function OptionLayout({ label: ( + className="flex flex-row gap-3 items-center truncate"> - {model.name} + {model.name} ), value: model.model diff --git a/src/libs/process-knowledge.ts b/src/libs/process-knowledge.ts index e44bfa0..f933d85 100644 --- a/src/libs/process-knowledge.ts +++ b/src/libs/process-knowledge.ts @@ -2,7 +2,8 @@ import { getKnowledgeById, updateKnowledgeStatus } from "@/db/knowledge" import { PageAssistPDFUrlLoader } from "@/loader/pdf-url" import { defaultEmbeddingChunkOverlap, - defaultEmbeddingChunkSize + defaultEmbeddingChunkSize, + getOllamaURL } from "@/services/ollama" import { OllamaEmbeddings } from "@langchain/community/embeddings/ollama" import { RecursiveCharacterTextSplitter } from "langchain/text_splitter" @@ -10,22 +11,14 @@ import { PageAssistVectorStore } from "./PageAssistVectorStore" import { PageAssisCSVUrlLoader } from "@/loader/csv" import { PageAssisTXTUrlLoader } from "@/loader/txt" import { PageAssistDocxLoader } from "@/loader/docx" +import { cleanUrl } from "./clean-url" -const readAsArrayBuffer = (file: File): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onload = () => { - resolve(reader.result as ArrayBuffer) - } - reader.onerror = reject - reader.readAsArrayBuffer(file) - }) -} export const processKnowledge = async (msg: any, id: string): Promise => { console.log(`Processing knowledge with id: ${id}`) try { const knowledge = await getKnowledgeById(id) + const ollamaUrl = await getOllamaURL() if (!knowledge) { console.error(`Knowledge with id ${id} not found`) @@ -35,6 +28,7 @@ export const processKnowledge = async (msg: any, id: string): Promise => { await updateKnowledgeStatus(id, "processing") const ollamaEmbedding = new OllamaEmbeddings({ + baseUrl: cleanUrl(ollamaUrl), model: knowledge.embedding_model }) const chunkSize = await defaultEmbeddingChunkSize() diff --git a/wxt.config.ts b/wxt.config.ts index 105c324..0e89e7a 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -48,7 +48,7 @@ export default defineConfig({ outDir: "build", manifest: { - version: "1.1.10", + version: "1.1.11", name: process.env.TARGET === "firefox" ? "Page Assist - A Web UI for Local AI Models" From 620b2b88f596317baf852e9d2b5eeb8fe139d694 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 29 May 2024 00:13:12 +0530 Subject: [PATCH 03/13] chore: Update package.json build command for Firefox --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7caa79..e3f67ca 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "dev": "cross-env TARGET=chrome wxt", "dev:firefox": "cross-env TARGET=firefox wxt -b firefox", "build": "cross-env TARGET=chrome wxt build", - "build:firefox": "cross-env TARGET=chrome cross-env TARGET=firefox wxt build -b firefox", + "build:firefox": "cross-env TARGET=firefox wxt build -b firefox", "zip": "cross-env TARGET=chrome wxt zip", "zip:firefox": "cross-env TARGET=firefox wxt zip -b firefox", "compile": "tsc --noEmit", From 721fad18c25f5b8526a9ee986d85eb1659af59f8 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 29 May 2024 00:13:20 +0530 Subject: [PATCH 04/13] chore: Update Layout component to adjust width for model selection --- src/components/Layouts/Layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index 2a4f5e4..eeb8e5b 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -136,7 +136,7 @@ export default function OptionLayout({ } showSearch placeholder={t("common:selectAModel")} - className="w-64 " + className="w-72" options={models?.map((model) => ({ label: ( Date: Wed, 29 May 2024 00:13:25 +0530 Subject: [PATCH 05/13] feat: Add Italian language translation --- src/public/_locales/it/messages.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/public/_locales/it/messages.json diff --git a/src/public/_locales/it/messages.json b/src/public/_locales/it/messages.json new file mode 100644 index 0000000..eb773d3 --- /dev/null +++ b/src/public/_locales/it/messages.json @@ -0,0 +1,11 @@ +{ + "extName": { + "message": "Page Assist - Un'interfaccia web per modelli AI locali" + }, + "extDescription": { + "message": "Usa i tuoi modelli AI in esecuzione locale per assisterti nella navigazione web." + }, + "openSidePanelToChat": { + "message": "Apri Copilot per chattare" + } +} From f5b0cc24aca8078db1b7bb8db11dc9293e08fcfc Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Wed, 29 May 2024 00:21:56 +0530 Subject: [PATCH 06/13] chore: Update save button styling in EditMessageForm --- src/components/Common/Playground/EditMessageForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Common/Playground/EditMessageForm.tsx b/src/components/Common/Playground/EditMessageForm.tsx index 00cc8d7..e864e1c 100644 --- a/src/components/Common/Playground/EditMessageForm.tsx +++ b/src/components/Common/Playground/EditMessageForm.tsx @@ -49,7 +49,7 @@ export const EditMessageForm = (props: Props) => {
+ +
+ + + {value} + + + + ) +}) diff --git a/src/components/Common/Markdown.tsx b/src/components/Common/Markdown.tsx index fa534ee..44adbf3 100644 --- a/src/components/Common/Markdown.tsx +++ b/src/components/Common/Markdown.tsx @@ -1,68 +1,38 @@ -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import remarkGfm from "remark-gfm" -import { nightOwl } from "react-syntax-highlighter/dist/cjs/styles/prism" import remarkMath from "remark-math" -import ReactMarkdown from "react-markdown" +import ReactMarkdown, { Options } from "react-markdown" + import "property-information" import React from "react" import { Tooltip } from "antd" import { CheckIcon, ClipboardIcon } from "lucide-react" import { useTranslation } from "react-i18next" +import { FC, memo } from "react" +import { CodeBlock } from "./CodeBlock" + +export const MemoizedReactMarkdown: FC = memo( + ReactMarkdown, + (prevProps, nextProps) => + prevProps.children === nextProps.children && + prevProps.className === nextProps.className +) + export default function Markdown({ message }: { message: string }) { - const [isBtnPressed, setIsBtnPressed] = React.useState(false) - const { t } = useTranslation("common") + return ( - -
- - {className && className.replace("language-", "")} - - -
- - - -
-
- - + ) : ( {children} @@ -85,7 +55,7 @@ export default function Markdown({ message }: { message: string }) { } }}> {message} -
+
) } From f12523d3f0ab42864cee5f645e01f3118e1a610e Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Thu, 30 May 2024 23:49:28 +0530 Subject: [PATCH 11/13] feat: Add useScrollAnchor hook for scrolling functionality --- src/hooks/useScrollAnchor.tsx | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/hooks/useScrollAnchor.tsx diff --git a/src/hooks/useScrollAnchor.tsx b/src/hooks/useScrollAnchor.tsx new file mode 100644 index 0000000..a8ec2ad --- /dev/null +++ b/src/hooks/useScrollAnchor.tsx @@ -0,0 +1,87 @@ +import { useCallback, useEffect, useRef, useState } from "react" + +export const useScrollAnchor = () => { + const messagesRef = useRef(null) + const scrollRef = useRef(null) + const visibilityRef = useRef(null) + + const [isAtBottom, setIsAtBottom] = useState(true) + const [isVisible, setIsVisible] = useState(false) + + const scrollToBottom = useCallback(() => { + if (messagesRef.current) { + messagesRef.current.scrollIntoView({ + block: "end", + behavior: "smooth" + }) + } + }, []) + + useEffect(() => { + if (messagesRef.current) { + if (isAtBottom && !isVisible) { + messagesRef.current.scrollIntoView({ + block: "end" + }) + } + } + }, [isAtBottom, isVisible]) + + useEffect(() => { + const { current } = scrollRef + + if (current) { + const handleScroll = (event: Event) => { + const target = event.target as HTMLDivElement + const offset = 25 + const isAtBottom = + target.scrollTop + target.clientHeight >= target.scrollHeight - offset + console.log(target.scrollTop, target.clientHeight, target.scrollHeight) + setIsAtBottom(isAtBottom) + } + + current.addEventListener("scroll", handleScroll, { + passive: true + }) + + return () => { + current.removeEventListener("scroll", handleScroll) + } + } + }, []) + + useEffect(() => { + if (visibilityRef.current) { + let observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + console.log(entry.isIntersecting) + if (entry.isIntersecting) { + setIsVisible(true) + } else { + setIsVisible(false) + } + }) + }, + { + rootMargin: "0px 0px -100px 0px" + } + ) + + observer.observe(visibilityRef.current) + + return () => { + observer.disconnect() + } + } + }) + + return { + messagesRef, + scrollRef, + visibilityRef, + scrollToBottom, + isAtBottom, + isVisible + } +} From b6bc617e5e24c0ae973416d0b343c05843f594c9 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Fri, 31 May 2024 13:50:05 +0530 Subject: [PATCH 12/13] refactor header --- src/components/Layouts/Header.tsx | 221 ++++++++++++++++++++++++++++ src/components/Layouts/Layout.tsx | 230 ++---------------------------- 2 files changed, 233 insertions(+), 218 deletions(-) create mode 100644 src/components/Layouts/Header.tsx diff --git a/src/components/Layouts/Header.tsx b/src/components/Layouts/Header.tsx new file mode 100644 index 0000000..af180c9 --- /dev/null +++ b/src/components/Layouts/Header.tsx @@ -0,0 +1,221 @@ +import { useStorage } from "@plasmohq/storage/hook" +import { + BrainCog, + ChevronLeft, + CogIcon, + ComputerIcon, + GithubIcon, + PanelLeftIcon, + SquarePen, + ZapIcon +} from "lucide-react" +import { useTranslation } from "react-i18next" +import { useLocation, NavLink } from "react-router-dom" +import { OllamaIcon } from "../Icons/Ollama" +import { SelectedKnowledge } from "../Option/Knowledge/SelectedKnwledge" +import { ModelSelect } from "../Common/ModelSelect" +import { PromptSelect } from "../Common/PromptSelect" +import { useQuery } from "@tanstack/react-query" +import { fetchChatModels } from "~/services/ollama" +import { useMessageOption } from "~/hooks/useMessageOption" +import { Select, Tooltip } from "antd" +import { getAllPrompts } from "@/db" +import { ShareBtn } from "~/components/Common/ShareBtn" +type Props = { + setSidebarOpen: (open: boolean) => void + setOpenModelSettings: (open: boolean) => void +} + +export const Header: React.FC = ({ + setOpenModelSettings, + setSidebarOpen +}) => { + const { t } = useTranslation(["option", "common"]) + const [shareModeEnabled] = useStorage("shareMode", false) + const [hideCurrentChatModelSettings] = useStorage( + "hideCurrentChatModelSettings", + false + ) + const { + selectedModel, + setSelectedModel, + clearChat, + selectedSystemPrompt, + setSelectedQuickPrompt, + setSelectedSystemPrompt, + messages, + streaming + } = useMessageOption() + const { + data: models, + isLoading: isModelsLoading, + isFetching: isModelsFetching + } = useQuery({ + queryKey: ["fetchModel"], + queryFn: () => fetchChatModels({ returnEmpty: true }), + refetchInterval: 15000 + }) + + const { data: prompts, isLoading: isPromptLoading } = useQuery({ + queryKey: ["fetchAllPromptsLayout"], + queryFn: getAllPrompts + }) + + const { pathname } = useLocation() + + const getPromptInfoById = (id: string) => { + return prompts?.find((prompt) => prompt.id === id) + } + + const handlePromptChange = (value?: string) => { + if (!value) { + setSelectedSystemPrompt(undefined) + setSelectedQuickPrompt(undefined) + return + } + const prompt = getPromptInfoById(value) + if (prompt?.is_system) { + setSelectedSystemPrompt(prompt.id) + } else { + setSelectedSystemPrompt(undefined) + setSelectedQuickPrompt(prompt!.content) + } + } + + return ( +
+
+ {pathname !== "/" && ( +
+ + + +
+ )} +
+ +
+
+ +
+ + {"/"} + +
+ + //@ts-ignore + option.label.key.toLowerCase().indexOf(input.toLowerCase()) >= 0 + } + options={prompts?.map((prompt) => ({ + label: ( + + {prompt.is_system ? ( + + ) : ( + + )} + {prompt.title} + + ), + value: prompt.id + }))} + /> +
+
+ +
+ +
+
+
+
+ {!hideCurrentChatModelSettings && ( + + + + )} + {pathname === "/" && + messages.length > 0 && + !streaming && + shareModeEnabled && } + + + + + + + + + + +
+
+
+
+ ) +} diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index eeb8e5b..699fce0 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -1,30 +1,12 @@ import React, { useState } from "react" -import { useLocation, NavLink } from "react-router-dom" import { Sidebar } from "../Option/Sidebar" -import { Drawer, Select, Tooltip } from "antd" -import { useQuery } from "@tanstack/react-query" -import { fetchChatModels, getAllModels } from "~/services/ollama" -import { useMessageOption } from "~/hooks/useMessageOption" -import { - BrainCog, - ChevronLeft, - CogIcon, - ComputerIcon, - GithubIcon, - PanelLeftIcon, - SquarePen, - ZapIcon -} from "lucide-react" -import { getAllPrompts } from "@/db" -import { ShareBtn } from "~/components/Common/ShareBtn" +import { Drawer } from "antd" + import { useTranslation } from "react-i18next" -import { OllamaIcon } from "../Icons/Ollama" -import { SelectedKnowledge } from "../Option/Knowledge/SelectedKnwledge" -import { useStorage } from "@plasmohq/storage/hook" -import { ModelSelect } from "../Common/ModelSelect" -import { PromptSelect } from "../Common/PromptSelect" + import { CurrentChatModelSettings } from "../Common/Settings/CurrentChatModelSettings" +import { Header } from "./Header" export default function OptionLayout({ children @@ -33,204 +15,16 @@ export default function OptionLayout({ }) { const [sidebarOpen, setSidebarOpen] = useState(false) const { t } = useTranslation(["option", "common"]) - const [shareModeEnabled] = useStorage("shareMode", false) const [openModelSettings, setOpenModelSettings] = useState(false) - const [hideCurrentChatModelSettings] = useStorage( - "hideCurrentChatModelSettings", - false - ) - - const { - selectedModel, - setSelectedModel, - clearChat, - selectedSystemPrompt, - setSelectedQuickPrompt, - setSelectedSystemPrompt, - messages, - streaming - } = useMessageOption() - - const { - data: models, - isLoading: isModelsLoading, - isFetching: isModelsFetching - } = useQuery({ - queryKey: ["fetchModel"], - queryFn: () => fetchChatModels({ returnEmpty: true }), - refetchInterval: 15000 - }) - - const { data: prompts, isLoading: isPromptLoading } = useQuery({ - queryKey: ["fetchAllPromptsLayout"], - queryFn: getAllPrompts - }) - - const { pathname } = useLocation() - - const getPromptInfoById = (id: string) => { - return prompts?.find((prompt) => prompt.id === id) - } - - const handlePromptChange = (value?: string) => { - if (!value) { - setSelectedSystemPrompt(undefined) - setSelectedQuickPrompt(undefined) - return - } - const prompt = getPromptInfoById(value) - if (prompt?.is_system) { - setSelectedSystemPrompt(prompt.id) - } else { - setSelectedSystemPrompt(undefined) - setSelectedQuickPrompt(prompt!.content) - } - } return ( -
-
-
-
-
- {pathname !== "/" && ( -
- - - -
- )} -
- -
-
- -
- - {"/"} - -
- - //@ts-ignore - option.label.key - .toLowerCase() - .indexOf(input.toLowerCase()) >= 0 - } - options={prompts?.map((prompt) => ({ - label: ( - - {prompt.is_system ? ( - - ) : ( - - )} - {prompt.title} - - ), - value: prompt.id - }))} - /> -
-
- -
- -
-
-
-
- {!hideCurrentChatModelSettings && ( - - - - )} - {pathname === "/" && - messages.length > 0 && - !streaming && - shareModeEnabled && } - - - - - - - - - - -
-
-
-
-
{children}
-
+ <> +
+
+
{children}
-
+ ) } From 7fa44e6fd3d233cd4f8d4e36099e0733ee50a2a4 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Fri, 31 May 2024 22:05:43 +0530 Subject: [PATCH 13/13] refactor: Remove unnecessary flex classes from main element in Layout component --- src/components/Layouts/Layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layouts/Layout.tsx b/src/components/Layouts/Layout.tsx index 699fce0..0da5433 100644 --- a/src/components/Layouts/Layout.tsx +++ b/src/components/Layouts/Layout.tsx @@ -24,7 +24,7 @@ export default function OptionLayout({ setSidebarOpen={setSidebarOpen} setOpenModelSettings={setOpenModelSettings} /> -
{children}
+
{children}