diff --git a/CHANGELOG.md b/CHANGELOG.md index 742d001..5dc55b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,13 +47,24 @@ All notable changes to LLeMbas Desktop. The format follows ### Added -- **Scheduled tasks** (with a server that has them): the list with each task's next run, how its - last run went and a switch to pause it; a task's page with what it does, when, how it tells you, - the next runs, its history (each run's status, time, model and its result to open) and **Run - now**; and a new task said in your own words -- the server reads them into a rule and an - instruction and shows the next five runs before you save. While the app runs, a scheduled task's - result the server decides to tell of comes as a system notification (never a run with nothing - to report). A server without scheduled tasks says so. +- **Scheduled tasks** (with a server that has them, LLeMbas 2.0.0-beta.6 or later): the list + with each task's next run, how its last run went and a switch to pause it, kept up to date as + the server runs them; a task's page with what it does, when (and in which time zone), its + model, effort, team and how it tells you, the next runs, and its history -- each run's status, + time, whether it was late, caught up or run by hand, the model it ran on (and that it switched + from a busy one), and its result to unfold in place, with its chat to open; earlier runs on + request. **Run now**, **Edit**, pause and resume, and delete. A new task is said in your own + words: the server reads them into a rule, a title and an instruction and shows the next five + runs before you save; the title and instruction can be changed, and where the words give no + time the days and the time can be set by hand. Each task's settings: when to tell you + (*when it matters*, *always*, *never*), a new chat for each run or one that grows, its model + (or the first free one of the scheduling models), its effort and its team. **Models** on the + list chooses which models scheduled tasks may run on, each shown free or busy. +- **A scheduled task's result comes as a system notification** while the app runs, by the + task's own setting: a result, never a run with nothing to report unless the task says + *always*, nothing for *never*; a failure the first time, and on the third in a row the + question whether to pause it. The app follows the server's event stream for this rather than + asking every few seconds. ## [1.0.0-beta.3] — 2026-11-09 diff --git a/scripts/gen-i18n.py b/scripts/gen-i18n.py index 9eb2737..c59633a 100755 --- a/scripts/gen-i18n.py +++ b/scripts/gen-i18n.py @@ -312,13 +312,52 @@ KEYS = ( "It has not run yet.", "late", "caught up", - "Open the result", "Delete this scheduled task?", "“%(title)s” stops, and its history goes. The chats it wrote stay.", "Every weekday at 8:00, summarise the news on my reading list", "What should happen, and when", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.", "These words give no run in the future.", + "%(model)s was busy, so it ran on this one.", + "%(n)s missed", + "A busy model is waited for, then the next free one is used.", + "A result, but not a run with nothing to report.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.", + "A team of up to %(n)s", + "A team works on it together, its own agent included.", + "Choose a model to choose its effort.", + "Due %(when)s", + "Each run sees the ones before it.", + "Each run starts with nothing but the instruction.", + "Earlier runs", + "Edit %(title)s", + "Every result, even with nothing to report.", + "Models for scheduled tasks", + "No team", + "Only in its history.", + "Open the chat", + "Result", + "The first free scheduling model", + "The instruction each run is given, on its own: say everything it needs.", + "The last %(n)s runs failed: %(why)s", + "The result is no longer there.", + "Times in %(zone)s", + "Title", + "Written to %(path)s", + "busy", + "free", + "switched", + "tried again %(when)s", + "Set the time yourself", + "Days", + "Time", + "No day ticked: every day.", + "A scheduled task failed 3 times in a row. Pause it?", + "Edit", + "Models", + "Open its chat", + "The model's own", + "run by hand", ) # The app's own sentences. The mode names (Manual, Edit, Auto, Plan) stay English, as the @@ -482,13 +521,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Ještě neběžela.", "late": "pozdě", "caught up": "dohnáno", - "Open the result": "Otevřít výsledek", "Delete this scheduled task?": "Odstranit tuto naplánovanou úlohu?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s“ se zastaví a její historie zmizí. Konverzace, které napsala, zůstanou.", "Every weekday at 8:00, summarise the news on my reading list": "Každý pracovní den v 8:00 shrň zprávy z mého seznamu ke čtení", "What should happen, and when": "Co se má stát a kdy", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Vlastními slovy. Server z nich vytvoří pravidlo a pokyn a před uložením vám ukáže další spuštění.", "These words give no run in the future.": "Z těchto slov nevychází žádné budoucí spuštění.", + "%(model)s was busy, so it ran on this one.": "%(model)s byl zaneprázdněný, proto běžela na tomto.", + "%(n)s missed": "zmeškáno: %(n)s", + "A busy model is waited for, then the next free one is used.": "Na zaneprázdněný model se čeká, pak se použije další volný.", + "A result, but not a run with nothing to report.": "Výsledek, ale ne spuštění, které nemá co hlásit.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Úloha bez vlastního modelu běží na prvním volném z těchto, v tomto pořadí. Žádný nezaškrtnutý: všechny.", + "A team of up to %(n)s": "Tým: nejvýše %(n)s", + "A team works on it together, its own agent included.": "Pracuje na ní tým společně, včetně jejího vlastního agenta.", + "Choose a model to choose its effort.": "Vyberte model, abyste mohli zvolit jeho náročnost.", + "Due %(when)s": "Naplánováno na %(when)s", + "Each run sees the ones before it.": "Každé spuštění vidí ta předchozí.", + "Each run starts with nothing but the instruction.": "Každé spuštění začíná jen s instrukcí.", + "Earlier runs": "Starší spuštění", + "Edit %(title)s": "Upravit %(title)s", + "Every result, even with nothing to report.": "Každý výsledek, i když není co hlásit.", + "Models for scheduled tasks": "Modely pro naplánované úlohy", + "No team": "Bez týmu", + "Only in its history.": "Jen v její historii.", + "Open the chat": "Otevřít konverzaci", + "Result": "Výsledek", + "The first free scheduling model": "První volný model pro plánování", + "The instruction each run is given, on its own: say everything it needs.": "Instrukce, kterou dostane každé spuštění samostatně: uveďte vše, co potřebuje.", + "The last %(n)s runs failed: %(why)s": "Posledních %(n)s spuštění selhalo: %(why)s", + "The result is no longer there.": "Výsledek už tu není.", + "Times in %(zone)s": "Časové pásmo: %(zone)s", + "Title": "Název", + "Written to %(path)s": "Zapsáno do %(path)s", + "busy": "zaneprázdněný", + "free": "volný", + "switched": "přepnuto", + "tried again %(when)s": "znovu %(when)s", + "Set the time yourself": "Nastavit čas sami", + "Days": "Dny", + "Time": "Čas", + "No day ticked: every day.": "Žádný den nezaškrtnutý: každý den.", }, "de": { "Menu": "Menü", @@ -647,13 +719,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Sie ist noch nicht gelaufen.", "late": "verspätet", "caught up": "nachgeholt", - "Open the result": "Ergebnis öffnen", "Delete this scheduled task?": "Diese geplante Aufgabe löschen?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s“ hört auf, und ihr Verlauf wird gelöscht. Die Chats, die sie geschrieben hat, bleiben.", "Every weekday at 8:00, summarise the news on my reading list": "Jeden Werktag um 8:00 die Nachrichten auf meiner Leseliste zusammenfassen", "What should happen, and when": "Was soll wann geschehen?", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "In Ihren eigenen Worten. Der Server macht daraus eine Regel und eine Anweisung und zeigt Ihnen die nächsten Läufe, bevor etwas gespeichert wird.", "These words give no run in the future.": "Diese Worte ergeben keinen künftigen Lauf.", + "%(model)s was busy, so it ran on this one.": "%(model)s war belegt, daher lief sie auf diesem.", + "%(n)s missed": "%(n)s verpasst", + "A busy model is waited for, then the next free one is used.": "Auf ein belegtes Modell wird gewartet, dann wird das nächste freie genommen.", + "A result, but not a run with nothing to report.": "Ein Ergebnis, aber kein Lauf ohne Neuigkeiten.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Eine Aufgabe ohne eigenes Modell läuft auf dem ersten freien davon, in dieser Reihenfolge. Keines angehakt: alle.", + "A team of up to %(n)s": "Ein Team mit bis zu %(n)s", + "A team works on it together, its own agent included.": "Ein Team arbeitet gemeinsam daran, ihr eigener Agent eingeschlossen.", + "Choose a model to choose its effort.": "Wählen Sie ein Modell, um seinen Aufwand zu wählen.", + "Due %(when)s": "Fällig %(when)s", + "Each run sees the ones before it.": "Jeder Lauf sieht die vorherigen.", + "Each run starts with nothing but the instruction.": "Jeder Lauf beginnt nur mit der Anweisung.", + "Earlier runs": "Frühere Läufe", + "Edit %(title)s": "%(title)s bearbeiten", + "Every result, even with nothing to report.": "Jedes Ergebnis, auch ohne Neuigkeiten.", + "Models for scheduled tasks": "Modelle für geplante Aufgaben", + "No team": "Kein Team", + "Only in its history.": "Nur in ihrem Verlauf.", + "Open the chat": "Chat öffnen", + "Result": "Ergebnis", + "The first free scheduling model": "Das erste freie Planungsmodell", + "The instruction each run is given, on its own: say everything it needs.": "Die Anweisung, die jeder Lauf für sich erhält: Sagen Sie alles, was er braucht.", + "The last %(n)s runs failed: %(why)s": "Die letzten %(n)s Läufe sind fehlgeschlagen: %(why)s", + "The result is no longer there.": "Das Ergebnis ist nicht mehr da.", + "Times in %(zone)s": "Zeitzone: %(zone)s", + "Title": "Titel", + "Written to %(path)s": "Geschrieben nach %(path)s", + "busy": "belegt", + "free": "frei", + "switched": "gewechselt", + "tried again %(when)s": "erneut %(when)s", + "Set the time yourself": "Zeit selbst festlegen", + "Days": "Tage", + "Time": "Uhrzeit", + "No day ticked: every day.": "Kein Tag angehakt: jeden Tag.", }, "es": { "Menu": "Menú", @@ -812,13 +917,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Aún no se ha ejecutado.", "late": "con retraso", "caught up": "recuperada", - "Open the result": "Abrir el resultado", "Delete this scheduled task?": "¿Eliminar esta tarea programada?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "«%(title)s» se detiene y su historial se borra. Los chats que escribió se conservan.", "Every weekday at 8:00, summarise the news on my reading list": "Cada día laborable a las 8:00, resume las noticias de mi lista de lectura", "What should happen, and when": "Qué debe ocurrir, y cuándo", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Con sus propias palabras. El servidor las convierte en una regla y una instrucción, y le muestra las próximas ejecuciones antes de guardar nada.", "These words give no run in the future.": "Estas palabras no dan ninguna ejecución futura.", + "%(model)s was busy, so it ran on this one.": "%(model)s estaba ocupado, así que se ejecutó en este.", + "%(n)s missed": "%(n)s perdidas", + "A busy model is waited for, then the next free one is used.": "Se espera a un modelo ocupado y luego se usa el siguiente libre.", + "A result, but not a run with nothing to report.": "Un resultado, pero no una ejecución sin nada que informar.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Una tarea sin modelo propio se ejecuta en el primero libre de estos, en este orden. Ninguno marcado: todos.", + "A team of up to %(n)s": "Un equipo de hasta %(n)s", + "A team works on it together, its own agent included.": "Un equipo trabaja en ella, incluido su propio agente.", + "Choose a model to choose its effort.": "Elija un modelo para elegir su esfuerzo.", + "Due %(when)s": "Prevista %(when)s", + "Each run sees the ones before it.": "Cada ejecución ve las anteriores.", + "Each run starts with nothing but the instruction.": "Cada ejecución empieza solo con la instrucción.", + "Earlier runs": "Ejecuciones anteriores", + "Edit %(title)s": "Editar %(title)s", + "Every result, even with nothing to report.": "Cada resultado, incluso sin nada que informar.", + "Models for scheduled tasks": "Modelos para tareas programadas", + "No team": "Sin equipo", + "Only in its history.": "Solo en su historial.", + "Open the chat": "Abrir el chat", + "Result": "Resultado", + "The first free scheduling model": "El primer modelo de programación libre", + "The instruction each run is given, on its own: say everything it needs.": "La instrucción que recibe cada ejecución por sí sola: diga todo lo que necesita.", + "The last %(n)s runs failed: %(why)s": "Las últimas %(n)s ejecuciones fallaron: %(why)s", + "The result is no longer there.": "El resultado ya no está.", + "Times in %(zone)s": "Zona horaria: %(zone)s", + "Title": "Título", + "Written to %(path)s": "Escrito en %(path)s", + "busy": "ocupado", + "free": "libre", + "switched": "cambiado", + "tried again %(when)s": "de nuevo %(when)s", + "Set the time yourself": "Fijar la hora usted mismo", + "Days": "Días", + "Time": "Hora", + "No day ticked: every day.": "Ningún día marcado: todos los días.", }, "fr": { "Menu": "Menu", @@ -977,13 +1115,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Elle n’a pas encore été exécutée.", "late": "en retard", "caught up": "rattrapée", - "Open the result": "Ouvrir le résultat", "Delete this scheduled task?": "Supprimer cette tâche planifiée ?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "« %(title)s » s’arrête et son historique disparaît. Les conversations qu’elle a écrites restent.", "Every weekday at 8:00, summarise the news on my reading list": "Chaque jour ouvré à 8 h, résume les nouvelles de ma liste de lecture", "What should happen, and when": "Ce qui doit se passer, et quand", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Avec vos propres mots. Le serveur en fait une règle et une instruction, et vous montre les prochaines exécutions avant que rien ne soit enregistré.", "These words give no run in the future.": "Ces mots ne donnent aucune exécution à venir.", + "%(model)s was busy, so it ran on this one.": "%(model)s était occupé, elle a donc tourné sur celui-ci.", + "%(n)s missed": "%(n)s manquées", + "A busy model is waited for, then the next free one is used.": "Un modèle occupé est attendu, puis le suivant libre est utilisé.", + "A result, but not a run with nothing to report.": "Un résultat, mais pas une exécution sans rien à signaler.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Une tâche sans modèle à elle tourne sur le premier libre de ceux-ci, dans cet ordre. Aucun coché : tous.", + "A team of up to %(n)s": "Une équipe jusqu'à %(n)s", + "A team works on it together, its own agent included.": "Une équipe y travaille ensemble, son propre agent compris.", + "Choose a model to choose its effort.": "Choisissez un modèle pour choisir son effort.", + "Due %(when)s": "Prévue %(when)s", + "Each run sees the ones before it.": "Chaque exécution voit les précédentes.", + "Each run starts with nothing but the instruction.": "Chaque exécution part de la seule instruction.", + "Earlier runs": "Exécutions précédentes", + "Edit %(title)s": "Modifier %(title)s", + "Every result, even with nothing to report.": "Chaque résultat, même sans rien à signaler.", + "Models for scheduled tasks": "Modèles pour les tâches planifiées", + "No team": "Pas d'équipe", + "Only in its history.": "Seulement dans son historique.", + "Open the chat": "Ouvrir la conversation", + "Result": "Résultat", + "The first free scheduling model": "Le premier modèle de planification libre", + "The instruction each run is given, on its own: say everything it needs.": "L'instruction donnée à chaque exécution, seule : dites tout ce dont elle a besoin.", + "The last %(n)s runs failed: %(why)s": "Les %(n)s dernières exécutions ont échoué : %(why)s", + "The result is no longer there.": "Le résultat n'est plus là.", + "Times in %(zone)s": "Fuseau horaire : %(zone)s", + "Title": "Titre", + "Written to %(path)s": "Écrit dans %(path)s", + "busy": "occupé", + "free": "libre", + "switched": "changé", + "tried again %(when)s": "de nouveau %(when)s", + "Set the time yourself": "Régler l'heure vous-même", + "Days": "Jours", + "Time": "Heure", + "No day ticked: every day.": "Aucun jour coché : tous les jours.", }, "hu": { "Menu": "Menü", @@ -1142,13 +1313,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Még nem futott.", "late": "késve", "caught up": "pótolva", - "Open the result": "Az eredmény megnyitása", "Delete this scheduled task?": "Törli ezt az ütemezett feladatot?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s” leáll, és az előzményei törlődnek. Az általa írt beszélgetések megmaradnak.", "Every weekday at 8:00, summarise the news on my reading list": "Minden hétköznap 8:00-kor foglald össze az olvasólistám híreit", "What should happen, and when": "Mi történjen, és mikor", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "A saját szavaival. A szerver szabályt és utasítást készít belőlük, és mentés előtt megmutatja a következő futásokat.", "These words give no run in the future.": "Ezekből a szavakból nem következik jövőbeli futás.", + "%(model)s was busy, so it ran on this one.": "%(model)s foglalt volt, ezért ezen futott.", + "%(n)s missed": "%(n)s kimaradt", + "A busy model is waited for, then the next free one is used.": "A foglalt modellre vár, majd a következő szabadot használja.", + "A result, but not a run with nothing to report.": "Eredmény, de nem olyan futás, amelynek nincs mit jelentenie.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "A saját modell nélküli feladat ezek közül az első szabadon fut, ebben a sorrendben. Ha egy sincs bejelölve: mindegyiken.", + "A team of up to %(n)s": "Legfeljebb %(n)s fős csapat", + "A team works on it together, its own agent included.": "Egy csapat dolgozik rajta együtt, a saját ügynökét is beleértve.", + "Choose a model to choose its effort.": "Válasszon modellt az erőfeszítés megadásához.", + "Due %(when)s": "Esedékes: %(when)s", + "Each run sees the ones before it.": "Minden futás látja az előzőeket.", + "Each run starts with nothing but the instruction.": "Minden futás csak az utasítással indul.", + "Earlier runs": "Korábbi futások", + "Edit %(title)s": "%(title)s szerkesztése", + "Every result, even with nothing to report.": "Minden eredmény, akkor is, ha nincs mit jelenteni.", + "Models for scheduled tasks": "Modellek az ütemezett feladatokhoz", + "No team": "Nincs csapat", + "Only in its history.": "Csak az előzményeiben.", + "Open the chat": "A csevegés megnyitása", + "Result": "Eredmény", + "The first free scheduling model": "Az első szabad ütemezési modell", + "The instruction each run is given, on its own: say everything it needs.": "Az utasítás, amelyet minden futás önmagában kap: mondjon el mindent, amire szüksége van.", + "The last %(n)s runs failed: %(why)s": "Az utolsó %(n)s futás sikertelen: %(why)s", + "The result is no longer there.": "Az eredmény már nincs meg.", + "Times in %(zone)s": "Időzóna: %(zone)s", + "Title": "Cím", + "Written to %(path)s": "Kiírva ide: %(path)s", + "busy": "foglalt", + "free": "szabad", + "switched": "váltva", + "tried again %(when)s": "újra %(when)s", + "Set the time yourself": "Az időpont megadása kézzel", + "Days": "Napok", + "Time": "Időpont", + "No day ticked: every day.": "Ha egy nap sincs bejelölve: minden nap.", }, "it": { "Menu": "Menu", @@ -1307,13 +1511,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Non è ancora stata eseguita.", "late": "in ritardo", "caught up": "recuperata", - "Open the result": "Apri il risultato", "Delete this scheduled task?": "Eliminare questa attività pianificata?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "«%(title)s» si ferma e la sua cronologia viene eliminata. Le chat che ha scritto restano.", "Every weekday at 8:00, summarise the news on my reading list": "Ogni giorno feriale alle 8:00, riassumi le notizie della mia lista di lettura", "What should happen, and when": "Cosa deve succedere, e quando", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Con parole tue. Il server ne ricava una regola e un’istruzione e ti mostra le prossime esecuzioni prima di salvare.", "These words give no run in the future.": "Queste parole non danno nessuna esecuzione futura.", + "%(model)s was busy, so it ran on this one.": "%(model)s era occupato, quindi è stata eseguita su questo.", + "%(n)s missed": "%(n)s perse", + "A busy model is waited for, then the next free one is used.": "Si attende un modello occupato, poi si usa il successivo libero.", + "A result, but not a run with nothing to report.": "Un risultato, ma non un’esecuzione senza nulla da segnalare.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Un’attività senza un modello proprio gira sul primo libero di questi, in quest’ordine. Nessuno spuntato: tutti.", + "A team of up to %(n)s": "Un team fino a %(n)s", + "A team works on it together, its own agent included.": "Un team ci lavora insieme, incluso il suo agente.", + "Choose a model to choose its effort.": "Scegli un modello per sceglierne l’impegno.", + "Due %(when)s": "Prevista %(when)s", + "Each run sees the ones before it.": "Ogni esecuzione vede quelle precedenti.", + "Each run starts with nothing but the instruction.": "Ogni esecuzione parte solo dall’istruzione.", + "Earlier runs": "Esecuzioni precedenti", + "Edit %(title)s": "Modifica %(title)s", + "Every result, even with nothing to report.": "Ogni risultato, anche senza nulla da segnalare.", + "Models for scheduled tasks": "Modelli per le attività pianificate", + "No team": "Nessun team", + "Only in its history.": "Solo nella sua cronologia.", + "Open the chat": "Apri la chat", + "Result": "Risultato", + "The first free scheduling model": "Il primo modello di pianificazione libero", + "The instruction each run is given, on its own: say everything it needs.": "L’istruzione che ogni esecuzione riceve da sola: di’ tutto ciò che le serve.", + "The last %(n)s runs failed: %(why)s": "Le ultime %(n)s esecuzioni non sono riuscite: %(why)s", + "The result is no longer there.": "Il risultato non c’è più.", + "Times in %(zone)s": "Fuso orario: %(zone)s", + "Title": "Titolo", + "Written to %(path)s": "Scritto in %(path)s", + "busy": "occupato", + "free": "libero", + "switched": "cambiato", + "tried again %(when)s": "di nuovo %(when)s", + "Set the time yourself": "Imposta tu l’orario", + "Days": "Giorni", + "Time": "Ora", + "No day ticked: every day.": "Nessun giorno spuntato: ogni giorno.", }, "pl": { "Menu": "Menu", @@ -1472,13 +1709,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Jeszcze nie było uruchomione.", "late": "z opóźnieniem", "caught up": "nadrobione", - "Open the result": "Otwórz wynik", "Delete this scheduled task?": "Usunąć to zaplanowane zadanie?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s” zatrzyma się, a jego historia zniknie. Czaty, które napisało, zostaną.", "Every weekday at 8:00, summarise the news on my reading list": "W każdy dzień roboczy o 8:00 streść wiadomości z mojej listy do czytania", "What should happen, and when": "Co ma się stać i kiedy", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Własnymi słowami. Serwer zamieni je w regułę i polecenie i pokaże następne uruchomienia, zanim cokolwiek zostanie zapisane.", "These words give no run in the future.": "Z tych słów nie wynika żadne przyszłe uruchomienie.", + "%(model)s was busy, so it ran on this one.": "%(model)s był zajęty, więc uruchomiono je na tym.", + "%(n)s missed": "pominięte: %(n)s", + "A busy model is waited for, then the next free one is used.": "Na zajęty model się czeka, potem używany jest następny wolny.", + "A result, but not a run with nothing to report.": "Wynik, ale nie uruchomienie bez niczego do zgłoszenia.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Zadanie bez własnego modelu działa na pierwszym wolnym z tych, w tej kolejności. Żaden niezaznaczony: wszystkie.", + "A team of up to %(n)s": "Zespół do %(n)s", + "A team works on it together, its own agent included.": "Pracuje nad nim zespół, wraz z jego własnym agentem.", + "Choose a model to choose its effort.": "Wybierz model, aby wybrać jego wysiłek.", + "Due %(when)s": "Zaplanowane na %(when)s", + "Each run sees the ones before it.": "Każde uruchomienie widzi poprzednie.", + "Each run starts with nothing but the instruction.": "Każde uruchomienie zaczyna tylko od instrukcji.", + "Earlier runs": "Wcześniejsze uruchomienia", + "Edit %(title)s": "Edytuj %(title)s", + "Every result, even with nothing to report.": "Każdy wynik, nawet bez niczego do zgłoszenia.", + "Models for scheduled tasks": "Modele dla zaplanowanych zadań", + "No team": "Bez zespołu", + "Only in its history.": "Tylko w jego historii.", + "Open the chat": "Otwórz czat", + "Result": "Wynik", + "The first free scheduling model": "Pierwszy wolny model do planowania", + "The instruction each run is given, on its own: say everything it needs.": "Instrukcja, którą każde uruchomienie dostaje samodzielnie: podaj wszystko, czego potrzebuje.", + "The last %(n)s runs failed: %(why)s": "Ostatnie uruchomienia nie powiodły się (%(n)s): %(why)s", + "The result is no longer there.": "Wyniku już nie ma.", + "Times in %(zone)s": "Strefa czasowa: %(zone)s", + "Title": "Tytuł", + "Written to %(path)s": "Zapisano do %(path)s", + "busy": "zajęty", + "free": "wolny", + "switched": "zmieniony", + "tried again %(when)s": "ponownie %(when)s", + "Set the time yourself": "Ustaw czas samodzielnie", + "Days": "Dni", + "Time": "Godzina", + "No day ticked: every day.": "Żaden dzień niezaznaczony: codziennie.", }, "sk": { "Menu": "Ponuka", @@ -1637,13 +1907,46 @@ OWN: dict[str, dict[str, str]] = { "It has not run yet.": "Ešte nebežala.", "late": "neskoro", "caught up": "dobehnuté", - "Open the result": "Otvoriť výsledok", "Delete this scheduled task?": "Odstrániť túto naplánovanú úlohu?", "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s“ sa zastaví a jej história zmizne. Konverzácie, ktoré napísala, zostanú.", "Every weekday at 8:00, summarise the news on my reading list": "Každý pracovný deň o 8:00 zhrň správy z môjho zoznamu na čítanie", "What should happen, and when": "Čo sa má stať a kedy", "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Vlastnými slovami. Server z nich vytvorí pravidlo a pokyn a pred uložením vám ukáže ďalšie spustenia.", "These words give no run in the future.": "Z týchto slov nevychádza žiadne budúce spustenie.", + "%(model)s was busy, so it ran on this one.": "%(model)s bol zaneprázdnený, preto bežala na tomto.", + "%(n)s missed": "zmeškané: %(n)s", + "A busy model is waited for, then the next free one is used.": "Na zaneprázdnený model sa čaká, potom sa použije ďalší voľný.", + "A result, but not a run with nothing to report.": "Výsledok, ale nie spustenie, ktoré nemá čo hlásiť.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Úloha bez vlastného modelu beží na prvom voľnom z týchto, v tomto poradí. Žiadny nezaškrtnutý: všetky.", + "A team of up to %(n)s": "Tím: najviac %(n)s", + "A team works on it together, its own agent included.": "Pracuje na nej tím spoločne, vrátane jej vlastného agenta.", + "Choose a model to choose its effort.": "Vyberte model, aby ste mohli zvoliť jeho náročnosť.", + "Due %(when)s": "Naplánované na %(when)s", + "Each run sees the ones before it.": "Každé spustenie vidí tie predchádzajúce.", + "Each run starts with nothing but the instruction.": "Každé spustenie začína len s inštrukciou.", + "Earlier runs": "Staršie spustenia", + "Edit %(title)s": "Upraviť %(title)s", + "Every result, even with nothing to report.": "Každý výsledok, aj keď nie je čo hlásiť.", + "Models for scheduled tasks": "Modely pre naplánované úlohy", + "No team": "Bez tímu", + "Only in its history.": "Len v jej histórii.", + "Open the chat": "Otvoriť konverzáciu", + "Result": "Výsledok", + "The first free scheduling model": "Prvý voľný model na plánovanie", + "The instruction each run is given, on its own: say everything it needs.": "Inštrukcia, ktorú dostane každé spustenie samostatne: uveďte všetko, čo potrebuje.", + "The last %(n)s runs failed: %(why)s": "Posledné spustenia zlyhali (%(n)s): %(why)s", + "The result is no longer there.": "Výsledok už tu nie je.", + "Times in %(zone)s": "Časové pásmo: %(zone)s", + "Title": "Názov", + "Written to %(path)s": "Zapísané do %(path)s", + "busy": "zaneprázdnený", + "free": "voľný", + "switched": "prepnuté", + "tried again %(when)s": "znova %(when)s", + "Set the time yourself": "Nastaviť čas sami", + "Days": "Dni", + "Time": "Čas", + "No day ticked: every day.": "Žiadny deň nezaškrtnutý: každý deň.", }, } diff --git a/ui/app.css b/ui/app.css index 5dacd95..3b48ecf 100644 --- a/ui/app.css +++ b/ui/app.css @@ -108,7 +108,7 @@ bdi { unicode-bidi: isolate; } .tool:hover, .pane__btn:hover { color: var(--ink); background: var(--surface-hover); } .tool:disabled { opacity: var(--opacity-dim); cursor: default; } -input[type="text"], input[type="search"], input[type="number"], select, textarea { +input[type="text"], input[type="search"], input[type="number"], input[type="time"], select, textarea { min-width: 0; font: var(--text-sm) var(--font-body); color: var(--ink); @@ -116,7 +116,7 @@ input[type="text"], input[type="search"], input[type="number"], select, textarea border: var(--border-w) solid var(--border-strong); border-radius: var(--radius-lg); } -input[type="text"], input[type="search"], input[type="number"], select { height: var(--control-h); padding: 0 var(--control-px-sm); } +input[type="text"], input[type="search"], input[type="number"], input[type="time"], select { height: var(--control-h); padding: 0 var(--control-px-sm); } textarea { padding: var(--sp-2) var(--control-px-sm); resize: none; line-height: var(--leading-normal); } :focus-visible { outline: var(--outline-w) solid var(--accent); outline-offset: var(--outline-offset); } @@ -1380,16 +1380,47 @@ details[open] > summary .step__chev .icon { transform: rotate(90deg); } font-size: var(--text-sm); border-bottom: var(--border-w) solid var(--border); } -.run__when { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; } +.run__when { display: flex; gap: var(--sp-2); align-items: center; min-width: 0; white-space: nowrap; overflow: hidden; } .run__late { font: var(--text-xs) var(--font-ui); color: var(--warning); } .run__model { display: inline-grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-2); align-items: center; min-width: 0; color: var(--ink-muted); } .run__model > span:last-child { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } -.run__error { max-width: var(--w-field); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--warning); font-size: var(--text-xs); } +.run__error { max-width: var(--w-field-sm); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--warning); font-size: var(--text-xs); } .sched--new { gap: var(--sp-3); } .sched__words { width: 100%; font-size: var(--text-base); border-radius: var(--radius-soft); padding: var(--sp-3) var(--sp-4); } .sched__preview { margin-top: var(--sp-3); } .sched__card { display: grid; gap: var(--sp-4); padding: var(--sp-4); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); } .sched__title { font: 600 var(--text-md) / var(--leading-tight) var(--font-body); } +/* A run's row opens its result under it, across every track. */ +.run__open { justify-self: end; } +.run__switched { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); } +.run__detail { grid-column: 1 / -1; display: grid; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); margin-bottom: var(--sp-2); background: var(--surface); border: var(--border-w) solid var(--border); border-radius: var(--radius-soft); } +.run__detail[hidden] { display: none; } +.run__report { font: var(--text-xs) var(--font-ui); color: var(--ink-muted); overflow-wrap: anywhere; } +.sched__more { justify-self: start; } +.sched + .sched__history { margin-top: var(--sp-6); } +.sched__trouble { padding: var(--sp-3) var(--sp-4); color: var(--warning); background: var(--warning-soft); border-radius: var(--radius-soft); overflow-wrap: anywhere; } +.fact__hint { font: var(--text-xs) var(--font-ui); color: var(--ink-faint); } +/* The form: the When field's words on the control's track, one line, the time zone as its hint. */ +.sched__desc { align-self: center; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-sm); } +.sched__desc.is-error { color: var(--warning); } +/* The rule by hand: seven day toggles on equal tracks, and a time. */ +.sched__manual[hidden] { display: none; } +.sdays { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: var(--sp-1); } +.sday { position: relative; display: grid; place-items: center; min-width: 0; height: var(--control-h); border: var(--border-w) solid var(--border); border-radius: var(--radius-lg); cursor: pointer; font: var(--text-xs) var(--font-ui); color: var(--ink-muted); overflow: hidden; } +.sday input { position: absolute; opacity: 0; pointer-events: none; } +.sday:has(input:checked) { color: var(--accent); background: var(--accent-soft); border-color: var(--accent); } +.sday:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; } +.sched__manual-open { justify-self: start; } +.sched__manual-open[hidden] { display: none; } +.sched__settings { padding-top: var(--sp-3); border-top: var(--border-w) solid var(--border); } +/* The scheduling models: a tick, the avatar, the name, free or busy -- on fixed tracks. */ +.smodels { width: 100%; } +.smodels__hint { font-size: var(--text-xs); color: var(--ink-muted); } +.smodels__list { list-style: none; margin: 0; padding: 0; display: grid; } +.smodel { display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto; gap: var(--sp-2); align-items: center; min-height: var(--control-h); color: var(--ink); cursor: pointer; } +.smodel__name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: var(--text-sm); } +.smodel__state { font: var(--text-xs) var(--font-ui); color: var(--warning); } +.smodel__state.is-free { color: var(--success); } /* A page's way back, before its title: "Scheduled ›". */ .crumb { display: inline-flex; gap: var(--sp-1); align-items: center; padding: 0; font: inherit; color: var(--ink-muted); background: none; border: 0; cursor: pointer; } .crumb:hover { color: var(--ink); } diff --git a/ui/i18n.js b/ui/i18n.js index 94a8d31..56ed091 100644 --- a/ui/i18n.js +++ b/ui/i18n.js @@ -11,8 +11,10 @@ const LEMBAS_I18N = { "cs": { + "%(model)s was busy, so it ran on this one.": "%(model)s byl zaneprázdněný, proto běžela na tomto.", "%(n)s agents working": "Pracující agenti: %(n)s", "%(n)s finished": "Dokončeno: %(n)s", + "%(n)s missed": "zmeškáno: %(n)s", "%(n)s queued": "Ve frontě: %(n)s", "%(n)s tokens": "tokeny: %(n)s", "%(n)s tools offered.": "Nabízené nástroje: %(n)s.", @@ -25,12 +27,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agent pracuje", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Služba LLeMbas CLI už tu běží (pid %(pid)s), ale není připojená: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Služba LLeMbas CLI už tu běží (pid %(pid)s); je agentem tohoto počítače.", + "A busy model is waited for, then the next free one is used.": "Na zaneprázdněný model se čeká, pak se použije další volný.", "A chat asking for more is lowered to this.": "Konverzace, která chce víc, je snížena na tohle.", "A chat on %(server)s, with your account's models and files.": "Konverzace na %(server)s s modely a soubory vašeho účtu.", "A new chat for each run": "Nová konverzace pro každé spuštění", + "A result, but not a run with nothing to report.": "Výsledek, ale ne spuštění, které nemá co hlásit.", + "A scheduled task failed 3 times in a row. Pause it?": "Naplánovaná úloha selhala 3krát za sebou. Pozastavit ji?", "A scheduled task failed.": "Naplánovaná úloha selhala.", "A scheduled task has a result.": "Naplánovaná úloha má výsledek.", "A shell as you, in one of these folders.": "Shell pod vaším účtem, v jedné z těchto složek.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Úloha bez vlastního modelu běží na prvním volném z těchto, v tomto pořadí. Žádný nezaškrtnutý: všechny.", + "A team of up to %(n)s": "Tým: nejvýše %(n)s", + "A team works on it together, its own agent included.": "Pracuje na ní tým společně, včetně jejího vlastního agenta.", "About": "O aplikaci", "Account": "Účet", "Activity": "Aktivita", @@ -57,6 +65,7 @@ const LEMBAS_I18N = { "Chat": "Konverzace", "Chats": "Konverzace", "Check for updates…": "Zkontrolovat aktualizace…", + "Choose a model to choose its effort.": "Vyberte model, abyste mohli zvolit jeho náročnost.", "Close": "Zavřít", "Close (LLeMbas stays in the tray)": "Zavřít (LLeMbas zůstane v oznamovací oblasti)", "Close the activity panel": "Zavřít panel aktivity", @@ -71,6 +80,7 @@ const LEMBAS_I18N = { "Copy": "Kopírovat", "Counted by the model at the last reply; the categories are estimated.": "Spočítal model při poslední odpovědi; kategorie jsou odhadnuté.", "Cut short here; the whole of it is in the web interface.": "Zde zkráceno; celé je to ve webovém rozhraní.", + "Days": "Dny", "Delete": "Smazat", "Delete chat": "Smazat konverzaci", "Delete this chat?": "Smazat tuto konverzaci?", @@ -78,12 +88,19 @@ const LEMBAS_I18N = { "Deny": "Zamítnout", "Done": "Hotovo", "Drop the files to attach them": "Pusťte soubory a přiloží se", + "Due %(when)s": "Naplánováno na %(when)s", + "Each run sees the ones before it.": "Každé spuštění vidí ta předchozí.", + "Each run starts with nothing but the instruction.": "Každé spuštění začíná jen s instrukcí.", "Earlier": "Starší", "Earlier messages": "Starší zprávy", + "Earlier runs": "Starší spuštění", + "Edit": "Upravit", + "Edit %(title)s": "Upravit %(title)s", "Edit message": "Upravit zprávu", "Edit — asks before commands": "Edit — ptá se před příkazy", "Empty folder": "Prázdná složka", "Estimated: no reply has been counted yet.": "Odhad: žádná odpověď ještě nebyla spočítána.", + "Every result, even with nothing to report.": "Každý výsledek, i když není co hlásit.", "Every weekday at 8:00, summarise the news on my reading list": "Každý pracovní den v 8:00 shrň zprávy z mého seznamu ke čtení", "Failed": "Selhalo", "Fewest": "Nejméně", @@ -96,11 +113,11 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Vlastními slovy. Server z nich vytvoří pravidlo a pokyn a před uložením vám ukáže další spuštění.", "Install LLeMbas %(version)s…": "Nainstalovat LLeMbas %(version)s…", "Instruction files": "Soubory s pokyny", - "It has not run yet.": "Ještě neběžela.", + "It has not run yet.": "Ještě neběžel.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Včetně vlastního agenta. Při více než jednom může přidat členy, kteří dělají práci, zatímco je vede.", "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozici LLeMbas %(version)s (tohle je %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop potřebuje server LLeMbas 2.0 nebo novější.", - "Last run": "Poslední spuštění", + "Last run": "Poslední běh", "Let the server open a terminal here": "Dovolit serveru otevřít zde terminál", "Link again": "Propojit znovu", "Link it again to give the agent a new token.": "Propojte ho znovu a agent dostane nový token.", @@ -119,6 +136,8 @@ const LEMBAS_I18N = { "Missed": "Zmeškáno", "Mode": "Režim", "Model": "Model", + "Models": "Modely", + "Models for scheduled tasks": "Modely pro naplánované úlohy", "More": "Více", "Most": "Nejvíce", "Never": "Nikdy", @@ -127,12 +146,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nová naplánovaná úloha", "Next run": "Další spuštění", "No chat yet. Start one with New chat.": "Zatím žádná konverzace. Začněte ji tlačítkem Nová konverzace.", + "No day ticked: every day.": "Žádný den nezaškrtnutý: každý den.", "No files written in this chat yet.": "V tomto chatu se zatím nezapsal žádný soubor.", "No folder yet: add one under Settings, Project folders.": "Zatím žádná složka: přidejte ji v Nastavení, Složky projektů.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Zatím žádná složka: agent nemůže nic otevřít. Přidejte složku projektu.", "No model is available to this account.": "Tomuto účtu není dostupný žádný model.", "No more runs": "Žádná další spuštění", "No project folder yet: the agent can open nothing on this computer.": "Zatím žádná složka projektu: agent na tomto počítači nemůže nic otevřít.", + "No team": "Bez týmu", "Not measured.": "Neměřeno.", "Not run yet": "Ještě neběžela", "Not yet": "Ještě ne", @@ -145,12 +166,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Zatím nic.", "Notifications": "Upozornění", "One chat that grows": "Jedna konverzace, která roste", + "Only in its history.": "Jen v její historii.", "Open in the viewer": "Otevřít v prohlížeči souborů", "Open in the web interface": "Otevřít ve webovém rozhraní", + "Open its chat": "Otevřít jeho konverzaci", "Open its conversation": "Otevřít jeho konverzaci", "Open the activity panel": "Otevřít panel aktivity", + "Open the chat": "Otevřít konverzaci", "Open the page again": "Otevřít stránku znovu", - "Open the result": "Otevřít výsledek", "Or type an answer": "Nebo napište odpověď", "Outputs": "Výstupy", "Page": "Stránka", @@ -171,6 +194,7 @@ const LEMBAS_I18N = { "Rename chat": "Přejmenovat konverzaci", "Rename…": "Přejmenovat…", "Restore": "Obnovit", + "Result": "Výsledek", "Resume": "Pokračovat", "Resume %(title)s": "Obnovit %(title)s", "Room": "Místnost", @@ -190,6 +214,7 @@ const LEMBAS_I18N = { "Send": "Odeslat", "Sending…": "Odesílá se…", "Server": "Server", + "Set the time yourself": "Nastavit čas sami", "Settings": "Nastavení", "Show %(n)s more": "Zobrazit další (%(n)s)", "Shown by the agent": "Ukázal agent", @@ -199,7 +224,7 @@ const LEMBAS_I18N = { "Signed in as %(who)s.": "Přihlášen jako %(who)s.", "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": "Přeskočeno", + "Skipped": "Vynecháno", "Source": "Zdroj", "Start the agent": "Spustit agenta", "Stop": "Zastavit", @@ -210,7 +235,7 @@ const LEMBAS_I18N = { "System prompt": "Systémový pokyn", "Taken in while it ran:": "Převzato během běhu:", "Team: %(size)s of %(max)s agents": "Tým: %(size)s z %(max)s agentů", - "Tell me": "Dát vědět", + "Tell me": "Upozornit mě", "Tell me when a reply or an agent's work is finished while I am elsewhere": "Upozornit mě, když je odpověď nebo práce agenta hotová, zatímco jsem jinde", "The agent asks to run %(tool)s:": "Agent žádá o spuštění %(tool)s:", "The agent asks:": "Agent se ptá:", @@ -226,9 +251,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "Aplikace se k vašemu serveru přihlásí jednou, ve vašem prohlížeči, jako každé zařízení. Pak může číst a psát vaše konverzace, jako webové rozhraní, a stejné schválení propojí tento počítač pro agenta.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikace se k vašemu serveru přihlásí jednou, ve vašem prohlížeči, jako každé zařízení. Pak může číst a psát vaše konverzace, jako webové rozhraní.", "The connection to the reply was lost: %(why)s": "Spojení s odpovědí se přerušilo: %(why)s", + "The first free scheduling model": "První volný model pro plánování", + "The instruction each run is given, on its own: say everything it needs.": "Instrukce, kterou dostane každé spuštění samostatně: uveďte vše, co potřebuje.", + "The last %(n)s runs failed: %(why)s": "Posledních %(n)s spuštění selhalo: %(why)s", + "The model's own": "Podle modelu", "The most an agent chat may do here": "Nejvíc, co tu agentní konverzace smí", "The next runs": "Další spuštění", "The reply is ready.": "Odpověď je hotová.", + "The result is no longer there.": "Výsledek už tu není.", "The server refused this computer's link: %(why)s": "Server odmítl propojení tohoto počítače: %(why)s", "These words give no run in the future.": "Z těchto slov nevychází žádné budoucí spuštění.", "Think before answering": "Přemýšlet před odpovědí", @@ -241,6 +271,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Toto není textový soubor; nelze ho zde zobrazit.", "This server has no scheduled tasks yet.": "Tento server zatím nemá naplánované úlohy.", "Thought": "Přemýšlel", + "Time": "Čas", + "Times in %(zone)s": "Časové pásmo: %(zone)s", + "Title": "Název", "Today": "Dnes", "Tools": "Nástroje", "Try again": "Zkusit znovu", @@ -255,6 +288,7 @@ const LEMBAS_I18N = { "When": "Kdy", "When it matters": "Když na tom záleží", "Writing…": "Píše se…", + "Written to %(path)s": "Zapsáno do %(path)s", "You asked: “%(text)s”": "Požádali jste: „%(text)s“", "Your answer": "Vaše odpověď", "Your files on the server, as the chats see them.": "Vaše soubory na serveru, tak jak je vidí konverzace.", @@ -267,16 +301,21 @@ const LEMBAS_I18N = { "agent stopped": "agent zastaven", "agent waiting": "agent čeká", "agent: no folders": "agent: žádné složky", + "busy": "zaneprázdněný", "caught up": "dohnáno", "default": "výchozí", "done": "hotovo", "failed": "selhalo", - "late": "pozdě", + "free": "volný", + "late": "se zpožděním", "not linked": "nepropojeno", "off": "vypnuto", "queued": "ve frontě", "report": "hlášení", + "run by hand": "spuštěno ručně", "stopped": "zastaveno", + "switched": "přepnuto", + "tried again %(when)s": "znovu %(when)s", "update": "aktualizace", "update %(version)s": "aktualizace %(version)s", "waiting": "čeká", @@ -286,8 +325,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s“ se zastaví a její historie zmizí. Konverzace, které napsala, zůstanou." }, "de": { + "%(model)s was busy, so it ran on this one.": "%(model)s war belegt, daher lief sie auf diesem.", "%(n)s agents working": "%(n)s Agenten arbeiten", "%(n)s finished": "%(n)s beendet", + "%(n)s missed": "%(n)s verpasst", "%(n)s queued": "%(n)s in der Warteschlange", "%(n)s tokens": "%(n)s Tokens", "%(n)s tools offered.": "%(n)s Werkzeuge angeboten.", @@ -300,12 +341,18 @@ const LEMBAS_I18N = { "1 agent working": "1 Agent arbeitet", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Ein LLeMbas-CLI-Dienst läuft hier bereits (PID %(pid)s), ist aber nicht verbunden: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Ein LLeMbas-CLI-Dienst läuft hier bereits (PID %(pid)s); er ist der Agent dieses Computers.", + "A busy model is waited for, then the next free one is used.": "Auf ein belegtes Modell wird gewartet, dann wird das nächste freie genommen.", "A chat asking for more is lowered to this.": "Ein Chat, der mehr verlangt, wird hierauf begrenzt.", "A chat on %(server)s, with your account's models and files.": "Ein Chat auf %(server)s, mit den Modellen und Dateien Ihres Kontos.", "A new chat for each run": "Ein neuer Chat für jeden Lauf", + "A result, but not a run with nothing to report.": "Ein Ergebnis, aber kein Lauf ohne Neuigkeiten.", + "A scheduled task failed 3 times in a row. Pause it?": "Eine geplante Aufgabe ist 3-mal hintereinander fehlgeschlagen. Pausieren?", "A scheduled task failed.": "Eine geplante Aufgabe ist fehlgeschlagen.", "A scheduled task has a result.": "Eine geplante Aufgabe hat ein Ergebnis.", "A shell as you, in one of these folders.": "Eine Shell als Sie, in einem dieser Ordner.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Eine Aufgabe ohne eigenes Modell läuft auf dem ersten freien davon, in dieser Reihenfolge. Keines angehakt: alle.", + "A team of up to %(n)s": "Ein Team mit bis zu %(n)s", + "A team works on it together, its own agent included.": "Ein Team arbeitet gemeinsam daran, ihr eigener Agent eingeschlossen.", "About": "Über", "Account": "Konto", "Activity": "Aktivität", @@ -332,6 +379,7 @@ const LEMBAS_I18N = { "Chat": "Chat", "Chats": "Chats", "Check for updates…": "Nach Updates suchen…", + "Choose a model to choose its effort.": "Wählen Sie ein Modell, um seinen Aufwand zu wählen.", "Close": "Schließen", "Close (LLeMbas stays in the tray)": "Schließen (LLeMbas bleibt im Benachrichtigungsbereich)", "Close the activity panel": "Aktivitätsbereich schließen", @@ -346,6 +394,7 @@ const LEMBAS_I18N = { "Copy": "Kopieren", "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.", "Cut short here; the whole of it is in the web interface.": "Hier gekürzt; vollständig steht es in der Weboberfläche.", + "Days": "Tage", "Delete": "Löschen", "Delete chat": "Chat löschen", "Delete this chat?": "Diesen Chat löschen?", @@ -353,12 +402,19 @@ const LEMBAS_I18N = { "Deny": "Ablehnen", "Done": "Fertig", "Drop the files to attach them": "Dateien ablegen, um sie anzuhängen", + "Due %(when)s": "Fällig %(when)s", + "Each run sees the ones before it.": "Jeder Lauf sieht die vorherigen.", + "Each run starts with nothing but the instruction.": "Jeder Lauf beginnt nur mit der Anweisung.", "Earlier": "Früher", "Earlier messages": "Frühere Nachrichten", + "Earlier runs": "Frühere Läufe", + "Edit": "Bearbeiten", + "Edit %(title)s": "%(title)s bearbeiten", "Edit message": "Nachricht bearbeiten", "Edit — asks before commands": "Edit — fragt vor Befehlen", "Empty folder": "Leerer Ordner", "Estimated: no reply has been counted yet.": "Geschätzt: Noch keine Antwort wurde gezählt.", + "Every result, even with nothing to report.": "Jedes Ergebnis, auch ohne Neuigkeiten.", "Every weekday at 8:00, summarise the news on my reading list": "Jeden Werktag um 8:00 die Nachrichten auf meiner Leseliste zusammenfassen", "Failed": "Fehlgeschlagen", "Fewest": "Mindestens", @@ -371,7 +427,7 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "In Ihren eigenen Worten. Der Server macht daraus eine Regel und eine Anweisung und zeigt Ihnen die nächsten Läufe, bevor etwas gespeichert wird.", "Install LLeMbas %(version)s…": "LLeMbas %(version)s installieren…", "Instruction files": "Anweisungsdateien", - "It has not run yet.": "Sie ist noch nicht gelaufen.", + "It has not run yet.": "Er ist noch nicht gelaufen.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Der eigene Agent eingeschlossen. Bei mehr als einem kann er Mitglieder hinzufügen, die die Arbeit machen, während er sie leitet.", "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s ist verfügbar (dies ist %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop braucht einen LLeMbas-Server 2.0 oder neuer.", @@ -394,6 +450,8 @@ const LEMBAS_I18N = { "Missed": "Verpasst", "Mode": "Modus", "Model": "Modell", + "Models": "Modelle", + "Models for scheduled tasks": "Modelle für geplante Aufgaben", "More": "Mehr", "Most": "Höchstens", "Never": "Nie", @@ -402,12 +460,14 @@ const LEMBAS_I18N = { "New scheduled task": "Neue geplante Aufgabe", "Next run": "Nächster Lauf", "No chat yet. Start one with New chat.": "Noch kein Chat. Beginnen Sie einen mit Neuer Chat.", + "No day ticked: every day.": "Kein Tag angehakt: jeden Tag.", "No files written in this chat yet.": "In diesem Chat wurde noch keine Datei geschrieben.", "No folder yet: add one under Settings, Project folders.": "Noch kein Ordner: Fügen Sie einen unter Einstellungen, Projektordner hinzu.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Noch kein Ordner: Der Agent kann nichts öffnen. Fügen Sie den Ordner eines Projekts hinzu.", "No model is available to this account.": "Diesem Konto steht kein Modell zur Verfügung.", "No more runs": "Keine weiteren Läufe", "No project folder yet: the agent can open nothing on this computer.": "Noch kein Projektordner: Der Agent kann auf diesem Computer nichts öffnen.", + "No team": "Kein Team", "Not measured.": "Nicht gemessen.", "Not run yet": "Noch nicht gelaufen", "Not yet": "Noch nicht", @@ -420,12 +480,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Noch nichts.", "Notifications": "Benachrichtigungen", "One chat that grows": "Ein Chat, der wächst", + "Only in its history.": "Nur in ihrem Verlauf.", "Open in the viewer": "Im Betrachter öffnen", "Open in the web interface": "In der Weboberfläche öffnen", + "Open its chat": "Seinen Chat öffnen", "Open its conversation": "Seine Unterhaltung öffnen", "Open the activity panel": "Aktivitätsbereich öffnen", + "Open the chat": "Chat öffnen", "Open the page again": "Seite erneut öffnen", - "Open the result": "Ergebnis öffnen", "Or type an answer": "Oder eine Antwort eingeben", "Outputs": "Ausgaben", "Page": "Seite", @@ -446,6 +508,7 @@ const LEMBAS_I18N = { "Rename chat": "Chat umbenennen", "Rename…": "Umbenennen…", "Restore": "Wiederherstellen", + "Result": "Ergebnis", "Resume": "Fortsetzen", "Resume %(title)s": "%(title)s fortsetzen", "Room": "Raum", @@ -465,6 +528,7 @@ const LEMBAS_I18N = { "Send": "Senden", "Sending…": "Wird gesendet…", "Server": "Server", + "Set the time yourself": "Zeit selbst festlegen", "Settings": "Einstellungen", "Show %(n)s more": "%(n)s weitere anzeigen", "Shown by the agent": "Vom Agenten gezeigt", @@ -474,7 +538,7 @@ const LEMBAS_I18N = { "Signed in as %(who)s.": "Angemeldet als %(who)s.", "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": "Übersprungen", + "Skipped": "Ausgelassen", "Source": "Quelltext", "Start the agent": "Agent starten", "Stop": "Stoppen", @@ -501,9 +565,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "Die App meldet sich einmal an Ihrem Server an, in Ihrem Browser, wie jedes Gerät. Danach kann sie Ihre Chats lesen und schreiben, wie die Weboberfläche, und dieselbe Freigabe verbindet diesen Computer für den Agenten.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Die App meldet sich einmal an Ihrem Server an, in Ihrem Browser, wie jedes Gerät. Danach kann sie Ihre Chats lesen und schreiben, wie die Weboberfläche.", "The connection to the reply was lost: %(why)s": "Die Verbindung zur Antwort ist abgebrochen: %(why)s", + "The first free scheduling model": "Das erste freie Planungsmodell", + "The instruction each run is given, on its own: say everything it needs.": "Die Anweisung, die jeder Lauf für sich erhält: Sagen Sie alles, was er braucht.", + "The last %(n)s runs failed: %(why)s": "Die letzten %(n)s Läufe sind fehlgeschlagen: %(why)s", + "The model's own": "Die des Modells", "The most an agent chat may do here": "Das Meiste, was ein Agent-Chat hier darf", "The next runs": "Die nächsten Läufe", "The reply is ready.": "Die Antwort ist fertig.", + "The result is no longer there.": "Das Ergebnis ist nicht mehr da.", "The server refused this computer's link: %(why)s": "Der Server hat die Verknüpfung dieses Computers abgelehnt: %(why)s", "These words give no run in the future.": "Diese Worte ergeben keinen künftigen Lauf.", "Think before answering": "Vor der Antwort nachdenken", @@ -516,6 +585,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Das ist keine Textdatei; sie kann hier nicht angezeigt werden.", "This server has no scheduled tasks yet.": "Dieser Server hat noch keine geplanten Aufgaben.", "Thought": "Nachgedacht", + "Time": "Uhrzeit", + "Times in %(zone)s": "Zeitzone: %(zone)s", + "Title": "Titel", "Today": "Heute", "Tools": "Werkzeuge", "Try again": "Erneut versuchen", @@ -528,8 +600,9 @@ const LEMBAS_I18N = { "What it does": "Was sie tut", "What should happen, and when": "Was soll wann geschehen?", "When": "Wann", - "When it matters": "Wenn es wichtig ist", + "When it matters": "Wenn es zählt", "Writing…": "Schreibt…", + "Written to %(path)s": "Geschrieben nach %(path)s", "You asked: “%(text)s”": "Ihre Worte: „%(text)s“", "Your answer": "Ihre Antwort", "Your files on the server, as the chats see them.": "Ihre Dateien auf dem Server, so wie die Chats sie sehen.", @@ -542,16 +615,21 @@ const LEMBAS_I18N = { "agent stopped": "Agent angehalten", "agent waiting": "Agent wartet", "agent: no folders": "Agent: keine Ordner", + "busy": "belegt", "caught up": "nachgeholt", "default": "Standard", "done": "fertig", "failed": "fehlgeschlagen", + "free": "frei", "late": "verspätet", "not linked": "nicht verbunden", "off": "aus", "queued": "in der Warteschlange", "report": "Bericht", + "run by hand": "von Hand gestartet", "stopped": "angehalten", + "switched": "gewechselt", + "tried again %(when)s": "erneut %(when)s", "update": "Update", "update %(version)s": "Update %(version)s", "waiting": "wartet", @@ -561,8 +639,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s“ hört auf, und ihr Verlauf wird gelöscht. Die Chats, die sie geschrieben hat, bleiben." }, "es": { + "%(model)s was busy, so it ran on this one.": "%(model)s estaba ocupado, así que se ejecutó en este.", "%(n)s agents working": "%(n)s agentes trabajando", "%(n)s finished": "%(n)s terminados", + "%(n)s missed": "%(n)s perdidas", "%(n)s queued": "%(n)s en cola", "%(n)s tokens": "tokens: %(n)s", "%(n)s tools offered.": "%(n)s herramientas ofrecidas.", @@ -575,12 +655,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agente trabajando", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Ya hay un servicio de LLeMbas CLI aquí (pid %(pid)s), pero no está conectado: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Ya se está ejecutando aquí un servicio de LLeMbas CLI (pid %(pid)s); es el agente de este ordenador.", + "A busy model is waited for, then the next free one is used.": "Se espera a un modelo ocupado y luego se usa el siguiente libre.", "A chat asking for more is lowered to this.": "Un chat que pida más se limita a esto.", "A chat on %(server)s, with your account's models and files.": "Un chat en %(server)s, con los modelos y archivos de su cuenta.", "A new chat for each run": "Un chat nuevo en cada ejecución", + "A result, but not a run with nothing to report.": "Un resultado, pero no una ejecución sin nada que informar.", + "A scheduled task failed 3 times in a row. Pause it?": "Una tarea programada falló 3 veces seguidas. ¿Pausarla?", "A scheduled task failed.": "Una tarea programada ha fallado.", "A scheduled task has a result.": "Una tarea programada tiene un resultado.", "A shell as you, in one of these folders.": "Un shell con su usuario, en una de estas carpetas.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Una tarea sin modelo propio se ejecuta en el primero libre de estos, en este orden. Ninguno marcado: todos.", + "A team of up to %(n)s": "Un equipo de hasta %(n)s", + "A team works on it together, its own agent included.": "Un equipo trabaja en ella, incluido su propio agente.", "About": "Acerca de", "Account": "Cuenta", "Activity": "Actividad", @@ -607,6 +693,7 @@ const LEMBAS_I18N = { "Chat": "Chat", "Chats": "Chats", "Check for updates…": "Buscar actualizaciones…", + "Choose a model to choose its effort.": "Elija un modelo para elegir su esfuerzo.", "Close": "Cerrar", "Close (LLeMbas stays in the tray)": "Cerrar (LLeMbas sigue en la bandeja del sistema)", "Close the activity panel": "Cerrar el panel de actividad", @@ -621,6 +708,7 @@ const LEMBAS_I18N = { "Copy": "Copiar", "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.", "Cut short here; the whole of it is in the web interface.": "Recortado aquí; completo está en la interfaz web.", + "Days": "Días", "Delete": "Eliminar", "Delete chat": "Eliminar chat", "Delete this chat?": "¿Eliminar este chat?", @@ -628,12 +716,19 @@ const LEMBAS_I18N = { "Deny": "Denegar", "Done": "Hecho", "Drop the files to attach them": "Suelte los archivos para adjuntarlos", + "Due %(when)s": "Prevista %(when)s", + "Each run sees the ones before it.": "Cada ejecución ve las anteriores.", + "Each run starts with nothing but the instruction.": "Cada ejecución empieza solo con la instrucción.", "Earlier": "Anteriores", "Earlier messages": "Mensajes anteriores", + "Earlier runs": "Ejecuciones anteriores", + "Edit": "Editar", + "Edit %(title)s": "Editar %(title)s", "Edit message": "Editar mensaje", "Edit — asks before commands": "Edit — pregunta antes de los comandos", "Empty folder": "Carpeta vacía", "Estimated: no reply has been counted yet.": "Estimado: aún no se ha contado ninguna respuesta.", + "Every result, even with nothing to report.": "Cada resultado, incluso sin nada que informar.", "Every weekday at 8:00, summarise the news on my reading list": "Cada día laborable a las 8:00, resume las noticias de mi lista de lectura", "Failed": "Fallidos", "Fewest": "Mínimo", @@ -646,7 +741,7 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Con sus propias palabras. El servidor las convierte en una regla y una instrucción, y le muestra las próximas ejecuciones antes de guardar nada.", "Install LLeMbas %(version)s…": "Instalar LLeMbas %(version)s…", "Instruction files": "Archivos de instrucciones", - "It has not run yet.": "Aún no se ha ejecutado.", + "It has not run yet.": "Todavía no se ha ejecutado.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Su propio agente incluido. Con más de uno, puede añadir miembros que hagan el trabajo mientras los dirige.", "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s está disponible (esta es la %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop necesita un servidor LLeMbas 2.0 o posterior.", @@ -669,6 +764,8 @@ const LEMBAS_I18N = { "Missed": "Perdida", "Mode": "Modo", "Model": "Modelo", + "Models": "Modelos", + "Models for scheduled tasks": "Modelos para tareas programadas", "More": "Más", "Most": "Máximo", "Never": "Nunca", @@ -677,12 +774,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nueva tarea programada", "Next run": "Próxima ejecución", "No chat yet. Start one with New chat.": "Aún no hay chats. Empiece uno con Nuevo chat.", + "No day ticked: every day.": "Ningún día marcado: todos los días.", "No files written in this chat yet.": "Todavía no se ha escrito ningún archivo en este chat.", "No folder yet: add one under Settings, Project folders.": "Aún no hay carpeta: añada una en Ajustes, Carpetas de proyecto.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Aún no hay ninguna carpeta: el agente no puede abrir nada. Añada la carpeta de un proyecto.", "No model is available to this account.": "Ningún modelo está disponible para esta cuenta.", "No more runs": "No hay más ejecuciones", "No project folder yet: the agent can open nothing on this computer.": "Aún no hay carpeta de proyecto: el agente no puede abrir nada en este equipo.", + "No team": "Sin equipo", "Not measured.": "Sin medir.", "Not run yet": "Aún no se ha ejecutado", "Not yet": "Todavía no", @@ -695,12 +794,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Aún nada.", "Notifications": "Notificaciones", "One chat that grows": "Un chat que va creciendo", + "Only in its history.": "Solo en su historial.", "Open in the viewer": "Abrir en el visor", "Open in the web interface": "Abrir en la interfaz web", + "Open its chat": "Abrir su chat", "Open its conversation": "Abrir su conversación", "Open the activity panel": "Abrir el panel de actividad", + "Open the chat": "Abrir el chat", "Open the page again": "Abrir la página de nuevo", - "Open the result": "Abrir el resultado", "Or type an answer": "O escriba una respuesta", "Outputs": "Resultados", "Page": "Página", @@ -721,6 +822,7 @@ const LEMBAS_I18N = { "Rename chat": "Renombrar chat", "Rename…": "Renombrar…", "Restore": "Restaurar", + "Result": "Resultado", "Resume": "Reanudar", "Resume %(title)s": "Reanudar %(title)s", "Room": "Sala", @@ -740,6 +842,7 @@ const LEMBAS_I18N = { "Send": "Enviar", "Sending…": "Enviando…", "Server": "Servidor", + "Set the time yourself": "Fijar la hora usted mismo", "Settings": "Ajustes", "Show %(n)s more": "Mostrar %(n)s más", "Shown by the agent": "Mostrado por el agente", @@ -776,9 +879,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "La aplicación inicia sesión en su servidor una vez, en su navegador, como cualquier dispositivo. Después puede leer y escribir sus chats, como la interfaz web, y la misma aprobación vincula este ordenador para el agente.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "La aplicación inicia sesión en su servidor una vez, en su navegador, como cualquier dispositivo. Después puede leer y escribir sus chats, como la interfaz web.", "The connection to the reply was lost: %(why)s": "Se perdió la conexión con la respuesta: %(why)s", + "The first free scheduling model": "El primer modelo de programación libre", + "The instruction each run is given, on its own: say everything it needs.": "La instrucción que recibe cada ejecución por sí sola: diga todo lo que necesita.", + "The last %(n)s runs failed: %(why)s": "Las últimas %(n)s ejecuciones fallaron: %(why)s", + "The model's own": "La del modelo", "The most an agent chat may do here": "Lo máximo que un chat de agente puede hacer aquí", "The next runs": "Las próximas ejecuciones", "The reply is ready.": "La respuesta está lista.", + "The result is no longer there.": "El resultado ya no está.", "The server refused this computer's link: %(why)s": "El servidor rechazó la vinculación de este equipo: %(why)s", "These words give no run in the future.": "Estas palabras no dan ninguna ejecución futura.", "Think before answering": "Razonar antes de responder", @@ -791,6 +899,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "No es un archivo de texto; no se puede mostrar aquí.", "This server has no scheduled tasks yet.": "Este servidor aún no tiene tareas programadas.", "Thought": "Pensamiento", + "Time": "Hora", + "Times in %(zone)s": "Zona horaria: %(zone)s", + "Title": "Título", "Today": "Hoy", "Tools": "Herramientas", "Try again": "Reintentar", @@ -805,6 +916,7 @@ const LEMBAS_I18N = { "When": "Cuándo", "When it matters": "Cuando importa", "Writing…": "Escribiendo…", + "Written to %(path)s": "Escrito en %(path)s", "You asked: “%(text)s”": "Usted pidió: «%(text)s»", "Your answer": "Su respuesta", "Your files on the server, as the chats see them.": "Sus archivos en el servidor, tal como los ven los chats.", @@ -817,16 +929,21 @@ const LEMBAS_I18N = { "agent stopped": "agente detenido", "agent waiting": "agente en espera", "agent: no folders": "agente: sin carpetas", + "busy": "ocupado", "caught up": "recuperada", "default": "predeterminado", "done": "terminado", "failed": "falló", + "free": "libre", "late": "con retraso", "not linked": "no vinculado", "off": "desactivado", "queued": "en cola", "report": "informe", + "run by hand": "ejecutada a mano", "stopped": "detenido", + "switched": "cambiado", + "tried again %(when)s": "de nuevo %(when)s", "update": "actualizar", "update %(version)s": "actualizar a %(version)s", "waiting": "esperando", @@ -836,8 +953,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "«%(title)s» se detiene y su historial se borra. Los chats que escribió se conservan." }, "fr": { + "%(model)s was busy, so it ran on this one.": "%(model)s était occupé, elle a donc tourné sur celui-ci.", "%(n)s agents working": "%(n)s agents au travail", "%(n)s finished": "%(n)s terminés", + "%(n)s missed": "%(n)s manquées", "%(n)s queued": "%(n)s en file d’attente", "%(n)s tokens": "%(n)s jetons", "%(n)s tools offered.": "%(n)s outils proposés.", @@ -850,12 +969,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agent au travail", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Un service LLeMbas CLI tourne déjà ici (pid %(pid)s), mais n'est pas connecté : %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Un service LLeMbas CLI tourne déjà ici (pid %(pid)s) ; c'est l'agent de cet ordinateur.", + "A busy model is waited for, then the next free one is used.": "Un modèle occupé est attendu, puis le suivant libre est utilisé.", "A chat asking for more is lowered to this.": "Une conversation qui en demande plus est ramenée à ceci.", "A chat on %(server)s, with your account's models and files.": "Une conversation sur %(server)s, avec les modèles et les fichiers de votre compte.", "A new chat for each run": "Une nouvelle conversation à chaque exécution", + "A result, but not a run with nothing to report.": "Un résultat, mais pas une exécution sans rien à signaler.", + "A scheduled task failed 3 times in a row. Pause it?": "Une tâche planifiée a échoué 3 fois de suite. La mettre en pause ?", "A scheduled task failed.": "Une tâche planifiée a échoué.", "A scheduled task has a result.": "Une tâche planifiée a un résultat.", "A shell as you, in one of these folders.": "Un shell en votre nom, dans l'un de ces dossiers.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Une tâche sans modèle à elle tourne sur le premier libre de ceux-ci, dans cet ordre. Aucun coché : tous.", + "A team of up to %(n)s": "Une équipe jusqu'à %(n)s", + "A team works on it together, its own agent included.": "Une équipe y travaille ensemble, son propre agent compris.", "About": "À propos", "Account": "Compte", "Activity": "Activité", @@ -882,6 +1007,7 @@ const LEMBAS_I18N = { "Chat": "Conversation", "Chats": "Conversations", "Check for updates…": "Rechercher des mises à jour…", + "Choose a model to choose its effort.": "Choisissez un modèle pour choisir son effort.", "Close": "Fermer", "Close (LLeMbas stays in the tray)": "Fermer (LLeMbas reste dans la zone de notification)", "Close the activity panel": "Fermer le panneau d’activité", @@ -896,6 +1022,7 @@ const LEMBAS_I18N = { "Copy": "Copier", "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.", "Cut short here; the whole of it is in the web interface.": "Tronqué ici ; le tout est dans l'interface web.", + "Days": "Jours", "Delete": "Supprimer", "Delete chat": "Supprimer la conversation", "Delete this chat?": "Supprimer cette conversation ?", @@ -903,12 +1030,19 @@ const LEMBAS_I18N = { "Deny": "Refuser", "Done": "Terminé", "Drop the files to attach them": "Déposez les fichiers pour les joindre", + "Due %(when)s": "Prévue %(when)s", + "Each run sees the ones before it.": "Chaque exécution voit les précédentes.", + "Each run starts with nothing but the instruction.": "Chaque exécution part de la seule instruction.", "Earlier": "Plus tôt", "Earlier messages": "Messages précédents", + "Earlier runs": "Exécutions précédentes", + "Edit": "Modifier", + "Edit %(title)s": "Modifier %(title)s", "Edit message": "Modifier le message", "Edit — asks before commands": "Edit — demande avant les commandes", "Empty folder": "Dossier vide", "Estimated: no reply has been counted yet.": "Estimation : aucune réponse n’a encore été comptée.", + "Every result, even with nothing to report.": "Chaque résultat, même sans rien à signaler.", "Every weekday at 8:00, summarise the news on my reading list": "Chaque jour ouvré à 8 h, résume les nouvelles de ma liste de lecture", "Failed": "Échecs", "Fewest": "Au moins", @@ -921,7 +1055,7 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Avec vos propres mots. Le serveur en fait une règle et une instruction, et vous montre les prochaines exécutions avant que rien ne soit enregistré.", "Install LLeMbas %(version)s…": "Installer LLeMbas %(version)s…", "Instruction files": "Fichiers d’instructions", - "It has not run yet.": "Elle n’a pas encore été exécutée.", + "It has not run yet.": "Elle ne s'est pas encore exécutée.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Son propre agent compris. Au-delà d’un, il peut ajouter des membres qui font le travail pendant qu’il les dirige.", "LLeMbas %(version)s is available (this is %(current)s)": "LLeMbas %(version)s est disponible (ceci est la %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop nécessite un serveur LLeMbas 2.0 ou plus récent.", @@ -944,6 +1078,8 @@ const LEMBAS_I18N = { "Missed": "Manquée", "Mode": "Mode", "Model": "Modèle", + "Models": "Modèles", + "Models for scheduled tasks": "Modèles pour les tâches planifiées", "More": "Plus", "Most": "Au plus", "Never": "Jamais", @@ -952,12 +1088,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nouvelle tâche planifiée", "Next run": "Prochaine exécution", "No chat yet. Start one with New chat.": "Aucune conversation pour l’instant. Commencez-en une avec Nouvelle conversation.", + "No day ticked: every day.": "Aucun jour coché : tous les jours.", "No files written in this chat yet.": "Aucun fichier n’a encore été écrit dans cette conversation.", "No folder yet: add one under Settings, Project folders.": "Aucun dossier pour l'instant : ajoutez-en un dans Paramètres, Dossiers de projet.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Aucun dossier pour l'instant : l'agent ne peut rien ouvrir. Ajoutez le dossier d'un projet.", "No model is available to this account.": "Aucun modèle n'est disponible pour ce compte.", "No more runs": "Plus d’exécution prévue", "No project folder yet: the agent can open nothing on this computer.": "Aucun dossier de projet pour l'instant : l'agent ne peut rien ouvrir sur cet ordinateur.", + "No team": "Pas d'équipe", "Not measured.": "Non mesuré.", "Not run yet": "Pas encore exécutée", "Not yet": "Pas encore", @@ -970,12 +1108,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Rien pour l'instant.", "Notifications": "Notifications", "One chat that grows": "Une conversation qui grandit", + "Only in its history.": "Seulement dans son historique.", "Open in the viewer": "Ouvrir dans la visionneuse", "Open in the web interface": "Ouvrir dans l'interface web", + "Open its chat": "Ouvrir sa conversation", "Open its conversation": "Ouvrir sa conversation", "Open the activity panel": "Ouvrir le panneau d’activité", + "Open the chat": "Ouvrir la conversation", "Open the page again": "Rouvrir la page", - "Open the result": "Ouvrir le résultat", "Or type an answer": "Ou saisissez une réponse", "Outputs": "Sorties", "Page": "Page", @@ -996,6 +1136,7 @@ const LEMBAS_I18N = { "Rename chat": "Renommer la conversation", "Rename…": "Renommer…", "Restore": "Restaurer", + "Result": "Résultat", "Resume": "Reprendre", "Resume %(title)s": "Reprendre %(title)s", "Room": "Salle", @@ -1015,6 +1156,7 @@ const LEMBAS_I18N = { "Send": "Envoyer", "Sending…": "Envoi…", "Server": "Serveur", + "Set the time yourself": "Régler l'heure vous-même", "Settings": "Paramètres", "Show %(n)s more": "Afficher %(n)s de plus", "Shown by the agent": "Montré par l'agent", @@ -1051,9 +1193,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "L'application se connecte une fois à votre serveur, dans votre navigateur, comme tout appareil. Elle peut ensuite lire et écrire vos discussions, comme l'interface web, et la même approbation relie cet ordinateur pour l'agent.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "L'application se connecte une fois à votre serveur, dans votre navigateur, comme tout appareil. Elle peut ensuite lire et écrire vos discussions, comme l'interface web.", "The connection to the reply was lost: %(why)s": "La connexion à la réponse a été perdue : %(why)s", + "The first free scheduling model": "Le premier modèle de planification libre", + "The instruction each run is given, on its own: say everything it needs.": "L'instruction donnée à chaque exécution, seule : dites tout ce dont elle a besoin.", + "The last %(n)s runs failed: %(why)s": "Les %(n)s dernières exécutions ont échoué : %(why)s", + "The model's own": "Celle du modèle", "The most an agent chat may do here": "Le maximum qu'une conversation d'agent peut faire ici", "The next runs": "Les prochaines exécutions", "The reply is ready.": "La réponse est prête.", + "The result is no longer there.": "Le résultat n'est plus là.", "The server refused this computer's link: %(why)s": "Le serveur a refusé la liaison de cet ordinateur : %(why)s", "These words give no run in the future.": "Ces mots ne donnent aucune exécution à venir.", "Think before answering": "Réfléchir avant de répondre", @@ -1066,6 +1213,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Ce n'est pas un fichier texte ; il ne peut pas être affiché ici.", "This server has no scheduled tasks yet.": "Ce serveur n’a pas encore de tâches planifiées.", "Thought": "Réflexion", + "Time": "Heure", + "Times in %(zone)s": "Fuseau horaire : %(zone)s", + "Title": "Titre", "Today": "Aujourd’hui", "Tools": "Outils", "Try again": "Réessayer", @@ -1078,8 +1228,9 @@ const LEMBAS_I18N = { "What it does": "Ce qu’elle fait", "What should happen, and when": "Ce qui doit se passer, et quand", "When": "Quand", - "When it matters": "Quand c’est important", + "When it matters": "Quand c'est important", "Writing…": "Rédaction…", + "Written to %(path)s": "Écrit dans %(path)s", "You asked: “%(text)s”": "Votre demande : « %(text)s »", "Your answer": "Votre réponse", "Your files on the server, as the chats see them.": "Vos fichiers sur le serveur, tels que les conversations les voient.", @@ -1092,16 +1243,21 @@ const LEMBAS_I18N = { "agent stopped": "agent interrompu", "agent waiting": "agent en attente", "agent: no folders": "agent : aucun dossier", + "busy": "occupé", "caught up": "rattrapée", "default": "par défaut", "done": "terminé", "failed": "échoué", + "free": "libre", "late": "en retard", "not linked": "non relié", "off": "désactivé", "queued": "en file d’attente", "report": "rapport", + "run by hand": "lancée à la main", "stopped": "arrêté", + "switched": "changé", + "tried again %(when)s": "de nouveau %(when)s", "update": "mise à jour", "update %(version)s": "mise à jour %(version)s", "waiting": "en attente", @@ -1111,8 +1267,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "« %(title)s » s’arrête et son historique disparaît. Les conversations qu’elle a écrites restent." }, "hu": { + "%(model)s was busy, so it ran on this one.": "%(model)s foglalt volt, ezért ezen futott.", "%(n)s agents working": "%(n)s ügynök dolgozik", "%(n)s finished": "%(n)s befejezve", + "%(n)s missed": "%(n)s kimaradt", "%(n)s queued": "%(n)s sorban áll", "%(n)s tokens": "%(n)s token", "%(n)s tools offered.": "%(n)s eszköz felkínálva.", @@ -1125,12 +1283,18 @@ const LEMBAS_I18N = { "1 agent working": "1 ügynök dolgozik", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Egy LLeMbas CLI szolgáltatás már fut itt (pid %(pid)s), de nincs csatlakozva: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Itt már fut egy LLeMbas CLI szolgáltatás (pid %(pid)s); ez a számítógép ügynöke.", + "A busy model is waited for, then the next free one is used.": "A foglalt modellre vár, majd a következő szabadot használja.", "A chat asking for more is lowered to this.": "Az ennél többet kérő csevegés erre korlátozódik.", "A chat on %(server)s, with your account's models and files.": "Beszélgetés itt: %(server)s, a fiókja modelljeivel és fájljaival.", "A new chat for each run": "Minden futáshoz új beszélgetés", + "A result, but not a run with nothing to report.": "Eredmény, de nem olyan futás, amelynek nincs mit jelentenie.", + "A scheduled task failed 3 times in a row. Pause it?": "Egy ütemezett feladat 3-szor egymás után sikertelen volt. Szünetelteti?", "A scheduled task failed.": "Egy ütemezett feladat sikertelen volt.", "A scheduled task has a result.": "Egy ütemezett feladatnak eredménye van.", "A shell as you, in one of these folders.": "Egy shell az Ön nevében, ezek egyik mappájában.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "A saját modell nélküli feladat ezek közül az első szabadon fut, ebben a sorrendben. Ha egy sincs bejelölve: mindegyiken.", + "A team of up to %(n)s": "Legfeljebb %(n)s fős csapat", + "A team works on it together, its own agent included.": "Egy csapat dolgozik rajta együtt, a saját ügynökét is beleértve.", "About": "Névjegy", "Account": "Fiók", "Activity": "Tevékenység", @@ -1157,6 +1321,7 @@ const LEMBAS_I18N = { "Chat": "Csevegés", "Chats": "Csevegések", "Check for updates…": "Frissítések keresése…", + "Choose a model to choose its effort.": "Válasszon modellt az erőfeszítés megadásához.", "Close": "Bezárás", "Close (LLeMbas stays in the tray)": "Bezárás (a LLeMbas a tálcán marad)", "Close the activity panel": "A tevékenységpanel bezárása", @@ -1171,6 +1336,7 @@ const LEMBAS_I18N = { "Copy": "Másolás", "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.", "Cut short here; the whole of it is in the web interface.": "Itt rövidítve; a teljes a webes felületen van.", + "Days": "Napok", "Delete": "Törlés", "Delete chat": "Csevegés törlése", "Delete this chat?": "Törli ezt a csevegést?", @@ -1178,12 +1344,19 @@ const LEMBAS_I18N = { "Deny": "Elutasítás", "Done": "Kész", "Drop the files to attach them": "Engedje el a fájlokat a csatoláshoz", + "Due %(when)s": "Esedékes: %(when)s", + "Each run sees the ones before it.": "Minden futás látja az előzőeket.", + "Each run starts with nothing but the instruction.": "Minden futás csak az utasítással indul.", "Earlier": "Korábban", "Earlier messages": "Korábbi üzenetek", + "Earlier runs": "Korábbi futások", + "Edit": "Szerkesztés", + "Edit %(title)s": "%(title)s szerkesztése", "Edit message": "Üzenet szerkesztése", "Edit — asks before commands": "Edit — parancsok előtt kérdez", "Empty folder": "Üres mappa", "Estimated: no reply has been counted yet.": "Becslés: még egy válasz sem lett megszámolva.", + "Every result, even with nothing to report.": "Minden eredmény, akkor is, ha nincs mit jelenteni.", "Every weekday at 8:00, summarise the news on my reading list": "Minden hétköznap 8:00-kor foglald össze az olvasólistám híreit", "Failed": "Sikertelen", "Fewest": "Legalább", @@ -1200,7 +1373,7 @@ const LEMBAS_I18N = { "Its own agent included. Above one, it can add members that do the work while it leads them.": "A saját ügynökét is beleértve. Egynél több esetén tagokat vehet fel, akik elvégzik a munkát, amíg ő vezeti őket.", "LLeMbas %(version)s is available (this is %(current)s)": "Elérhető a LLeMbas %(version)s (ez a %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "A LLeMbas Desktophoz LLeMbas 2.0 vagy újabb szerver kell.", - "Last run": "Utolsó futás", + "Last run": "Utolsó futtatás", "Let the server open a terminal here": "A kiszolgáló nyithasson itt terminált", "Link again": "Újra összekapcsolás", "Link it again to give the agent a new token.": "Kapcsolja össze újra, és az ügynök új tokent kap.", @@ -1216,9 +1389,11 @@ const LEMBAS_I18N = { "Message": "Üzenet", "Messages": "Üzenetek", "Minimise": "Kis méret", - "Missed": "Kimaradt", + "Missed": "Elmaradt", "Mode": "Mód", "Model": "Modell", + "Models": "Modellek", + "Models for scheduled tasks": "Modellek az ütemezett feladatokhoz", "More": "Több", "Most": "Legfeljebb", "Never": "Soha", @@ -1227,12 +1402,14 @@ const LEMBAS_I18N = { "New scheduled task": "Új ütemezett feladat", "Next run": "Következő futás", "No chat yet. Start one with New chat.": "Még nincs beszélgetés. Indítson egyet az Új beszélgetés gombbal.", + "No day ticked: every day.": "Ha egy nap sincs bejelölve: minden nap.", "No files written in this chat yet.": "Ebben a csevegésben még nem íródott fájl.", "No folder yet: add one under Settings, Project folders.": "Még nincs mappa: adjon hozzá egyet a Beállítások, Projektmappák alatt.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Még nincs mappa: az ügynök semmit nem nyithat meg. Adja hozzá egy projekt mappáját.", "No model is available to this account.": "Ennek a fióknak nem érhető el modell.", "No more runs": "Nincs több futás", "No project folder yet: the agent can open nothing on this computer.": "Még nincs projektmappa: az ügynök semmit sem nyithat meg ezen a számítógépen.", + "No team": "Nincs csapat", "Not measured.": "Nincs mérve.", "Not run yet": "Még nem futott", "Not yet": "Még nem", @@ -1245,12 +1422,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Még semmi.", "Notifications": "Értesítések", "One chat that grows": "Egy beszélgetés, amely bővül", + "Only in its history.": "Csak az előzményeiben.", "Open in the viewer": "Megnyitás a megjelenítőben", "Open in the web interface": "Megnyitás a webes felületen", + "Open its chat": "Csevegésének megnyitása", "Open its conversation": "A beszélgetésének megnyitása", "Open the activity panel": "A tevékenységpanel megnyitása", + "Open the chat": "A csevegés megnyitása", "Open the page again": "Oldal újbóli megnyitása", - "Open the result": "Az eredmény megnyitása", "Or type an answer": "Vagy írjon választ", "Outputs": "Kimenetek", "Page": "Oldal", @@ -1271,6 +1450,7 @@ const LEMBAS_I18N = { "Rename chat": "Csevegés átnevezése", "Rename…": "Átnevezés…", "Restore": "Visszaállítás", + "Result": "Eredmény", "Resume": "Folytatás", "Resume %(title)s": "%(title)s folytatása", "Room": "Szoba", @@ -1290,6 +1470,7 @@ const LEMBAS_I18N = { "Send": "Küldés", "Sending…": "Küldés…", "Server": "Kiszolgáló", + "Set the time yourself": "Az időpont megadása kézzel", "Settings": "Beállítások", "Show %(n)s more": "Még %(n)s megjelenítése", "Shown by the agent": "Az ügynök mutatta", @@ -1326,9 +1507,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "Az alkalmazás egyszer jelentkezik be a szerverére, a böngészőjében, mint bármely eszköz. Ezután olvashatja és írhatja a csevegéseit, mint a webes felület, és ugyanez a jóváhagyás összekapcsolja ezt a számítógépet az ügynök számára.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Az alkalmazás egyszer jelentkezik be a szerverére, a böngészőjében, mint bármely eszköz. Ezután olvashatja és írhatja a csevegéseit, mint a webes felület.", "The connection to the reply was lost: %(why)s": "A kapcsolat a válasszal megszakadt: %(why)s", + "The first free scheduling model": "Az első szabad ütemezési modell", + "The instruction each run is given, on its own: say everything it needs.": "Az utasítás, amelyet minden futás önmagában kap: mondjon el mindent, amire szüksége van.", + "The last %(n)s runs failed: %(why)s": "Az utolsó %(n)s futás sikertelen: %(why)s", + "The model's own": "A modell sajátja", "The most an agent chat may do here": "A legtöbb, amit egy ügynök-csevegés itt tehet", "The next runs": "A következő futások", "The reply is ready.": "A válasz kész.", + "The result is no longer there.": "Az eredmény már nincs meg.", "The server refused this computer's link: %(why)s": "A szerver elutasította ennek a számítógépnek a kapcsolatát: %(why)s", "These words give no run in the future.": "Ezekből a szavakból nem következik jövőbeli futás.", "Think before answering": "Gondolkodjon válasz előtt", @@ -1341,6 +1527,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Ez nem szövegfájl; itt nem jeleníthető meg.", "This server has no scheduled tasks yet.": "Ezen a szerveren még nincsenek ütemezett feladatok.", "Thought": "Gondolkodott", + "Time": "Időpont", + "Times in %(zone)s": "Időzóna: %(zone)s", + "Title": "Cím", "Today": "Ma", "Tools": "Eszközök", "Try again": "Újrapróbálás", @@ -1355,6 +1544,7 @@ const LEMBAS_I18N = { "When": "Mikor", "When it matters": "Ha számít", "Writing…": "Írás…", + "Written to %(path)s": "Kiírva ide: %(path)s", "You asked: “%(text)s”": "Az Ön kérése: „%(text)s”", "Your answer": "Az Ön válasza", "Your files on the server, as the chats see them.": "Az Ön fájljai a szerveren, ahogy a beszélgetések látják őket.", @@ -1367,16 +1557,21 @@ const LEMBAS_I18N = { "agent stopped": "ügynök leállt", "agent waiting": "ügynök vár", "agent: no folders": "ügynök: nincs mappa", + "busy": "foglalt", "caught up": "pótolva", "default": "alapértelmezett", "done": "kész", "failed": "sikertelen", + "free": "szabad", "late": "késve", "not linked": "nincs összekapcsolva", "off": "ki", "queued": "sorban áll", "report": "jelentés", + "run by hand": "kézzel indítva", "stopped": "leállítva", + "switched": "váltva", + "tried again %(when)s": "újra %(when)s", "update": "frissítés", "update %(version)s": "frissítés: %(version)s", "waiting": "várakozik", @@ -1386,8 +1581,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s” leáll, és az előzményei törlődnek. Az általa írt beszélgetések megmaradnak." }, "it": { + "%(model)s was busy, so it ran on this one.": "%(model)s era occupato, quindi è stata eseguita su questo.", "%(n)s agents working": "%(n)s agenti al lavoro", "%(n)s finished": "%(n)s terminati", + "%(n)s missed": "%(n)s perse", "%(n)s queued": "%(n)s in coda", "%(n)s tokens": "token: %(n)s", "%(n)s tools offered.": "%(n)s strumenti offerti.", @@ -1400,12 +1597,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agente al lavoro", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Un servizio LLeMbas CLI è già in esecuzione qui (pid %(pid)s), ma non è collegato: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Un servizio LLeMbas CLI è già in esecuzione qui (pid %(pid)s); è l'agente di questo computer.", + "A busy model is waited for, then the next free one is used.": "Si attende un modello occupato, poi si usa il successivo libero.", "A chat asking for more is lowered to this.": "Una chat che chiede di più viene limitata a questo.", "A chat on %(server)s, with your account's models and files.": "Una chat su %(server)s, con i modelli e i file del tuo account.", "A new chat for each run": "Una nuova chat per ogni esecuzione", - "A scheduled task failed.": "Un’attività pianificata non è riuscita.", + "A result, but not a run with nothing to report.": "Un risultato, ma non un’esecuzione senza nulla da segnalare.", + "A scheduled task failed 3 times in a row. Pause it?": "Un'attività pianificata è fallita 3 volte di fila. Metterla in pausa?", + "A scheduled task failed.": "Un'attività pianificata è fallita.", "A scheduled task has a result.": "Un’attività pianificata ha un risultato.", "A shell as you, in one of these folders.": "Una shell con il tuo utente, in una di queste cartelle.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Un’attività senza un modello proprio gira sul primo libero di questi, in quest’ordine. Nessuno spuntato: tutti.", + "A team of up to %(n)s": "Un team fino a %(n)s", + "A team works on it together, its own agent included.": "Un team ci lavora insieme, incluso il suo agente.", "About": "Informazioni", "Account": "Account", "Activity": "Attività", @@ -1432,6 +1635,7 @@ const LEMBAS_I18N = { "Chat": "Chat", "Chats": "Chat", "Check for updates…": "Cerca aggiornamenti…", + "Choose a model to choose its effort.": "Scegli un modello per sceglierne l’impegno.", "Close": "Chiudi", "Close (LLeMbas stays in the tray)": "Chiudi (LLeMbas resta nell'area di notifica)", "Close the activity panel": "Chiudi il pannello attività", @@ -1446,6 +1650,7 @@ const LEMBAS_I18N = { "Copy": "Copia", "Counted by the model at the last reply; the categories are estimated.": "Contato dal modello all’ultima risposta; le categorie sono stime.", "Cut short here; the whole of it is in the web interface.": "Troncato qui; il resto è nell'interfaccia web.", + "Days": "Giorni", "Delete": "Elimina", "Delete chat": "Elimina chat", "Delete this chat?": "Eliminare questa chat?", @@ -1453,12 +1658,19 @@ const LEMBAS_I18N = { "Deny": "Nega", "Done": "Fatto", "Drop the files to attach them": "Rilasci i file per allegarli", + "Due %(when)s": "Prevista %(when)s", + "Each run sees the ones before it.": "Ogni esecuzione vede quelle precedenti.", + "Each run starts with nothing but the instruction.": "Ogni esecuzione parte solo dall’istruzione.", "Earlier": "Prima", "Earlier messages": "Messaggi precedenti", + "Earlier runs": "Esecuzioni precedenti", + "Edit": "Modifica", + "Edit %(title)s": "Modifica %(title)s", "Edit message": "Modifica messaggio", "Edit — asks before commands": "Edit — chiede prima dei comandi", "Empty folder": "Cartella vuota", "Estimated: no reply has been counted yet.": "Stima: nessuna risposta è stata ancora contata.", + "Every result, even with nothing to report.": "Ogni risultato, anche senza nulla da segnalare.", "Every weekday at 8:00, summarise the news on my reading list": "Ogni giorno feriale alle 8:00, riassumi le notizie della mia lista di lettura", "Failed": "Non riusciti", "Fewest": "Minimo", @@ -1491,9 +1703,11 @@ const LEMBAS_I18N = { "Message": "Messaggio", "Messages": "Messaggi", "Minimise": "Riduci a icona", - "Missed": "Persa", + "Missed": "Mancata", "Mode": "Modalità", "Model": "Modello", + "Models": "Modelli", + "Models for scheduled tasks": "Modelli per le attività pianificate", "More": "Altro", "Most": "Massimo", "Never": "Mai", @@ -1502,12 +1716,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nuova attività pianificata", "Next run": "Prossima esecuzione", "No chat yet. Start one with New chat.": "Ancora nessuna chat. Iniziane una con Nuova chat.", + "No day ticked: every day.": "Nessun giorno spuntato: ogni giorno.", "No files written in this chat yet.": "In questa chat non è ancora stato scritto alcun file.", "No folder yet: add one under Settings, Project folders.": "Nessuna cartella ancora: ne aggiunga una in Impostazioni, Cartelle di progetto.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Ancora nessuna cartella: l'agente non può aprire nulla. Aggiungi la cartella di un progetto.", "No model is available to this account.": "Nessun modello è disponibile per questo account.", "No more runs": "Nessun’altra esecuzione", "No project folder yet: the agent can open nothing on this computer.": "Nessuna cartella di progetto ancora: l'agente non può aprire nulla su questo computer.", + "No team": "Nessun team", "Not measured.": "Non misurato.", "Not run yet": "Non ancora eseguita", "Not yet": "Non ancora", @@ -1520,12 +1736,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Ancora niente.", "Notifications": "Notifiche", "One chat that grows": "Una chat che cresce", + "Only in its history.": "Solo nella sua cronologia.", "Open in the viewer": "Apri nel visualizzatore", "Open in the web interface": "Apri nell'interfaccia web", + "Open its chat": "Apri la sua chat", "Open its conversation": "Apri la sua conversazione", "Open the activity panel": "Apri il pannello attività", + "Open the chat": "Apri la chat", "Open the page again": "Riapri la pagina", - "Open the result": "Apri il risultato", "Or type an answer": "Oppure scriva una risposta", "Outputs": "Risultati", "Page": "Pagina", @@ -1546,6 +1764,7 @@ const LEMBAS_I18N = { "Rename chat": "Rinomina chat", "Rename…": "Rinomina…", "Restore": "Ripristina", + "Result": "Risultato", "Resume": "Riprendi", "Resume %(title)s": "Riprendi %(title)s", "Room": "Stanza", @@ -1565,6 +1784,7 @@ const LEMBAS_I18N = { "Send": "Invia", "Sending…": "Invio…", "Server": "Server", + "Set the time yourself": "Imposta tu l’orario", "Settings": "Impostazioni", "Show %(n)s more": "Mostra altri %(n)s", "Shown by the agent": "Mostrato dall'agente", @@ -1601,9 +1821,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "L'app accede al suo server una volta, nel suo browser, come ogni dispositivo. Poi può leggere e scrivere le sue chat, come l'interfaccia web, e la stessa approvazione collega questo computer per l'agente.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "L'app accede al suo server una volta, nel suo browser, come ogni dispositivo. Poi può leggere e scrivere le sue chat, come l'interfaccia web.", "The connection to the reply was lost: %(why)s": "La connessione alla risposta si è interrotta: %(why)s", + "The first free scheduling model": "Il primo modello di pianificazione libero", + "The instruction each run is given, on its own: say everything it needs.": "L’istruzione che ogni esecuzione riceve da sola: di’ tutto ciò che le serve.", + "The last %(n)s runs failed: %(why)s": "Le ultime %(n)s esecuzioni non sono riuscite: %(why)s", + "The model's own": "Quello del modello", "The most an agent chat may do here": "Il massimo che una chat agente può fare qui", "The next runs": "Le prossime esecuzioni", "The reply is ready.": "La risposta è pronta.", + "The result is no longer there.": "Il risultato non c’è più.", "The server refused this computer's link: %(why)s": "Il server ha rifiutato il collegamento di questo computer: %(why)s", "These words give no run in the future.": "Queste parole non danno nessuna esecuzione futura.", "Think before answering": "Ragiona prima di rispondere", @@ -1616,6 +1841,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Questo non è un file di testo; non può essere mostrato qui.", "This server has no scheduled tasks yet.": "Questo server non ha ancora attività pianificate.", "Thought": "Ha ragionato", + "Time": "Ora", + "Times in %(zone)s": "Fuso orario: %(zone)s", + "Title": "Titolo", "Today": "Oggi", "Tools": "Strumenti", "Try again": "Riprova", @@ -1630,6 +1858,7 @@ const LEMBAS_I18N = { "When": "Quando", "When it matters": "Quando conta", "Writing…": "Sta scrivendo…", + "Written to %(path)s": "Scritto in %(path)s", "You asked: “%(text)s”": "Hai chiesto: «%(text)s»", "Your answer": "La sua risposta", "Your files on the server, as the chats see them.": "I tuoi file sul server, come li vedono le chat.", @@ -1642,16 +1871,21 @@ const LEMBAS_I18N = { "agent stopped": "agente fermato", "agent waiting": "agente in attesa", "agent: no folders": "agente: nessuna cartella", + "busy": "occupato", "caught up": "recuperata", "default": "predefinito", "done": "finito", "failed": "fallito", + "free": "libero", "late": "in ritardo", "not linked": "non collegato", "off": "disattivato", "queued": "in coda", "report": "resoconto", + "run by hand": "avviata a mano", "stopped": "interrotto", + "switched": "cambiato", + "tried again %(when)s": "di nuovo %(when)s", "update": "aggiorna", "update %(version)s": "aggiorna a %(version)s", "waiting": "in attesa", @@ -1661,8 +1895,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "«%(title)s» si ferma e la sua cronologia viene eliminata. Le chat che ha scritto restano." }, "pl": { + "%(model)s was busy, so it ran on this one.": "%(model)s był zajęty, więc uruchomiono je na tym.", "%(n)s agents working": "Pracujący agenci: %(n)s", "%(n)s finished": "Zakończone: %(n)s", + "%(n)s missed": "pominięte: %(n)s", "%(n)s queued": "W kolejce: %(n)s", "%(n)s tokens": "Tokeny: %(n)s", "%(n)s tools offered.": "Oferowane narzędzia: %(n)s.", @@ -1675,12 +1911,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agent pracuje", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Usługa LLeMbas CLI już tu działa (pid %(pid)s), ale nie jest połączona: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Usługa LLeMbas CLI już tu działa (pid %(pid)s); jest agentem tego komputera.", + "A busy model is waited for, then the next free one is used.": "Na zajęty model się czeka, potem używany jest następny wolny.", "A chat asking for more is lowered to this.": "Czat, który prosi o więcej, zostaje ograniczony do tego.", "A chat on %(server)s, with your account's models and files.": "Czat na %(server)s, z modelami i plikami Twojego konta.", "A new chat for each run": "Nowy czat dla każdego uruchomienia", + "A result, but not a run with nothing to report.": "Wynik, ale nie uruchomienie bez niczego do zgłoszenia.", + "A scheduled task failed 3 times in a row. Pause it?": "Zaplanowane zadanie nie powiodło się 3 razy z rzędu. Wstrzymać je?", "A scheduled task failed.": "Zaplanowane zadanie nie powiodło się.", "A scheduled task has a result.": "Zaplanowane zadanie ma wynik.", "A shell as you, in one of these folders.": "Powłoka na Twoim koncie, w jednym z tych folderów.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Zadanie bez własnego modelu działa na pierwszym wolnym z tych, w tej kolejności. Żaden niezaznaczony: wszystkie.", + "A team of up to %(n)s": "Zespół do %(n)s", + "A team works on it together, its own agent included.": "Pracuje nad nim zespół, wraz z jego własnym agentem.", "About": "O aplikacji", "Account": "Konto", "Activity": "Aktywność", @@ -1707,6 +1949,7 @@ const LEMBAS_I18N = { "Chat": "Czat", "Chats": "Czaty", "Check for updates…": "Sprawdź aktualizacje…", + "Choose a model to choose its effort.": "Wybierz model, aby wybrać jego wysiłek.", "Close": "Zamknij", "Close (LLeMbas stays in the tray)": "Zamknij (LLeMbas zostaje w zasobniku systemowym)", "Close the activity panel": "Zamknij panel aktywności", @@ -1721,6 +1964,7 @@ const LEMBAS_I18N = { "Copy": "Kopiuj", "Counted by the model at the last reply; the categories are estimated.": "Policzone przez model przy ostatniej odpowiedzi; kategorie są szacunkowe.", "Cut short here; the whole of it is in the web interface.": "Tu skrócone; całość jest w interfejsie WWW.", + "Days": "Dni", "Delete": "Usuń", "Delete chat": "Usuń czat", "Delete this chat?": "Usunąć ten czat?", @@ -1728,12 +1972,19 @@ const LEMBAS_I18N = { "Deny": "Odrzuć", "Done": "Gotowe", "Drop the files to attach them": "Upuść pliki, aby je dołączyć", + "Due %(when)s": "Zaplanowane na %(when)s", + "Each run sees the ones before it.": "Każde uruchomienie widzi poprzednie.", + "Each run starts with nothing but the instruction.": "Każde uruchomienie zaczyna tylko od instrukcji.", "Earlier": "Wcześniej", "Earlier messages": "Wcześniejsze wiadomości", + "Earlier runs": "Wcześniejsze uruchomienia", + "Edit": "Edytuj", + "Edit %(title)s": "Edytuj %(title)s", "Edit message": "Edytuj wiadomość", "Edit — asks before commands": "Edit — pyta przed poleceniami", "Empty folder": "Pusty folder", "Estimated: no reply has been counted yet.": "Szacunek: żadna odpowiedź nie została jeszcze policzona.", + "Every result, even with nothing to report.": "Każdy wynik, nawet bez niczego do zgłoszenia.", "Every weekday at 8:00, summarise the news on my reading list": "W każdy dzień roboczy o 8:00 streść wiadomości z mojej listy do czytania", "Failed": "Nieudane", "Fewest": "Najmniej", @@ -1746,11 +1997,11 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Własnymi słowami. Serwer zamieni je w regułę i polecenie i pokaże następne uruchomienia, zanim cokolwiek zostanie zapisane.", "Install LLeMbas %(version)s…": "Zainstaluj LLeMbas %(version)s…", "Instruction files": "Pliki z instrukcjami", - "It has not run yet.": "Jeszcze nie było uruchomione.", + "It has not run yet.": "Jeszcze się nie uruchomił.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Wliczając własnego agenta. Przy więcej niż jednym może dodać członków, którzy wykonują pracę, gdy on nimi kieruje.", "LLeMbas %(version)s is available (this is %(current)s)": "Dostępny jest LLeMbas %(version)s (to jest %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop wymaga serwera LLeMbas 2.0 lub nowszego.", - "Last run": "Ostatnie uruchomienie", + "Last run": "Ostatni przebieg", "Let the server open a terminal here": "Pozwól serwerowi otworzyć tu terminal", "Link again": "Połącz ponownie", "Link it again to give the agent a new token.": "Połącz go ponownie, aby agent dostał nowy token.", @@ -1766,9 +2017,11 @@ const LEMBAS_I18N = { "Message": "Wiadomość", "Messages": "Wiadomości", "Minimise": "Minimalizuj", - "Missed": "Opuszczone", + "Missed": "Przegapiony", "Mode": "Tryb", "Model": "Model", + "Models": "Modele", + "Models for scheduled tasks": "Modele dla zaplanowanych zadań", "More": "Więcej", "Most": "Najwięcej", "Never": "Nigdy", @@ -1777,12 +2030,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nowe zaplanowane zadanie", "Next run": "Następne uruchomienie", "No chat yet. Start one with New chat.": "Jeszcze nie ma czatu. Zacznij go przyciskiem Nowy czat.", + "No day ticked: every day.": "Żaden dzień niezaznaczony: codziennie.", "No files written in this chat yet.": "W tym czacie nie zapisano jeszcze żadnego pliku.", "No folder yet: add one under Settings, Project folders.": "Jeszcze nie ma folderu: dodaj go w Ustawieniach, Foldery projektów.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Nie ma jeszcze folderu: agent nie może nic otworzyć. Dodaj folder projektu.", "No model is available to this account.": "Dla tego konta nie jest dostępny żaden model.", "No more runs": "Brak kolejnych uruchomień", "No project folder yet: the agent can open nothing on this computer.": "Jeszcze nie ma folderu projektu: agent nie może niczego otworzyć na tym komputerze.", + "No team": "Bez zespołu", "Not measured.": "Nie zmierzono.", "Not run yet": "Jeszcze nie uruchomione", "Not yet": "Jeszcze nie", @@ -1795,18 +2050,20 @@ const LEMBAS_I18N = { "Nothing yet.": "Jeszcze nic.", "Notifications": "Powiadomienia", "One chat that grows": "Jeden czat, który rośnie", + "Only in its history.": "Tylko w jego historii.", "Open in the viewer": "Otwórz w podglądzie", "Open in the web interface": "Otwórz w interfejsie WWW", + "Open its chat": "Otwórz jego czat", "Open its conversation": "Otwórz jego rozmowę", "Open the activity panel": "Otwórz panel aktywności", + "Open the chat": "Otwórz czat", "Open the page again": "Otwórz stronę ponownie", - "Open the result": "Otwórz wynik", "Or type an answer": "Albo wpisz odpowiedź", "Outputs": "Wyniki", "Page": "Strona", "Pause": "Wstrzymaj", "Pause %(title)s": "Wstrzymaj %(title)s", - "Paused": "Wstrzymane", + "Paused": "Wstrzymany", "Plan": "Plan", "Plan — changes nothing": "Plan — niczego nie zmienia", "Preview": "Podgląd", @@ -1821,6 +2078,7 @@ const LEMBAS_I18N = { "Rename chat": "Zmień nazwę czatu", "Rename…": "Zmień nazwę…", "Restore": "Przywróć", + "Result": "Wynik", "Resume": "Wznów", "Resume %(title)s": "Wznów %(title)s", "Room": "Pokój", @@ -1840,6 +2098,7 @@ const LEMBAS_I18N = { "Send": "Wyślij", "Sending…": "Wysyłanie…", "Server": "Serwer", + "Set the time yourself": "Ustaw czas samodzielnie", "Settings": "Ustawienia", "Show %(n)s more": "Pokaż jeszcze %(n)s", "Shown by the agent": "Pokazane przez agenta", @@ -1849,7 +2108,7 @@ const LEMBAS_I18N = { "Signed in as %(who)s.": "Zalogowano jako %(who)s.", "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ęte", + "Skipped": "Pominięty", "Source": "Źródło", "Start the agent": "Uruchom agenta", "Stop": "Zatrzymać", @@ -1876,9 +2135,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "Aplikacja loguje się do Twojego serwera raz, w przeglądarce, jak każde urządzenie. Potem może czytać i pisać Twoje czaty, jak interfejs WWW, a to samo zatwierdzenie łączy ten komputer dla agenta.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikacja loguje się do Twojego serwera raz, w przeglądarce, jak każde urządzenie. Potem może czytać i pisać Twoje czaty, jak interfejs WWW.", "The connection to the reply was lost: %(why)s": "Połączenie z odpowiedzią zostało zerwane: %(why)s", + "The first free scheduling model": "Pierwszy wolny model do planowania", + "The instruction each run is given, on its own: say everything it needs.": "Instrukcja, którą każde uruchomienie dostaje samodzielnie: podaj wszystko, czego potrzebuje.", + "The last %(n)s runs failed: %(why)s": "Ostatnie uruchomienia nie powiodły się (%(n)s): %(why)s", + "The model's own": "Domyślne modelu", "The most an agent chat may do here": "Najwięcej, co czat agenta może tu zrobić", "The next runs": "Następne uruchomienia", "The reply is ready.": "Odpowiedź jest gotowa.", + "The result is no longer there.": "Wyniku już nie ma.", "The server refused this computer's link: %(why)s": "Serwer odrzucił połączenie tego komputera: %(why)s", "These words give no run in the future.": "Z tych słów nie wynika żadne przyszłe uruchomienie.", "Think before answering": "Rozumuj przed odpowiedzią", @@ -1891,6 +2155,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "To nie jest plik tekstowy; nie można go tu pokazać.", "This server has no scheduled tasks yet.": "Ten serwer nie ma jeszcze zaplanowanych zadań.", "Thought": "Rozumowanie", + "Time": "Godzina", + "Times in %(zone)s": "Strefa czasowa: %(zone)s", + "Title": "Tytuł", "Today": "Dzisiaj", "Tools": "Narzędzia", "Try again": "Spróbuj ponownie", @@ -1905,6 +2172,7 @@ const LEMBAS_I18N = { "When": "Kiedy", "When it matters": "Gdy to ważne", "Writing…": "Pisze…", + "Written to %(path)s": "Zapisano do %(path)s", "You asked: “%(text)s”": "Twoja prośba: „%(text)s”", "Your answer": "Twoja odpowiedź", "Your files on the server, as the chats see them.": "Twoje pliki na serwerze, tak jak widzą je czaty.", @@ -1917,16 +2185,21 @@ const LEMBAS_I18N = { "agent stopped": "agent zatrzymany", "agent waiting": "agent czeka", "agent: no folders": "agent: brak folderów", - "caught up": "nadrobione", + "busy": "zajęty", + "caught up": "nadrobiony", "default": "domyślna", "done": "gotowe", "failed": "nie powiodło się", + "free": "wolny", "late": "z opóźnieniem", "not linked": "niepołączony", "off": "wył.", "queued": "w kolejce", "report": "raport", + "run by hand": "uruchomiony ręcznie", "stopped": "zatrzymane", + "switched": "zmieniony", + "tried again %(when)s": "ponownie %(when)s", "update": "aktualizacja", "update %(version)s": "aktualizacja %(version)s", "waiting": "czeka", @@ -1936,8 +2209,10 @@ const LEMBAS_I18N = { "“%(title)s” stops, and its history goes. The chats it wrote stay.": "„%(title)s” zatrzyma się, a jego historia zniknie. Czaty, które napisało, zostaną." }, "sk": { + "%(model)s was busy, so it ran on this one.": "%(model)s bol zaneprázdnený, preto bežala na tomto.", "%(n)s agents working": "Pracujúci agenti: %(n)s", "%(n)s finished": "Dokončené: %(n)s", + "%(n)s missed": "zmeškané: %(n)s", "%(n)s queued": "V rade: %(n)s", "%(n)s tokens": "tokeny: %(n)s", "%(n)s tools offered.": "Ponúknuté nástroje: %(n)s.", @@ -1950,12 +2225,18 @@ const LEMBAS_I18N = { "1 agent working": "1 agent pracuje", "A LLeMbas CLI service already runs here (pid %(pid)s), but is not connected: %(why)s": "Služba LLeMbas CLI tu už beží (pid %(pid)s), ale nie je pripojená: %(why)s", "A LLeMbas CLI service already runs here (pid %(pid)s); it is this computer's agent.": "Služba LLeMbas CLI tu už beží (pid %(pid)s); je agentom tohto počítača.", + "A busy model is waited for, then the next free one is used.": "Na zaneprázdnený model sa čaká, potom sa použije ďalší voľný.", "A chat asking for more is lowered to this.": "Konverzácia, ktorá chce viac, je znížená na toto.", "A chat on %(server)s, with your account's models and files.": "Konverzácia na %(server)s s modelmi a súbormi vášho účtu.", "A new chat for each run": "Nová konverzácia pre každé spustenie", + "A result, but not a run with nothing to report.": "Výsledok, ale nie spustenie, ktoré nemá čo hlásiť.", + "A scheduled task failed 3 times in a row. Pause it?": "Naplánovaná úloha zlyhala 3-krát za sebou. Pozastaviť ju?", "A scheduled task failed.": "Naplánovaná úloha zlyhala.", "A scheduled task has a result.": "Naplánovaná úloha má výsledok.", "A shell as you, in one of these folders.": "Shell pod vaším účtom, v jednom z týchto priečinkov.", + "A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.": "Úloha bez vlastného modelu beží na prvom voľnom z týchto, v tomto poradí. Žiadny nezaškrtnutý: všetky.", + "A team of up to %(n)s": "Tím: najviac %(n)s", + "A team works on it together, its own agent included.": "Pracuje na nej tím spoločne, vrátane jej vlastného agenta.", "About": "O aplikácii", "Account": "Účet", "Activity": "Aktivita", @@ -1982,6 +2263,7 @@ const LEMBAS_I18N = { "Chat": "Konverzácia", "Chats": "Konverzácie", "Check for updates…": "Skontrolovať aktualizácie…", + "Choose a model to choose its effort.": "Vyberte model, aby ste mohli zvoliť jeho náročnosť.", "Close": "Zavrieť", "Close (LLeMbas stays in the tray)": "Zavrieť (LLeMbas zostane v oznamovacej oblasti)", "Close the activity panel": "Zavrieť panel aktivity", @@ -1996,6 +2278,7 @@ const LEMBAS_I18N = { "Copy": "Kopírovať", "Counted by the model at the last reply; the categories are estimated.": "Spočítal model pri poslednej odpovedi; kategórie sú odhadnuté.", "Cut short here; the whole of it is in the web interface.": "Tu skrátené; celé je to vo webovom rozhraní.", + "Days": "Dni", "Delete": "Zmazať", "Delete chat": "Zmazať konverzáciu", "Delete this chat?": "Zmazať túto konverzáciu?", @@ -2003,12 +2286,19 @@ const LEMBAS_I18N = { "Deny": "Odmietnuť", "Done": "Hotovo", "Drop the files to attach them": "Pustite súbory a priložia sa", + "Due %(when)s": "Naplánované na %(when)s", + "Each run sees the ones before it.": "Každé spustenie vidí tie predchádzajúce.", + "Each run starts with nothing but the instruction.": "Každé spustenie začína len s inštrukciou.", "Earlier": "Staršie", "Earlier messages": "Staršie správy", + "Earlier runs": "Staršie spustenia", + "Edit": "Upraviť", + "Edit %(title)s": "Upraviť %(title)s", "Edit message": "Upraviť správu", "Edit — asks before commands": "Edit — pýta sa pred príkazmi", "Empty folder": "Prázdny priečinok", "Estimated: no reply has been counted yet.": "Odhad: žiadna odpoveď ešte nebola spočítaná.", + "Every result, even with nothing to report.": "Každý výsledok, aj keď nie je čo hlásiť.", "Every weekday at 8:00, summarise the news on my reading list": "Každý pracovný deň o 8:00 zhrň správy z môjho zoznamu na čítanie", "Failed": "Zlyhalo", "Fewest": "Najmenej", @@ -2021,11 +2311,11 @@ const LEMBAS_I18N = { "In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.": "Vlastnými slovami. Server z nich vytvorí pravidlo a pokyn a pred uložením vám ukáže ďalšie spustenia.", "Install LLeMbas %(version)s…": "Nainštalovať LLeMbas %(version)s…", "Instruction files": "Súbory s pokynmi", - "It has not run yet.": "Ešte nebežala.", + "It has not run yet.": "Ešte nebežal.", "Its own agent included. Above one, it can add members that do the work while it leads them.": "Vrátane vlastného agenta. Pri viac ako jednom môže pridať členov, ktorí robia prácu, kým ich vedie.", "LLeMbas %(version)s is available (this is %(current)s)": "Je k dispozícii LLeMbas %(version)s (toto je %(current)s)", "LLeMbas Desktop needs a LLeMbas 2.0 server or later.": "LLeMbas Desktop potrebuje server LLeMbas 2.0 alebo novší.", - "Last run": "Posledné spustenie", + "Last run": "Posledný beh", "Let the server open a terminal here": "Dovoliť serveru otvoriť tu terminál", "Link again": "Prepojiť znova", "Link it again to give the agent a new token.": "Prepojte ho znova a agent dostane nový token.", @@ -2044,6 +2334,8 @@ const LEMBAS_I18N = { "Missed": "Zmeškané", "Mode": "Režim", "Model": "Model", + "Models": "Modely", + "Models for scheduled tasks": "Modely pre naplánované úlohy", "More": "Viac", "Most": "Najviac", "Never": "Nikdy", @@ -2052,12 +2344,14 @@ const LEMBAS_I18N = { "New scheduled task": "Nová naplánovaná úloha", "Next run": "Ďalšie spustenie", "No chat yet. Start one with New chat.": "Zatiaľ žiadna konverzácia. Začnite ju tlačidlom Nová konverzácia.", + "No day ticked: every day.": "Žiadny deň nezaškrtnutý: každý deň.", "No files written in this chat yet.": "V tomto chate sa zatiaľ nezapísal žiadny súbor.", "No folder yet: add one under Settings, Project folders.": "Zatiaľ žiadny priečinok: pridajte ho v Nastaveniach, Priečinky projektov.", "No folder yet: the agent can open nothing. Add the folder of a project.": "Zatiaľ žiadny priečinok: agent nemôže nič otvoriť. Pridajte priečinok projektu.", "No model is available to this account.": "Tomuto účtu nie je dostupný žiadny model.", "No more runs": "Žiadne ďalšie spustenia", "No project folder yet: the agent can open nothing on this computer.": "Zatiaľ žiadny priečinok projektu: agent na tomto počítači nemôže nič otvoriť.", + "No team": "Bez tímu", "Not measured.": "Nezmerané.", "Not run yet": "Ešte nebežala", "Not yet": "Ešte nie", @@ -2070,12 +2364,14 @@ const LEMBAS_I18N = { "Nothing yet.": "Zatiaľ nič.", "Notifications": "Upozornenia", "One chat that grows": "Jedna konverzácia, ktorá rastie", + "Only in its history.": "Len v jej histórii.", "Open in the viewer": "Otvoriť v prehliadači súborov", "Open in the web interface": "Otvoriť vo webovom rozhraní", + "Open its chat": "Otvoriť jeho konverzáciu", "Open its conversation": "Otvoriť jeho konverzáciu", "Open the activity panel": "Otvoriť panel aktivity", + "Open the chat": "Otvoriť konverzáciu", "Open the page again": "Otvoriť stránku znova", - "Open the result": "Otvoriť výsledok", "Or type an answer": "Alebo napíšte odpoveď", "Outputs": "Výstupy", "Page": "Stránka", @@ -2096,6 +2392,7 @@ const LEMBAS_I18N = { "Rename chat": "Premenovať konverzáciu", "Rename…": "Premenovať…", "Restore": "Obnoviť", + "Result": "Výsledok", "Resume": "Pokračovať", "Resume %(title)s": "Obnoviť %(title)s", "Room": "Miestnosť", @@ -2115,6 +2412,7 @@ const LEMBAS_I18N = { "Send": "Poslať", "Sending…": "Odosiela sa…", "Server": "Server", + "Set the time yourself": "Nastaviť čas sami", "Settings": "Nastavenia", "Show %(n)s more": "Zobraziť ďalšie (%(n)s)", "Shown by the agent": "Ukázal agent", @@ -2124,7 +2422,7 @@ const LEMBAS_I18N = { "Signed in as %(who)s.": "Prihlásený ako %(who)s.", "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": "Preskočené", + "Skipped": "Vynechané", "Source": "Zdroj", "Start the agent": "Spustiť agenta", "Stop": "Zastaviť", @@ -2135,7 +2433,7 @@ const LEMBAS_I18N = { "System prompt": "Systémový pokyn", "Taken in while it ran:": "Prevzaté počas behu:", "Team: %(size)s of %(max)s agents": "Tím: %(size)s z %(max)s agentov", - "Tell me": "Dať vedieť", + "Tell me": "Upozorniť ma", "Tell me when a reply or an agent's work is finished while I am elsewhere": "Upozorniť ma, keď je odpoveď alebo práca agenta hotová, kým som inde", "The agent asks to run %(tool)s:": "Agent žiada spustiť %(tool)s:", "The agent asks:": "Agent sa pýta:", @@ -2151,9 +2449,14 @@ const LEMBAS_I18N = { "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does, and the same approval links this computer for the agent.": "Aplikácia sa na váš server prihlási raz, vo vašom prehliadači, ako každé zariadenie. Potom môže čítať a písať vaše konverzácie, ako webové rozhranie, a to isté schválenie prepojí tento počítač pre agenta.", "The app signs in to your server once, in your browser, as any device does. It may then read and write your chats, as the web interface does.": "Aplikácia sa na váš server prihlási raz, vo vašom prehliadači, ako každé zariadenie. Potom môže čítať a písať vaše konverzácie, ako webové rozhranie.", "The connection to the reply was lost: %(why)s": "Spojenie s odpoveďou sa prerušilo: %(why)s", + "The first free scheduling model": "Prvý voľný model na plánovanie", + "The instruction each run is given, on its own: say everything it needs.": "Inštrukcia, ktorú dostane každé spustenie samostatne: uveďte všetko, čo potrebuje.", + "The last %(n)s runs failed: %(why)s": "Posledné spustenia zlyhali (%(n)s): %(why)s", + "The model's own": "Podľa modelu", "The most an agent chat may do here": "Najviac, čo tu agentská konverzácia smie", "The next runs": "Ďalšie spustenia", "The reply is ready.": "Odpoveď je hotová.", + "The result is no longer there.": "Výsledok už tu nie je.", "The server refused this computer's link: %(why)s": "Server odmietol prepojenie tohto počítača: %(why)s", "These words give no run in the future.": "Z týchto slov nevychádza žiadne budúce spustenie.", "Think before answering": "Premýšľať pred odpoveďou", @@ -2166,6 +2469,9 @@ const LEMBAS_I18N = { "This is not a text file; it cannot be shown here.": "Toto nie je textový súbor; nedá sa tu zobraziť.", "This server has no scheduled tasks yet.": "Tento server zatiaľ nemá naplánované úlohy.", "Thought": "Premýšľal", + "Time": "Čas", + "Times in %(zone)s": "Časové pásmo: %(zone)s", + "Title": "Názov", "Today": "Dnes", "Tools": "Nástroje", "Try again": "Skúsiť znova", @@ -2180,6 +2486,7 @@ const LEMBAS_I18N = { "When": "Kedy", "When it matters": "Keď na tom záleží", "Writing…": "Píše sa…", + "Written to %(path)s": "Zapísané do %(path)s", "You asked: “%(text)s”": "Požiadali ste: „%(text)s“", "Your answer": "Vaša odpoveď", "Your files on the server, as the chats see them.": "Vaše súbory na serveri, tak ako ich vidia konverzácie.", @@ -2192,16 +2499,21 @@ const LEMBAS_I18N = { "agent stopped": "agent zastavený", "agent waiting": "agent čaká", "agent: no folders": "agent: žiadne priečinky", - "caught up": "dobehnuté", + "busy": "zaneprázdnený", + "caught up": "dohnané", "default": "predvolené", "done": "hotovo", "failed": "zlyhalo", - "late": "neskoro", + "free": "voľný", + "late": "s oneskorením", "not linked": "neprepojené", "off": "vypnuté", "queued": "v rade", "report": "hlásenie", + "run by hand": "spustené ručne", "stopped": "zastavené", + "switched": "prepnuté", + "tried again %(when)s": "znova %(when)s", "update": "aktualizácia", "update %(version)s": "aktualizácia %(version)s", "waiting": "čaká", diff --git a/ui/js/main.js b/ui/js/main.js index a1963c3..b336f23 100644 --- a/ui/js/main.js +++ b/ui/js/main.js @@ -332,7 +332,7 @@ async function boot() { } }) - invoke("set_words", { words: { reply: t("The reply is ready."), agent: t("The agent has finished."), scheduled: t("A scheduled task has a result."), scheduled_failed: t("A scheduled task failed.") } }) + invoke("set_words", { words: { reply: t("The reply is ready."), agent: t("The agent has finished."), scheduled: t("A scheduled task has a result."), scheduled_failed: t("A scheduled task failed."), scheduled_pause: t("A scheduled task failed 3 times in a row. Pause it?") } }) listen("status", (e) => { const before = status status = e.payload diff --git a/ui/js/scheduled.js b/ui/js/scheduled.js index 9b87768..eb090c6 100644 --- a/ui/js/scheduled.js +++ b/ui/js/scheduled.js @@ -1,32 +1,47 @@ // Scheduled tasks: standing instructions the server runs on its own -- "every weekday at 8:00, // summarise …". The list (each task's next run, how its last run went, a switch to pause it), a -// task's page (what it does, when, how it tells, its history, Run now), and a new one said in -// words: the server turns them into a rule and a standalone instruction, the next five runs are -// shown, and only then is it saved. The server runs them; this page only reads and asks. +// task's page (what it does, when, its settings, the next runs, its history with each run's +// result, Run now, edit, pause, delete), and a task said in words: the server reads them into a +// rule and an instruction (compile), the next runs are shown, the settings chosen, and only then +// is it saved. The server runs them; this page only reads and asks. // -// Its routes are the server's /api/v2 for scheduling: +// The server's /api/v2 for scheduling (LLeMbas `api/v2/schedules.py`; the shapes are its contract +// fixtures `schedules.json`, `schedule*.json`, `event_schedules.json`): // -// GET /schedules { schedules: [Schedule] } -// GET /schedules/{id} { schedule, runs: [Run], more } -// POST /schedules/compile { text } -> { title, instruction, summary, rule, next: [stamp] } -// POST /schedules { request, title, instruction, rule, model?, notify?, mode? } -> { schedule } -// PATCH /schedules/{id} { enabled?, notify?, mode?, model? } -> { schedule } -// POST /schedules/{id}/run -> { run } -// DELETE /schedules/{id} +// GET /schedules { schedules: [schedule] }, newest first +// POST /schedules { title, instruction, request, rule, model, notify, +// mode, effort, agents } -> 201 { schedule } +// GET /schedules/{id} { schedule } +// PATCH /schedules/{id} any of the create fields -> { schedule } +// DELETE /schedules/{id}?keep_chat=true 204 +// POST /schedules/{id}/pause | /resume { schedule } +// POST /schedules/{id}/run 202 { run }: now, not counted against the rule +// GET /schedules/{id}/runs?before=&limit= { runs: [run], more }, newest first +// GET /schedules/{id}/runs/{run} { run, text }: the result, Markdown (untrusted) +// POST /schedules/compile { text } -> { ok, reason, title, instruction, +// target, rule, description, next_runs, timezone } +// POST /schedules/preview { rule } -> { rule, description, next_runs, timezone } +// GET /schedules/models { models: [model + free, busy, chosen], instance, mine } +// PUT /schedules/models { models: [id] }: the person's narrowing +// GET /events `schedules` frames, { version, schedules }: followed +// by the app (Rust, watch.rs), handed here as the +// `schedules` event // // A server without them answers 404: the page says this server has no scheduled tasks yet. -// Everything a task says (its title, instruction, a run's error) is the person's or a model's -// words, set as text. +// Everything a task says (its title, instruction, a run's result or error) is the person's or a +// model's words, set as text; a result's Markdown goes through markdown.js. import { $, h, clear, icon, ICONS, when } from "./dom.js" -import { get, post, patch, del, why } from "./bridge.js" -import { ask, menu } from "./popup.js" -import { avatar } from "./models.js" +import { api, get, post, patch, del, why, listen } from "./bridge.js" +import { ask, menu, popover } from "./popup.js" +import { avatar, levelsOf, levelWord } from "./models.js" +import { markdown } from "./markdown.js" let ctx = null // { status(), models(), me(), openChat(id), show() } -let view = { name: "list" } // { name: "list" } | { name: "task", id } | { name: "new" } +let view = { name: "list" } // { name: "list" } | { name: "task", id } | { name: "form", task? } let tasks = [] let missing = false // the server has no scheduling routes +let shown = null // the task whose page is open: { id, last, running } const STATUS = { ok: () => t("Done"), @@ -39,36 +54,56 @@ const STATUS = { } const NOTIFY = { - always: () => t("Always"), matters: () => t("When it matters"), + always: () => t("Always"), never: () => t("Never"), } +const NOTIFY_HINT = { + matters: () => t("A result, but not a run with nothing to report."), + always: () => t("Every result, even with nothing to report."), + never: () => t("Only in its history."), +} + const MODE = { fresh: () => t("A new chat for each run"), continue: () => t("One chat that grows"), } -const enc = encodeURIComponent +const TIMING = { + late: () => t("late"), + catch_up: () => t("caught up"), + manual: () => t("run by hand"), +} -function statusChip(run) { +const MAX_AGENTS = 8 // services/schedules.py MAX_AGENTS +const enc = encodeURIComponent +const RUNS_PAGE = 20 + +function statusChip(run, running) { + if (running) return h("span", { class: "schip is-running", text: STATUS.running() }) if (!run) return h("span", { class: "schip", text: t("Not run yet") }) const word = STATUS[run.status]?.() || run.status - const n = run.status === "missed" && run.missed > 1 ? ` ×${run.missed}` : "" - return h("span", { class: `schip is-${run.status}`, text: `${word}${n}` }) + // A missed run counts the slots that passed unrun; any other run says how many it followed. + const n = run.status === "missed" ? (run.missed > 1 ? ` ×${run.missed}` : "") : run.missed > 0 ? ` · ${t("%(n)s missed", { n: run.missed })}` : "" + return h("span", { class: `schip is-${run.status}`, title: `${word}${n}`, text: `${word}${n}` }) } /** A stamp as a date and time in the person's language: "Mon 10 Nov, 08:00". */ function stamp(value) { const d = new Date(value) - if (Number.isNaN(d.getTime())) return "" + if (!value || Number.isNaN(d.getTime())) return "" return d.toLocaleString(document.documentElement.lang || undefined, { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }) } function nextWords(task) { if (!task.enabled) return t("Paused") - if (!task.next_run_at) return t("No more runs") - return `${when(task.next_run_at)} · ${stamp(task.next_run_at)}` + if (!task.next_run) return t("No more runs") + return `${when(task.next_run)} · ${stamp(task.next_run)}` +} + +function onPage() { + return !$("page-scheduled").hidden } // --- Reading ------------------------------------------------------------------------------------ @@ -84,6 +119,15 @@ async function load() { } } +/** The scheduling models, in the order they are tried; null when the server has none to say. */ +async function schedulingModels() { + try { + return await get("/schedules/models") + } catch { + return null + } +} + /** Show the page: the list, or one task's page (`id`). */ export async function showScheduled(id) { view = id ? { name: "task", id } : { name: "list" } @@ -94,10 +138,11 @@ async function draw() { const body = clear($("scheduled-body")) clear($("scheduled-tools")) clear($("scheduled-place")) + shown = null body.append(h("p", { class: "muted page__loading", text: t("Loading…") })) try { if (view.name === "task") await drawTask(view.id) - else if (view.name === "new") drawNew() + else if (view.name === "form") await drawForm(view.task) else { await load() drawList() @@ -114,6 +159,11 @@ function header(title, back) { else h1.textContent = title } +function go(next) { + view = next + draw() +} + // --- The list ------------------------------------------------------------------------------------- function drawList() { @@ -124,11 +174,14 @@ function drawList() { body.append(empty(t("This server has no scheduled tasks yet."), t("Scheduled tasks need a newer LLeMbas server. Ask its administrator to update it."))) return } - tools.append(h("button", { type: "button", class: "btn btn--sm btn--primary", onclick: () => go({ name: "new" }) }, icon(ICONS.plus), h("span", { text: t("New scheduled task") }))) + tools.append( + h("button", { type: "button", class: "btn btn--sm", id: "sched-models", "aria-haspopup": "dialog", onclick: (e) => modelsPanel(e.currentTarget) }, h("span", { text: t("Models") })), + h("button", { type: "button", class: "btn btn--sm btn--primary", id: "sched-new", onclick: () => go({ name: "form" }) }, icon(ICONS.plus), h("span", { text: t("New scheduled task") })), + ) if (!tasks.length) { body.append( empty(t("Nothing is scheduled."), t("Say what should happen and when — “every weekday at 8:00, summarise the news on my reading list” — and the server runs it for you.")), - h("div", { class: "empty__actions" }, h("button", { type: "button", class: "btn btn--primary", onclick: () => go({ name: "new" }) }, icon(ICONS.plus), h("span", { text: t("New scheduled task") }))), + h("div", { class: "empty__actions" }, h("button", { type: "button", class: "btn btn--primary", onclick: () => go({ name: "form" }) }, icon(ICONS.plus), h("span", { text: t("New scheduled task") }))), ) return } @@ -152,30 +205,30 @@ function taskRow(task) { onclick: (e) => e.stopPropagation(), onchange: (e) => setEnabled(task, e.target.checked), }) - const row = h( + return h( "li", - { class: `task${task.enabled ? "" : " is-paused"}` }, + { class: `task${task.enabled ? "" : " is-paused"}`, dataset: { id: task.id } }, h( "button", { type: "button", class: "task__open", onclick: () => go({ name: "task", id: task.id }) }, h("span", { class: "task__icon" }, icon(ICONS.clock)), - h("span", { class: "task__what" }, h("span", { class: "task__title", text: task.title || task.request || "" }), h("span", { class: "task__rule", text: task.summary || "" })), + h("span", { class: "task__what" }, h("span", { class: "task__title", text: task.title || task.request || "" }), h("span", { class: "task__rule", text: task.description || "" })), h( "span", - { class: "task__next", title: task.enabled && task.next_run_at ? stamp(task.next_run_at) : "" }, + { class: "task__next", title: task.enabled && task.next_run ? stamp(task.next_run) : "" }, h("span", { class: "task__label", text: t("Next run") }), - h("span", { class: "task__value", text: task.enabled && task.next_run_at ? when(task.next_run_at) : nextWords(task) }), + h("span", { class: "task__value", text: task.enabled && task.next_run ? when(task.next_run) : nextWords(task) }), ), - h("span", { class: "task__last" }, statusChip(task.last_run)), + h("span", { class: "task__last" }, statusChip(task.last_run, task.running)), ), h("label", { class: "toggle task__toggle" }, toggle), ) - return row } +/** Pause or resume: the server's own verbs, which answer the schedule as it now is. */ async function setEnabled(task, on) { try { - const r = await patch(`/schedules/${enc(task.id)}`, { enabled: on }) + const r = await post(`/schedules/${enc(task.id)}/${on ? "resume" : "pause"}`) Object.assign(task, r.schedule || { enabled: on }) } catch (err) { await ask({ title: t("Scheduled"), text: why(err), ok: t("Close"), cancel: t("Cancel") }) @@ -184,60 +237,129 @@ async function setEnabled(task, on) { else draw() } -function go(next) { - view = next - draw() +/** + * The scheduling models: the ones scheduled work may run on, in the order they are tried, each + * free now or busy (and why). A task without a model of its own runs on the first free one of + * those ticked; ticking none means all of them. + */ +async function modelsPanel(anchor) { + const data = await schedulingModels() + if (!data) return + let mine = new Set(data.mine || []) + const said = h("p", { class: "status is-error", hidden: true }) + const save = async () => { + try { + const r = await api("PUT", "/schedules/models", { models: data.models.filter((m) => mine.has(m.id)).map((m) => m.id) }) + Object.assign(data, r) + mine = new Set(r.mine || []) + said.hidden = true + } catch (err) { + said.textContent = why(err) + said.hidden = false + } + pop.redraw() + } + const content = () => + h( + "div", + { class: "pop__body smodels" }, + h("p", { class: "pop__title", text: t("Models for scheduled tasks") }), + h("p", { class: "smodels__hint", text: t("A task without a model of its own runs on the first free one of these, in this order. None ticked: all of them.") }), + h( + "ul", + { class: "smodels__list", role: "list" }, + data.models.map((m) => + h( + "li", + null, + h( + "label", + { class: "smodel" }, + h("input", { + type: "checkbox", + checked: mine.has(m.id), + onchange: (e) => { + if (e.target.checked) mine.add(m.id) + else mine.delete(m.id) + save() + }, + }), + avatar(m), + h("span", { class: "smodel__name", text: m.name }), + h("span", { class: `smodel__state${m.free ? " is-free" : ""}`, title: m.busy || "", text: m.free ? t("free") : t("busy") }), + ), + ), + ), + ), + data.models.length ? null : h("p", { class: "muted", text: t("No model is available to this account.") }), + said, + ) + const pop = popover(anchor, content, { align: "end", label: t("Models for scheduled tasks") }) } // --- One task ----------------------------------------------------------------------------------- async function drawTask(id) { - const r = await get(`/schedules/${enc(id)}`) + const [r, page] = await Promise.all([get(`/schedules/${enc(id)}`), get(`/schedules/${enc(id)}/runs?limit=${RUNS_PAGE}`)]) const task = r.schedule - const runs = r.runs || [] + shown = { id: task.id, last: task.last_run?.id || null, status: task.last_run?.status || null, running: task.running } header(task.title || t("Scheduled"), () => go({ name: "list" })) const tools = clear($("scheduled-tools")) tools.append( - h("button", { type: "button", class: "btn btn--sm btn--primary", onclick: (e) => runNow(task, e.currentTarget) }, icon(ICONS.play), h("span", { text: t("Run now") })), - h("button", { - type: "button", - class: "tool", - "aria-label": t("More"), - title: t("More"), - onclick: (e) => - menu(e.currentTarget, [ - { label: task.enabled ? t("Pause") : t("Resume"), run: () => setEnabled(task, !task.enabled) }, - null, - { label: t("Delete"), danger: true, run: () => remove(task) }, - ]), - }, icon(ICONS.more)), + h("button", { type: "button", class: "btn btn--sm btn--primary", id: "sched-run", disabled: !!task.running, onclick: (e) => runNow(task, e.currentTarget) }, icon(ICONS.play), h("span", { text: t("Run now") })), + h("button", { type: "button", class: "btn btn--sm", id: "sched-edit", onclick: () => go({ name: "form", task }) }, icon(ICONS.edit), h("span", { text: t("Edit") })), + h( + "button", + { + type: "button", + class: "tool", + "aria-label": t("More"), + title: t("More"), + onclick: (e) => + menu(e.currentTarget, [ + { label: task.enabled ? t("Pause") : t("Resume"), run: () => setEnabled(task, !task.enabled) }, + ...(task.chat_id ? [{ label: t("Open its chat"), run: () => ctx.openChat(task.chat_id) }] : []), + null, + { label: t("Delete"), danger: true, run: () => remove(task) }, + ]), + }, + icon(ICONS.more), + ), ) - const models = ctx.models() - const model = models.find((m) => m.id === task.model) + const models = (await schedulingModels())?.models || ctx.models() const body = clear($("scheduled-body")) body.append( h( "section", { class: "sched" }, + task.failure_streak > 0 && task.last_error ? h("p", { class: "status is-error sched__trouble", text: t("The last %(n)s runs failed: %(why)s", { n: task.failure_streak, why: task.last_error }) }) : null, h( "div", { class: "sched__facts" }, - fact(t("When"), h("span", { text: task.summary || "" })), + fact(t("When"), h("span", { text: task.description || "" }), task.timezone ? t("Times in %(zone)s", { zone: task.timezone }) : ""), fact(t("Next run"), h("span", { text: nextWords(task) })), - fact(t("Last run"), statusChip(task.last_run)), - fact(t("Model"), model ? h("span", { class: "sched__model" }, avatar(model), h("span", { text: model.name })) : h("span", { text: task.model || t("default") })), + fact(t("Last run"), statusChip(task.last_run, task.running)), + fact(t("Model"), modelWords(models, task.model)), + fact(t("Reasoning effort"), h("span", { text: task.effort ? levelWord(task.effort) : t("The model's own") })), + fact(t("Agents"), h("span", { text: task.agents ? t("A team of up to %(n)s", { n: task.agents }) : t("No team") })), fact(t("Tell me"), h("span", { text: NOTIFY[task.notify]?.() || NOTIFY.matters() })), fact(t("Runs"), h("span", { text: MODE[task.mode]?.() || MODE.fresh() })), ), h("div", { class: "sched__what" }, h("h2", { class: "sched__h", text: t("What it does") }), h("p", { class: "sched__instruction", text: task.instruction || task.request || "" }), task.request && task.request !== task.instruction ? h("p", { class: "sched__asked", text: t("You asked: “%(text)s”", { text: task.request }) }) : null), - upcoming(task.next || []), + upcoming(task.next_runs || []), ), - history(runs), + history(task, page.runs || [], page.more), ) } -function fact(label, value) { - return h("div", { class: "fact" }, h("span", { class: "fact__label", text: label }), h("span", { class: "fact__value" }, value)) +function fact(label, value, hint) { + return h("div", { class: "fact" }, h("span", { class: "fact__label", text: label }), h("span", { class: "fact__value" }, value), hint ? h("span", { class: "fact__hint", text: hint }) : null) +} + +function modelWords(models, id) { + if (!id) return h("span", { text: t("The first free scheduling model") }) + const m = models.find((x) => x.id === id) + return m ? h("span", { class: "sched__model" }, avatar(m), h("span", { text: m.name })) : h("span", { text: id }) } function upcoming(next) { @@ -245,34 +367,93 @@ function upcoming(next) { return h("div", { class: "sched__next" }, h("h2", { class: "sched__h", text: t("The next runs") }), h("ol", { class: "runs-next" }, next.slice(0, 5).map((s) => h("li", null, h("span", { text: stamp(s) }), h("span", { class: "muted", text: when(s) }))))) } -/** The history: one row per run on fixed tracks -- status, when, model, and its result. */ -function history(runs) { +/** The history: one row per run on fixed tracks -- status, when, model, and a way to its result. */ +function history(task, runs, more) { const box = h("section", { class: "sched__history" }, h("h2", { class: "sched__h", text: t("History") })) if (!runs.length) { box.append(h("p", { class: "muted", text: t("It has not run yet.") })) return box } - const models = ctx.models() const list = h("ul", { class: "runs" }) - for (const run of runs) { - const m = models.find((x) => x.id === run.model) - list.append( - h( - "li", - { class: "run" }, - h("span", { class: "run__status" }, statusChip(run)), - h("span", { class: "run__when", title: run.started_at || run.due_at || "" }, h("span", { text: stamp(run.started_at || run.due_at) }), run.timing === "late" || run.timing === "catch_up" ? h("span", { class: "run__late", text: run.timing === "late" ? t("late") : t("caught up") }) : null), - h("span", { class: "run__model", title: run.model_reason || "" }, m ? avatar(m) : null, h("span", { text: m?.name || run.model || "" })), - run.chat_id - ? h("button", { type: "button", class: "btn btn--sm", text: t("Open the result"), onclick: () => ctx.openChat(run.chat_id) }) - : h("span", { class: "run__error", title: run.error || "", text: run.error || "" }), - ), - ) + const add = (page) => { + for (const run of page) list.append(runRow(task, run)) } + add(runs) box.append(list) + if (more) { + const earlier = h("button", { type: "button", class: "btn btn--sm sched__more", text: t("Earlier runs") }) + earlier.addEventListener("click", async () => { + earlier.disabled = true + try { + const last = list.lastElementChild?.dataset.id || "" + const r = await get(`/schedules/${enc(task.id)}/runs?limit=${RUNS_PAGE}&before=${enc(last)}`) + add(r.runs || []) + if (!r.more) earlier.remove() + else earlier.disabled = false + } catch (err) { + earlier.disabled = false + await ask({ title: t("History"), text: why(err), ok: t("Close"), cancel: t("Cancel") }) + } + }) + box.append(earlier) + } return box } +/** What a run that did not finish well says: waiting and until when, or its error. */ +function runNote(run) { + if (run.status === "waiting") { + const again = run.retry_at ? t("tried again %(when)s", { when: when(run.retry_at) }) : "" + return [run.error?.text || STATUS.waiting(), again].filter(Boolean).join(" · ") + } + return run.error?.text || "" +} + +function runRow(task, run) { + const model = run.model || {} + const switched = model.reason === "switched" && model.switched_from ? t("%(model)s was busy, so it ran on this one.", { model: model.switched_from }) : "" + const hasResult = run.message_id || run.report_path || run.preview + const detail = h("div", { class: "run__detail", hidden: true }) + const open = hasResult + ? h("button", { type: "button", class: "btn btn--sm run__open", "aria-expanded": "false", text: t("Result") }) + : null + open?.addEventListener("click", async () => { + const opening = detail.hidden + open.setAttribute("aria-expanded", String(opening)) + if (!opening) { + detail.hidden = true + return + } + detail.hidden = false + detail.replaceChildren(h("p", { class: "muted", text: t("Loading…") })) + try { + const r = await get(`/schedules/${enc(task.id)}/runs/${enc(run.id)}`) + detail.replaceChildren( + r.text ? h("div", { class: "md run__text" }, markdown(r.text)) : h("p", { class: "muted", text: run.preview || t("The result is no longer there.") }), + r.run?.report_path ? h("p", { class: "run__report", text: t("Written to %(path)s", { path: r.run.report_path }) }) : null, + r.run?.chat_id ? h("div", { class: "set__buttons" }, h("button", { type: "button", class: "btn btn--sm", text: t("Open the chat"), onclick: () => ctx.openChat(r.run.chat_id) })) : null, + ) + } catch (err) { + detail.replaceChildren(h("p", { class: "status is-error", text: why(err) })) + } + }) + const note = runNote(run) + return h( + "li", + { class: "run", dataset: { id: run.id } }, + h("span", { class: "run__status" }, statusChip(run)), + h( + "span", + { class: "run__when", title: run.due_at ? t("Due %(when)s", { when: stamp(run.due_at) }) : "" }, + h("span", { text: stamp(run.started_at || run.due_at) }), + TIMING[run.timing] ? h("span", { class: "run__late", text: TIMING[run.timing]() }) : null, + ), + h("span", { class: "run__model", title: switched }, h("span", { text: model.name || "" }), switched ? h("span", { class: "run__switched", text: t("switched") }) : null), + open || h("span", { class: "run__error", title: note, text: note }), + detail, + ) +} + async function runNow(task, btn) { btn.disabled = true try { @@ -288,95 +469,258 @@ async function remove(task) { const yes = await ask({ title: t("Delete this scheduled task?"), text: t("“%(title)s” stops, and its history goes. The chats it wrote stay."), ok: t("Delete"), cancel: t("Cancel"), danger: true }) if (!yes) return try { - await del(`/schedules/${enc(task.id)}`) + await del(`/schedules/${enc(task.id)}?keep_chat=true`) go({ name: "list" }) } catch (err) { await ask({ title: t("Delete"), text: why(err), ok: t("Close"), cancel: t("Cancel") }) } } -// --- A new task, said in words ------------------------------------------------------------------ +// --- A task said in words: new, or an existing one edited --------------------------------------- -let compiled = null // the server's reading of the words: { title, instruction, summary, rule, next } - -function drawNew() { - header(t("New scheduled task"), () => go({ name: "list" })) +/** + * The form. The words first: Read them asks the server (compile) for the rule, its title and + * instruction, and the next runs; the title and instruction can then be changed. Below, the + * settings the server keeps per task. An existing task starts from what it is: its words, title, + * instruction, and its rule read back (preview) -- a paused task's next runs too. + */ +async function drawForm(task) { + const editing = !!task + header(editing ? t("Edit %(title)s", { title: task.title }) : t("New scheduled task"), () => go(editing ? { name: "task", id: task.id } : { name: "list" })) clear($("scheduled-tools")) - compiled = null - const words = h("textarea", { id: "sched-words", class: "sched__words", rows: "4", placeholder: t("Every weekday at 8:00, summarise the news on my reading list"), "aria-label": t("What should happen, and when") }) + const smodels = (await schedulingModels())?.models || [] + // What will be saved: the rule only from the server's reading of words (or the task's own). + const draft = { + request: task?.request || "", + title: task?.title || "", + instruction: task?.instruction || "", + rule: task?.rule || null, + target: task?.target || "chat", + model: task?.model || "", + effort: task?.effort || "", + agents: task?.agents || null, + notify: task?.notify || "matters", + mode: task?.mode || "fresh", + } + let reading = editing ? await post("/schedules/preview", { rule: task.rule }).catch(() => null) : null + + const words = h("textarea", { id: "sched-words", class: "sched__words", rows: "3", placeholder: t("Every weekday at 8:00, summarise the news on my reading list"), "aria-label": t("What should happen, and when"), value: draft.request }) + const readBtn = h("button", { type: "submit", class: "btn", text: editing ? t("Read again") : t("Preview") }) const said = h("p", { class: "status is-error", hidden: true }) - const preview = h("div", { class: "sched__preview", hidden: true }) - const read = h("button", { type: "submit", class: "btn btn--primary", text: t("Preview") }) + const when_ = h("span", { class: "sched__desc", id: "sched-when" }) + const zone = h("p", { class: "hint" }) + const title = h("input", { type: "text", id: "sched-title", value: draft.title, "aria-label": t("Title") }) + const instruction = h("textarea", { id: "sched-instruction", class: "sched__words", rows: "3", "aria-label": t("What it does"), value: draft.instruction }) + const settings = h("div", { class: "fields sched__settings" }) + const saveBtn = h("button", { type: "button", class: "btn btn--primary", id: "sched-save", text: t("Save") }) + const saveSaid = h("p", { class: "status is-error", hidden: true }) + const nextBox = h("div", { class: "sched__nextbox" }) + const manual = manualRule((rule) => { + post("/schedules/preview", { rule }) + .then((r) => { + reading = r + draft.rule = r.rule || {} + drawWhen() + }) + .catch((err) => { + saveSaid.textContent = why(err) + saveSaid.hidden = false + }) + }) + const manualBtn = h("button", { type: "button", class: "btn btn--sm sched__manual-open", text: t("Set the time yourself"), onclick: () => ((manual.hidden = false), (manualBtn.hidden = true), manual.querySelector("input")?.focus()) }) + const rest = h( + "div", + { class: "sched__card", hidden: !editing }, + h("div", { class: "fields" }, field(t("Title"), title, "", "sched-title"), h("div", { class: "field" }, h("span", { class: "label", text: t("When") }), when_, zone)), + manualBtn, + manual, + nextBox, + h("label", { class: "sched__h", for: "sched-instruction", text: t("What it does") }), + instruction, + h("p", { class: "hint", text: t("The instruction each run is given, on its own: say everything it needs.") }), + settings, + saveSaid, + h("div", { class: "set__buttons" }, saveBtn), + ) + + const drawWhen = () => { + const r = reading + const next = r?.next_runs || [] + const runs = !!(draft.rule && Object.keys(draft.rule).length) + when_.textContent = (runs && r?.description) || t("These words give no run in the future.") + when_.title = when_.textContent + when_.classList.toggle("is-error", !runs) + zone.textContent = runs && r?.timezone ? t("Times in %(zone)s", { zone: r.timezone }) : "" + nextBox.replaceChildren(upcoming(next) || "") + saveBtn.disabled = !(draft.rule && Object.keys(draft.rule).length) + } + + const drawSettings = () => { + const model = smodels.find((m) => m.id === draft.model) || ctx.models().find((m) => m.id === draft.model) + const select = (id, table, key, onChange) => + h( + "select", + { id, onchange: (e) => ((draft[key] = e.target.value), onChange?.()) }, + Object.entries(table).map(([v, word]) => h("option", { value: v, text: word(), selected: draft[key] === v })), + ) + const modelSelect = h( + "select", + { id: "sched-model", onchange: (e) => ((draft.model = e.target.value), (draft.effort = ""), drawSettings()) }, + h("option", { value: "", text: t("The first free scheduling model"), selected: !draft.model }), + smodels.map((m) => h("option", { value: m.id, text: m.free ? m.name : `${m.name} · ${t("busy")}`, selected: draft.model === m.id })), + draft.model && !smodels.some((m) => m.id === draft.model) ? h("option", { value: draft.model, text: draft.model, selected: true }) : null, + ) + const levels = model ? levelsOf(model).levels.filter((l) => l !== "default") : [] + const effort = h( + "select", + { id: "sched-effort", disabled: !levels.length && !draft.effort, onchange: (e) => (draft.effort = e.target.value) }, + h("option", { value: "", text: t("The model's own"), selected: !draft.effort }), + levels.map((l) => h("option", { value: l, text: levelWord(l), selected: draft.effort === l })), + // A level the task has that its model no longer offers stays, as it is, until changed. + draft.effort && !levels.includes(draft.effort) ? h("option", { value: draft.effort, text: levelWord(draft.effort), selected: true }) : null, + ) + const agents = h( + "select", + { id: "sched-agents", onchange: (e) => (draft.agents = Number(e.target.value) || null) }, + h("option", { value: "", text: t("No team"), selected: !draft.agents }), + Array.from({ length: MAX_AGENTS - 1 }, (_, i) => i + 2).map((n) => h("option", { value: String(n), text: t("A team of up to %(n)s", { n }), selected: draft.agents === n })), + ) + settings.replaceChildren( + field(t("Tell me"), select("sched-notify", NOTIFY, "notify", () => (settings.querySelector("#sched-notify-hint").textContent = NOTIFY_HINT[draft.notify]())), NOTIFY_HINT[draft.notify]?.() || "", "sched-notify", "sched-notify-hint"), + field(t("Runs"), select("sched-mode", MODE, "mode"), draft.mode === "continue" ? t("Each run sees the ones before it.") : t("Each run starts with nothing but the instruction."), "sched-mode"), + field(t("Model"), modelSelect, t("A busy model is waited for, then the next free one is used."), "sched-model"), + field(t("Reasoning effort"), effort, levels.length ? "" : t("Choose a model to choose its effort."), "sched-effort"), + field(t("Agents"), agents, t("A team works on it together, its own agent included."), "sched-agents"), + ) + settings.querySelector("#sched-mode").addEventListener("change", drawSettings) + } + const form = h( "form", { class: "sched sched--new" }, h("label", { class: "sched__h", for: "sched-words", text: t("What should happen, and when") }), words, h("p", { class: "hint", text: t("In your own words. The server reads them into a rule and an instruction, and shows you the next runs before anything is saved.") }), - h("div", { class: "set__buttons" }, read), + h("div", { class: "set__buttons" }, readBtn), said, - preview, + rest, ) form.addEventListener("submit", async (e) => { e.preventDefault() const text = words.value.trim() if (!text) return words.focus() - read.disabled = true + readBtn.disabled = true said.hidden = true try { - compiled = await post("/schedules/compile", { text }) - drawPreview(preview, text) + const c = await post("/schedules/compile", { text }) + draft.request = text + draft.rule = c.rule || {} + if (c.title || !editing) title.value = c.title || "" + if (c.instruction || !editing) instruction.value = c.instruction || text + if (c.target) draft.target = c.target + reading = c + if (!c.ok && c.reason) { + said.textContent = c.reason + said.hidden = false + } + // The words gave no rule: the form for one, rather than a task that cannot run. + if (!(c.rule && Object.keys(c.rule).length)) { + manual.hidden = false + manualBtn.hidden = true + } + rest.hidden = false + drawWhen() + ;(c.ok ? saveBtn : instruction).focus() } catch (err) { said.textContent = why(err) said.hidden = false } finally { - read.disabled = false + readBtn.disabled = false } }) words.addEventListener("keydown", (e) => { if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) form.requestSubmit() }) + saveBtn.addEventListener("click", async () => { + saveBtn.disabled = true + saveSaid.hidden = true + const body = { + request: draft.request || words.value.trim(), + title: title.value.trim(), + instruction: instruction.value.trim(), + rule: draft.rule, + target: draft.target, + model: draft.model, + effort: draft.effort, + agents: draft.agents, + notify: draft.notify, + mode: draft.mode, + } + try { + const r = editing ? await patch(`/schedules/${enc(task.id)}`, body) : await post("/schedules", body) + go({ name: "task", id: r.schedule.id }) + } catch (err) { + saveSaid.textContent = why(err) + saveSaid.hidden = false + saveBtn.disabled = false + } + }) clear($("scheduled-body")).append(form) + drawSettings() + drawWhen() words.focus() } -/** What the server made of the words: the rule in words, the instruction, the next five runs. */ -function drawPreview(box, text) { - const c = compiled - box.hidden = false - const choice = { notify: "matters", mode: "fresh" } - const select = (key, table) => - h( - "select", - { "aria-label": key === "notify" ? t("Tell me") : t("Runs"), onchange: (e) => (choice[key] = e.target.value) }, - Object.entries(table).map(([v, word]) => h("option", { value: v, text: word(), selected: choice[key] === v })), - ) - const save = h("button", { type: "button", class: "btn btn--primary", text: t("Save") }) - const said = h("p", { class: "status is-error", hidden: true }) - save.addEventListener("click", async () => { - save.disabled = true - try { - const r = await post("/schedules", { request: text, title: c.title, instruction: c.instruction, rule: c.rule, notify: choice.notify, mode: choice.mode }) - go({ name: "task", id: r.schedule.id }) - } catch (err) { - said.textContent = why(err) - said.hidden = false - save.disabled = false - } +/** + * The rule set by hand, for words the server could not read into one: the days of the week (none + * ticked: every day) and a time, as the rule's calendar (`{ at: { weekdays, times } }`, Monday 0). + * `onRule(rule)` on every change; the server reads it back (preview). + */ +function manualRule(onRule) { + const lang = document.documentElement.lang || undefined + const days = Array.from({ length: 7 }, (_, i) => { + const d = new Date(2024, 0, 1 + i) // 1 January 2024 was a Monday + return h("label", { class: "sday", title: d.toLocaleDateString(lang, { weekday: "long" }) }, h("input", { type: "checkbox", value: String(i) }), h("span", { text: d.toLocaleDateString(lang, { weekday: "short" }) })) }) - box.replaceChildren( - h( - "div", - { class: "sched__card" }, - h("p", { class: "sched__title", text: c.title || "" }), - h("div", { class: "sched__facts" }, fact(t("When"), h("span", { text: c.summary || "" })), fact(t("What it does"), h("span", { text: c.instruction || "" }))), - upcoming(c.next || []) || h("p", { class: "status is-error", text: t("These words give no run in the future.") }), - h("div", { class: "sched__facts" }, fact(t("Tell me"), select("notify", NOTIFY)), fact(t("Runs"), select("mode", MODE))), - said, - h("div", { class: "set__buttons" }, save), - ), + const time = h("input", { type: "time", id: "sched-time", value: "08:00", required: true }) + const box = h( + "div", + { class: "sched__manual", hidden: true }, + h("div", { class: "fields" }, h("div", { class: "field" }, h("span", { class: "label", text: t("Days") }), h("div", { class: "sdays", role: "group", "aria-label": t("Days") }, days), h("p", { class: "hint", text: t("No day ticked: every day.") })), field(t("Time"), time, "", "sched-time")), ) - save.focus() + const changed = () => { + if (!time.value) return + const weekdays = days.map((d) => d.querySelector("input")).filter((i) => i.checked).map((i) => Number(i.value)) + onRule({ at: { ...(weekdays.length ? { weekdays } : {}), times: [time.value] } }) + } + box.addEventListener("change", changed) + return box +} + +/** A field on the form's shared tracks: label, control, hint -- always all three. */ +function field(label, control, hint, forId, hintId) { + return h("div", { class: "field" }, h("label", { for: forId || null, text: label }), control, h("p", { class: "hint", id: hintId || null, text: hint || "" })) +} + +// --- Live: the list as the server's event stream gives it --------------------------------------- + +/** A `schedules` frame (`{ version, schedules }`), handed on by the app: redraw what it changes. */ +function onSchedules(list) { + if (!Array.isArray(list?.schedules)) return + tasks = list.schedules + missing = false + if (!onPage() || popoverOpenHere()) return + if (view.name === "list") drawList() + else if (view.name === "task" && shown) { + const now = tasks.find((x) => x.id === shown.id) + if (!now) return go({ name: "list" }) + const last = now.last_run || {} + if ((last.id || null) !== shown.last || (last.status || null) !== shown.status || now.running !== shown.running) draw() + } +} + +function popoverOpenHere() { + return !!document.querySelector(".pop .smodels") } /** The palette's commands for scheduled tasks. */ @@ -386,7 +730,7 @@ export function scheduledCommands() { label: t("New scheduled task"), run: () => { ctx.show() - go({ name: "new" }) + go({ name: "form" }) }, }, ] @@ -394,4 +738,5 @@ export function scheduledCommands() { export function initScheduled(context) { ctx = context + listen("schedules", (e) => onSchedules(e.payload)) }