refactor(web): extract ShellPage sidebar JSX into components
ShellPage's JSX carried the whole sidebar inline, including two near-copies of the group header and of the main header's sidebar toggles, which made the render hard to follow and easy to change in one place only. Move-only: the sidebar header, group header (one component for the conversation and project variants), the team and project roster rows, the archived section, the user menu and the sidebar toggle buttons now live in pages/shell. DOM, classNames, test ids, aria attributes, handlers and messages are unchanged; the catalogs only update their file references, and the desktop source checks for the header toggles now read the file they moved to. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
parent
a0060b8df7
commit
3c678519a6
@ -63,6 +63,7 @@ describe("window chrome", () => {
|
||||
it("keeps conversation header controls clickable", () => {
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "../pages");
|
||||
const shell = readFileSync(path.join(root, "Shell.tsx"), "utf8");
|
||||
const toggles = readFileSync(path.join(root, "shell/sidebar-toggle-buttons.tsx"), "utf8");
|
||||
expect(shell).toContain(
|
||||
'className="app-drag flex items-center justify-between border-b border-sidebar-border',
|
||||
);
|
||||
@ -70,14 +71,16 @@ describe("window chrome", () => {
|
||||
expect(shell).toContain('className="app-no-drag flex min-w-0 items-center gap-3"');
|
||||
// Ensure header sidebar toggle controls stay clickable (not part of drag region)
|
||||
expect(
|
||||
shell.match(/app-no-drag .* size-control-lg .* place-items-center/g)?.length ?? 0,
|
||||
toggles.match(/app-no-drag .* size-control-lg .* place-items-center/g)?.length ?? 0,
|
||||
).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it("moves window chrome into the conversation header when the bots sidebar is collapsed", () => {
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), "../pages");
|
||||
const shell = readFileSync(path.join(root, "Shell.tsx"), "utf8");
|
||||
expect(shell).toContain("{botsSidebarCollapsed && desktopBridge() ? <WindowChrome /> : null}");
|
||||
const toggles = readFileSync(path.join(root, "shell/sidebar-toggle-buttons.tsx"), "utf8");
|
||||
expect(shell).toContain("collapsed={botsSidebarCollapsed}");
|
||||
expect(toggles).toContain("{collapsed && desktopBridge() ? <WindowChrome /> : null}");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (ungelesen)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr "{0}, {label}"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr "{0}, 1:1-Unterhaltung"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr "{0}, Gruppenunterhaltung"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr "{0}, Hauptunterhaltung"
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "Aktionsbestätigungen"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "Aktionen für {0}"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "Aktiv"
|
||||
msgid "Active model"
|
||||
msgstr "Aktives Modell"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Aktivität"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Archivieren"
|
||||
msgid "archived"
|
||||
msgstr "archiviert"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "Archiviert"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr "Coder"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "{0} einklappen"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "Avatare konnten nicht aktualisiert werden"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "Messaging-Einstellungen konnten nicht aktualisiert werden"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Erstellen"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Standardbereich"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "{0} löschen"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Erweitern"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "{0} ausklappen"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr "Lokales Docker"
|
||||
msgid "Local Server Settings"
|
||||
msgstr "Einstellungen des lokalen Servers"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Abmelden"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Minimal"
|
||||
msgid "Minimize"
|
||||
msgstr "Minimieren"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr "Bots minimieren"
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Öffnen"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "{0} öffnen"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr "Im Browser öffnen"
|
||||
msgid "Open in full window"
|
||||
msgstr "In vollständigem Fenster öffnen"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Navigation öffnen"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "Wird zurückgesetzt…"
|
||||
msgid "Restart to update"
|
||||
msgstr "Zum Aktualisieren neu starten"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Wiederherstellen"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Suche…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr "{0} auswählen"
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr "Stimme einrichten"
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr "Alle {0} anzeigen"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr "Bots anzeigen"
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Hochladen"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Nutzung"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (unread)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr "{0}, {label}"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr "{0}, 1:1 conversation"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr "{0}, group conversation"
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr "{0}, main conversation"
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "Action confirmations"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "Actions for {0}"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "Active"
|
||||
msgid "Active model"
|
||||
msgstr "Active model"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Activity"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Archive"
|
||||
msgid "archived"
|
||||
msgstr "archived"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "Archived"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr "Coder"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "Collapse {0}"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "Couldn't update avatars"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "Couldn't update messaging settings"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Create"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Default scope"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Delete"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "Delete {0}"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Expand"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "Expand {0}"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr "Local Docker"
|
||||
msgid "Local Server Settings"
|
||||
msgstr "Local Server Settings"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Log out"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Minimal"
|
||||
msgid "Minimize"
|
||||
msgstr "Minimize"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr "Minimize bots"
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Open"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "Open {0}"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr "Open in browser"
|
||||
msgid "Open in full window"
|
||||
msgstr "Open in full window"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Open navigation"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "Resetting…"
|
||||
msgid "Restart to update"
|
||||
msgstr "Restart to update"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Restore"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Searching…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr "Select {0}"
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr "Set up voice"
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Settings"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr "Show all {0}"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr "Show bots"
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Upload"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Usage"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (sin leer)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "Confirmaciones de acción"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "Acciones para {0}"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "Activo"
|
||||
msgid "Active model"
|
||||
msgstr "Modelo activo"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Actividad"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Archivar"
|
||||
msgid "archived"
|
||||
msgstr "archivado"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "Archivado"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "Contraer {0}"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "No se pudieron actualizar los avatares"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "No se pudo actualizar la configuración de mensajería"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Crear"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Alcance predeterminado"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "Eliminar {0}"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Expandir"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "Expandir {0}"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Cerrar sesión"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Mínimo"
|
||||
msgid "Minimize"
|
||||
msgstr "Minimizar"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Abrir"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "Abrir {0}"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "Abrir en ventana completa"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Abrir navegación"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "Restableciendo…"
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Buscando…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Configuración"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Subir"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (अपठित)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "कार्रवाई की पुष्टि"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "{0} के लिए कार्रवाइयां"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr ""
|
||||
msgid "Active model"
|
||||
msgstr "सक्रिय मॉडल"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "गतिविधि"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "आर्काइव करें"
|
||||
msgid "archived"
|
||||
msgstr "आर्काइव किया गया"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "आर्काइव किया गया"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr ""
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "बनाएं"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "डिफ़ॉल्ट स्कोप"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "डिलीट करें"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "{0} डिलीट करें"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "विस्तृत करें"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "लॉग आउट"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "न्यूनतम"
|
||||
msgid "Minimize"
|
||||
msgstr "छोटा करें"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "खोलें"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "पूरी विंडो में खोलें"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "नेविगेशन खोलें"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "रीसेट हो रहा है…"
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "बहाल करें"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "खोजा जा रहा है…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "सेटिंग्स"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "अपलोड"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "उपयोग"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (읽지 않음)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "작업 실행 전 확인"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "{0} 작업 메뉴"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "활성"
|
||||
msgid "Active model"
|
||||
msgstr "현재 모델"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "활동"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "보관"
|
||||
msgid "archived"
|
||||
msgstr "보관됨"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "보관됨"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "{0} 접기"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "아바타를 업데이트할 수 없습니다"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "메시징 설정을 업데이트할 수 없습니다"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "만들기"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "기본 기억 범위"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "삭제"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "{0} 삭제"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "펼치기"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "{0} 펼치기"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "로그아웃"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "최소"
|
||||
msgid "Minimize"
|
||||
msgstr "최소화"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "열기"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "{0} 열기"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "전체 창으로 열기"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "메뉴 열기"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "재설정 중…"
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "복원"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "검색 중…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "설정"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "업로드"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "사용량"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (não lido)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "Confirmações de ação"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "Ações para {0}"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr ""
|
||||
msgid "Active model"
|
||||
msgstr "Modelo ativo"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Atividade"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Arquivar"
|
||||
msgid "archived"
|
||||
msgstr "arquivado"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "Arquivado"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr ""
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Criar"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Escopo padrão"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Excluir"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "Excluir {0}"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Expandir"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Sair"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Mínimo"
|
||||
msgid "Minimize"
|
||||
msgstr "Minimizar"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Abrir"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "Abrir em janela cheia"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Abrir a navegação"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "Redefinindo…"
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Pesquisando…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Configurações"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Enviar"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Uso"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (непрочитано)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "Подтверждения действий"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "Действия для {0}"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "Активный"
|
||||
msgid "Active model"
|
||||
msgstr "Активная модель"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Активность"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Архивировать"
|
||||
msgid "archived"
|
||||
msgstr "в архиве"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "В архиве"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "Свернуть {0}"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "Не удалось обновить аватары"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "Не удалось обновить настройки обмена сообщениями."
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Создать"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Область действия по умолчанию"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Удалить"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "Удалить {0}"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Развернуть"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "Развернуть {0}"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr "Настройки локального сервера"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Выйти"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Минимальный"
|
||||
msgid "Minimize"
|
||||
msgstr "Свернуть"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr "Свернуть ботов"
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Открыть"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "Открыть {0}"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "Открыть в полном окне"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Открыть навигацию"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "Сброс…"
|
||||
msgid "Restart to update"
|
||||
msgstr "Перезапустить для обновления"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Восстановить"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Поиск…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr "Показать ботов"
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Загрузить"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Использование"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr " (okunmadı)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "İşlem onayları"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "{0} için işlemler"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr ""
|
||||
msgid "Active model"
|
||||
msgstr "Etkin model"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "Etkinlik"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "Arşivle"
|
||||
msgid "archived"
|
||||
msgstr "arşivlendi"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "Arşivlendi"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr ""
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "Oluştur"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "Varsayılan kapsam"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "Sil"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "{0} sil"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "Genişlet"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "Çıkış yap"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "Minimal"
|
||||
msgid "Minimize"
|
||||
msgstr "Küçült"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "Aç"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "Tam pencerede aç"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "Gezinmeyi aç"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr ""
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "Geri yükle"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "Aranıyor…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Ayarlar"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "Yükle"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "Kullanım"
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ msgstr ""
|
||||
"Plural-Forms: \n"
|
||||
"X-Generator: @lingui/cli\n"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid " (unread)"
|
||||
msgstr "(未读)"
|
||||
|
||||
@ -126,17 +126,17 @@ msgid "{0}, {label}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, 1:1 conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, group conversation"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: item.chat.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/roster-rows.tsx
|
||||
msgid "{0}, main conversation"
|
||||
msgstr ""
|
||||
|
||||
@ -339,6 +339,7 @@ msgstr "操作确认"
|
||||
#. placeholder {0}: sectionMenu.section.name
|
||||
#: src/pages/BotContextMenu.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Actions for {0}"
|
||||
msgstr "{0} 的操作"
|
||||
|
||||
@ -354,7 +355,7 @@ msgstr "启用"
|
||||
msgid "Active model"
|
||||
msgstr "当前模型"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Activity"
|
||||
msgstr "动态"
|
||||
|
||||
@ -647,7 +648,7 @@ msgstr "归档"
|
||||
msgid "archived"
|
||||
msgstr "已归档"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Archived"
|
||||
msgstr "已归档"
|
||||
|
||||
@ -1159,7 +1160,7 @@ msgid "Coder"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Collapse {0}"
|
||||
msgstr "折叠 {0}"
|
||||
|
||||
@ -1743,9 +1744,9 @@ msgstr "无法更新头像"
|
||||
msgid "Couldn't update messaging settings"
|
||||
msgstr "无法更新消息设置"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/bot-panel.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
#: src/pages/shell/sidebar-header.tsx
|
||||
msgid "Create"
|
||||
msgstr "创建"
|
||||
|
||||
@ -1902,14 +1903,14 @@ msgstr "默认范围"
|
||||
#: src/pages/KnowledgeSection.tsx
|
||||
#: src/pages/McpServersOverlay.tsx
|
||||
#: src/pages/RoutineEditor.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
#: src/pages/shell/dialogs.tsx
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#. placeholder {0}: bot.name
|
||||
#. placeholder {0}: group.name
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Delete {0}"
|
||||
msgstr "删除 {0}"
|
||||
|
||||
@ -2218,7 +2219,7 @@ msgid "Expand"
|
||||
msgstr "展开"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Expand {0}"
|
||||
msgstr "展开 {0}"
|
||||
|
||||
@ -2791,7 +2792,7 @@ msgstr ""
|
||||
msgid "Local Server Settings"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Log out"
|
||||
msgstr "退出登录"
|
||||
|
||||
@ -2974,7 +2975,7 @@ msgstr "最低"
|
||||
msgid "Minimize"
|
||||
msgstr "最小化"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Minimize bots"
|
||||
msgstr ""
|
||||
|
||||
@ -3396,7 +3397,7 @@ msgid "Open"
|
||||
msgstr "打开"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Open {0}"
|
||||
msgstr "打开 {0}"
|
||||
|
||||
@ -3416,7 +3417,7 @@ msgstr ""
|
||||
msgid "Open in full window"
|
||||
msgstr "在完整窗口中打开"
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Open navigation"
|
||||
msgstr "打开导航"
|
||||
|
||||
@ -3931,7 +3932,7 @@ msgstr "正在重置…"
|
||||
msgid "Restart to update"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/archived-section.tsx
|
||||
msgid "Restore"
|
||||
msgstr "恢复"
|
||||
|
||||
@ -4136,7 +4137,7 @@ msgid "Searching…"
|
||||
msgstr "正在搜索…"
|
||||
|
||||
#. placeholder {0}: group.title
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-group-header.tsx
|
||||
msgid "Select {0}"
|
||||
msgstr ""
|
||||
|
||||
@ -4202,6 +4203,7 @@ msgstr ""
|
||||
#: src/pages/MessagingSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Settings"
|
||||
msgstr "设置"
|
||||
|
||||
@ -4253,6 +4255,7 @@ msgid "Show all {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/sidebar-toggle-buttons.tsx
|
||||
msgid "Show bots"
|
||||
msgstr ""
|
||||
|
||||
@ -4832,7 +4835,7 @@ msgstr "上传"
|
||||
|
||||
#: src/pages/AccountSettingsOverlay.tsx
|
||||
#: src/pages/SettingsOverlay.tsx
|
||||
#: src/pages/Shell.tsx
|
||||
#: src/pages/shell/user-menu.tsx
|
||||
msgid "Usage"
|
||||
msgstr "用量"
|
||||
|
||||
|
||||
@ -84,25 +84,13 @@ import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Wordmark,
|
||||
} from "@rakazo/ui-web";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Bell,
|
||||
Calendar,
|
||||
ChevronDown,
|
||||
Folder,
|
||||
Gauge,
|
||||
Globe,
|
||||
GripVertical,
|
||||
LayoutGrid,
|
||||
Lock,
|
||||
LogOut,
|
||||
Maximize2,
|
||||
Menu,
|
||||
MoreHorizontal,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
PanelRight,
|
||||
Pencil,
|
||||
Plus,
|
||||
@ -140,7 +128,7 @@ import { TeachRecordingChrome, TeachStopButton } from "../components/teach/Teach
|
||||
import { useSidebarResize } from "../hooks/useSidebarResize";
|
||||
import { useWideLayout } from "../hooks/useWideLayout";
|
||||
import { readActivityMode, writeActivityMode } from "../lib/activity-mode";
|
||||
import { needsYouChipLabel, rosterActivitySubtitle, rosterStatusDot } from "../lib/agent-activity";
|
||||
import { rosterActivitySubtitle } from "../lib/agent-activity";
|
||||
import { agentPointerFeed } from "../lib/agent-pointer";
|
||||
import type { ArtifactTarget } from "../lib/artifact-open";
|
||||
import { attachmentMaxBytes, uploadAttachment } from "../lib/artifact-upload";
|
||||
@ -157,7 +145,6 @@ import {
|
||||
shouldNotifyBrowser,
|
||||
} from "../lib/browser-notifications";
|
||||
import { loadComputerScreen } from "../lib/computer-screen";
|
||||
import { desktopBridge } from "../lib/desktop";
|
||||
import { scheduleFocusPrompt } from "../lib/focus-prompt";
|
||||
import { formatBytes } from "../lib/format-bytes";
|
||||
import {
|
||||
@ -216,8 +203,9 @@ import {
|
||||
} from "./RoutineEditor";
|
||||
import type { SettingsSection } from "./SettingsOverlay";
|
||||
import { SpaceSearchResults } from "./SpaceSearch";
|
||||
import { ArchivedSection } from "./shell/archived-section";
|
||||
import { BotSettings, CreateBotForm } from "./shell/bot-panel";
|
||||
import { BotCreatePicker, ConversationBotPicker } from "./shell/bot-picker";
|
||||
import { ConversationBotPicker } from "./shell/bot-picker";
|
||||
import {
|
||||
collapsedSidebarSectionsStorageKey,
|
||||
readCollapsedSidebarSections,
|
||||
@ -243,15 +231,13 @@ import {
|
||||
} from "./shell/dialogs";
|
||||
import { MainConversationRow } from "./shell/main-conversation-row";
|
||||
import { cleanRosterPreview, formatRosterTime, stripSpaceTitlePrefix } from "./shell/roster-format";
|
||||
import {
|
||||
rosterNameClass,
|
||||
rosterPreviewClass,
|
||||
rosterRowClass,
|
||||
rosterTimeClass,
|
||||
rosterUnreadDotClass,
|
||||
} from "./shell/roster-row-style";
|
||||
import { rosterRowClass } from "./shell/roster-row-style";
|
||||
import { ProjectRosterRow, TeamRosterRow } from "./shell/roster-rows";
|
||||
import type { PendingAttachment } from "./shell/shell-types";
|
||||
import { FALLBACK_BOT_COLOR } from "./shell/shell-types";
|
||||
import { SidebarGroupHeader } from "./shell/sidebar-group-header";
|
||||
import { SidebarHeader } from "./shell/sidebar-header";
|
||||
import { SidebarToggleButtons } from "./shell/sidebar-toggle-buttons";
|
||||
import {
|
||||
applyThreadEvent,
|
||||
firstThreadRoute,
|
||||
@ -259,7 +245,7 @@ import {
|
||||
threadSnapshotSignal,
|
||||
} from "./shell/thread-state";
|
||||
import { Transcript } from "./shell/transcript";
|
||||
import { WindowChrome } from "./WindowChrome";
|
||||
import { UserMenu } from "./shell/user-menu";
|
||||
|
||||
const BotContextMenu = lazyWithRecovery("BotContextMenu", () =>
|
||||
import("./BotContextMenu").then((module) => ({ default: module.BotContextMenu })),
|
||||
@ -326,7 +312,7 @@ function voiceStatusRefreshTimeout(): Promise<never> {
|
||||
|
||||
export type NavPillar = "heute" | "team" | "projekte";
|
||||
|
||||
import { ProjectDragGrip, SortableGroup } from "../components/SortableGroup";
|
||||
import { SortableGroup } from "../components/SortableGroup";
|
||||
|
||||
export function ShellPage() {
|
||||
const { t } = useLingui();
|
||||
@ -3384,85 +3370,20 @@ export function ShellPage() {
|
||||
onPointerDown={startResizingLeft}
|
||||
className="absolute -right-2 top-0 bottom-0 w-4 cursor-col-resize z-50 hidden md:block"
|
||||
/>
|
||||
<div className="app-drag flex items-center justify-between px-4 lg:px-6 2xl:px-8 pb-3 pt-4">
|
||||
<WindowChrome />
|
||||
<div className="flex-1 px-1">
|
||||
<Wordmark />
|
||||
</div>
|
||||
<div className="relative flex items-center gap-2.5">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t`Activity`}
|
||||
aria-pressed={activityMode}
|
||||
title={t`Activity`}
|
||||
data-activity-mode={activityMode ? "on" : "off"}
|
||||
onClick={toggleActivityMode}
|
||||
className={`app-no-drag flex size-control-sm items-center justify-center rounded-full ${
|
||||
activityMode
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "rk-sidebar-muted hover:text-foreground/75"
|
||||
}`}
|
||||
>
|
||||
<Bell
|
||||
strokeWidth={1.8}
|
||||
fill={activityMode ? "currentColor" : "none"}
|
||||
aria-hidden="true"
|
||||
className="size-icon-md"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Popover open={createMenuOpen} onOpenChange={setCreateMenuOpen}>
|
||||
<PopoverTrigger
|
||||
className="app-no-drag text-[21px] rk-sidebar-muted hover:text-foreground/75"
|
||||
title={t`Create`}
|
||||
data-testid="create-menu-trigger"
|
||||
>
|
||||
+
|
||||
</PopoverTrigger>
|
||||
{/* Unmount with the state change so the panel it opens never coexists with the menu. */}
|
||||
{createMenuOpen ? (
|
||||
<PopoverContent
|
||||
align="end"
|
||||
className="app-no-drag w-auto gap-0 overflow-hidden p-0 data-closed:animate-none"
|
||||
>
|
||||
<BotCreatePicker
|
||||
bots={bots}
|
||||
onCreateBot={() => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
setPanel("create");
|
||||
}}
|
||||
onOpenBot={(id) => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
navigate(`/app/${id}`);
|
||||
}}
|
||||
onCreateGroup={() => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
setPanel("create-group");
|
||||
}}
|
||||
onCreateSpace={() => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
openNewSpaceDialog("space");
|
||||
}}
|
||||
onShowGroupInfo={() => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
setPickerInfoTopic("group");
|
||||
}}
|
||||
onShowSpaceInfo={() => {
|
||||
setCreateMenuOpen(false);
|
||||
setMobileSidebarOpen(false);
|
||||
setPickerInfoTopic("space");
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
) : null}
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
<SidebarHeader
|
||||
activityMode={activityMode}
|
||||
onToggleActivityMode={toggleActivityMode}
|
||||
createMenuOpen={createMenuOpen}
|
||||
onCreateMenuOpenChange={setCreateMenuOpen}
|
||||
onCloseMobileSidebar={() => setMobileSidebarOpen(false)}
|
||||
bots={bots}
|
||||
onCreateBot={() => setPanel("create")}
|
||||
onOpenBot={(id) => navigate(`/app/${id}`)}
|
||||
onCreateGroup={() => setPanel("create-group")}
|
||||
onCreateSpace={() => openNewSpaceDialog("space")}
|
||||
onShowGroupInfo={() => setPickerInfoTopic("group")}
|
||||
onShowSpaceInfo={() => setPickerInfoTopic("space")}
|
||||
/>
|
||||
|
||||
<InputGroup
|
||||
data-testid="sidebar-search"
|
||||
@ -3598,229 +3519,41 @@ export function ShellPage() {
|
||||
return (
|
||||
<div key={group.key} data-sidebar-group={group.key}>
|
||||
{group.title ? (
|
||||
<div className="flex items-center pt-3 pb-0.5">
|
||||
<button
|
||||
type="button"
|
||||
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg px-2.5 py-1 text-ui-label font-semibold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
||||
onClick={() => {
|
||||
if (group.emptySpaceId) {
|
||||
openSpaceChat(group.emptySpaceId, "/onboarding");
|
||||
return;
|
||||
}
|
||||
toggleSidebarSection(group.key);
|
||||
}}
|
||||
onContextMenu={
|
||||
group.sectionId
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
// Prefer section rename over delete-space when both apply;
|
||||
// the dedicated space-actions button still opens the space menu.
|
||||
const sections =
|
||||
group.spaceId === bootstrapMe?.spaceId
|
||||
? botSections
|
||||
: (spaces.find((space) => space.id === group.spaceId)
|
||||
?.botSections ?? []);
|
||||
const section = sections.find(
|
||||
(item) => item.id === group.sectionId,
|
||||
);
|
||||
if (!section) return;
|
||||
sectionMenuAnchor.current = event.currentTarget;
|
||||
setSectionMenu({
|
||||
section,
|
||||
spaceId: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: group.canDeleteSpace
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
setSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
aria-expanded={group.emptySpaceId ? undefined : !collapsed}
|
||||
aria-label={
|
||||
group.emptySpaceId
|
||||
? t`Open ${group.title}`
|
||||
: collapsed
|
||||
? t`Expand ${group.title}`
|
||||
: t`Collapse ${group.title}`
|
||||
}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-1.5 truncate">
|
||||
<Folder strokeWidth={2} aria-hidden="true" className="size-icon-sm" />
|
||||
<span className="truncate">{group.title}</span>
|
||||
</span>
|
||||
{group.emptySpaceId ? null : (
|
||||
<ChevronDown
|
||||
strokeWidth={1.8}
|
||||
className={`size-icon-md ${collapsed ? "-rotate-90 transition-transform" : "transition-transform"}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
{group.canDeleteSpace ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t`Actions for ${group.spaceName}`}
|
||||
onClick={(event) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
setSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: rect.left, y: rect.bottom },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MoreHorizontal aria-hidden="true" className="size-icon-md" />
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<SidebarGroupHeader
|
||||
variant="conversation"
|
||||
group={group}
|
||||
collapsed={collapsed}
|
||||
activeSpaceId={bootstrapMe?.spaceId}
|
||||
botSections={botSections}
|
||||
spaces={spaces}
|
||||
onOpenEmptySpace={(spaceId) => openSpaceChat(spaceId, "/onboarding")}
|
||||
onToggle={toggleSidebarSection}
|
||||
sectionMenuAnchor={sectionMenuAnchor}
|
||||
onSectionMenu={setSectionMenu}
|
||||
spaceMenuAnchor={spaceMenuAnchor}
|
||||
onSpaceMenu={setSpaceMenu}
|
||||
/>
|
||||
) : null}
|
||||
{!collapsed &&
|
||||
group.bots
|
||||
.slice(0, expandedTeamGroups[group.key] ? undefined : 4)
|
||||
.map((item) => (
|
||||
<button
|
||||
<TeamRosterRow
|
||||
key={`${item.kind}:${item.chat.id}`}
|
||||
type="button"
|
||||
draggable={item.kind === "bot"}
|
||||
data-roster-bot-id={item.kind === "bot" ? item.chat.id : undefined}
|
||||
aria-keyshortcuts={
|
||||
item.kind === "bot" ? "Alt+ArrowUp Alt+ArrowDown" : undefined
|
||||
}
|
||||
onDragStart={(event) => {
|
||||
if (item.kind !== "bot") return;
|
||||
setDraggedBotId(item.chat.id);
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("text/plain", item.chat.id);
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
if (
|
||||
item.kind === "bot" &&
|
||||
draggedBotId &&
|
||||
groupBotIds.includes(draggedBotId)
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
if (item.kind !== "bot" || !draggedBotId) return;
|
||||
event.preventDefault();
|
||||
reorderRosterBot(draggedBotId, item.chat.id, groupBotIds);
|
||||
setDraggedBotId(null);
|
||||
}}
|
||||
onDragEnd={() => setDraggedBotId(null)}
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
item.kind !== "bot" ||
|
||||
!event.altKey ||
|
||||
(event.key !== "ArrowUp" && event.key !== "ArrowDown")
|
||||
)
|
||||
return;
|
||||
const index = groupBotIds.indexOf(item.chat.id);
|
||||
const target =
|
||||
groupBotIds[index + (event.key === "ArrowUp" ? -1 : 1)];
|
||||
if (!target) return;
|
||||
event.preventDefault();
|
||||
reorderRosterBot(item.chat.id, target, groupBotIds);
|
||||
}}
|
||||
aria-label={
|
||||
item.kind === "bot"
|
||||
? t`${item.chat.name}, 1:1 conversation`
|
||||
: t`${item.chat.name}, group conversation`
|
||||
}
|
||||
onClick={() => {
|
||||
item={item}
|
||||
groupBotIds={groupBotIds}
|
||||
draggedBotId={draggedBotId}
|
||||
onDraggedBotIdChange={setDraggedBotId}
|
||||
onReorder={reorderRosterBot}
|
||||
selected={!inGroup && active?.id === item.chat.id}
|
||||
onOpen={() => {
|
||||
setActiveNavPillar("team");
|
||||
openSpaceChat(item.chat.spaceId, `/app/${item.chat.id}`);
|
||||
}}
|
||||
onContextMenu={(event) => {
|
||||
if (item.chat.spaceId !== bootstrapMe?.spaceId) return;
|
||||
event.preventDefault();
|
||||
botMenuAnchor.current = event.currentTarget;
|
||||
setBotMenu({
|
||||
kind: item.kind,
|
||||
id: item.chat.id,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}}
|
||||
className={`${rosterRowClass(
|
||||
!inGroup && active?.id === item.chat.id,
|
||||
)} cursor-grab active:cursor-grabbing`}
|
||||
style={{
|
||||
opacity: draggedBotId === item.chat.id ? 0.55 : 1,
|
||||
}}
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<BotAvatar
|
||||
color={item.chat.color}
|
||||
identity={item.chat.id}
|
||||
size={40}
|
||||
status={item.chat.status}
|
||||
/>
|
||||
<span
|
||||
className={`absolute -bottom-0.5 -right-0.5 size-2.5 rounded-full border-2 border-sidebar ${
|
||||
rosterStatusDot(item.chat.status) === "idle"
|
||||
? "bg-success"
|
||||
: "bg-warning animate-pulse"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center justify-between gap-1.5">
|
||||
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
|
||||
<span
|
||||
dir="auto"
|
||||
data-roster-bot-name=""
|
||||
className={`shrink-0 max-w-[170px] truncate text-ui-body tracking-tight ${rosterNameClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{item.chat.name}
|
||||
</span>
|
||||
{needsYouChipLabel(item.chat.status) ? (
|
||||
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-foreground dark:text-warning">
|
||||
{needsYouChipLabel(item.chat.status)}
|
||||
</span>
|
||||
) : item.chat.title ? (
|
||||
<span className="min-w-0 truncate rounded-full border border-border bg-muted px-2 py-0.5 text-ui-label font-medium text-muted-foreground">
|
||||
{item.chat.title}
|
||||
</span>
|
||||
) : null}
|
||||
{item.chat.unread ? (
|
||||
<span className="sr-only">
|
||||
<Trans> (unread)</Trans>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<span className={rosterTimeClass}>
|
||||
{formatRosterTime(item.chat.updatedAt)}
|
||||
</span>
|
||||
{item.chat.unread ? (
|
||||
<span aria-hidden="true" className={rosterUnreadDotClass} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
dir="auto"
|
||||
className={`mt-0.5 truncate text-ui-body leading-snug ${rosterPreviewClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{rosterActivitySubtitle(
|
||||
item.chat.status,
|
||||
cleanRosterPreview(item.chat.preview),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
activeSpaceId={bootstrapMe?.spaceId}
|
||||
menuAnchor={botMenuAnchor}
|
||||
onMenu={setBotMenu}
|
||||
/>
|
||||
))}
|
||||
{!collapsed && group.bots.length > 4 && (
|
||||
<button
|
||||
@ -3894,98 +3627,20 @@ export function ShellPage() {
|
||||
<SortableGroup key={`proj-${group.key}`} group={group}>
|
||||
<div data-sidebar-group={group.key}>
|
||||
{group.title ? (
|
||||
<div className="flex items-center pt-3 pb-0.5 relative">
|
||||
{/* Drag grip — positioned to the left of the title text */}
|
||||
<ProjectDragGrip className="absolute left-1 top-1/2 -translate-y-1/2 w-6 h-6 rounded flex items-center justify-center text-muted-foreground/30 hover:text-muted-foreground/70 active:text-muted-foreground transition-colors cursor-grab active:cursor-grabbing">
|
||||
<GripVertical className="size-icon-sm" strokeWidth={2} />
|
||||
</ProjectDragGrip>
|
||||
<button
|
||||
type="button"
|
||||
className="flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg pl-8 py-1 text-ui-label font-semibold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
||||
onClick={() => {
|
||||
if (group.emptySpaceId) {
|
||||
void startProject(group.emptySpaceId);
|
||||
return;
|
||||
}
|
||||
toggleSidebarSection(group.key);
|
||||
}}
|
||||
onContextMenu={
|
||||
group.sectionId
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
const sections =
|
||||
group.spaceId === bootstrapMe?.spaceId
|
||||
? botSections
|
||||
: (spaces.find(
|
||||
(space) => space.id === group.spaceId,
|
||||
)?.botSections ?? []);
|
||||
const section = sections.find(
|
||||
(item) => item.id === group.sectionId,
|
||||
);
|
||||
if (!section) return;
|
||||
sectionMenuAnchor.current = event.currentTarget;
|
||||
setSectionMenu({
|
||||
section,
|
||||
spaceId: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: group.canDeleteSpace
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
setSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
aria-expanded={group.emptySpaceId ? undefined : !collapsed}
|
||||
aria-label={
|
||||
group.emptySpaceId
|
||||
? t`Select ${group.title}`
|
||||
: collapsed
|
||||
? t`Expand ${group.title}`
|
||||
: t`Collapse ${group.title}`
|
||||
}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-1.5 truncate">
|
||||
{group.showLock ? (
|
||||
<Lock
|
||||
strokeWidth={2}
|
||||
aria-hidden="true"
|
||||
className="size-icon-sm"
|
||||
/>
|
||||
) : null}
|
||||
<span className="truncate">{group.title}</span>
|
||||
</span>
|
||||
{group.emptySpaceId ? null : (
|
||||
<ChevronDown
|
||||
strokeWidth={1.8}
|
||||
className={`size-icon-md ${collapsed ? "-rotate-90 transition-transform" : "transition-transform"}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
{group.canDeleteSpace ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t`Actions for ${group.spaceName}`}
|
||||
onClick={(event) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
setSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: rect.left, y: rect.bottom },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MoreHorizontal aria-hidden="true" className="size-icon-md" />
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
<SidebarGroupHeader
|
||||
variant="project"
|
||||
group={group}
|
||||
collapsed={collapsed}
|
||||
activeSpaceId={bootstrapMe?.spaceId}
|
||||
botSections={botSections}
|
||||
spaces={spaces}
|
||||
onOpenEmptySpace={(spaceId) => void startProject(spaceId)}
|
||||
onToggle={toggleSidebarSection}
|
||||
sectionMenuAnchor={sectionMenuAnchor}
|
||||
onSectionMenu={setSectionMenu}
|
||||
spaceMenuAnchor={spaceMenuAnchor}
|
||||
onSpaceMenu={setSpaceMenu}
|
||||
/>
|
||||
) : null}
|
||||
{!collapsed && group.startSpaceId ? (
|
||||
<div className="px-3 pb-2">
|
||||
@ -4003,17 +3658,15 @@ export function ShellPage() {
|
||||
) : null}
|
||||
{!collapsed &&
|
||||
group.bots.map((item) => (
|
||||
<button
|
||||
<ProjectRosterRow
|
||||
key={`${item.kind}:${item.chat.id}`}
|
||||
type="button"
|
||||
aria-label={
|
||||
item={item}
|
||||
selected={
|
||||
item.kind === "bot"
|
||||
? t`${item.chat.name}, 1:1 conversation`
|
||||
: item.chat.kind === "main"
|
||||
? t`${item.chat.name}, main conversation`
|
||||
: t`${item.chat.name}, group conversation`
|
||||
? !inGroup && active?.id === item.chat.id
|
||||
: inGroup && activeGroup?.id === item.chat.id
|
||||
}
|
||||
onClick={() => {
|
||||
onOpen={() => {
|
||||
setActiveNavPillar("projekte");
|
||||
openSpaceChat(
|
||||
item.chat.spaceId,
|
||||
@ -4022,71 +3675,10 @@ export function ShellPage() {
|
||||
: `/app/g/${item.chat.id}`,
|
||||
);
|
||||
}}
|
||||
onContextMenu={(event) => {
|
||||
// A project's main conversation has no chat actions.
|
||||
if (
|
||||
item.chat.spaceId !== bootstrapMe?.spaceId ||
|
||||
item.chat.kind === "main"
|
||||
)
|
||||
return;
|
||||
event.preventDefault();
|
||||
botMenuAnchor.current = event.currentTarget;
|
||||
setBotMenu({
|
||||
kind: item.kind,
|
||||
id: item.chat.id,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}}
|
||||
className={rosterRowClass(
|
||||
item.kind === "bot"
|
||||
? !inGroup && active?.id === item.chat.id
|
||||
: inGroup && activeGroup?.id === item.chat.id,
|
||||
)}
|
||||
>
|
||||
{item.kind === "bot" ? (
|
||||
<BotAvatar
|
||||
color={item.chat.color}
|
||||
identity={item.chat.id}
|
||||
size={40}
|
||||
status={item.chat.status}
|
||||
/>
|
||||
) : (
|
||||
<GroupAvatar members={(item.chat as any).members} size={40} />
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center justify-between gap-1.5">
|
||||
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
|
||||
<span
|
||||
dir="auto"
|
||||
className={`shrink-0 max-w-[170px] truncate text-ui-body tracking-tight ${rosterNameClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{item.chat.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<span className={rosterTimeClass}>
|
||||
{formatRosterTime(item.chat.updatedAt)}
|
||||
</span>
|
||||
{item.chat.unread ? (
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={rosterUnreadDotClass}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
dir="auto"
|
||||
className={`mt-0.5 truncate text-ui-body leading-snug ${rosterPreviewClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{cleanRosterPreview(localizedPreview(item.chat))}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
activeSpaceId={bootstrapMe?.spaceId}
|
||||
menuAnchor={botMenuAnchor}
|
||||
onMenu={setBotMenu}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</SortableGroup>
|
||||
@ -4099,154 +3691,46 @@ export function ShellPage() {
|
||||
</>
|
||||
)}
|
||||
{archivedBots.length + archivedGroups.length > 0 && !showSpaceSearch ? (
|
||||
<div className="mt-2 border-t border-border pt-2">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={archivedOpen}
|
||||
onClick={() => setArchivedOpen((open) => !open)}
|
||||
className="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-ui-body rk-sidebar-muted hover:bg-sidebar-accent"
|
||||
>
|
||||
<span>
|
||||
<Trans>Archived</Trans>
|
||||
</span>
|
||||
<span>{archivedBots.length + archivedGroups.length}</span>
|
||||
</button>
|
||||
{archivedOpen ? (
|
||||
<>
|
||||
{archivedBots.map((bot) => (
|
||||
<div key={bot.id} className="flex items-center gap-2 rounded-lg px-2.5 py-2">
|
||||
<BotAvatar
|
||||
color={bot.color}
|
||||
identity={bot.id}
|
||||
size={28}
|
||||
status={bot.status}
|
||||
/>
|
||||
<span
|
||||
className="min-w-0 flex-1 truncate text-ui-body text-foreground/75"
|
||||
dir="auto"
|
||||
>
|
||||
{bot.name}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={() =>
|
||||
void rpc.bots.restore({ botId: bot.id }).then(() => refreshBots(true))
|
||||
}
|
||||
>
|
||||
<Trans>Restore</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-destructive hover:text-destructive"
|
||||
aria-label={t`Delete ${bot.name}`}
|
||||
onClick={() => setDeleteTarget(bot)}
|
||||
>
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{archivedGroups.map((group) => (
|
||||
<div key={group.id} className="flex items-center gap-2 rounded-lg px-2.5 py-2">
|
||||
<GroupAvatar members={group.members} size={28} />
|
||||
<span
|
||||
className="min-w-0 flex-1 truncate text-ui-body text-foreground/75"
|
||||
dir="auto"
|
||||
>
|
||||
{group.name}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
onClick={() =>
|
||||
void rpc.groups
|
||||
.restore({ groupId: group.id })
|
||||
.then(() => refreshBots(true))
|
||||
}
|
||||
>
|
||||
<Trans>Restore</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-destructive hover:text-destructive"
|
||||
aria-label={t`Delete ${group.name}`}
|
||||
onClick={() => setDeleteGroupTarget(group)}
|
||||
>
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<ArchivedSection
|
||||
bots={archivedBots}
|
||||
groups={archivedGroups}
|
||||
open={archivedOpen}
|
||||
onToggle={() => setArchivedOpen((open) => !open)}
|
||||
onRestoreBot={(bot) =>
|
||||
void rpc.bots.restore({ botId: bot.id }).then(() => refreshBots(true))
|
||||
}
|
||||
onRestoreGroup={(group) =>
|
||||
void rpc.groups.restore({ groupId: group.id }).then(() => refreshBots(true))
|
||||
}
|
||||
onDeleteBot={setDeleteTarget}
|
||||
onDeleteGroup={setDeleteGroupTarget}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<PopoverTrigger
|
||||
data-testid="user-menu-trigger"
|
||||
className="flex items-center gap-[11px] px-[18px] py-3.5"
|
||||
>
|
||||
<span className="grid size-control-lg place-items-center rounded-full bg-accent text-ui-body-sm text-foreground/75">
|
||||
{initials}
|
||||
</span>
|
||||
<span className="text-[14.5px] text-foreground/90">{userName}</span>
|
||||
</PopoverTrigger>
|
||||
{menuOpen ? (
|
||||
<PopoverContent
|
||||
side="top"
|
||||
align="start"
|
||||
className="w-[calc(316px-1.5rem)] max-w-[calc(100vw-3rem)] gap-0 p-1 data-closed:animate-none"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start font-normal"
|
||||
aria-label={t`Settings`}
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
openSettings("general");
|
||||
}}
|
||||
>
|
||||
<Settings className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Settings</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start font-normal"
|
||||
aria-label={t`Usage`}
|
||||
onClick={() => {
|
||||
setMenuOpen(false);
|
||||
void rpc.usage
|
||||
.summary()
|
||||
.then(setUsage)
|
||||
.catch(() => undefined);
|
||||
openSettings("usage");
|
||||
}}
|
||||
>
|
||||
<Gauge className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Usage</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start font-normal"
|
||||
onClick={() =>
|
||||
// This browser stops receiving the account's pushes once it signs out.
|
||||
void disableWebPush()
|
||||
.catch(() => undefined)
|
||||
.then(() => authClient.signOut())
|
||||
.then(() => {
|
||||
clearSpaceSelection();
|
||||
navigate("/");
|
||||
})
|
||||
}
|
||||
>
|
||||
<LogOut className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Log out</Trans>
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
) : null}
|
||||
</Popover>
|
||||
<UserMenu
|
||||
open={menuOpen}
|
||||
onOpenChange={setMenuOpen}
|
||||
initials={initials}
|
||||
userName={userName}
|
||||
onOpenSettings={() => openSettings("general")}
|
||||
onOpenUsage={() => {
|
||||
void rpc.usage
|
||||
.summary()
|
||||
.then(setUsage)
|
||||
.catch(() => undefined);
|
||||
openSettings("usage");
|
||||
}}
|
||||
onLogOut={() =>
|
||||
// This browser stops receiving the account's pushes once it signs out.
|
||||
void disableWebPush()
|
||||
.catch(() => undefined)
|
||||
.then(() => authClient.signOut())
|
||||
.then(() => {
|
||||
clearSpaceSelection();
|
||||
navigate("/");
|
||||
})
|
||||
}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<button
|
||||
@ -4301,29 +3785,11 @@ export function ShellPage() {
|
||||
<div className="flex h-full flex-1 flex-col overflow-hidden">
|
||||
<div className="app-drag flex items-center border-b border-sidebar-border px-3 py-[17px] md:px-[22px] lg:px-8 2xl:px-12">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
{botsSidebarCollapsed && desktopBridge() ? <WindowChrome /> : null}
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t`Open navigation`}
|
||||
onClick={() => setMobileSidebarOpen(true)}
|
||||
className="app-no-drag grid size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:hidden"
|
||||
>
|
||||
<Menu strokeWidth={1.7} className="size-icon-lg" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="toggle-bots-sidebar"
|
||||
aria-label={botsSidebarCollapsed ? t`Show bots` : t`Minimize bots`}
|
||||
title={botsSidebarCollapsed ? t`Show bots` : t`Minimize bots`}
|
||||
onClick={() => setBotsSidebarCollapsedPref(!botsSidebarCollapsed)}
|
||||
className="app-no-drag hidden size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:grid"
|
||||
>
|
||||
{botsSidebarCollapsed ? (
|
||||
<PanelLeftOpen strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
) : (
|
||||
<PanelLeftClose strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
)}
|
||||
</button>
|
||||
<SidebarToggleButtons
|
||||
collapsed={botsSidebarCollapsed}
|
||||
onCollapsedChange={setBotsSidebarCollapsedPref}
|
||||
onOpenNavigation={() => setMobileSidebarOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4362,30 +3828,11 @@ export function ShellPage() {
|
||||
<>
|
||||
<div className="app-drag flex items-center justify-between border-b border-sidebar-border px-3.5 py-2 md:px-6 md:py-3 lg:px-8 lg:py-4 2xl:px-12 2xl:py-6 bg-background/80 backdrop-blur-md">
|
||||
<div className="flex min-w-0 items-center gap-2.5">
|
||||
{/* Collapsed bots sidebar: this header is the leading edge for window chrome. */}
|
||||
{botsSidebarCollapsed && desktopBridge() ? <WindowChrome /> : null}
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t`Open navigation`}
|
||||
onClick={() => setMobileSidebarOpen(true)}
|
||||
className="app-no-drag grid size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:hidden"
|
||||
>
|
||||
<Menu strokeWidth={1.7} className="size-icon-lg" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="toggle-bots-sidebar"
|
||||
aria-label={botsSidebarCollapsed ? t`Show bots` : t`Minimize bots`}
|
||||
title={botsSidebarCollapsed ? t`Show bots` : t`Minimize bots`}
|
||||
onClick={() => setBotsSidebarCollapsedPref(!botsSidebarCollapsed)}
|
||||
className="app-no-drag hidden size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:grid"
|
||||
>
|
||||
{botsSidebarCollapsed ? (
|
||||
<PanelLeftOpen strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
) : (
|
||||
<PanelLeftClose strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
)}
|
||||
</button>
|
||||
<SidebarToggleButtons
|
||||
collapsed={botsSidebarCollapsed}
|
||||
onCollapsedChange={setBotsSidebarCollapsedPref}
|
||||
onOpenNavigation={() => setMobileSidebarOpen(true)}
|
||||
/>
|
||||
{inGroup && projectSessionKey ? (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
85
apps/web/src/pages/shell/archived-section.tsx
Normal file
85
apps/web/src/pages/shell/archived-section.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import type { Bot, Group } from "@rakazo/contracts";
|
||||
import { BotAvatar, Button, GroupAvatar } from "@rakazo/ui-web";
|
||||
|
||||
/** Collapsible list of archived bots and groups at the foot of the sidebar. */
|
||||
export function ArchivedSection({
|
||||
bots,
|
||||
groups,
|
||||
open,
|
||||
onToggle,
|
||||
onRestoreBot,
|
||||
onRestoreGroup,
|
||||
onDeleteBot,
|
||||
onDeleteGroup,
|
||||
}: {
|
||||
bots: Bot[];
|
||||
groups: Group[];
|
||||
open: boolean;
|
||||
onToggle: () => void;
|
||||
onRestoreBot: (bot: Bot) => void;
|
||||
onRestoreGroup: (group: Group) => void;
|
||||
onDeleteBot: (bot: Bot) => void;
|
||||
onDeleteGroup: (group: Group) => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<div className="mt-2 border-t border-border pt-2">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={open}
|
||||
onClick={onToggle}
|
||||
className="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-ui-body rk-sidebar-muted hover:bg-sidebar-accent"
|
||||
>
|
||||
<span>
|
||||
<Trans>Archived</Trans>
|
||||
</span>
|
||||
<span>{bots.length + groups.length}</span>
|
||||
</button>
|
||||
{open ? (
|
||||
<>
|
||||
{bots.map((bot) => (
|
||||
<div key={bot.id} className="flex items-center gap-2 rounded-lg px-2.5 py-2">
|
||||
<BotAvatar color={bot.color} identity={bot.id} size={28} status={bot.status} />
|
||||
<span className="min-w-0 flex-1 truncate text-ui-body text-foreground/75" dir="auto">
|
||||
{bot.name}
|
||||
</span>
|
||||
<Button variant="ghost" size="xs" onClick={() => onRestoreBot(bot)}>
|
||||
<Trans>Restore</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-destructive hover:text-destructive"
|
||||
aria-label={t`Delete ${bot.name}`}
|
||||
onClick={() => onDeleteBot(bot)}
|
||||
>
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{groups.map((group) => (
|
||||
<div key={group.id} className="flex items-center gap-2 rounded-lg px-2.5 py-2">
|
||||
<GroupAvatar members={group.members} size={28} />
|
||||
<span className="min-w-0 flex-1 truncate text-ui-body text-foreground/75" dir="auto">
|
||||
{group.name}
|
||||
</span>
|
||||
<Button variant="ghost" size="xs" onClick={() => onRestoreGroup(group)}>
|
||||
<Trans>Restore</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
className="text-destructive hover:text-destructive"
|
||||
aria-label={t`Delete ${group.name}`}
|
||||
onClick={() => onDeleteGroup(group)}
|
||||
>
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
250
apps/web/src/pages/shell/roster-rows.tsx
Normal file
250
apps/web/src/pages/shell/roster-rows.tsx
Normal file
@ -0,0 +1,250 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import type { SpaceBot, SpaceGroup } from "@rakazo/contracts";
|
||||
import { BotAvatar, GroupAvatar } from "@rakazo/ui-web";
|
||||
import type { RefObject } from "react";
|
||||
import {
|
||||
needsYouChipLabel,
|
||||
rosterActivitySubtitle,
|
||||
rosterStatusDot,
|
||||
} from "../../lib/agent-activity";
|
||||
import { localizedPreview } from "../../lib/onboarding-copy";
|
||||
import type { ContextMenuPosition } from "../BotContextMenu";
|
||||
import { cleanRosterPreview, formatRosterTime } from "./roster-format";
|
||||
import {
|
||||
rosterNameClass,
|
||||
rosterPreviewClass,
|
||||
rosterRowClass,
|
||||
rosterTimeClass,
|
||||
rosterUnreadDotClass,
|
||||
} from "./roster-row-style";
|
||||
|
||||
export type SidebarChatItem = { kind: "bot"; chat: SpaceBot } | { kind: "group"; chat: SpaceGroup };
|
||||
|
||||
export type BotMenuRequest = {
|
||||
kind: "bot" | "group";
|
||||
id: string;
|
||||
position: ContextMenuPosition;
|
||||
};
|
||||
|
||||
/** A bot in the conversations section: reorderable by drag or Alt+Arrow within its group. */
|
||||
export function TeamRosterRow({
|
||||
item,
|
||||
groupBotIds,
|
||||
draggedBotId,
|
||||
onDraggedBotIdChange,
|
||||
onReorder,
|
||||
selected,
|
||||
onOpen,
|
||||
activeSpaceId,
|
||||
menuAnchor,
|
||||
onMenu,
|
||||
}: {
|
||||
item: Extract<SidebarChatItem, { kind: "bot" }>;
|
||||
groupBotIds: string[];
|
||||
draggedBotId: string | null;
|
||||
onDraggedBotIdChange: (botId: string | null) => void;
|
||||
onReorder: (sourceId: string, targetId: string, groupBotIds: string[]) => void;
|
||||
selected: boolean;
|
||||
onOpen: () => void;
|
||||
activeSpaceId: string | undefined;
|
||||
menuAnchor: RefObject<HTMLElement | null>;
|
||||
onMenu: (menu: BotMenuRequest) => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
draggable={item.kind === "bot"}
|
||||
data-roster-bot-id={item.kind === "bot" ? item.chat.id : undefined}
|
||||
aria-keyshortcuts={item.kind === "bot" ? "Alt+ArrowUp Alt+ArrowDown" : undefined}
|
||||
onDragStart={(event) => {
|
||||
if (item.kind !== "bot") return;
|
||||
onDraggedBotIdChange(item.chat.id);
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("text/plain", item.chat.id);
|
||||
}}
|
||||
onDragOver={(event) => {
|
||||
if (item.kind === "bot" && draggedBotId && groupBotIds.includes(draggedBotId)) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
}
|
||||
}}
|
||||
onDrop={(event) => {
|
||||
if (item.kind !== "bot" || !draggedBotId) return;
|
||||
event.preventDefault();
|
||||
onReorder(draggedBotId, item.chat.id, groupBotIds);
|
||||
onDraggedBotIdChange(null);
|
||||
}}
|
||||
onDragEnd={() => onDraggedBotIdChange(null)}
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
item.kind !== "bot" ||
|
||||
!event.altKey ||
|
||||
(event.key !== "ArrowUp" && event.key !== "ArrowDown")
|
||||
)
|
||||
return;
|
||||
const index = groupBotIds.indexOf(item.chat.id);
|
||||
const target = groupBotIds[index + (event.key === "ArrowUp" ? -1 : 1)];
|
||||
if (!target) return;
|
||||
event.preventDefault();
|
||||
onReorder(item.chat.id, target, groupBotIds);
|
||||
}}
|
||||
aria-label={
|
||||
item.kind === "bot"
|
||||
? t`${item.chat.name}, 1:1 conversation`
|
||||
: t`${item.chat.name}, group conversation`
|
||||
}
|
||||
onClick={onOpen}
|
||||
onContextMenu={(event) => {
|
||||
if (item.chat.spaceId !== activeSpaceId) return;
|
||||
event.preventDefault();
|
||||
menuAnchor.current = event.currentTarget;
|
||||
onMenu({
|
||||
kind: item.kind,
|
||||
id: item.chat.id,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}}
|
||||
className={`${rosterRowClass(selected)} cursor-grab active:cursor-grabbing`}
|
||||
style={{
|
||||
opacity: draggedBotId === item.chat.id ? 0.55 : 1,
|
||||
}}
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<BotAvatar
|
||||
color={item.chat.color}
|
||||
identity={item.chat.id}
|
||||
size={40}
|
||||
status={item.chat.status}
|
||||
/>
|
||||
<span
|
||||
className={`absolute -bottom-0.5 -right-0.5 size-2.5 rounded-full border-2 border-sidebar ${
|
||||
rosterStatusDot(item.chat.status) === "idle" ? "bg-success" : "bg-warning animate-pulse"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center justify-between gap-1.5">
|
||||
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
|
||||
<span
|
||||
dir="auto"
|
||||
data-roster-bot-name=""
|
||||
className={`shrink-0 max-w-[170px] truncate text-ui-body tracking-tight ${rosterNameClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{item.chat.name}
|
||||
</span>
|
||||
{needsYouChipLabel(item.chat.status) ? (
|
||||
<span className="shrink-0 rounded-full border border-warning/30 bg-warning/10 px-2 py-0.5 text-ui-label font-medium text-foreground dark:text-warning">
|
||||
{needsYouChipLabel(item.chat.status)}
|
||||
</span>
|
||||
) : item.chat.title ? (
|
||||
<span className="min-w-0 truncate rounded-full border border-border bg-muted px-2 py-0.5 text-ui-label font-medium text-muted-foreground">
|
||||
{item.chat.title}
|
||||
</span>
|
||||
) : null}
|
||||
{item.chat.unread ? (
|
||||
<span className="sr-only">
|
||||
<Trans> (unread)</Trans>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<span className={rosterTimeClass}>{formatRosterTime(item.chat.updatedAt)}</span>
|
||||
{item.chat.unread ? <span aria-hidden="true" className={rosterUnreadDotClass} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
dir="auto"
|
||||
className={`mt-0.5 truncate text-ui-body leading-snug ${rosterPreviewClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{rosterActivitySubtitle(item.chat.status, cleanRosterPreview(item.chat.preview))}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
/** A conversation in the projects section; a project's main conversation has no chat actions. */
|
||||
export function ProjectRosterRow({
|
||||
item,
|
||||
selected,
|
||||
onOpen,
|
||||
activeSpaceId,
|
||||
menuAnchor,
|
||||
onMenu,
|
||||
}: {
|
||||
item: SidebarChatItem;
|
||||
selected: boolean;
|
||||
onOpen: () => void;
|
||||
activeSpaceId: string | undefined;
|
||||
menuAnchor: RefObject<HTMLElement | null>;
|
||||
onMenu: (menu: BotMenuRequest) => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={
|
||||
item.kind === "bot"
|
||||
? t`${item.chat.name}, 1:1 conversation`
|
||||
: item.chat.kind === "main"
|
||||
? t`${item.chat.name}, main conversation`
|
||||
: t`${item.chat.name}, group conversation`
|
||||
}
|
||||
onClick={onOpen}
|
||||
onContextMenu={(event) => {
|
||||
// A project's main conversation has no chat actions.
|
||||
if (item.chat.spaceId !== activeSpaceId || item.chat.kind === "main") return;
|
||||
event.preventDefault();
|
||||
menuAnchor.current = event.currentTarget;
|
||||
onMenu({
|
||||
kind: item.kind,
|
||||
id: item.chat.id,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}}
|
||||
className={rosterRowClass(selected)}
|
||||
>
|
||||
{item.kind === "bot" ? (
|
||||
<BotAvatar
|
||||
color={item.chat.color}
|
||||
identity={item.chat.id}
|
||||
size={40}
|
||||
status={item.chat.status}
|
||||
/>
|
||||
) : (
|
||||
<GroupAvatar members={(item.chat as any).members} size={40} />
|
||||
)}
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center justify-between gap-1.5">
|
||||
<div className="flex min-w-0 items-center gap-2 overflow-hidden">
|
||||
<span
|
||||
dir="auto"
|
||||
className={`shrink-0 max-w-[170px] truncate text-ui-body tracking-tight ${rosterNameClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{item.chat.name}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
<span className={rosterTimeClass}>{formatRosterTime(item.chat.updatedAt)}</span>
|
||||
{item.chat.unread ? <span aria-hidden="true" className={rosterUnreadDotClass} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
dir="auto"
|
||||
className={`mt-0.5 truncate text-ui-body leading-snug ${rosterPreviewClass(
|
||||
item.chat.unread,
|
||||
)}`}
|
||||
>
|
||||
{cleanRosterPreview(localizedPreview(item.chat))}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
161
apps/web/src/pages/shell/sidebar-group-header.tsx
Normal file
161
apps/web/src/pages/shell/sidebar-group-header.tsx
Normal file
@ -0,0 +1,161 @@
|
||||
import { useLingui } from "@lingui/react/macro";
|
||||
import type { BotSection, Space } from "@rakazo/contracts";
|
||||
import { Button } from "@rakazo/ui-web";
|
||||
import { ChevronDown, Folder, GripVertical, Lock, MoreHorizontal } from "lucide-react";
|
||||
import type { RefObject } from "react";
|
||||
import { ProjectDragGrip } from "../../components/SortableGroup";
|
||||
import type { ContextMenuPosition } from "../BotContextMenu";
|
||||
|
||||
export type SidebarGroupHeaderGroup = {
|
||||
key: string;
|
||||
title: string | null;
|
||||
sectionId: string | null;
|
||||
spaceId: string;
|
||||
spaceName: string;
|
||||
canDeleteSpace: boolean;
|
||||
emptySpaceId: string | undefined;
|
||||
showLock: boolean;
|
||||
};
|
||||
|
||||
export type SpaceMenuRequest = { id: string; position: ContextMenuPosition };
|
||||
|
||||
export type SectionMenuRequest = {
|
||||
section: BotSection;
|
||||
spaceId: string;
|
||||
position: ContextMenuPosition;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collapsible title of a sidebar group. Project groups add a drag grip, show a lock for other
|
||||
* spaces and select an empty space instead of opening it.
|
||||
*/
|
||||
export function SidebarGroupHeader({
|
||||
variant,
|
||||
group,
|
||||
collapsed,
|
||||
activeSpaceId,
|
||||
botSections,
|
||||
spaces,
|
||||
onOpenEmptySpace,
|
||||
onToggle,
|
||||
sectionMenuAnchor,
|
||||
onSectionMenu,
|
||||
spaceMenuAnchor,
|
||||
onSpaceMenu,
|
||||
}: {
|
||||
variant: "conversation" | "project";
|
||||
group: SidebarGroupHeaderGroup;
|
||||
collapsed: boolean;
|
||||
activeSpaceId: string | undefined;
|
||||
botSections: BotSection[];
|
||||
spaces: Space[];
|
||||
onOpenEmptySpace: (spaceId: string) => void;
|
||||
onToggle: (key: string) => void;
|
||||
sectionMenuAnchor: RefObject<HTMLElement | null>;
|
||||
onSectionMenu: (menu: SectionMenuRequest) => void;
|
||||
spaceMenuAnchor: RefObject<HTMLElement | null>;
|
||||
onSpaceMenu: (menu: SpaceMenuRequest) => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
const project = variant === "project";
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
project ? "flex items-center pt-3 pb-0.5 relative" : "flex items-center pt-3 pb-0.5"
|
||||
}
|
||||
>
|
||||
{project ? (
|
||||
// Drag grip — positioned to the left of the title text
|
||||
<ProjectDragGrip className="absolute left-1 top-1/2 -translate-y-1/2 w-6 h-6 rounded flex items-center justify-center text-muted-foreground/30 hover:text-muted-foreground/70 active:text-muted-foreground transition-colors cursor-grab active:cursor-grabbing">
|
||||
<GripVertical className="size-icon-sm" strokeWidth={2} />
|
||||
</ProjectDragGrip>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className={`flex min-w-0 flex-1 items-center justify-between gap-2 rounded-lg ${project ? "pl-8" : "px-2.5"} py-1 text-ui-label font-semibold tracking-wider uppercase text-foreground/65 dark:text-muted-foreground/80 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring`}
|
||||
onClick={() => {
|
||||
if (group.emptySpaceId) {
|
||||
onOpenEmptySpace(group.emptySpaceId);
|
||||
return;
|
||||
}
|
||||
onToggle(group.key);
|
||||
}}
|
||||
onContextMenu={
|
||||
group.sectionId
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
// Prefer section rename over delete-space when both apply;
|
||||
// the dedicated space-actions button still opens the space menu.
|
||||
const sections =
|
||||
group.spaceId === activeSpaceId
|
||||
? botSections
|
||||
: (spaces.find((space) => space.id === group.spaceId)?.botSections ?? []);
|
||||
const section = sections.find((item) => item.id === group.sectionId);
|
||||
if (!section) return;
|
||||
sectionMenuAnchor.current = event.currentTarget;
|
||||
onSectionMenu({
|
||||
section,
|
||||
spaceId: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: group.canDeleteSpace
|
||||
? (event) => {
|
||||
event.preventDefault();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
onSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: event.clientX, y: event.clientY },
|
||||
});
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
aria-expanded={group.emptySpaceId ? undefined : !collapsed}
|
||||
aria-label={
|
||||
group.emptySpaceId
|
||||
? project
|
||||
? t`Select ${group.title}`
|
||||
: t`Open ${group.title}`
|
||||
: collapsed
|
||||
? t`Expand ${group.title}`
|
||||
: t`Collapse ${group.title}`
|
||||
}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-1.5 truncate">
|
||||
{project ? (
|
||||
group.showLock ? (
|
||||
<Lock strokeWidth={2} aria-hidden="true" className="size-icon-sm" />
|
||||
) : null
|
||||
) : (
|
||||
<Folder strokeWidth={2} aria-hidden="true" className="size-icon-sm" />
|
||||
)}
|
||||
<span className="truncate">{group.title}</span>
|
||||
</span>
|
||||
{group.emptySpaceId ? null : (
|
||||
<ChevronDown
|
||||
strokeWidth={1.8}
|
||||
className={`size-icon-md ${collapsed ? "-rotate-90 transition-transform" : "transition-transform"}`}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
{group.canDeleteSpace ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t`Actions for ${group.spaceName}`}
|
||||
onClick={(event) => {
|
||||
const rect = event.currentTarget.getBoundingClientRect();
|
||||
spaceMenuAnchor.current = event.currentTarget;
|
||||
onSpaceMenu({
|
||||
id: group.spaceId,
|
||||
position: { x: rect.left, y: rect.bottom },
|
||||
});
|
||||
}}
|
||||
>
|
||||
<MoreHorizontal aria-hidden="true" className="size-icon-md" />
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
118
apps/web/src/pages/shell/sidebar-header.tsx
Normal file
118
apps/web/src/pages/shell/sidebar-header.tsx
Normal file
@ -0,0 +1,118 @@
|
||||
import { useLingui } from "@lingui/react/macro";
|
||||
import type { Bot } from "@rakazo/contracts";
|
||||
import { Popover, PopoverContent, PopoverTrigger, Wordmark } from "@rakazo/ui-web";
|
||||
import { Bell } from "lucide-react";
|
||||
import { WindowChrome } from "../WindowChrome";
|
||||
import { BotCreatePicker } from "./bot-picker";
|
||||
|
||||
/** Window chrome, wordmark, the activity toggle and the create menu atop the sidebar. */
|
||||
export function SidebarHeader({
|
||||
activityMode,
|
||||
onToggleActivityMode,
|
||||
createMenuOpen,
|
||||
onCreateMenuOpenChange,
|
||||
onCloseMobileSidebar,
|
||||
bots,
|
||||
onCreateBot,
|
||||
onOpenBot,
|
||||
onCreateGroup,
|
||||
onCreateSpace,
|
||||
onShowGroupInfo,
|
||||
onShowSpaceInfo,
|
||||
}: {
|
||||
activityMode: boolean;
|
||||
onToggleActivityMode: () => void;
|
||||
createMenuOpen: boolean;
|
||||
onCreateMenuOpenChange: (open: boolean) => void;
|
||||
onCloseMobileSidebar: () => void;
|
||||
bots: Bot[];
|
||||
onCreateBot: () => void;
|
||||
onOpenBot: (id: string) => void;
|
||||
onCreateGroup: () => void;
|
||||
onCreateSpace: () => void;
|
||||
onShowGroupInfo: () => void;
|
||||
onShowSpaceInfo: () => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<div className="app-drag flex items-center justify-between px-4 lg:px-6 2xl:px-8 pb-3 pt-4">
|
||||
<WindowChrome />
|
||||
<div className="flex-1 px-1">
|
||||
<Wordmark />
|
||||
</div>
|
||||
<div className="relative flex items-center gap-2.5">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t`Activity`}
|
||||
aria-pressed={activityMode}
|
||||
title={t`Activity`}
|
||||
data-activity-mode={activityMode ? "on" : "off"}
|
||||
onClick={onToggleActivityMode}
|
||||
className={`app-no-drag flex size-control-sm items-center justify-center rounded-full ${
|
||||
activityMode
|
||||
? "bg-primary text-primary-foreground"
|
||||
: "rk-sidebar-muted hover:text-foreground/75"
|
||||
}`}
|
||||
>
|
||||
<Bell
|
||||
strokeWidth={1.8}
|
||||
fill={activityMode ? "currentColor" : "none"}
|
||||
aria-hidden="true"
|
||||
className="size-icon-md"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<Popover open={createMenuOpen} onOpenChange={onCreateMenuOpenChange}>
|
||||
<PopoverTrigger
|
||||
className="app-no-drag text-[21px] rk-sidebar-muted hover:text-foreground/75"
|
||||
title={t`Create`}
|
||||
data-testid="create-menu-trigger"
|
||||
>
|
||||
+
|
||||
</PopoverTrigger>
|
||||
{/* Unmount with the state change so the panel it opens never coexists with the menu. */}
|
||||
{createMenuOpen ? (
|
||||
<PopoverContent
|
||||
align="end"
|
||||
className="app-no-drag w-auto gap-0 overflow-hidden p-0 data-closed:animate-none"
|
||||
>
|
||||
<BotCreatePicker
|
||||
bots={bots}
|
||||
onCreateBot={() => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onCreateBot();
|
||||
}}
|
||||
onOpenBot={(id) => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onOpenBot(id);
|
||||
}}
|
||||
onCreateGroup={() => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onCreateGroup();
|
||||
}}
|
||||
onCreateSpace={() => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onCreateSpace();
|
||||
}}
|
||||
onShowGroupInfo={() => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onShowGroupInfo();
|
||||
}}
|
||||
onShowSpaceInfo={() => {
|
||||
onCreateMenuOpenChange(false);
|
||||
onCloseMobileSidebar();
|
||||
onShowSpaceInfo();
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
) : null}
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
45
apps/web/src/pages/shell/sidebar-toggle-buttons.tsx
Normal file
45
apps/web/src/pages/shell/sidebar-toggle-buttons.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { useLingui } from "@lingui/react/macro";
|
||||
import { Menu, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||
import { desktopBridge } from "../../lib/desktop";
|
||||
import { WindowChrome } from "../WindowChrome";
|
||||
|
||||
/** Leading controls of a main header: open the mobile navigation or collapse the bots sidebar. */
|
||||
export function SidebarToggleButtons({
|
||||
collapsed,
|
||||
onCollapsedChange,
|
||||
onOpenNavigation,
|
||||
}: {
|
||||
collapsed: boolean;
|
||||
onCollapsedChange: (collapsed: boolean) => void;
|
||||
onOpenNavigation: () => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<>
|
||||
{/* Collapsed bots sidebar: this header is the leading edge for window chrome. */}
|
||||
{collapsed && desktopBridge() ? <WindowChrome /> : null}
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t`Open navigation`}
|
||||
onClick={onOpenNavigation}
|
||||
className="app-no-drag grid size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:hidden"
|
||||
>
|
||||
<Menu strokeWidth={1.7} className="size-icon-lg" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="toggle-bots-sidebar"
|
||||
aria-label={collapsed ? t`Show bots` : t`Minimize bots`}
|
||||
title={collapsed ? t`Show bots` : t`Minimize bots`}
|
||||
onClick={() => onCollapsedChange(!collapsed)}
|
||||
className="app-no-drag hidden size-control-lg shrink-0 place-items-center rounded-lg text-foreground/75 hover:bg-accent md:grid"
|
||||
>
|
||||
{collapsed ? (
|
||||
<PanelLeftOpen strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
) : (
|
||||
<PanelLeftClose strokeWidth={1.7} aria-hidden="true" className="size-icon-lg" />
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
73
apps/web/src/pages/shell/user-menu.tsx
Normal file
73
apps/web/src/pages/shell/user-menu.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { Trans, useLingui } from "@lingui/react/macro";
|
||||
import { Button, Popover, PopoverContent, PopoverTrigger } from "@rakazo/ui-web";
|
||||
import { Gauge, LogOut, Settings } from "lucide-react";
|
||||
|
||||
/** The account button at the foot of the sidebar and its menu. */
|
||||
export function UserMenu({
|
||||
open,
|
||||
onOpenChange,
|
||||
initials,
|
||||
userName,
|
||||
onOpenSettings,
|
||||
onOpenUsage,
|
||||
onLogOut,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
initials: string;
|
||||
userName: string;
|
||||
onOpenSettings: () => void;
|
||||
onOpenUsage: () => void;
|
||||
onLogOut: () => void;
|
||||
}) {
|
||||
const { t } = useLingui();
|
||||
return (
|
||||
<Popover open={open} onOpenChange={onOpenChange}>
|
||||
<PopoverTrigger
|
||||
data-testid="user-menu-trigger"
|
||||
className="flex items-center gap-[11px] px-[18px] py-3.5"
|
||||
>
|
||||
<span className="grid size-control-lg place-items-center rounded-full bg-accent text-ui-body-sm text-foreground/75">
|
||||
{initials}
|
||||
</span>
|
||||
<span className="text-[14.5px] text-foreground/90">{userName}</span>
|
||||
</PopoverTrigger>
|
||||
{open ? (
|
||||
<PopoverContent
|
||||
side="top"
|
||||
align="start"
|
||||
className="w-[calc(316px-1.5rem)] max-w-[calc(100vw-3rem)] gap-0 p-1 data-closed:animate-none"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start font-normal"
|
||||
aria-label={t`Settings`}
|
||||
onClick={() => {
|
||||
onOpenChange(false);
|
||||
onOpenSettings();
|
||||
}}
|
||||
>
|
||||
<Settings className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Settings</Trans>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="w-full justify-start font-normal"
|
||||
aria-label={t`Usage`}
|
||||
onClick={() => {
|
||||
onOpenChange(false);
|
||||
onOpenUsage();
|
||||
}}
|
||||
>
|
||||
<Gauge className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Usage</Trans>
|
||||
</Button>
|
||||
<Button variant="ghost" className="w-full justify-start font-normal" onClick={onLogOut}>
|
||||
<LogOut className="text-muted-foreground" strokeWidth={1.75} />
|
||||
<Trans>Log out</Trans>
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
) : null}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user