From d691a4dcfa811b60d03c75c51154c4e733175621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Bene=C5=A1?= Date: Tue, 10 Nov 2026 13:44:49 +0000 Subject: [PATCH] Memory page and search inside chats, files and memory (Ctrl+K, GET /search) The account's memory as the web's Settings -> Memory keeps it: add, change, remove, over /api/v2/memories. Ctrl+K asks the server's search once two letters are typed and lists the hits below the titles, the matched ranges drawn as elements over plain text. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 7 ++ scripts/gen-i18n.py | 65 ++++++++++++++++++ tests/ui/run.mjs | 93 ++++++++++++++++++++++++++ ui/app.css | 27 ++++++++ ui/i18n.js | 64 ++++++++++++++++++ ui/index.html | 10 +++ ui/js/main.js | 23 ++++++- ui/js/memory.js | 159 ++++++++++++++++++++++++++++++++++++++++++++ ui/js/palette.js | 78 +++++++++++++++++++--- 9 files changed, 515 insertions(+), 11 deletions(-) create mode 100644 ui/js/memory.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a13187..ee46906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,13 @@ All notable changes to LLeMbas Desktop. The format follows folders, nested and folded as the server keeps them, then the rest by day. A chat's menu pins it and moves it into a folder or a new one; a folder's menu renames it, makes one inside it, or deletes it (its chats stay, back in the list). The search line still lists every match. +- **Memory.** A page in the sidebar (and in Ctrl+K) lists what the account's models remember about + the person, as the web's Settings → Memory does: add a record (into a data group, where there is + more than one), change it, remove it. A record a model noted says so. +- **Search inside.** Ctrl+K finds more than titles: once two letters are typed it asks the + server's search, and lists below the titles what it found inside chats, in the file library and + in memory, each with the words around the match, the match marked. A chat opens, a file opens + in the viewer, a memory opens the Memory page at it. A server without search finds titles only. ### Fixed diff --git a/scripts/gen-i18n.py b/scripts/gen-i18n.py index 2d16c6c..57babcf 100755 --- a/scripts/gen-i18n.py +++ b/scripts/gen-i18n.py @@ -484,6 +484,15 @@ KEYS = ( "New folder…", "Create", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.", + # palette.js, memory.js: search inside, and the memory page + "Memory", + "Add", + "Data group", + "Remove this memory?", + "Noted by a model", + "What your models remember about you, across chats. Change or remove anything here.", + "Something to remember…", + "Nothing remembered yet.", ) # The app's own sentences. The mode names (Manual, Edit, Auto, Plan) stay English, as the @@ -732,6 +741,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nová složka…", "Create": "Vytvořit", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "„%(name)s“ a složky v ní se smažou. Jejich konverzace zůstanou, zpět v seznamu.", + "Add": "Přidat", + "Data group": "Skupina dat", + "Remove this memory?": "Odstranit tento záznam z paměti?", + "Noted by a model": "Zaznamenal model", + "What your models remember about you, across chats. Change or remove anything here.": "Co si o vás vaše modely pamatují napříč konverzacemi. Cokoli tu můžete změnit nebo odstranit.", + "Something to remember…": "Něco k zapamatování…", + "Nothing remembered yet.": "Zatím si nic nepamatuje.", }, "de": { "Sign in again: the server no longer accepts this app's sign-in.": "Melden Sie sich erneut an: Der Server nimmt die Anmeldung dieser App nicht mehr an.", @@ -975,6 +991,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Neuer Ordner…", "Create": "Erstellen", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "„%(name)s“ und die Ordner darin werden gelöscht. Ihre Chats bleiben, wieder in der Liste.", + "Add": "Hinzufügen", + "Data group": "Datengruppe", + "Remove this memory?": "Diese Erinnerung entfernen?", + "Noted by a model": "Von einem Modell notiert", + "What your models remember about you, across chats. Change or remove anything here.": "Was Ihre Modelle über Sie behalten, über alle Chats hinweg. Hier können Sie alles ändern oder entfernen.", + "Something to remember…": "Etwas, das behalten werden soll…", + "Nothing remembered yet.": "Noch nichts behalten.", }, "es": { "Sign in again: the server no longer accepts this app's sign-in.": "Vuelva a iniciar sesión: el servidor ya no acepta el inicio de sesión de esta aplicación.", @@ -1218,6 +1241,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nueva carpeta…", "Create": "Crear", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "Se eliminan «%(name)s» y las carpetas que contiene. Sus chats se quedan, de vuelta en la lista.", + "Add": "Añadir", + "Data group": "Grupo de datos", + "Remove this memory?": "¿Quitar este recuerdo?", + "Noted by a model": "Anotado por un modelo", + "What your models remember about you, across chats. Change or remove anything here.": "Lo que sus modelos recuerdan de usted entre chats. Aquí puede cambiar o quitar cualquier cosa.", + "Something to remember…": "Algo que recordar…", + "Nothing remembered yet.": "Todavía no se recuerda nada.", }, "fr": { "Sign in again: the server no longer accepts this app's sign-in.": "Reconnectez-vous : le serveur n’accepte plus la connexion de cette application.", @@ -1461,6 +1491,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nouveau dossier…", "Create": "Créer", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "« %(name)s » et les dossiers qu’il contient sont supprimés. Leurs conversations restent, de retour dans la liste.", + "Add": "Ajouter", + "Data group": "Groupe de données", + "Remove this memory?": "Supprimer ce souvenir ?", + "Noted by a model": "Noté par un modèle", + "What your models remember about you, across chats. Change or remove anything here.": "Ce que vos modèles retiennent de vous, d’une conversation à l’autre. Modifiez ou supprimez ce que vous voulez ici.", + "Something to remember…": "Quelque chose à retenir…", + "Nothing remembered yet.": "Rien n’est encore retenu.", }, "hu": { "Sign in again: the server no longer accepts this app's sign-in.": "Jelentkezzen be újra: a szerver már nem fogadja el ennek az alkalmazásnak a bejelentkezését.", @@ -1704,6 +1741,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Új mappa…", "Create": "Létrehozás", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "A(z) „%(name)s” és a benne lévő mappák törlődnek. A beszélgetéseik megmaradnak, vissza a listába.", + "Add": "Hozzáadás", + "Data group": "Adatcsoport", + "Remove this memory?": "Eltávolítja ezt az emléket?", + "Noted by a model": "Egy modell jegyezte fel", + "What your models remember about you, across chats. Change or remove anything here.": "Amit a modelljei megjegyeznek Önről a beszélgetéseken át. Itt bármit módosíthat vagy eltávolíthat.", + "Something to remember…": "Valami, amit meg kell jegyezni…", + "Nothing remembered yet.": "Még nincs megjegyezve semmi.", }, "it": { "Sign in again: the server no longer accepts this app's sign-in.": "Accedi di nuovo: il server non accetta più l’accesso di questa app.", @@ -1947,6 +1991,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nuova cartella…", "Create": "Crea", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "«%(name)s» e le cartelle al suo interno vengono eliminate. Le loro chat restano, di nuovo nell’elenco.", + "Add": "Aggiungi", + "Data group": "Gruppo di dati", + "Remove this memory?": "Rimuovere questo ricordo?", + "Noted by a model": "Annotato da un modello", + "What your models remember about you, across chats. Change or remove anything here.": "Ciò che i tuoi modelli ricordano di te tra una chat e l’altra. Qui puoi cambiare o rimuovere qualsiasi cosa.", + "Something to remember…": "Qualcosa da ricordare…", + "Nothing remembered yet.": "Ancora niente da ricordare.", }, "pl": { "Sign in again: the server no longer accepts this app's sign-in.": "Zaloguj się ponownie: serwer nie przyjmuje już logowania tej aplikacji.", @@ -2190,6 +2241,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nowy folder…", "Create": "Utwórz", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "„%(name)s” i foldery w nim zostaną usunięte. Ich czaty zostaną, z powrotem na liście.", + "Add": "Dodaj", + "Data group": "Grupa danych", + "Remove this memory?": "Usunąć to wspomnienie?", + "Noted by a model": "Zanotowane przez model", + "What your models remember about you, across chats. Change or remove anything here.": "Co Twoje modele pamiętają o Tobie we wszystkich czatach. Tutaj możesz wszystko zmienić lub usunąć.", + "Something to remember…": "Coś do zapamiętania…", + "Nothing remembered yet.": "Na razie nic nie zapamiętano.", }, "sk": { "Sign in again: the server no longer accepts this app's sign-in.": "Prihláste sa znova: server už neprijíma prihlásenie tejto aplikácie.", @@ -2433,6 +2491,13 @@ OWN: dict[str, dict[str, str]] = { "New folder…": "Nový priečinok…", "Create": "Vytvoriť", "“%(name)s” and the folders inside it are deleted. Their chats stay, back in the list.": "„%(name)s“ a priečinky v ňom sa zmažú. Ich konverzácie zostanú, späť v zozname.", + "Add": "Pridať", + "Data group": "Skupina údajov", + "Remove this memory?": "Odstrániť tento záznam z pamäte?", + "Noted by a model": "Zaznamenal model", + "What your models remember about you, across chats. Change or remove anything here.": "Čo si o vás vaše modely pamätajú naprieč konverzáciami. Čokoľvek tu môžete zmeniť alebo odstrániť.", + "Something to remember…": "Niečo na zapamätanie…", + "Nothing remembered yet.": "Zatiaľ si nič nepamätá.", }, } diff --git a/tests/ui/run.mjs b/tests/ui/run.mjs index fb2f4b2..e67164b 100644 --- a/tests/ui/run.mjs +++ b/tests/ui/run.mjs @@ -1200,6 +1200,99 @@ test("F8: at 390 px the folders stay inside the sidebar", async () => { assert.deepEqual(p.errors, []) }) +// G8: search inside (GET /search, fixture search.json) and the memory page (memories.json). + +function g8(over = {}) { + return { + status: status(), + routes: baseRoutes({ + "GET /search": fixture("search"), + "GET /memories": fixture("memories"), + "POST /memories": { memory: { ...fixture("memory").memory, id: "mem2", content: "Walks to Bree on Sundays." } }, + "PATCH /memories/mem1": { memory: { ...fixture("memory").memory, content: "Prefers lembas cold." } }, + "DELETE /memories/mem1": null, + "GET /files/content": { name: "bread.md", path: "/home/frodo/bread.md", text: "# Lembas recipe", kind: "markdown" }, + ...over, + }), + streams: {}, + } +} + +test("G8: the palette finds words inside chats, files and memory through the server, the matches marked as text", async () => { + const p = await page(g8()) + await p.keyboard.press("Control+k") + await p.fill(".palette__input", "lembas") + await p.waitForSelector(".palette__snippet") + const [q] = await p.evaluate(() => window.__mock.api("GET", "/search")) + assert.match(q.args.path, /^\/search\?q=lembas&in=all&limit=8$/) + const hits = await p.$$eval(".palette__item:has(.palette__snippet)", (l) => l.map((x) => ({ title: x.querySelector(".palette__title").textContent, mark: x.querySelector("mark")?.textContent, hint: x.querySelector(".palette__hint").textContent }))) + assert.deepEqual(hits.map((x) => x.hint), ["Chats", "Files", "Memory"]) + assert.equal(hits[0].title, "Waybread") + assert.equal(hits[0].mark, "lembas") + assert.equal(hits[2].mark, "lembas") + await shot(p, "g8-search-1280") + // A memory opens the memory page at it. + await p.click(".palette__item:has(.palette__hint:text('Memory'))") + await p.waitForSelector('#page-memory:not([hidden]) .mrow.is-marked[data-id="mem1"]') + assert.deepEqual(p.errors, []) +}) + +test("G8: a chat found inside opens; a server without search finds nothing more, and says nothing", async () => { + let p = await page(g8()) + await p.keyboard.press("Control+k") + await p.fill(".palette__input", "lembas") + await p.waitForSelector(".palette__snippet") + await p.click(".palette__item:has(.palette__hint:text('Chats'))") + await p.waitForFunction(() => window.__mock.api("GET", "/chats/chat1").length > 0) + await p.context().close() + p = await page(g8({ "GET /search": { __error: { status: 404, detail: "Not Found" } } })) + await p.keyboard.press("Control+k") + await p.fill(".palette__input", "lembas") + await p.waitForFunction(() => window.__mock.api("GET", "/search").length === 1) + await p.waitForTimeout(100) + assert.equal(await p.locator(".palette__snippet").count(), 0) + assert.deepEqual(p.errors, []) +}) + +test("G8: the memory page adds, changes and removes what the models remember", async () => { + const p = await page(g8()) + await p.click("#nav-memory") + await p.waitForSelector('.mrow[data-id="mem1"]') + assert.match(await p.textContent('.mrow[data-id="mem1"]'), /Prefers lembas warm\./) + await p.fill("#memory-new", "Walks to Bree on Sundays.") + await p.click(".mnew button[type=submit]") + await p.waitForSelector('.mrow[data-id="mem2"]') + assert.deepEqual((await p.evaluate(() => window.__mock.api("POST", "/memories")))[0].args.body, { content: "Walks to Bree on Sundays." }) + assert.equal(await p.inputValue("#memory-new"), "") + await p.click('.mrow[data-id="mem1"] .btn >> text=Edit') + await p.fill(".mrow.is-editing textarea", "Prefers lembas cold.") + await p.click(".mrow.is-editing .btn--primary") + await p.waitForFunction(() => /cold/.test(document.querySelector('.mrow[data-id="mem1"]').textContent)) + assert.deepEqual((await p.evaluate(() => window.__mock.api("PATCH", "/memories/mem1")))[0].args.body, { content: "Prefers lembas cold." }) + await p.click('.mrow[data-id="mem1"] .tool') + await p.click(".menu .menu__item >> text=Remove") + await p.click("dialog[open] .btn--danger") + await p.waitForFunction(() => !document.querySelector('.mrow[data-id="mem1"]')) + assert.deepEqual(p.errors, []) +}) + +test("G8: at 390 px the memory page and the palette's hits stay inside the window", async () => { + const p = await page(g8(), { width: 390, height: 844 }) + await p.evaluate(() => document.getElementById("nav-memory").click()) + await p.waitForSelector('.mrow[data-id="mem1"]') + assert.ok(await noSideways(p, 390), "nothing scrolls sideways") + await shot(p, "g8-memory-390") + await p.setViewportSize({ width: 1280, height: 860 }) + await shot(p, "g8-memory-1280") + await p.setViewportSize({ width: 390, height: 844 }) + await p.keyboard.press("Control+k") + await p.fill(".palette__input", "lembas") + await p.waitForSelector(".palette__snippet") + assert.ok(await noSideways(p, 390), "nothing scrolls sideways") + await shot(p, "g8-search-390") + assert.deepEqual(p.errors, []) +}) + // --- Run ---------------------------------------------------------------------------------------- const only = process.argv[2] || "" diff --git a/ui/app.css b/ui/app.css index c31ef21..32e786c 100644 --- a/ui/app.css +++ b/ui/app.css @@ -1205,6 +1205,11 @@ details[open] > summary .step__chev .icon { transform: rotate(90deg); } .palette__icon { display: grid; place-items: center; color: var(--ink-muted); } .palette__label { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .palette__hint { font-size: var(--text-xs); color: var(--ink-faint); white-space: nowrap; } +/* A hit found inside: its title over the snippet, the words that matched marked. */ +.palette__label--two { display: grid; padding: var(--sp-1) 0; white-space: normal; } +.palette__title, .palette__snippet { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } +.palette__snippet { font-size: var(--text-xs); color: var(--ink-muted); } +.palette__snippet mark { color: var(--ink); background: var(--surface-active); border-radius: var(--radius-sm); } .palette__none { padding: var(--sp-3); font-size: var(--text-sm); color: var(--ink-muted); } @@ -1691,6 +1696,28 @@ details[open] > summary .step__chev .icon { transform: rotate(90deg); } .arch__page { display: grid; gap: var(--sp-3); } .head__unarchive { white-space: nowrap; } +/* --- Memory: a new one on top, then each record over its group and date · Edit · more. */ + +.mnew { display: grid; gap: var(--sp-2); } +.mnew textarea { width: 100%; } +.mnew__foot { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; } +.mnew__foot select { justify-self: start; max-width: 100%; } +.mrow { + display: grid; + grid-template-columns: minmax(0, 1fr) auto var(--control-h-sm); + gap: var(--sp-3); + align-items: center; + min-height: var(--control-h); + padding: var(--sp-2) 0; + border-bottom: var(--border-w) solid var(--border); +} +.mrow.is-editing { grid-template-columns: minmax(0, 1fr); } +.mrow.is-editing textarea { width: 100%; } +.mrow.is-marked { background: var(--surface-active); border-radius: var(--radius-lg); padding-inline: var(--sp-2); } +.mrow__words { display: grid; gap: var(--sp-1); min-width: 0; } +.mrow__text { margin: 0; font: var(--text-sm) / var(--leading-normal) var(--font-body); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; } +.mrow__sub { margin: 0; font: var(--text-xs) var(--font-ui); color: var(--ink-faint); } + /* --- Narrower windows ---------------------------------------------------------------------- */ /* 64rem (--bp-wide): a panel goes over the chat instead of beside it. */ diff --git a/ui/i18n.js b/ui/i18n.js index 192be46..82bc589 100644 --- a/ui/i18n.js +++ b/ui/i18n.js @@ -44,6 +44,7 @@ const LEMBAS_I18N = { "About": "O aplikaci", "Account": "Účet", "Activity": "Aktivita", + "Add": "Přidat", "Add folder…": "Přidat složku…", "Address of your LLeMbas server": "Adresa vašeho serveru LLeMbas", "Agent": "Agent", @@ -105,6 +106,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Spočítal model při poslední odpovědi; kategorie jsou odhadnuté.", "Create": "Vytvořit", "Cut short here; the whole of it is in the web interface.": "Zde zkráceno; celé je to ve webovém rozhraní.", + "Data group": "Datová skupina", "Days": "Dny", "Delete": "Smazat", "Delete chat": "Smazat konverzaci", @@ -167,6 +169,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — nejdřív se zeptá", "Max": "Max", "Maximise": "Maximalizovat", + "Memory": "Paměť", "Memory and personal": "Paměť a osobní", "Menu": "Nabídka", "Message": "Zpráva", @@ -201,10 +204,12 @@ const LEMBAS_I18N = { "Not measured.": "Neměřeno.", "Not run yet": "Ještě neběžela", "Not yet": "Ještě ne", + "Noted by a model": "Zaznamenal model", "Nothing has been run in this shell yet.": "V tomto shellu se zatím nic nespustilo.", "Nothing has been said in the room yet.": "V místnosti zatím nikdo nic neřekl.", "Nothing is scheduled.": "Nic není naplánováno.", "Nothing matches.": "Nic neodpovídá.", + "Nothing remembered yet.": "Zatím si nic nepamatuje.", "Nothing running.": "Nic neběží.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "Na pozadí nic neběží. Podagenti, členové týmu a příkazy na pozadí se tu zobrazí, dokud pracují.", "Nothing to report": "Nic k hlášení", @@ -245,6 +250,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Vygenerovat odpověď znovu", "Remove": "Odebrat", "Remove %(dir)s": "Odebrat %(dir)s", + "Remove this memory?": "Odstranit tento záznam z paměti?", "Rename": "Přejmenovat", "Rename chat": "Přejmenovat konverzaci", "Rename folder": "Přejmenovat složku", @@ -288,6 +294,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Odhlášením se přihlášení aplikace na serveru zruší; propojení agenta zůstane. Chcete-li použít jiný server, odhlaste se.", "Since the last reply": "Od poslední odpovědi", "Skipped": "Vynecháno", + "Something to remember…": "Něco k zapamatování…", "Source": "Zdroj", "Start the agent": "Spustit agenta", "Stop": "Zastavit", @@ -382,6 +389,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Čeká se na {{model}}: {{reason}}", "What it does": "Co dělá", "What should happen, and when": "Co se má stát a kdy", + "What your models remember about you, across chats. Change or remove anything here.": "Co si o vás vaše modely pamatují napříč konverzacemi. Cokoli tu můžete změnit nebo odstranit.", "When": "Kdy", "When it matters": "Když na tom záleží", "Writing": "Píše", @@ -476,6 +484,7 @@ const LEMBAS_I18N = { "About": "Über", "Account": "Konto", "Activity": "Aktivität", + "Add": "Hinzufügen", "Add folder…": "Ordner hinzufügen…", "Address of your LLeMbas server": "Adresse Ihres LLeMbas-Servers", "Agent": "Agent", @@ -537,6 +546,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Vom Modell bei der letzten Antwort gezählt; die Kategorien sind geschätzt.", "Create": "Erstellen", "Cut short here; the whole of it is in the web interface.": "Hier gekürzt; vollständig steht es in der Weboberfläche.", + "Data group": "Datengruppe", "Days": "Tage", "Delete": "Löschen", "Delete chat": "Chat löschen", @@ -599,6 +609,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — fragt zuerst", "Max": "Max", "Maximise": "Maximieren", + "Memory": "Gedächtnis", "Memory and personal": "Gedächtnis und Persönliches", "Menu": "Menü", "Message": "Nachricht", @@ -633,10 +644,12 @@ const LEMBAS_I18N = { "Not measured.": "Nicht gemessen.", "Not run yet": "Noch nicht gelaufen", "Not yet": "Noch nicht", + "Noted by a model": "Von einem Modell notiert", "Nothing has been run in this shell yet.": "In dieser Shell wurde noch nichts ausgeführt.", "Nothing has been said in the room yet.": "Im Raum wurde noch nichts gesagt.", "Nothing is scheduled.": "Nichts ist geplant.", "Nothing matches.": "Nichts gefunden.", + "Nothing remembered yet.": "Noch nichts behalten.", "Nothing running.": "Nichts läuft.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "Im Hintergrund läuft nichts. Subagenten, die Mitglieder eines Teams und Hintergrundbefehle erscheinen hier, solange sie arbeiten.", "Nothing to report": "Nichts zu melden", @@ -677,6 +690,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Antwort neu generieren", "Remove": "Entfernen", "Remove %(dir)s": "%(dir)s entfernen", + "Remove this memory?": "Diese Erinnerung entfernen?", "Rename": "Umbenennen", "Rename chat": "Chat umbenennen", "Rename folder": "Ordner umbenennen", @@ -720,6 +734,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Beim Abmelden gibt die App ihre Anmeldung am Server auf; die Verbindung des Agenten bleibt. Melden Sie sich ab, um einen anderen Server zu verwenden.", "Since the last reply": "Seit der letzten Antwort", "Skipped": "Ausgelassen", + "Something to remember…": "Etwas, das behalten werden soll…", "Source": "Quelltext", "Start the agent": "Agent starten", "Stop": "Stoppen", @@ -814,6 +829,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Warte auf {{model}}: {{reason}}", "What it does": "Was sie tut", "What should happen, and when": "Was soll wann geschehen?", + "What your models remember about you, across chats. Change or remove anything here.": "Was Ihre Modelle über Sie behalten, über alle Chats hinweg. Hier können Sie alles ändern oder entfernen.", "When": "Wann", "When it matters": "Wenn es zählt", "Writing": "Schreibe", @@ -908,6 +924,7 @@ const LEMBAS_I18N = { "About": "Acerca de", "Account": "Cuenta", "Activity": "Actividad", + "Add": "Añadir", "Add folder…": "Añadir carpeta…", "Address of your LLeMbas server": "Dirección de su servidor LLeMbas", "Agent": "Agente", @@ -969,6 +986,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Contado por el modelo en la última respuesta; las categorías son estimaciones.", "Create": "Crear", "Cut short here; the whole of it is in the web interface.": "Recortado aquí; completo está en la interfaz web.", + "Data group": "Grupo de datos", "Days": "Días", "Delete": "Eliminar", "Delete chat": "Eliminar chat", @@ -1031,6 +1049,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — pregunta primero", "Max": "Máx", "Maximise": "Maximizar", + "Memory": "Memoria", "Memory and personal": "Memoria y datos personales", "Menu": "Menú", "Message": "Mensaje", @@ -1065,10 +1084,12 @@ const LEMBAS_I18N = { "Not measured.": "Sin medir.", "Not run yet": "Aún no se ha ejecutado", "Not yet": "Todavía no", + "Noted by a model": "Anotado por un modelo", "Nothing has been run in this shell yet.": "Aún no se ha ejecutado nada en este shell.", "Nothing has been said in the room yet.": "Todavía no se ha dicho nada en la sala.", "Nothing is scheduled.": "No hay nada programado.", "Nothing matches.": "No hay coincidencias.", + "Nothing remembered yet.": "Todavía no se recuerda nada.", "Nothing running.": "Nada en marcha.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "No hay nada en segundo plano. Los subagentes, los miembros de un equipo y los comandos en segundo plano aparecen aquí mientras trabajan.", "Nothing to report": "Nada que informar", @@ -1109,6 +1130,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Regenerar respuesta", "Remove": "Quitar", "Remove %(dir)s": "Quitar %(dir)s", + "Remove this memory?": "¿Quitar este recuerdo?", "Rename": "Renombrar", "Rename chat": "Renombrar chat", "Rename folder": "Renombrar carpeta", @@ -1152,6 +1174,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Al cerrar sesión, la aplicación renuncia a su acceso en el servidor; la vinculación del agente se mantiene. Cierre sesión para usar otro servidor.", "Since the last reply": "Desde la última respuesta", "Skipped": "Omitida", + "Something to remember…": "Algo que recordar…", "Source": "Código fuente", "Start the agent": "Iniciar el agente", "Stop": "Detener", @@ -1246,6 +1269,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Esperando a {{model}}: {{reason}}", "What it does": "Qué hace", "What should happen, and when": "Qué debe ocurrir, y cuándo", + "What your models remember about you, across chats. Change or remove anything here.": "Lo que sus modelos recuerdan de usted entre chats. Aquí puede cambiar o quitar cualquier cosa.", "When": "Cuándo", "When it matters": "Cuando importa", "Writing": "Escribiendo", @@ -1340,6 +1364,7 @@ const LEMBAS_I18N = { "About": "À propos", "Account": "Compte", "Activity": "Activité", + "Add": "Ajouter", "Add folder…": "Ajouter un dossier…", "Address of your LLeMbas server": "Adresse de votre serveur LLeMbas", "Agent": "Agent", @@ -1401,6 +1426,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Compté par le modèle lors de la dernière réponse ; les catégories sont estimées.", "Create": "Créer", "Cut short here; the whole of it is in the web interface.": "Tronqué ici ; le tout est dans l'interface web.", + "Data group": "Groupe de données", "Days": "Jours", "Delete": "Supprimer", "Delete chat": "Supprimer la conversation", @@ -1463,6 +1489,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — demande d'abord", "Max": "Max", "Maximise": "Agrandir", + "Memory": "Mémoire", "Memory and personal": "Mémoire et informations personnelles", "Menu": "Menu", "Message": "Message", @@ -1497,10 +1524,12 @@ const LEMBAS_I18N = { "Not measured.": "Non mesuré.", "Not run yet": "Pas encore exécutée", "Not yet": "Pas encore", + "Noted by a model": "Noté par un modèle", "Nothing has been run in this shell yet.": "Rien n'a encore été exécuté dans ce shell.", "Nothing has been said in the room yet.": "Rien n’a encore été dit dans la salle.", "Nothing is scheduled.": "Rien n’est planifié.", "Nothing matches.": "Aucun résultat.", + "Nothing remembered yet.": "Rien n’est encore retenu.", "Nothing running.": "Rien en cours.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "Rien ne tourne en arrière-plan. Les sous-agents, les membres d’une équipe et les commandes en arrière-plan s’affichent ici pendant qu’ils travaillent.", "Nothing to report": "Rien à signaler", @@ -1541,6 +1570,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Régénérer la réponse", "Remove": "Retirer", "Remove %(dir)s": "Retirer %(dir)s", + "Remove this memory?": "Supprimer ce souvenir ?", "Rename": "Renommer", "Rename chat": "Renommer la conversation", "Rename folder": "Renommer le dossier", @@ -1584,6 +1614,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "La déconnexion abandonne la connexion de l'application au serveur ; la liaison de l'agent reste. Déconnectez-vous pour utiliser un autre serveur.", "Since the last reply": "Depuis la dernière réponse", "Skipped": "Ignorée", + "Something to remember…": "Quelque chose à retenir…", "Source": "Source", "Start the agent": "Démarrer l'agent", "Stop": "Arrêter", @@ -1678,6 +1709,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "En attente de {{model}} : {{reason}}", "What it does": "Ce qu’elle fait", "What should happen, and when": "Ce qui doit se passer, et quand", + "What your models remember about you, across chats. Change or remove anything here.": "Ce que vos modèles retiennent de vous, d’une conversation à l’autre. Modifiez ou supprimez ce que vous voulez ici.", "When": "Quand", "When it matters": "Quand c'est important", "Writing": "Écriture", @@ -1772,6 +1804,7 @@ const LEMBAS_I18N = { "About": "Névjegy", "Account": "Fiók", "Activity": "Tevékenység", + "Add": "Hozzáadás", "Add folder…": "Mappa hozzáadása…", "Address of your LLeMbas server": "A LLeMbas-kiszolgáló címe", "Agent": "Ügynök", @@ -1833,6 +1866,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "A modell számolta az utolsó válasznál; a kategóriák becslések.", "Create": "Létrehozás", "Cut short here; the whole of it is in the web interface.": "Itt rövidítve; a teljes a webes felületen van.", + "Data group": "Adatcsoport", "Days": "Napok", "Delete": "Törlés", "Delete chat": "Csevegés törlése", @@ -1895,6 +1929,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — előbb kérdez", "Max": "Max", "Maximise": "Teljes méret", + "Memory": "Memória", "Memory and personal": "Memória és személyes adatok", "Menu": "Menü", "Message": "Üzenet", @@ -1929,10 +1964,12 @@ const LEMBAS_I18N = { "Not measured.": "Nincs mérve.", "Not run yet": "Még nem futott", "Not yet": "Még nem", + "Noted by a model": "Egy modell jegyezte fel", "Nothing has been run in this shell yet.": "Ebben a shellben még semmi sem futott.", "Nothing has been said in the room yet.": "A szobában még senki nem mondott semmit.", "Nothing is scheduled.": "Nincs semmi ütemezve.", "Nothing matches.": "Nincs találat.", + "Nothing remembered yet.": "Még nincs megjegyezve semmi.", "Nothing running.": "Semmi sem fut.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "A háttérben semmi sem fut. Az alügynökök, egy csapat tagjai és a háttérparancsok itt jelennek meg, amíg dolgoznak.", "Nothing to report": "Nincs jelentenivaló", @@ -1973,6 +2010,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Válasz újragenerálása", "Remove": "Eltávolítás", "Remove %(dir)s": "%(dir)s eltávolítása", + "Remove this memory?": "Eltávolítja ezt az emléket?", "Rename": "Átnevezés", "Rename chat": "Csevegés átnevezése", "Rename folder": "Mappa átnevezése", @@ -2016,6 +2054,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Kijelentkezéskor az alkalmazás lemond a szerveren lévő bejelentkezéséről; az ügynök kapcsolata megmarad. Más szerver használatához jelentkezzen ki.", "Since the last reply": "Az utolsó válasz óta", "Skipped": "Kihagyva", + "Something to remember…": "Valami, amit meg kell jegyezni…", "Source": "Forrás", "Start the agent": "Ügynök indítása", "Stop": "Leállítás", @@ -2110,6 +2149,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Várakozás a(z) {{model}} modellre: {{reason}}", "What it does": "Mit csinál", "What should happen, and when": "Mi történjen, és mikor", + "What your models remember about you, across chats. Change or remove anything here.": "Amit a modelljei megjegyeznek Önről a beszélgetéseken át. Itt bármit módosíthat vagy eltávolíthat.", "When": "Mikor", "When it matters": "Ha számít", "Writing": "Írás", @@ -2204,6 +2244,7 @@ const LEMBAS_I18N = { "About": "Informazioni", "Account": "Account", "Activity": "Attività", + "Add": "Aggiungi", "Add folder…": "Aggiungi cartella…", "Address of your LLeMbas server": "Indirizzo del tuo server LLeMbas", "Agent": "Agente", @@ -2265,6 +2306,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Contato dal modello all’ultima risposta; le categorie sono stime.", "Create": "Crea", "Cut short here; the whole of it is in the web interface.": "Troncato qui; il resto è nell'interfaccia web.", + "Data group": "Gruppo di dati", "Days": "Giorni", "Delete": "Elimina", "Delete chat": "Elimina chat", @@ -2327,6 +2369,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — chiede prima", "Max": "Max", "Maximise": "Ingrandisci", + "Memory": "Memoria", "Memory and personal": "Memoria e dati personali", "Menu": "Menu", "Message": "Messaggio", @@ -2361,10 +2404,12 @@ const LEMBAS_I18N = { "Not measured.": "Non misurato.", "Not run yet": "Non ancora eseguita", "Not yet": "Non ancora", + "Noted by a model": "Annotato da un modello", "Nothing has been run in this shell yet.": "In questa shell non è ancora stato eseguito nulla.", "Nothing has been said in the room yet.": "Nella stanza non è ancora stato detto nulla.", "Nothing is scheduled.": "Non c’è niente di pianificato.", "Nothing matches.": "Nessun risultato.", + "Nothing remembered yet.": "Ancora niente da ricordare.", "Nothing running.": "Nulla in esecuzione.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "Nulla è in esecuzione in background. Sottoagenti, membri di una squadra e comandi in background compaiono qui mentre lavorano.", "Nothing to report": "Niente da segnalare", @@ -2405,6 +2450,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Rigenera risposta", "Remove": "Rimuovi", "Remove %(dir)s": "Rimuovi %(dir)s", + "Remove this memory?": "Rimuovere questo ricordo?", "Rename": "Rinomina", "Rename chat": "Rinomina chat", "Rename folder": "Rinomina cartella", @@ -2448,6 +2494,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Uscendo, l'app rinuncia al suo accesso sul server; il collegamento dell'agente resta. Esca per usare un altro server.", "Since the last reply": "Dall’ultima risposta", "Skipped": "Saltata", + "Something to remember…": "Qualcosa da ricordare…", "Source": "Sorgente", "Start the agent": "Avvia l'agente", "Stop": "Ferma", @@ -2542,6 +2589,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "In attesa di {{model}}: {{reason}}", "What it does": "Cosa fa", "What should happen, and when": "Cosa deve succedere, e quando", + "What your models remember about you, across chats. Change or remove anything here.": "Ciò che i tuoi modelli ricordano di te tra una chat e l’altra. Qui puoi cambiare o rimuovere qualsiasi cosa.", "When": "Quando", "When it matters": "Quando conta", "Writing": "Scrittura", @@ -2636,6 +2684,7 @@ const LEMBAS_I18N = { "About": "O aplikacji", "Account": "Konto", "Activity": "Aktywność", + "Add": "Dodaj", "Add folder…": "Dodaj folder…", "Address of your LLeMbas server": "Adres Twojego serwera LLeMbas", "Agent": "Agent", @@ -2697,6 +2746,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Policzone przez model przy ostatniej odpowiedzi; kategorie są szacunkowe.", "Create": "Utwórz", "Cut short here; the whole of it is in the web interface.": "Tu skrócone; całość jest w interfejsie WWW.", + "Data group": "Grupa danych", "Days": "Dni", "Delete": "Usuń", "Delete chat": "Usuń czat", @@ -2759,6 +2809,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — najpierw pyta", "Max": "Max", "Maximise": "Maksymalizuj", + "Memory": "Pamięć", "Memory and personal": "Pamięć i dane osobiste", "Menu": "Menu", "Message": "Wiadomość", @@ -2793,10 +2844,12 @@ const LEMBAS_I18N = { "Not measured.": "Nie zmierzono.", "Not run yet": "Jeszcze nie uruchomione", "Not yet": "Jeszcze nie", + "Noted by a model": "Zanotowane przez model", "Nothing has been run in this shell yet.": "W tej powłoce nic jeszcze nie uruchomiono.", "Nothing has been said in the room yet.": "W pokoju nikt jeszcze nic nie powiedział.", "Nothing is scheduled.": "Nic nie jest zaplanowane.", "Nothing matches.": "Nic nie pasuje.", + "Nothing remembered yet.": "Na razie nic nie zapamiętano.", "Nothing running.": "Nic nie działa.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "W tle nic nie działa. Podagenci, członkowie zespołu i polecenia w tle pojawiają się tu, gdy pracują.", "Nothing to report": "Nic do zgłoszenia", @@ -2837,6 +2890,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Wygeneruj odpowiedź ponownie", "Remove": "Usuń", "Remove %(dir)s": "Usuń %(dir)s", + "Remove this memory?": "Usunąć to wspomnienie?", "Rename": "Zmień nazwę", "Rename chat": "Zmień nazwę czatu", "Rename folder": "Zmień nazwę folderu", @@ -2880,6 +2934,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Wylogowanie zrzeka się logowania aplikacji na serwerze; połączenie agenta zostaje. Wyloguj się, aby użyć innego serwera.", "Since the last reply": "Od ostatniej odpowiedzi", "Skipped": "Pominięty", + "Something to remember…": "Coś do zapamiętania…", "Source": "Źródło", "Start the agent": "Uruchom agenta", "Stop": "Zatrzymać", @@ -2974,6 +3029,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Czekam na {{model}}: {{reason}}", "What it does": "Co robi", "What should happen, and when": "Co ma się stać i kiedy", + "What your models remember about you, across chats. Change or remove anything here.": "Co Twoje modele pamiętają o Tobie we wszystkich czatach. Tutaj możesz wszystko zmienić lub usunąć.", "When": "Kiedy", "When it matters": "Gdy to ważne", "Writing": "Pisanie", @@ -3068,6 +3124,7 @@ const LEMBAS_I18N = { "About": "O aplikácii", "Account": "Účet", "Activity": "Aktivita", + "Add": "Pridať", "Add folder…": "Pridať priečinok…", "Address of your LLeMbas server": "Adresa vášho servera LLeMbas", "Agent": "Agent", @@ -3129,6 +3186,7 @@ const LEMBAS_I18N = { "Counted by the model at the last reply; the categories are estimated.": "Spočítal model pri poslednej odpovedi; kategórie sú odhadnuté.", "Create": "Vytvoriť", "Cut short here; the whole of it is in the web interface.": "Tu skrátené; celé je to vo webovom rozhraní.", + "Data group": "Dátová oblasť", "Days": "Dni", "Delete": "Zmazať", "Delete chat": "Zmazať konverzáciu", @@ -3191,6 +3249,7 @@ const LEMBAS_I18N = { "Manual — asks first": "Manual — najprv sa opýta", "Max": "Max", "Maximise": "Maximalizovať", + "Memory": "Pamäť", "Memory and personal": "Pamäť a osobné", "Menu": "Ponuka", "Message": "Správa", @@ -3225,10 +3284,12 @@ const LEMBAS_I18N = { "Not measured.": "Nezmerané.", "Not run yet": "Ešte nebežala", "Not yet": "Ešte nie", + "Noted by a model": "Zaznamenal model", "Nothing has been run in this shell yet.": "V tomto shelli sa zatiaľ nič nespustilo.", "Nothing has been said in the room yet.": "V miestnosti zatiaľ nikto nič nepovedal.", "Nothing is scheduled.": "Nič nie je naplánované.", "Nothing matches.": "Nič nezodpovedá.", + "Nothing remembered yet.": "Zatiaľ si nič nepamätá.", "Nothing running.": "Nič nebeží.", "Nothing runs in the background. Subagents, a team's members and background commands show here while they work.": "Na pozadí nič nebeží. Podagenti, členovia tímu a príkazy na pozadí sa tu zobrazia, kým pracujú.", "Nothing to report": "Nič na hlásenie", @@ -3269,6 +3330,7 @@ const LEMBAS_I18N = { "Regenerate reply": "Vygenerovať odpoveď znova", "Remove": "Odstrániť", "Remove %(dir)s": "Odstrániť %(dir)s", + "Remove this memory?": "Odstrániť tento záznam z pamäte?", "Rename": "Premenovať", "Rename chat": "Premenovať konverzáciu", "Rename folder": "Premenovať zložku", @@ -3312,6 +3374,7 @@ const LEMBAS_I18N = { "Signing out gives the app's sign-in up on the server; the agent's link stays. Sign out to use another server.": "Odhlásením sa prihlásenie aplikácie na serveri zruší; prepojenie agenta zostane. Ak chcete použiť iný server, odhláste sa.", "Since the last reply": "Od poslednej odpovede", "Skipped": "Vynechané", + "Something to remember…": "Niečo na zapamätanie…", "Source": "Zdroj", "Start the agent": "Spustiť agenta", "Stop": "Zastaviť", @@ -3406,6 +3469,7 @@ const LEMBAS_I18N = { "Waiting for {{model}}: {{reason}}": "Čaká sa na {{model}}: {{reason}}", "What it does": "Čo robí", "What should happen, and when": "Čo sa má stať a kedy", + "What your models remember about you, across chats. Change or remove anything here.": "Čo si o vás vaše modely pamätajú naprieč konverzáciami. Čokoľvek tu môžete zmeniť alebo odstrániť.", "When": "Kedy", "When it matters": "Keď na tom záleží", "Writing": "Píše", diff --git a/ui/index.html b/ui/index.html index 2a17a70..bcf81f2 100644 --- a/ui/index.html +++ b/ui/index.html @@ -54,6 +54,7 @@ +
    @@ -133,6 +134,15 @@
    + +