feat(webui): present chats as topics

This commit is contained in:
chengyongru
2026-07-24 10:29:13 +08:00
committed by chengyongru
parent 8bcab8885e
commit 6a9157f477
18 changed files with 233 additions and 233 deletions
+1 -1
View File
@@ -295,7 +295,7 @@ Need help with `PATH`, API keys, provider/model matching, or JSON errors? See th
## 🌐 WebUI ## 🌐 WebUI
The WebUI ships **inside the published wheel** — no extra build step. It is the browser workbench for chat sessions, workspace controls, Apps, Skills, Automations, and settings. For the full user guide, see [`docs/webui.md`](./docs/webui.md). The WebUI ships **inside the published wheel** — no extra build step. It is the browser workbench for topics, workspace controls, Apps, Skills, Automations, and settings. For the full user guide, see [`docs/webui.md`](./docs/webui.md).
<p align="center"> <p align="center">
<img src="images/nanobot_webui.png" alt="nanobot webui preview" width="900"> <img src="images/nanobot_webui.png" alt="nanobot webui preview" width="900">
+16 -16
View File
@@ -2,21 +2,21 @@
<!-- Meta description: Create, run, and manage nanobot scheduled automations, local triggers, and heartbeat-backed background checks. --> <!-- Meta description: Create, run, and manage nanobot scheduled automations, local triggers, and heartbeat-backed background checks. -->
Automations are agent turns that run later in a linked chat/session. Use them Automations are agent turns that run later in a linked topic. Use them
when nanobot should do work without someone actively typing: reminders, when nanobot should do work without someone actively typing: reminders,
recurring checks, nightly summaries, CI follow-ups, local script reports, or recurring checks, nightly summaries, CI follow-ups, local script reports, or
webhook-driven events. webhook-driven events.
Create automations from the chat, channel, or WebUI session where the result Create automations from the chat channel or WebUI topic where the
should appear. That lets nanobot keep the right session history, workspace, and result should appear. That lets nanobot keep the right session history,
reply target. workspace, and reply target.
## Choose an Automation Type ## Choose an Automation Type
| Type | Starts from | Best for | Created with | | Type | Starts from | Best for | Created with |
|---|---|---|---| |---|---|---|---|
| Scheduled automation | Time, interval, or cron expression | Recurring reminders, scheduled summaries, one-time future tasks | Ask nanobot in the target session to schedule it with the `cron` tool | | Scheduled automation | Time, interval, or cron expression | Recurring reminders, scheduled summaries, one-time future tasks | Ask nanobot in the target topic to schedule it with the `cron` tool |
| Local trigger | A local `nanobot trigger ...` command | CI jobs, webhooks, shell scripts, generated reports | `/trigger <name>` in the target session | | Local trigger | A local `nanobot trigger ...` command | CI jobs, webhooks, shell scripts, generated reports | `/trigger <name>` in the target topic |
| Heartbeat | Protected system schedule | Quiet recurring checks that should only report useful results | Edit `<workspace>/HEARTBEAT.md` | | Heartbeat | Protected system schedule | Quiet recurring checks that should only report useful results | Edit `<workspace>/HEARTBEAT.md` |
The two user-created automation types are scheduled automations and local The two user-created automation types are scheduled automations and local
@@ -26,21 +26,21 @@ protected from normal automation edits.
## Before You Create One ## Before You Create One
Keep `nanobot gateway` running. The gateway owns background delivery for chat Keep `nanobot gateway` running. The gateway owns background delivery for chat
apps, WebUI sessions, scheduled automations, local triggers, heartbeat, and apps, WebUI topics, scheduled automations, local triggers, heartbeat, and
Dream jobs. Dream jobs.
Use the same workspace and config for the gateway and any process that sends Use the same workspace and config for the gateway and any process that sends
local trigger messages. If you run multiple nanobot instances, pass the matching local trigger messages. If you run multiple nanobot instances, pass the matching
`--config` or `--workspace` option to `nanobot trigger`. `--config` or `--workspace` option to `nanobot trigger`.
Create each automation from the target session. An automation without a linked Create each automation from the target topic. An automation without a linked
chat/session cannot be enabled or run from the WebUI because nanobot would not topic cannot be enabled or run from the WebUI because nanobot would not know
know where to deliver the turn. where to deliver the turn.
## Scheduled Automations ## Scheduled Automations
Scheduled automations are created by the agent's `cron` tool. In practice, ask Scheduled automations are created by the agent's `cron` tool. In practice, ask
nanobot from the target chat or WebUI session: nanobot from the target chat or WebUI topic:
```text ```text
Every weekday at 9am, check open pull requests and summarize blockers here. Every weekday at 9am, check open pull requests and summarize blockers here.
@@ -68,7 +68,7 @@ report, use heartbeat instead of a user-created scheduled automation.
Local triggers let a local script or external service send a message into a Local triggers let a local script or external service send a message into a
specific nanobot session later. specific nanobot session later.
Create the trigger from the chat or WebUI session where future messages should Create the trigger from the chat or WebUI topic where future messages should
arrive: arrive:
```text ```text
@@ -120,7 +120,7 @@ Heartbeat is enabled by default when `nanobot gateway` starts. Configure it in
Use the WebUI Automations view to: Use the WebUI Automations view to:
- filter by all, active, paused, needs-attention, or system jobs; - filter by all, active, paused, needs-attention, or system jobs;
- search by task name, message, trigger command, linked chat, schedule, or - search by task name, message, trigger command, linked topic, schedule, or
status; status;
- sort by next run, last run, updated time, or name; - sort by next run, last run, updated time, or name;
- run scheduled automations now; - run scheduled automations now;
@@ -138,7 +138,7 @@ Automation delivery is workspace-local. Scheduled jobs and local trigger
deliveries use the same workspace as the gateway. deliveries use the same workspace as the gateway.
Local trigger messages are written to a durable queue. If the gateway is not Local trigger messages are written to a durable queue. If the gateway is not
running yet, the message waits in that workspace. If the linked session is running yet, the message waits in that workspace. If the linked topic is
already running a turn, the trigger waits until the session becomes idle instead already running a turn, the trigger waits until the session becomes idle instead
of being injected into the active turn. of being injected into the active turn.
@@ -154,7 +154,7 @@ queue is not a distributed multi-consumer queue.
## Common Patterns ## Common Patterns
For a nightly report, ask from the target session: For a nightly report, ask from the target topic:
```text ```text
Every night at 9pm, review today's workspace changes and summarize anything I should handle tomorrow. Every night at 9pm, review today's workspace changes and summarize anything I should handle tomorrow.
@@ -181,7 +181,7 @@ generate-report | nanobot trigger <trigger-id>
## Troubleshooting ## Troubleshooting
If an automation does not run, check that `nanobot gateway` is running, the If an automation does not run, check that `nanobot gateway` is running, the
automation is enabled, and it was created from a linked chat/session. automation is enabled, and it was created from a linked topic.
If a local trigger waits forever, confirm the command uses the same workspace or If a local trigger waits forever, confirm the command uses the same workspace or
config as the gateway. config as the gateway.
+17 -17
View File
@@ -1,8 +1,8 @@
# Nanobot WebUI: Browser Workbench for Self-Hosted AI Agents # Nanobot WebUI: Browser Workbench for Self-Hosted AI Agents
<!-- Meta description: Run nanobot from a browser WebUI with persistent chat sessions, visible tool activity, workspace controls, Apps, MCP presets, Skills, settings, and Automations. --> <!-- Meta description: Run nanobot from a browser WebUI with persistent topics, visible tool activity, workspace controls, Apps, MCP presets, Skills, settings, and Automations. -->
The WebUI is nanobot's browser workbench for persistent chat sessions, visible The WebUI is nanobot's browser workbench for persistent topics, visible
agent activity, workspace controls, Apps, Skills, settings, and Automations in agent activity, workspace controls, Apps, Skills, settings, and Automations in
one place. one place.
@@ -57,9 +57,9 @@ gateway health endpoint, `18790` by default, is not the browser UI.
Use the WebUI as the primary setup surface after Quick Start: Use the WebUI as the primary setup surface after Quick Start:
1. Send `Hello!` in a new chat to prove the selected model works. 1. Send `Hello!` in a new topic to prove the selected model works.
2. Open **Settings → Models** and confirm the active model preset. 2. Open **Settings → Models** and confirm the active model preset.
3. Start a separate chat before project work, then choose the intended workspace and access mode. 3. Start a separate topic before project work, then choose the intended workspace and access mode.
4. Add only one capability next: a chat channel in **Settings → Channels**, a web/voice/image provider in **Settings**, or an App/MCP integration in **Apps**. 4. Add only one capability next: a chat channel in **Settings → Channels**, a web/voice/image provider in **Settings**, or an App/MCP integration in **Apps**.
5. Restart when the WebUI shows a restart requirement, then test that capability with the smallest possible request. 5. Restart when the WebUI shows a restart requirement, then test that capability with the smallest possible request.
@@ -69,7 +69,7 @@ This path avoids hand-editing `config.json` for normal setup. Use the reference
| Area | Use it for | | Area | Use it for |
|---|---| |---|---|
| Chat | Start, switch, search, fork, and delete browser sessions | | Topics | Start, switch, search, fork, and delete browser topics |
| Agent activity | See thinking, tool calls, file edits with diffs, command output, and generated artifacts in context | | Agent activity | See thinking, tool calls, file edits with diffs, command output, and generated artifacts in context |
| Workspace | Pick the project workspace before asking for file or shell work | | Workspace | Pick the project workspace before asking for file or shell work |
| Access | Choose the access mode for local capabilities allowed by your gateway configuration | | Access | Choose the access mode for local capabilities allowed by your gateway configuration |
@@ -80,10 +80,10 @@ This path avoids hand-editing `config.json` for normal setup. Use the reference
| Automations | Review, search, run, pause, edit, and delete scheduled and local-trigger agent turns | | Automations | Review, search, run, pause, edit, and delete scheduled and local-trigger agent turns |
| Settings | Adjust models, providers, image generation, voice, web tools, runtime, and safety options | | Settings | Adjust models, providers, image generation, voice, web tools, runtime, and safety options |
## Chat Workspace ## Topic Workspace
The sidebar is the session switcher. A session keeps its own history, title, The sidebar is the topic switcher. Each topic keeps its own history, title,
workspace metadata, and linked automations. Use a new session when you want a workspace selection, and linked automations. Use a new topic when you want a
separate context; use fork when you want to continue from an existing point separate context; use fork when you want to continue from an existing point
without changing the original thread. without changing the original thread.
@@ -123,7 +123,7 @@ directory.
The access control in the composer controls the local capability level for the The access control in the composer controls the local capability level for the
chat. It does not bypass your gateway, provider, shell sandbox, or operating chat. It does not bypass your gateway, provider, shell sandbox, or operating
system configuration; it only selects among the capabilities that are already system configuration; it only selects among the capabilities that are already
available to this WebUI session. available to the current topic.
In Restricted mode, ordinary file and shell work stays inside the selected In Restricted mode, ordinary file and shell work stays inside the selected
project. To preserve agent continuity, filesystem/search tools receive narrow, project. To preserve agent continuity, filesystem/search tools receive narrow,
@@ -133,7 +133,7 @@ neighboring memory or profile files, and it does not allow writes outside the
selected project. These tool exceptions do not broaden the browser's file selected project. These tool exceptions do not broaden the browser's file
preview boundary. preview boundary.
Remote WebUI sessions may reduce access for the current workspace. Selecting a Remote WebUI connections may reduce access for the current workspace. Selecting a
different workspace or enabling Full Access remains limited to local and native different workspace or enabling Full Access remains limited to local and native
clients. clients.
@@ -199,10 +199,10 @@ to perform that task.
## Automations ## Automations
Automations are agent turns that run later in a linked chat/session. They should Automations are agent turns that run later in a linked topic. Create them from
be created from the chat, channel, or session where they are supposed to run so the topic or channel where they are supposed to run so nanobot keeps the
nanobot keeps the correct target context. When an automation runs, it normally correct target context. When an automation runs, it normally delivers the
delivers the result back to that linked chat. result back to that topic.
For the full automation model, creation flow, trigger CLI usage, and delivery For the full automation model, creation flow, trigger CLI usage, and delivery
semantics, see [`automations.md`](./automations.md). semantics, see [`automations.md`](./automations.md).
@@ -221,7 +221,7 @@ instead of creating a chat automation.
Use the Automations view to: Use the Automations view to:
- Filter by all, active, paused, needs-attention, or system jobs. - Filter by all, active, paused, needs-attention, or system jobs.
- Search by task name, message, trigger command, linked chat, schedule, or status. - Search by task name, message, trigger command, linked topic, schedule, or status.
- Sort by next run, last run, updated time, or name. - Sort by next run, last run, updated time, or name.
- Run scheduled automations now. - Run scheduled automations now.
- Pause or resume, rename, or delete user-created automations. - Pause or resume, rename, or delete user-created automations.
@@ -232,9 +232,9 @@ Search accepts plain text and field filters such as `name:backup`,
`chat:WeChat`, `schedule:09:30`, `cron:"0 23 * * *"`, `trigger`, and `chat:WeChat`, `schedule:09:30`, `cron:"0 23 * * *"`, `trigger`, and
`status:paused`. `status:paused`.
An automation without a linked chat cannot be enabled or run from the WebUI, An automation without a linked topic cannot be enabled or run from the WebUI,
because nanobot would not know where to deliver the scheduled turn. Recreate it because nanobot would not know where to deliver the scheduled turn. Recreate it
from the target chat or channel so the automation has complete context. from the target topic or channel so the automation has complete context.
Local triggers do not have a WebUI "Run now" action because each run needs a Local triggers do not have a WebUI "Run now" action because each run needs a
message. Use the copied `nanobot trigger ...` command and replace `"message"` message. Use the copied `nanobot trigger ...` command and replace `"message"`
+1 -1
View File
@@ -516,7 +516,7 @@ function ChatsFoldFooter({
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const collapsedFallback = i18n.resolvedLanguage?.startsWith("zh") const collapsedFallback = i18n.resolvedLanguage?.startsWith("zh")
? `已折叠 ${hiddenCount} 个对话` ? `已折叠 ${hiddenCount} 个对话`
: `${hiddenCount} hidden chats`; : `${hiddenCount} hidden topics`;
return ( return (
<div className="px-2 pb-1 pt-1"> <div className="px-2 pb-1 pt-1">
+16 -16
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Sidebar navigation", "navigation": "Sidebar navigation",
"collapse": "Collapse sidebar", "collapse": "Collapse sidebar",
"newChat": "New chat", "newChat": "New topic",
"searchAria": "Search", "searchAria": "Search",
"searchPlaceholder": "Search", "searchPlaceholder": "Search",
"searchResults": "Results", "searchResults": "Results",
"noSearchResults": "No matching chats.", "noSearchResults": "No matching topics.",
"recent": "Recent", "recent": "Recent topics",
"settings": "Settings", "settings": "Settings",
"language": { "language": {
"label": "Language", "label": "Language",
@@ -819,15 +819,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Chat {{id}}", "fallbackTitle": "Topic {{id}}",
"forkTitle": "Fork: {{title}}", "forkTitle": "Fork: {{title}}",
"loading": "Loading…", "loading": "Loading…",
"noSessions": "No sessions yet.", "noSessions": "No sessions yet.",
"showMore": "Show {{count}} more", "showMore": "Show {{count}} more",
"collapsed": "{{count}} hidden chats", "collapsed": "{{count}} hidden topics",
"showLess": "Show less", "showLess": "Show less",
"actions": "Chat actions for {{title}}", "actions": "Topic actions for {{title}}",
"newInProject": "Start a new chat in {{project}}", "newInProject": "Start a new topic in {{project}}",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -836,9 +836,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "Rename topic",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "Choose a local sidebar name for this topic.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "Topic name",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -848,10 +848,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "Delete", "delete": "Delete",
"newChat": "New chat", "newChat": "New topic",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "Topics",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -860,7 +860,7 @@
} }
}, },
"deleteConfirm": { "deleteConfirm": {
"title": "Delete this chat?", "title": "Delete this topic?",
"description": "This action cannot be undone.", "description": "This action cannot be undone.",
"cancel": "Cancel", "cancel": "Cancel",
"confirm": "Delete", "confirm": "Delete",
@@ -954,14 +954,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Toggle sidebar", "toggleSidebar": "Toggle sidebar",
"newChat": "Start a new chat", "newChat": "Start a new topic",
"toggleTheme": "Toggle theme from header", "toggleTheme": "Toggle theme from header",
"settings": "Open settings", "settings": "Open settings",
"sessionInfo": "Session details" "sessionInfo": "Session details"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Session", "title": "Session",
"untitled": "Untitled chat", "untitled": "Untitled topic",
"automations": "Automations", "automations": "Automations",
"count": "{{count}}", "count": "{{count}}",
"loading": "Loading automations...", "loading": "Loading automations...",
@@ -987,7 +987,7 @@
"composer": { "composer": {
"placeholderThread": "Type your message…", "placeholderThread": "Type your message…",
"placeholderHero": "Ask anything...", "placeholderHero": "Ask anything...",
"placeholderOpening": "Opening a new chat…", "placeholderOpening": "Opening a new topic…",
"placeholderStreaming": "Model is responding…", "placeholderStreaming": "Model is responding…",
"inputAria": "Message input", "inputAria": "Message input",
"sendHint": "Enter to send · Shift+Enter for newline", "sendHint": "Enter to send · Shift+Enter for newline",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Navegación de la barra lateral", "navigation": "Navegación de la barra lateral",
"collapse": "Contraer barra lateral", "collapse": "Contraer barra lateral",
"newChat": "Nuevo chat", "newChat": "Nuevo tema",
"searchAria": "Buscar", "searchAria": "Buscar",
"searchPlaceholder": "Buscar", "searchPlaceholder": "Buscar",
"searchResults": "Resultados", "searchResults": "Resultados",
"noSearchResults": "No hay chats coincidentes.", "noSearchResults": "No hay temas coincidentes.",
"recent": "Recientes", "recent": "Temas recientes",
"settings": "Configuración", "settings": "Configuración",
"language": { "language": {
"label": "Idioma", "label": "Idioma",
@@ -806,15 +806,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Chat {{id}}", "fallbackTitle": "Tema {{id}}",
"forkTitle": "Bifurcación: {{title}}", "forkTitle": "Bifurcación: {{title}}",
"loading": "Cargando…", "loading": "Cargando…",
"noSessions": "Todavía no hay sesiones.", "noSessions": "Todavía no hay sesiones.",
"showMore": "Mostrar {{count}} más", "showMore": "Mostrar {{count}} más",
"collapsed": "{{count}} chats ocultos", "collapsed": "{{count}} temas ocultos",
"showLess": "Mostrar menos", "showLess": "Mostrar menos",
"actions": "Acciones del chat {{title}}", "actions": "Acciones del tema {{title}}",
"newInProject": "Iniciar un chat nuevo en {{project}}", "newInProject": "Iniciar un tema nuevo en {{project}}",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -823,9 +823,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "Renombrar tema",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "Elige un nombre local de la barra lateral para este tema.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "Nombre del tema",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -835,10 +835,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "Eliminar", "delete": "Eliminar",
"newChat": "Nuevo chat", "newChat": "Nuevo tema",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "Temas",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -941,14 +941,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Mostrar u ocultar la barra lateral", "toggleSidebar": "Mostrar u ocultar la barra lateral",
"newChat": "Iniciar un chat nuevo", "newChat": "Iniciar un tema nuevo",
"toggleTheme": "Cambiar tema desde el encabezado", "toggleTheme": "Cambiar tema desde el encabezado",
"settings": "Abrir configuración", "settings": "Abrir configuración",
"sessionInfo": "Detalles de la sesión" "sessionInfo": "Detalles de la sesión"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Sesión", "title": "Sesión",
"untitled": "Chat sin título", "untitled": "Tema sin título",
"automations": "Automatizaciones", "automations": "Automatizaciones",
"count": "{{count}}", "count": "{{count}}",
"loading": "Cargando automatizaciones...", "loading": "Cargando automatizaciones...",
@@ -974,7 +974,7 @@
"composer": { "composer": {
"placeholderThread": "Escribe tu mensaje…", "placeholderThread": "Escribe tu mensaje…",
"placeholderHero": "¿Qué tienes en mente?", "placeholderHero": "¿Qué tienes en mente?",
"placeholderOpening": "Abriendo un nuevo chat…", "placeholderOpening": "Abriendo un nuevo tema…",
"placeholderStreaming": "El modelo está respondiendo…", "placeholderStreaming": "El modelo está respondiendo…",
"inputAria": "Entrada de mensaje", "inputAria": "Entrada de mensaje",
"sendHint": "Enter para enviar · Shift+Enter para nueva línea", "sendHint": "Enter para enviar · Shift+Enter para nueva línea",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Navigation de la barre latérale", "navigation": "Navigation de la barre latérale",
"collapse": "Réduire la barre latérale", "collapse": "Réduire la barre latérale",
"newChat": "Nouvelle discussion", "newChat": "Nouveau sujet",
"searchAria": "Rechercher", "searchAria": "Rechercher",
"searchPlaceholder": "Rechercher", "searchPlaceholder": "Rechercher",
"searchResults": "Résultats", "searchResults": "Résultats",
"noSearchResults": "Aucun chat correspondant.", "noSearchResults": "Aucun sujet correspondant.",
"recent": "Récentes", "recent": "Sujets récents",
"settings": "Paramètres", "settings": "Paramètres",
"language": { "language": {
"label": "Langue", "label": "Langue",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Discussion {{id}}", "fallbackTitle": "Sujet {{id}}",
"forkTitle": "Branche : {{title}}", "forkTitle": "Branche : {{title}}",
"loading": "Chargement…", "loading": "Chargement…",
"noSessions": "Aucune session pour le moment.", "noSessions": "Aucune session pour le moment.",
"showMore": "Afficher {{count}} de plus", "showMore": "Afficher {{count}} de plus",
"collapsed": "{{count}} discussions masquées", "collapsed": "{{count}} sujets masqués",
"showLess": "Afficher moins", "showLess": "Afficher moins",
"actions": "Actions de la discussion {{title}}", "actions": "Actions du sujet {{title}}",
"newInProject": "Démarrer une nouvelle discussion dans {{project}}", "newInProject": "Démarrer un nouveau sujet dans {{project}}",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -822,9 +822,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "Renommer le sujet",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "Choisissez un nom local dans la barre latérale pour ce sujet.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "Nom du sujet",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -834,10 +834,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "Supprimer", "delete": "Supprimer",
"newChat": "Nouvelle discussion", "newChat": "Nouveau sujet",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "Sujets",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Afficher ou masquer la barre latérale", "toggleSidebar": "Afficher ou masquer la barre latérale",
"newChat": "Démarrer un nouveau chat", "newChat": "Démarrer un nouveau sujet",
"toggleTheme": "Changer le thème depuis len-tête", "toggleTheme": "Changer le thème depuis len-tête",
"settings": "Ouvrir les paramètres", "settings": "Ouvrir les paramètres",
"sessionInfo": "Détails de la session" "sessionInfo": "Détails de la session"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Session", "title": "Session",
"untitled": "Chat sans titre", "untitled": "Sujet sans titre",
"automations": "Automatisations", "automations": "Automatisations",
"count": "{{count}}", "count": "{{count}}",
"loading": "Chargement des automatisations...", "loading": "Chargement des automatisations...",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "Saisissez votre message…", "placeholderThread": "Saisissez votre message…",
"placeholderHero": "Quavez-vous en tête ?", "placeholderHero": "Quavez-vous en tête ?",
"placeholderOpening": "Ouverture dune nouvelle discussion…", "placeholderOpening": "Ouverture dun nouveau sujet…",
"placeholderStreaming": "Le modèle est en train de répondre…", "placeholderStreaming": "Le modèle est en train de répondre…",
"inputAria": "Champ de message", "inputAria": "Champ de message",
"sendHint": "Entrée pour envoyer · Maj+Entrée pour un retour à la ligne", "sendHint": "Entrée pour envoyer · Maj+Entrée pour un retour à la ligne",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Navigasi bilah samping", "navigation": "Navigasi bilah samping",
"collapse": "Ciutkan sidebar", "collapse": "Ciutkan sidebar",
"newChat": "Obrolan baru", "newChat": "Topik baru",
"searchAria": "Cari", "searchAria": "Cari",
"searchPlaceholder": "Cari", "searchPlaceholder": "Cari",
"searchResults": "Hasil", "searchResults": "Hasil",
"noSearchResults": "Tidak ada chat yang cocok.", "noSearchResults": "Tidak ada topik yang cocok.",
"recent": "Terbaru", "recent": "Topik terbaru",
"settings": "Pengaturan", "settings": "Pengaturan",
"language": { "language": {
"label": "Bahasa", "label": "Bahasa",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Obrolan {{id}}", "fallbackTitle": "Topik {{id}}",
"forkTitle": "Cabang: {{title}}", "forkTitle": "Cabang: {{title}}",
"loading": "Memuat…", "loading": "Memuat…",
"noSessions": "Belum ada sesi.", "noSessions": "Belum ada sesi.",
"showMore": "Tampilkan {{count}} lagi", "showMore": "Tampilkan {{count}} lagi",
"collapsed": "{{count}} obrolan diciutkan", "collapsed": "{{count}} topik diciutkan",
"showLess": "Tampilkan lebih sedikit", "showLess": "Tampilkan lebih sedikit",
"actions": "Aksi obrolan untuk {{title}}", "actions": "Aksi topik untuk {{title}}",
"newInProject": "Mulai obrolan baru di {{project}}", "newInProject": "Mulai topik baru di {{project}}",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -822,9 +822,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "Ganti nama topik",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "Pilih nama lokal di bilah sisi untuk topik ini.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "Nama topik",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -834,10 +834,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "Hapus", "delete": "Hapus",
"newChat": "Obrolan baru", "newChat": "Topik baru",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "Topik",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Tampilkan atau sembunyikan sidebar", "toggleSidebar": "Tampilkan atau sembunyikan sidebar",
"newChat": "Mulai chat baru", "newChat": "Mulai topik baru",
"toggleTheme": "Alihkan tema dari header", "toggleTheme": "Alihkan tema dari header",
"settings": "Buka pengaturan", "settings": "Buka pengaturan",
"sessionInfo": "Detail sesi" "sessionInfo": "Detail sesi"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Sesi", "title": "Sesi",
"untitled": "Chat tanpa judul", "untitled": "Topik tanpa judul",
"automations": "Otomasi", "automations": "Otomasi",
"count": "{{count}}", "count": "{{count}}",
"loading": "Memuat otomasi...", "loading": "Memuat otomasi...",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "Ketik pesan Anda…", "placeholderThread": "Ketik pesan Anda…",
"placeholderHero": "Apa yang sedang Anda pikirkan?", "placeholderHero": "Apa yang sedang Anda pikirkan?",
"placeholderOpening": "Membuka obrolan baru…", "placeholderOpening": "Membuka topik baru…",
"placeholderStreaming": "Model sedang merespons…", "placeholderStreaming": "Model sedang merespons…",
"inputAria": "Input pesan", "inputAria": "Input pesan",
"sendHint": "Enter untuk kirim · Shift+Enter untuk baris baru", "sendHint": "Enter untuk kirim · Shift+Enter untuk baris baru",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "サイドバーのナビゲーション", "navigation": "サイドバーのナビゲーション",
"collapse": "サイドバーを閉じる", "collapse": "サイドバーを閉じる",
"newChat": "新しいチャット", "newChat": "新しいトピック",
"searchAria": "検索", "searchAria": "検索",
"searchPlaceholder": "検索", "searchPlaceholder": "検索",
"searchResults": "検索結果", "searchResults": "検索結果",
"noSearchResults": "一致するチャットはありません。", "noSearchResults": "一致するトピックはありません。",
"recent": "最近のチャット", "recent": "最近のトピック",
"settings": "設定", "settings": "設定",
"language": { "language": {
"label": "言語", "label": "言語",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "チャット {{id}}", "fallbackTitle": "トピック {{id}}",
"forkTitle": "分岐:{{title}}", "forkTitle": "分岐:{{title}}",
"loading": "読み込み中…", "loading": "読み込み中…",
"noSessions": "まだセッションがありません。", "noSessions": "まだセッションがありません。",
"showMore": "さらに {{count}} 件表示", "showMore": "さらに {{count}} 件表示",
"collapsed": "{{count}} 件のチャットを折りたたみ中", "collapsed": "{{count}} 件のトピックを折りたたみ中",
"showLess": "折りたたむ", "showLess": "折りたたむ",
"actions": "「{{title}}」のチャット操作", "actions": "「{{title}}」のトピック操作",
"newInProject": "「{{project}}」で新しいチャットを開始", "newInProject": "「{{project}}」で新しいトピックを開始",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -822,9 +822,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "トピック名を変更",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "このトピックのサイドバー表示名を選択します。",
"renamePlaceholder": "Chat name", "renamePlaceholder": "トピック名",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -834,10 +834,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "削除", "delete": "削除",
"newChat": "新しいチャット", "newChat": "新しいトピック",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "トピック",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "サイドバーを切り替える", "toggleSidebar": "サイドバーを切り替える",
"newChat": "新しいチャットを開始", "newChat": "新しいトピックを開始",
"toggleTheme": "ヘッダーからテーマを切り替える", "toggleTheme": "ヘッダーからテーマを切り替える",
"settings": "設定を開く", "settings": "設定を開く",
"sessionInfo": "セッション詳細" "sessionInfo": "セッション詳細"
}, },
"sessionInfo": { "sessionInfo": {
"title": "セッション", "title": "セッション",
"untitled": "無題のチャット", "untitled": "無題のトピック",
"automations": "自動タスク", "automations": "自動タスク",
"count": "{{count}}", "count": "{{count}}",
"loading": "自動タスクを読み込み中...", "loading": "自動タスクを読み込み中...",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "メッセージを入力…", "placeholderThread": "メッセージを入力…",
"placeholderHero": "何を考えていますか?", "placeholderHero": "何を考えていますか?",
"placeholderOpening": "新しいチャットを開いています…", "placeholderOpening": "新しいトピックを開いています…",
"placeholderStreaming": "モデルが応答しています…", "placeholderStreaming": "モデルが応答しています…",
"inputAria": "メッセージ入力欄", "inputAria": "メッセージ入力欄",
"sendHint": "Enter で送信 · Shift+Enter で改行", "sendHint": "Enter で送信 · Shift+Enter で改行",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "사이드바 탐색", "navigation": "사이드바 탐색",
"collapse": "사이드바 접기", "collapse": "사이드바 접기",
"newChat": "새 채팅", "newChat": "새 주제",
"searchAria": "검색", "searchAria": "검색",
"searchPlaceholder": "검색", "searchPlaceholder": "검색",
"searchResults": "결과", "searchResults": "결과",
"noSearchResults": "일치하는 채팅이 없습니다.", "noSearchResults": "일치하는 주제가 없습니다.",
"recent": "최근 대화", "recent": "최근 주제",
"settings": "설정", "settings": "설정",
"language": { "language": {
"label": "언어", "label": "언어",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "채팅 {{id}}", "fallbackTitle": "주제 {{id}}",
"forkTitle": "분기: {{title}}", "forkTitle": "분기: {{title}}",
"loading": "불러오는 중…", "loading": "불러오는 중…",
"noSessions": "아직 세션이 없습니다.", "noSessions": "아직 세션이 없습니다.",
"showMore": "{{count}}개 더 보기", "showMore": "{{count}}개 더 보기",
"collapsed": "{{count}}개 채팅 접힘", "collapsed": "{{count}}개 주제 접힘",
"showLess": "접기", "showLess": "접기",
"actions": "{{title}} 채팅 작업", "actions": "{{title}} 주제 작업",
"newInProject": "{{project}}에서 새 채팅 시작", "newInProject": "{{project}}에서 새 주제 시작",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -822,9 +822,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "주제 이름 변경",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "이 주제에 사용할 사이드바 이름을 선택하세요.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "주제 이름",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -834,10 +834,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "삭제", "delete": "삭제",
"newChat": "새 채팅", "newChat": "새 주제",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "주제",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "사이드바 전환", "toggleSidebar": "사이드바 전환",
"newChat": "새 채팅 시작", "newChat": "새 주제 시작",
"toggleTheme": "헤더에서 테마 전환", "toggleTheme": "헤더에서 테마 전환",
"settings": "설정 열기", "settings": "설정 열기",
"sessionInfo": "세션 세부 정보" "sessionInfo": "세션 세부 정보"
}, },
"sessionInfo": { "sessionInfo": {
"title": "세션", "title": "세션",
"untitled": "제목 없는 채팅", "untitled": "제목 없는 주제",
"automations": "자동화", "automations": "자동화",
"count": "{{count}}", "count": "{{count}}",
"loading": "자동화를 불러오는 중...", "loading": "자동화를 불러오는 중...",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "메시지를 입력하세요…", "placeholderThread": "메시지를 입력하세요…",
"placeholderHero": "무슨 생각을 하고 있나요?", "placeholderHero": "무슨 생각을 하고 있나요?",
"placeholderOpening": "새 채팅을 여는 중…", "placeholderOpening": "새 주제를 여는 중…",
"placeholderStreaming": "모델이 응답 중입니다…", "placeholderStreaming": "모델이 응답 중입니다…",
"inputAria": "메시지 입력", "inputAria": "메시지 입력",
"sendHint": "Enter로 전송 · Shift+Enter로 줄바꿈", "sendHint": "Enter로 전송 · Shift+Enter로 줄바꿈",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Navegação da barra lateral", "navigation": "Navegação da barra lateral",
"collapse": "Recolher barra lateral", "collapse": "Recolher barra lateral",
"newChat": "Nova conversa", "newChat": "Novo tópico",
"searchAria": "Buscar", "searchAria": "Buscar",
"searchPlaceholder": "Buscar", "searchPlaceholder": "Buscar",
"searchResults": "Resultados", "searchResults": "Resultados",
"noSearchResults": "Nenhuma conversa encontrada.", "noSearchResults": "Nenhum tópico encontrado.",
"recent": "Recentes", "recent": "Tópicos recentes",
"settings": "Configurações", "settings": "Configurações",
"language": { "language": {
"label": "Idioma", "label": "Idioma",
@@ -819,15 +819,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Conversa {{id}}", "fallbackTitle": "Tópico {{id}}",
"forkTitle": "Fork: {{title}}", "forkTitle": "Fork: {{title}}",
"loading": "Carregando…", "loading": "Carregando…",
"noSessions": "Nenhuma sessão ainda.", "noSessions": "Nenhuma sessão ainda.",
"showMore": "Mostrar mais {{count}}", "showMore": "Mostrar mais {{count}}",
"collapsed": "{{count}} conversas ocultas", "collapsed": "{{count}} tópicos ocultos",
"showLess": "Mostrar menos", "showLess": "Mostrar menos",
"actions": "Ações da conversa {{title}}", "actions": "Ações do tópico {{title}}",
"newInProject": "Iniciar uma nova conversa em {{project}}", "newInProject": "Iniciar um novo tópico em {{project}}",
"activity": { "activity": {
"running": "Agente em execução", "running": "Agente em execução",
"complete": "Agente finalizado", "complete": "Agente finalizado",
@@ -836,9 +836,9 @@
"pin": "Fixar", "pin": "Fixar",
"unpin": "Desafixar", "unpin": "Desafixar",
"rename": "Renomear", "rename": "Renomear",
"renameTitle": "Renomear conversa", "renameTitle": "Renomear tópico",
"renameDescription": "Escolha um nome local da barra lateral para esta conversa.", "renameDescription": "Escolha um nome local da barra lateral para este tópico.",
"renamePlaceholder": "Nome da conversa", "renamePlaceholder": "Nome do tópico",
"renameProjectTitle": "Renomear projeto", "renameProjectTitle": "Renomear projeto",
"renameProjectDescription": "Escolha um nome local da barra lateral para este projeto.", "renameProjectDescription": "Escolha um nome local da barra lateral para este projeto.",
"renameProjectPlaceholder": "Nome do projeto", "renameProjectPlaceholder": "Nome do projeto",
@@ -848,10 +848,10 @@
"showArchived": "Mostrar arquivadas", "showArchived": "Mostrar arquivadas",
"hideArchived": "Ocultar arquivadas", "hideArchived": "Ocultar arquivadas",
"delete": "Excluir", "delete": "Excluir",
"newChat": "Nova conversa", "newChat": "Novo tópico",
"groups": { "groups": {
"pinned": "Fixadas", "pinned": "Fixadas",
"all": "Conversas", "all": "Tópicos",
"projects": "Projetos", "projects": "Projetos",
"today": "Hoje", "today": "Hoje",
"yesterday": "Ontem", "yesterday": "Ontem",
@@ -954,14 +954,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Alternar barra lateral", "toggleSidebar": "Alternar barra lateral",
"newChat": "Iniciar uma nova conversa", "newChat": "Iniciar um novo tópico",
"toggleTheme": "Alternar tema pelo cabeçalho", "toggleTheme": "Alternar tema pelo cabeçalho",
"settings": "Abrir configurações", "settings": "Abrir configurações",
"sessionInfo": "Detalhes da sessão" "sessionInfo": "Detalhes da sessão"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Sessão", "title": "Sessão",
"untitled": "Conversa sem título", "untitled": "Tópico sem título",
"automations": "Automações", "automations": "Automações",
"count": "{{count}}", "count": "{{count}}",
"loading": "Carregando automações...", "loading": "Carregando automações...",
@@ -987,7 +987,7 @@
"composer": { "composer": {
"placeholderThread": "Digite sua mensagem…", "placeholderThread": "Digite sua mensagem…",
"placeholderHero": "Pergunte qualquer coisa...", "placeholderHero": "Pergunte qualquer coisa...",
"placeholderOpening": "Abrindo uma nova conversa…", "placeholderOpening": "Abrindo um novo tópico…",
"placeholderStreaming": "O modelo está respondendo…", "placeholderStreaming": "O modelo está respondendo…",
"inputAria": "Campo de mensagem", "inputAria": "Campo de mensagem",
"sendHint": "Enter para enviar · Shift+Enter para nova linha", "sendHint": "Enter para enviar · Shift+Enter para nova linha",
+15 -15
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "Điều hướng thanh bên", "navigation": "Điều hướng thanh bên",
"collapse": "Thu gọn thanh bên", "collapse": "Thu gọn thanh bên",
"newChat": "Cuộc trò chuyện mới", "newChat": "Chủ đề mới",
"searchAria": "Tìm kiếm", "searchAria": "Tìm kiếm",
"searchPlaceholder": "Tìm kiếm", "searchPlaceholder": "Tìm kiếm",
"searchResults": "Kết quả", "searchResults": "Kết quả",
"noSearchResults": "Không có cuộc trò chuyện phù hợp.", "noSearchResults": "Không có chủ đề phù hợp.",
"recent": "Gần đây", "recent": "Chủ đề gần đây",
"settings": "Cài đặt", "settings": "Cài đặt",
"language": { "language": {
"label": "Ngôn ngữ", "label": "Ngôn ngữ",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "Trò chuyện {{id}}", "fallbackTitle": "Chủ đề {{id}}",
"forkTitle": "Nhánh: {{title}}", "forkTitle": "Nhánh: {{title}}",
"loading": "Đang tải…", "loading": "Đang tải…",
"noSessions": "Chưa có phiên nào.", "noSessions": "Chưa có phiên nào.",
"showMore": "Hiển thị thêm {{count}}", "showMore": "Hiển thị thêm {{count}}",
"collapsed": "Đã thu gọn {{count}} cuộc trò chuyện", "collapsed": "Đã thu gọn {{count}} chủ đề",
"showLess": "Thu gọn", "showLess": "Thu gọn",
"actions": "Tác vụ cho cuộc trò chuyện {{title}}", "actions": "Tác vụ cho chủ đề {{title}}",
"newInProject": "Bắt đầu cuộc trò chuyện mới trong {{project}}", "newInProject": "Bắt đầu chủ đề mới trong {{project}}",
"activity": { "activity": {
"running": "Agent running", "running": "Agent running",
"complete": "Agent finished", "complete": "Agent finished",
@@ -822,9 +822,9 @@
"pin": "Pin", "pin": "Pin",
"unpin": "Unpin", "unpin": "Unpin",
"rename": "Rename", "rename": "Rename",
"renameTitle": "Rename chat", "renameTitle": "Đổi tên chủ đề",
"renameDescription": "Choose a local sidebar name for this chat.", "renameDescription": "Chọn tên hiển thị trong thanh bên cho chủ đề này.",
"renamePlaceholder": "Chat name", "renamePlaceholder": "Tên chủ đề",
"renameProjectTitle": "Rename project", "renameProjectTitle": "Rename project",
"renameProjectDescription": "Choose a local sidebar name for this project.", "renameProjectDescription": "Choose a local sidebar name for this project.",
"renameProjectPlaceholder": "Project name", "renameProjectPlaceholder": "Project name",
@@ -834,10 +834,10 @@
"showArchived": "Show archived", "showArchived": "Show archived",
"hideArchived": "Hide archived", "hideArchived": "Hide archived",
"delete": "Xóa", "delete": "Xóa",
"newChat": "Cuộc trò chuyện mới", "newChat": "Chủ đề mới",
"groups": { "groups": {
"pinned": "Pinned", "pinned": "Pinned",
"all": "Chats", "all": "Chủ đề",
"projects": "Projects", "projects": "Projects",
"today": "Today", "today": "Today",
"yesterday": "Yesterday", "yesterday": "Yesterday",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "Bật/tắt thanh bên", "toggleSidebar": "Bật/tắt thanh bên",
"newChat": "Bắt đầu chat mới", "newChat": "Bắt đầu chủ đề mới",
"toggleTheme": "Chuyển chủ đề từ header", "toggleTheme": "Chuyển chủ đề từ header",
"settings": "Mở cài đặt", "settings": "Mở cài đặt",
"sessionInfo": "Chi tiết phiên" "sessionInfo": "Chi tiết phiên"
}, },
"sessionInfo": { "sessionInfo": {
"title": "Phiên", "title": "Phiên",
"untitled": "Chat chưa đặt tên", "untitled": "Chủ đề chưa đặt tên",
"automations": "Tự động hóa", "automations": "Tự động hóa",
"count": "{{count}}", "count": "{{count}}",
"loading": "Đang tải tự động hóa...", "loading": "Đang tải tự động hóa...",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "Nhập tin nhắn…", "placeholderThread": "Nhập tin nhắn…",
"placeholderHero": "Bạn đang nghĩ gì?", "placeholderHero": "Bạn đang nghĩ gì?",
"placeholderOpening": "Đang mở cuộc trò chuyện mới…", "placeholderOpening": "Đang mở chủ đề mới…",
"placeholderStreaming": "Mô hình đang trả lời…", "placeholderStreaming": "Mô hình đang trả lời…",
"inputAria": "Ô nhập tin nhắn", "inputAria": "Ô nhập tin nhắn",
"sendHint": "Enter để gửi · Shift+Enter để xuống dòng", "sendHint": "Enter để gửi · Shift+Enter để xuống dòng",
+21 -21
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "侧边栏导航", "navigation": "侧边栏导航",
"collapse": "收起侧边栏", "collapse": "收起侧边栏",
"newChat": "新建话", "newChat": "新建话",
"searchAria": "搜索", "searchAria": "搜索",
"searchPlaceholder": "搜索", "searchPlaceholder": "搜索",
"searchResults": "搜索结果", "searchResults": "搜索结果",
"noSearchResults": "没有匹配的话。", "noSearchResults": "没有匹配的话。",
"recent": "最近话", "recent": "最近话",
"settings": "设置", "settings": "设置",
"language": { "language": {
"label": "语言", "label": "语言",
@@ -819,15 +819,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "话 {{id}}", "fallbackTitle": "话 {{id}}",
"forkTitle": "分叉:{{title}}", "forkTitle": "分叉:{{title}}",
"loading": "加载中…", "loading": "加载中…",
"noSessions": "还没有话。", "noSessions": "还没有话。",
"showMore": "再显示 {{count}} 个", "showMore": "再显示 {{count}} 个",
"collapsed": "已折叠 {{count}} 个话", "collapsed": "已折叠 {{count}} 个话",
"showLess": "收起", "showLess": "收起",
"actions": "“{{title}}” 的话操作", "actions": "“{{title}}” 的话操作",
"newInProject": "在 {{project}} 中开始新话", "newInProject": "在 {{project}} 中开始新话",
"activity": { "activity": {
"running": "Agent 正在运行", "running": "Agent 正在运行",
"complete": "Agent 已完成", "complete": "Agent 已完成",
@@ -836,9 +836,9 @@
"pin": "置顶", "pin": "置顶",
"unpin": "取消置顶", "unpin": "取消置顶",
"rename": "重命名", "rename": "重命名",
"renameTitle": "重命名话", "renameTitle": "重命名话",
"renameDescription": "为这个话设置一个仅用于 WebUI 侧边栏的名称。", "renameDescription": "为这个话设置一个仅用于 WebUI 侧边栏的名称。",
"renamePlaceholder": "话名称", "renamePlaceholder": "话名称",
"renameProjectTitle": "重命名项目", "renameProjectTitle": "重命名项目",
"renameProjectDescription": "为这个项目设置一个仅用于 WebUI 侧边栏的名称。", "renameProjectDescription": "为这个项目设置一个仅用于 WebUI 侧边栏的名称。",
"renameProjectPlaceholder": "项目名称", "renameProjectPlaceholder": "项目名称",
@@ -848,10 +848,10 @@
"showArchived": "显示归档", "showArchived": "显示归档",
"hideArchived": "隐藏归档", "hideArchived": "隐藏归档",
"delete": "删除", "delete": "删除",
"newChat": "新建话", "newChat": "新建话",
"groups": { "groups": {
"pinned": "置顶", "pinned": "置顶",
"all": "话", "all": "话",
"projects": "项目", "projects": "项目",
"today": "今天", "today": "今天",
"yesterday": "昨天", "yesterday": "昨天",
@@ -860,11 +860,11 @@
} }
}, },
"deleteConfirm": { "deleteConfirm": {
"title": "删除这个话?", "title": "删除这个话",
"description": "此操作无法撤销。", "description": "此操作无法撤销。",
"cancel": "取消", "cancel": "取消",
"confirm": "删除", "confirm": "删除",
"automationsDescription": "这个话有关联的自动任务。删除话也会删除这些自动任务。", "automationsDescription": "这个话有关联的自动任务。删除话也会删除这些自动任务。",
"moreAutomations": "另有 {{count}} 个", "moreAutomations": "另有 {{count}} 个",
"confirmWithAutomations": "删除", "confirmWithAutomations": "删除",
"schedule": { "schedule": {
@@ -891,7 +891,7 @@
"error": "连接出错" "error": "连接出错"
}, },
"thread": { "thread": {
"loadingConversation": "正在加载话…", "loadingConversation": "正在加载话…",
"empty": { "empty": {
"greetings": { "greetings": {
"workOn": "我们要一起做点什么?", "workOn": "我们要一起做点什么?",
@@ -954,14 +954,14 @@
}, },
"header": { "header": {
"toggleSidebar": "切换侧边栏", "toggleSidebar": "切换侧边栏",
"newChat": "从顶部新建话", "newChat": "从顶部新建话",
"toggleTheme": "从顶部切换主题", "toggleTheme": "从顶部切换主题",
"settings": "打开设置", "settings": "打开设置",
"sessionInfo": "会话详情" "sessionInfo": "会话详情"
}, },
"sessionInfo": { "sessionInfo": {
"title": "会话", "title": "会话",
"untitled": "未命名话", "untitled": "未命名话",
"automations": "自动任务", "automations": "自动任务",
"count": "{{count}}", "count": "{{count}}",
"loading": "正在加载自动任务...", "loading": "正在加载自动任务...",
@@ -987,7 +987,7 @@
"composer": { "composer": {
"placeholderThread": "输入消息…", "placeholderThread": "输入消息…",
"placeholderHero": "问任何问题...", "placeholderHero": "问任何问题...",
"placeholderOpening": "正在打开新话…", "placeholderOpening": "正在打开新话…",
"placeholderStreaming": "模型正在回复…", "placeholderStreaming": "模型正在回复…",
"inputAria": "消息输入框", "inputAria": "消息输入框",
"sendHint": "Enter 发送 · Shift+Enter 换行", "sendHint": "Enter 发送 · Shift+Enter 换行",
@@ -1065,8 +1065,8 @@
}, },
"commands": { "commands": {
"new": { "new": {
"title": "新建话", "title": "新建话",
"description": "重置当前话,并开始一个新的话。" "description": "重置当前话,并开始一个新的话。"
}, },
"stop": { "stop": {
"title": "停止当前任务", "title": "停止当前任务",
+21 -21
View File
@@ -43,12 +43,12 @@
"sidebar": { "sidebar": {
"navigation": "側邊欄導覽", "navigation": "側邊欄導覽",
"collapse": "收合側邊欄", "collapse": "收合側邊欄",
"newChat": "新增話", "newChat": "新增話",
"searchAria": "搜尋", "searchAria": "搜尋",
"searchPlaceholder": "搜尋", "searchPlaceholder": "搜尋",
"searchResults": "搜尋結果", "searchResults": "搜尋結果",
"noSearchResults": "沒有符合的話。", "noSearchResults": "沒有符合的話。",
"recent": "最近話", "recent": "最近話",
"settings": "設定", "settings": "設定",
"language": { "language": {
"label": "語言", "label": "語言",
@@ -805,15 +805,15 @@
} }
}, },
"chat": { "chat": {
"fallbackTitle": "話 {{id}}", "fallbackTitle": "話 {{id}}",
"forkTitle": "分支:{{title}}", "forkTitle": "分支:{{title}}",
"loading": "載入中…", "loading": "載入中…",
"noSessions": "目前還沒有話。", "noSessions": "目前還沒有話。",
"showMore": "再顯示 {{count}} 個", "showMore": "再顯示 {{count}} 個",
"collapsed": "已隱藏 {{count}} 個話", "collapsed": "已隱藏 {{count}} 個話",
"showLess": "收起", "showLess": "收起",
"actions": "「{{title}}」的話操作", "actions": "「{{title}}」的話操作",
"newInProject": "在 {{project}} 中開始新話", "newInProject": "在 {{project}} 中開始新話",
"activity": { "activity": {
"running": "Agent 正在執行", "running": "Agent 正在執行",
"complete": "Agent 已完成", "complete": "Agent 已完成",
@@ -822,9 +822,9 @@
"pin": "置頂", "pin": "置頂",
"unpin": "取消置頂", "unpin": "取消置頂",
"rename": "重新命名", "rename": "重新命名",
"renameTitle": "重新命名話", "renameTitle": "重新命名話",
"renameDescription": "為這個話選擇僅顯示於本機側邊欄的名稱。", "renameDescription": "為這個話選擇僅顯示於本機側邊欄的名稱。",
"renamePlaceholder": "話名稱", "renamePlaceholder": "話名稱",
"renameProjectTitle": "重新命名專案", "renameProjectTitle": "重新命名專案",
"renameProjectDescription": "為這個專案選擇僅顯示於本機側邊欄的名稱。", "renameProjectDescription": "為這個專案選擇僅顯示於本機側邊欄的名稱。",
"renameProjectPlaceholder": "專案名稱", "renameProjectPlaceholder": "專案名稱",
@@ -834,10 +834,10 @@
"showArchived": "顯示封存", "showArchived": "顯示封存",
"hideArchived": "隱藏封存", "hideArchived": "隱藏封存",
"delete": "刪除", "delete": "刪除",
"newChat": "新增話", "newChat": "新增話",
"groups": { "groups": {
"pinned": "置頂", "pinned": "置頂",
"all": "話", "all": "話",
"projects": "專案", "projects": "專案",
"today": "今天", "today": "今天",
"yesterday": "昨天", "yesterday": "昨天",
@@ -846,11 +846,11 @@
} }
}, },
"deleteConfirm": { "deleteConfirm": {
"title": "刪除這個話?", "title": "刪除這個話",
"description": "此操作無法復原。", "description": "此操作無法復原。",
"cancel": "取消", "cancel": "取消",
"confirm": "刪除", "confirm": "刪除",
"automationsDescription": "這個話含有已排程的自動任務。刪除話時也會一併刪除這些任務。", "automationsDescription": "這個話含有已排程的自動任務。刪除話時也會一併刪除這些任務。",
"moreAutomations": "另有 {{count}} 個", "moreAutomations": "另有 {{count}} 個",
"confirmWithAutomations": "刪除", "confirmWithAutomations": "刪除",
"schedule": { "schedule": {
@@ -877,7 +877,7 @@
"error": "連線錯誤" "error": "連線錯誤"
}, },
"thread": { "thread": {
"loadingConversation": "正在載入話…", "loadingConversation": "正在載入話…",
"empty": { "empty": {
"greetings": { "greetings": {
"workOn": "我們要一起做點什麼?", "workOn": "我們要一起做點什麼?",
@@ -940,14 +940,14 @@
}, },
"header": { "header": {
"toggleSidebar": "切換側邊欄", "toggleSidebar": "切換側邊欄",
"newChat": "開始新話", "newChat": "開始新話",
"toggleTheme": "切換佈景主題", "toggleTheme": "切換佈景主題",
"settings": "開啟設定", "settings": "開啟設定",
"sessionInfo": "對話詳細資料" "sessionInfo": "對話詳細資料"
}, },
"sessionInfo": { "sessionInfo": {
"title": "對話", "title": "對話",
"untitled": "未命名話", "untitled": "未命名話",
"automations": "自動任務", "automations": "自動任務",
"count": "{{count}}", "count": "{{count}}",
"loading": "正在載入自動任務…", "loading": "正在載入自動任務…",
@@ -973,7 +973,7 @@
"composer": { "composer": {
"placeholderThread": "輸入訊息…", "placeholderThread": "輸入訊息…",
"placeholderHero": "任何問題都可以問…", "placeholderHero": "任何問題都可以問…",
"placeholderOpening": "正在開啟新話…", "placeholderOpening": "正在開啟新話…",
"placeholderStreaming": "模型正在回覆…", "placeholderStreaming": "模型正在回覆…",
"inputAria": "訊息輸入框", "inputAria": "訊息輸入框",
"sendHint": "Enter 送出 · Shift+Enter 換行", "sendHint": "Enter 送出 · Shift+Enter 換行",
@@ -1042,8 +1042,8 @@
"closeHint": "Esc 關閉", "closeHint": "Esc 關閉",
"commands": { "commands": {
"new": { "new": {
"title": "新增話", "title": "新增話",
"description": "重置目前話,並開始新的話。" "description": "重置目前話,並開始新的話。"
}, },
"stop": { "stop": {
"title": "停止目前任務", "title": "停止目前任務",
+20 -20
View File
@@ -858,13 +858,13 @@ describe("App layout", () => {
).toBeInTheDocument(), ).toBeInTheDocument(),
); );
fireEvent.pointerDown(screen.getByLabelText("Chat actions for First chat"), { fireEvent.pointerDown(screen.getByLabelText("Topic actions for First chat"), {
button: 0, button: 0,
}); });
fireEvent.click(await screen.findByRole("menuitem", { name: "Delete" })); fireEvent.click(await screen.findByRole("menuitem", { name: "Delete" }));
await waitFor(() => await waitFor(() =>
expect(screen.getByText("Delete this chat?")).toBeInTheDocument(), expect(screen.getByText("Delete this topic?")).toBeInTheDocument(),
); );
fireEvent.click(screen.getByRole("button", { name: "Delete" })); fireEvent.click(screen.getByRole("button", { name: "Delete" }));
@@ -876,7 +876,7 @@ describe("App layout", () => {
within(sidebar).getByRole("button", { name: /^Second chat$/ }), within(sidebar).getByRole("button", { name: /^Second chat$/ }),
).toBeInTheDocument(), ).toBeInTheDocument(),
); );
expect(screen.queryByText("Delete this chat?")).not.toBeInTheDocument(); expect(screen.queryByText("Delete this topic?")).not.toBeInTheDocument();
expect(document.body.style.pointerEvents).not.toBe("none"); expect(document.body.style.pointerEvents).not.toBe("none");
}, 15_000); }, 15_000);
@@ -921,7 +921,7 @@ describe("App layout", () => {
).toBeInTheDocument(), ).toBeInTheDocument(),
); );
fireEvent.pointerDown(screen.getByLabelText(/First chat.*话操作/), { fireEvent.pointerDown(screen.getByLabelText(/First chat.*话操作/), {
button: 0, button: 0,
}); });
fireEvent.click(await screen.findByRole("menuitem", { name: "删除" })); fireEvent.click(await screen.findByRole("menuitem", { name: "删除" }));
@@ -931,7 +931,7 @@ describe("App layout", () => {
); );
expect(getSessionAutomationsSpy).toHaveBeenCalledWith("websocket:chat-a"); expect(getSessionAutomationsSpy).toHaveBeenCalledWith("websocket:chat-a");
expect( expect(
screen.getByText("这个话有关联的自动任务。删除话也会删除这些自动任务。"), screen.getByText("这个话有关联的自动任务。删除话也会删除这些自动任务。"),
).toBeInTheDocument(); ).toBeInTheDocument();
expect( expect(
screen.queryByText("This chat has scheduled automations. Deleting it will also delete them."), screen.queryByText("This chat has scheduled automations. Deleting it will also delete them."),
@@ -989,7 +989,7 @@ describe("App layout", () => {
); );
fireEvent.pointerDown( fireEvent.pointerDown(
within(mobileSidebar).getByLabelText("Chat actions for Existing chat"), within(mobileSidebar).getByLabelText("Topic actions for Existing chat"),
{ button: 0 }, { button: 0 },
); );
@@ -1000,7 +1000,7 @@ describe("App layout", () => {
fireEvent.click(deleteItem); fireEvent.click(deleteItem);
await waitFor(() => await waitFor(() =>
expect(screen.getByText("Delete this chat?")).toBeInTheDocument(), expect(screen.getByText("Delete this topic?")).toBeInTheDocument(),
); );
}, 15_000); }, 15_000);
@@ -1143,16 +1143,16 @@ describe("App layout", () => {
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
await waitFor(() => await waitFor(() =>
expect(within(sidebar).getByText("Chats")).toBeInTheDocument(), expect(within(sidebar).getByText("Topics")).toBeInTheDocument(),
); );
const group = within(sidebar).getByText("Chats").closest("section"); const group = within(sidebar).getByText("Topics").closest("section");
expect(group).toBeTruthy(); expect(group).toBeTruthy();
const labels = within(group as HTMLElement) const labels = within(group as HTMLElement)
.getAllByRole("button") .getAllByRole("button")
.map((button) => button.textContent?.trim()) .map((button) => button.textContent?.trim())
.filter(Boolean); .filter(Boolean);
expect(labels).toEqual(["Alpha plan", "New chat", "Zulu work"]); expect(labels).toEqual(["Alpha plan", "New topic", "Zulu work"]);
}); });
it("shows running and completed session indicators in the sidebar", async () => { it("shows running and completed session indicators in the sidebar", async () => {
@@ -1969,7 +1969,7 @@ describe("App layout", () => {
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
fireEvent.click(within(sidebar).getByRole("button", { name: "New chat" })); fireEvent.click(within(sidebar).getByRole("button", { name: "New topic" }));
await waitFor(() => expect(document.title).toBe("nanobot")); await waitFor(() => expect(document.title).toBe("nanobot"));
fireEvent.click(within(sidebar).getByRole("button", { name: "Settings" })); fireEvent.click(within(sidebar).getByRole("button", { name: "Settings" }));
@@ -2009,7 +2009,7 @@ describe("App layout", () => {
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
expect(within(sidebar).getByText("Q2 roadmap")).toBeInTheDocument(); expect(within(sidebar).getByText("Q2 roadmap")).toBeInTheDocument();
expect(within(sidebar).getByText("Travel ideas")).toBeInTheDocument(); expect(within(sidebar).getByText("Travel ideas")).toBeInTheDocument();
const newChatButton = within(sidebar).getByRole("button", { name: "New chat" }); const newChatButton = within(sidebar).getByRole("button", { name: "New topic" });
const searchButton = within(sidebar).getByRole("button", { name: "Search" }); const searchButton = within(sidebar).getByRole("button", { name: "Search" });
expect( expect(
newChatButton.compareDocumentPosition(searchButton) & newChatButton.compareDocumentPosition(searchButton) &
@@ -2139,10 +2139,10 @@ describe("App layout", () => {
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
const newChatButton = within(sidebar).getByRole("button", { name: "New chat" }); const newChatButton = within(sidebar).getByRole("button", { name: "New topic" });
expect(newChatButton).toHaveAttribute( expect(newChatButton).toHaveAttribute(
"title", "title",
"New chat (Ctrl+Shift+O)", "New topic (Ctrl+Shift+O)",
); );
expect(newChatButton).toHaveAttribute( expect(newChatButton).toHaveAttribute(
"aria-keyshortcuts", "aria-keyshortcuts",
@@ -2157,9 +2157,9 @@ describe("App layout", () => {
await waitFor(() => expect(connectSpy).toHaveBeenCalled()); await waitFor(() => expect(connectSpy).toHaveBeenCalled());
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
expect(within(sidebar).getByRole("button", { name: "New chat" })).toHaveAttribute( expect(within(sidebar).getByRole("button", { name: "New topic" })).toHaveAttribute(
"title", "title",
"New chat (⌘⇧O)", "New topic (⌘⇧O)",
); );
}); });
@@ -2230,9 +2230,9 @@ describe("App layout", () => {
const sidebarAside = container.querySelector("aside.lg\\:block") as HTMLElement; const sidebarAside = container.querySelector("aside.lg\\:block") as HTMLElement;
await waitFor(() => expect(sidebarAside.style.width).toBe("56px")); await waitFor(() => expect(sidebarAside.style.width).toBe("56px"));
expect(screen.queryByRole("button", { name: "Start a new chat" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Start a new topic" })).not.toBeInTheDocument();
const rail = screen.getByRole("navigation", { name: "Sidebar navigation" }); const rail = screen.getByRole("navigation", { name: "Sidebar navigation" });
expect(within(rail).getByRole("button", { name: "New chat" })).toBeInTheDocument(); expect(within(rail).getByRole("button", { name: "New topic" })).toBeInTheDocument();
expect(within(rail).getByRole("button", { name: "Search" })).toBeInTheDocument(); expect(within(rail).getByRole("button", { name: "Search" })).toBeInTheDocument();
expect(within(rail).queryByRole("button", { name: "View" })).not.toBeInTheDocument(); expect(within(rail).queryByRole("button", { name: "View" })).not.toBeInTheDocument();
expect(within(rail).queryByText("Existing chat")).not.toBeInTheDocument(); expect(within(rail).queryByText("Existing chat")).not.toBeInTheDocument();
@@ -2241,10 +2241,10 @@ describe("App layout", () => {
await waitFor(() => expect(sidebarAside.style.width).toBe("272px")); await waitFor(() => expect(sidebarAside.style.width).toBe("272px"));
const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" }); const sidebar = screen.getByRole("navigation", { name: "Sidebar navigation" });
fireEvent.click(within(sidebar).getByRole("button", { name: "New chat" })); fireEvent.click(within(sidebar).getByRole("button", { name: "New topic" }));
expect(createChatSpy).not.toHaveBeenCalled(); expect(createChatSpy).not.toHaveBeenCalled();
expect(screen.getByText(HERO_GREETING_PATTERN)).toBeInTheDocument(); expect(screen.getByText(HERO_GREETING_PATTERN)).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Start a new chat" })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: "Start a new topic" })).not.toBeInTheDocument();
expect(screen.getByRole("button", { name: "Toggle theme from header" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Toggle theme from header" })).toBeInTheDocument();
expect(within(sidebar).getByRole("button", { name: "Settings" })).toBeInTheDocument(); expect(within(sidebar).getByRole("button", { name: "Settings" })).toBeInTheDocument();
+13 -13
View File
@@ -142,7 +142,7 @@ describe("ChatList", () => {
expect(screen.queryByText("Today")).not.toBeInTheDocument(); expect(screen.queryByText("Today")).not.toBeInTheDocument();
}); });
it("keeps default workspace chats in the Chats section instead of a project folder", () => { it("keeps default workspace topics in the Topics section instead of a project folder", () => {
const sessions = [ const sessions = [
session({ session({
chatId: "default", chatId: "default",
@@ -184,7 +184,7 @@ describe("ChatList", () => {
expect(screen.getByRole("region", { name: "nanobot" })).toBeInTheDocument(); expect(screen.getByRole("region", { name: "nanobot" })).toBeInTheDocument();
expect(screen.queryByRole("region", { name: "workspace" })).not.toBeInTheDocument(); expect(screen.queryByRole("region", { name: "workspace" })).not.toBeInTheDocument();
const chatsSection = screen.getByRole("region", { name: "Chats" }); const chatsSection = screen.getByRole("region", { name: "Topics" });
expect(within(chatsSection).getByText("Default workspace chat")).toBeInTheDocument(); expect(within(chatsSection).getByText("Default workspace chat")).toBeInTheDocument();
expect(within(chatsSection).queryByText("Project chat")).not.toBeInTheDocument(); expect(within(chatsSection).queryByText("Project chat")).not.toBeInTheDocument();
}); });
@@ -229,13 +229,13 @@ describe("ChatList", () => {
expect(within(projectSection).queryByText("Alpha task")).not.toBeInTheDocument(); expect(within(projectSection).queryByText("Alpha task")).not.toBeInTheDocument();
fireEvent.click( fireEvent.click(
within(projectSection).getByRole("button", { name: "Start a new chat in Photos" }), within(projectSection).getByRole("button", { name: "Start a new topic in Photos" }),
); );
expect(onNewChatInProject).toHaveBeenCalledWith("/Users/me/nanobot", "Photos"); expect(onNewChatInProject).toHaveBeenCalledWith("/Users/me/nanobot", "Photos");
expect(onToggleGroup).toHaveBeenCalledTimes(1); expect(onToggleGroup).toHaveBeenCalledTimes(1);
fireEvent.pointerDown( fireEvent.pointerDown(
within(projectSection).getByLabelText("Chat actions for Photos"), within(projectSection).getByLabelText("Topic actions for Photos"),
{ button: 0 }, { button: 0 },
); );
fireEvent.click(await screen.findByRole("menuitem", { name: "Rename" })); fireEvent.click(await screen.findByRole("menuitem", { name: "Rename" }));
@@ -300,13 +300,13 @@ describe("ChatList", () => {
}; };
const { rerender } = render(<ChatList {...baseProps} />); const { rerender } = render(<ChatList {...baseProps} />);
const chatsSection = screen.getByRole("region", { name: "Chats" }); const chatsSection = screen.getByRole("region", { name: "Topics" });
expect(within(chatsSection).getByText("Chat 9")).toBeInTheDocument(); expect(within(chatsSection).getByText("Chat 9")).toBeInTheDocument();
expect(within(chatsSection).getByText("Chat 2")).toBeInTheDocument(); expect(within(chatsSection).getByText("Chat 2")).toBeInTheDocument();
expect(within(chatsSection).queryByText("Chat 1")).not.toBeInTheDocument(); expect(within(chatsSection).queryByText("Chat 1")).not.toBeInTheDocument();
expect(within(chatsSection).queryByRole("button", { name: "Show all" })).not.toBeInTheDocument(); expect(within(chatsSection).queryByRole("button", { name: "Show all" })).not.toBeInTheDocument();
fireEvent.click(within(chatsSection).getByRole("button", { name: "2 hidden chats" })); fireEvent.click(within(chatsSection).getByRole("button", { name: "2 hidden topics" }));
expect(onToggleGroup).toHaveBeenCalledWith("workspace:chats"); expect(onToggleGroup).toHaveBeenCalledWith("workspace:chats");
@@ -321,7 +321,7 @@ describe("ChatList", () => {
expect(within(chatsSection).getByRole("button", { name: "Show less" })).toBeInTheDocument(); expect(within(chatsSection).getByRole("button", { name: "Show less" })).toBeInTheDocument();
}); });
it("sorts Chats section among project groups by recency, not always last", () => { it("sorts Topics section among project groups by recency, not always last", () => {
const sessions = [ const sessions = [
session({ session({
chatId: "recent-chat", chatId: "recent-chat",
@@ -367,8 +367,8 @@ describe("ChatList", () => {
const regionNames = allRegions.map((r) => r.getAttribute("aria-label") ?? r.textContent); const regionNames = allRegions.map((r) => r.getAttribute("aria-label") ?? r.textContent);
// The most recently updated conversation ("Recent chat" at 12:00) must be // The most recently updated conversation ("Recent chat" at 12:00) must be
// in the first group — Chats should come before both projects. // in the first group — Topics should come before both projects.
const chatsIdx = regionNames.findIndex((n) => n?.includes("Chats")); const chatsIdx = regionNames.findIndex((n) => n?.includes("Topics"));
const projAIdx = regionNames.findIndex((n) => n?.includes("project-a")); const projAIdx = regionNames.findIndex((n) => n?.includes("project-a"));
const projBIdx = regionNames.findIndex((n) => n?.includes("project-b")); const projBIdx = regionNames.findIndex((n) => n?.includes("project-b"));
@@ -377,7 +377,7 @@ describe("ChatList", () => {
expect(within(allRegions[chatsIdx]).getByText("Recent chat")).toBeInTheDocument(); expect(within(allRegions[chatsIdx]).getByText("Recent chat")).toBeInTheDocument();
}); });
it("keeps one Projects heading when Chats sorts between project groups", () => { it("keeps one Projects heading when Topics sorts between project groups", () => {
const sessions = [ const sessions = [
session({ session({
chatId: "project-a", chatId: "project-a",
@@ -423,11 +423,11 @@ describe("ChatList", () => {
.getAllByRole("region") .getAllByRole("region")
.map((r) => r.getAttribute("aria-label") ?? ""); .map((r) => r.getAttribute("aria-label") ?? "");
expect(regionNames).toEqual(["project-a", "Chats", "project-b"]); expect(regionNames).toEqual(["project-a", "Topics", "project-b"]);
expect(screen.getAllByText("Projects")).toHaveLength(1); expect(screen.getAllByText("Projects")).toHaveLength(1);
}); });
it("keeps Chats last when its latest conversation is older than all projects", () => { it("keeps Topics last when its latest conversation is older than all projects", () => {
const sessions = [ const sessions = [
session({ session({
chatId: "project-a", chatId: "project-a",
@@ -473,7 +473,7 @@ describe("ChatList", () => {
.getAllByRole("region") .getAllByRole("region")
.map((r) => r.getAttribute("aria-label") ?? ""); .map((r) => r.getAttribute("aria-label") ?? "");
expect(regionNames).toEqual(["project-a", "project-b", "Chats"]); expect(regionNames).toEqual(["project-a", "project-b", "Topics"]);
expect(screen.getAllByText("Projects")).toHaveLength(1); expect(screen.getAllByText("Projects")).toHaveLength(1);
}); });
}); });
+1 -1
View File
@@ -445,7 +445,7 @@ describe("webui i18n", () => {
const chat = resources["pt-BR"].common.chat; const chat = resources["pt-BR"].common.chat;
expect(sidebar.settings).toBe("Configurações"); expect(sidebar.settings).toBe("Configurações");
expect(chat.newChat).toBe("Nova conversa"); expect(chat.newChat).toBe("Novo tópico");
expect(settings.nav.browser).toBe("Web"); expect(settings.nav.browser).toBe("Web");
expect(settings.sections.webSearch).toBe("Busca na web"); expect(settings.sections.webSearch).toBe("Busca na web");
expect(settings.byok.tabs.webSearch).toBe("Busca na web"); expect(settings.byok.tabs.webSearch).toBe("Busca na web");
+1 -1
View File
@@ -71,7 +71,7 @@ describe("useSessions", () => {
updatedAt: "2026-04-16T10:00:00Z", updatedAt: "2026-04-16T10:00:00Z",
title: "", title: "",
preview: "hi", preview: "hi",
})).toBe("New chat"); })).toBe("New topic");
expect(sessionTitle({ expect(sessionTitle({
key: "websocket:chat-work", key: "websocket:chat-work",