From 6344470ed25266844e74c63635442a2d87be075e Mon Sep 17 00:00:00 2001 From: Serhii Vecherenko Date: Wed, 17 Jun 2026 14:43:48 -0700 Subject: [PATCH 1/2] feat(i18n): add Lingui localization across renderer and AI generators - introduce i18n runtime, locale resolution, and shared message catalogs - wrap renderer UI strings with Lingui msg/Trans macros app-wide - add 14 locale .po catalogs and lingui extract tooling - thread AI content language through title/commit/PR generators - wire Lingui Babel plugin into vite/vitest and add i18n test helpers --- lingui.config.ts | 19 + package.json | 8 + pnpm-lock.yaml | 528 +- src/main/sharedSettingsFile.test.ts | 4 + src/renderer/RendererCrashScreen.tsx | 30 +- src/renderer/actions/agentLoginActions.ts | 14 +- src/renderer/actions/worktreeActions.ts | 4 +- src/renderer/app.test.tsx | 3 +- src/renderer/app.tsx | 5 +- src/renderer/commands/CommandPalette.tsx | 36 +- src/renderer/commands/registry.ts | 68 +- .../BranchSelector/BranchSelector.test.tsx | 3 +- .../common/BranchSelector/BranchSelector.tsx | 50 +- .../parts/BranchFooterActions.test.tsx | 3 +- .../parts/BranchFooterActions.tsx | 30 +- .../parts/BranchListBox.test.tsx | 10 +- .../BranchSelector/parts/BranchListBox.tsx | 28 +- .../BranchSelector/parts/useBranchList.ts | 8 +- .../components/common/ConfirmDialog.tsx | 7 +- .../EffortContextMenu/EffortContextMenu.tsx | 16 +- .../components/common/OptionMenu.test.tsx | 3 +- src/renderer/components/common/OptionMenu.tsx | 11 +- .../components/common/PixelLoader.tsx | 4 +- .../ProviderModelMenu.test.tsx | 3 +- .../ProviderModelMenu/ProviderModelMenu.tsx | 22 +- .../ProviderModelMenu/parts/buildItems.ts | 8 +- .../common/ProviderModelMenu/parts/types.ts | 3 +- .../composer/AttachmentBar.test.tsx | 3 +- .../components/composer/AttachmentBar.tsx | 15 +- .../components/composer/ComposerAddMenu.tsx | 28 +- .../components/composer/ImageLightbox.tsx | 10 +- .../components/composer/MentionPopover.tsx | 3 +- .../components/composer/VoiceInputButton.tsx | 29 +- .../components/composer/voiceError.ts | 12 +- src/renderer/components/diff/DiffCardList.tsx | 15 +- .../components/layout/BrowserDrawerShell.tsx | 4 +- .../components/layout/SplitPaneContainer.tsx | 4 +- .../components/layout/UnifiedRightPanel.tsx | 22 +- .../providers/ProviderUsageRail.tsx | 29 +- .../components/providers/acpGeneric/index.tsx | 2 + .../components/providers/codex/index.tsx | 60 +- .../components/providers/commitGen.ts | 3 + .../providers/composerControlBuilders.tsx | 3 + src/renderer/components/providers/titleGen.ts | 3 + .../components/providers/usageFormat.ts | 42 +- .../components/terminal/XTermSurface.test.tsx | 3 +- .../components/terminal/XTermSurface.tsx | 10 +- .../thread/AgentDiscoveryScreen.tsx | 83 +- .../components/thread/ChatPane/ChatPane.tsx | 17 +- .../thread/ChatPane/ChatRuntimeDebugPanel.tsx | 26 +- .../parts/CheckpointRevertControls.tsx | 40 +- .../thread/ChatPane/parts/MessageList.tsx | 6 +- .../parts/items/ActiveSubAgentTile.tsx | 25 +- .../parts/items/ContextCompaction.tsx | 32 +- .../thread/ChatPane/parts/items/ErrorItem.tsx | 4 +- .../ChatPane/parts/items/FileChange.tsx | 33 +- .../ChatPane/parts/items/InlineDiffView.tsx | 7 +- .../parts/items/InlineFolderPathChip.tsx | 14 +- .../thread/ChatPane/parts/items/PlanItem.tsx | 8 +- .../ChatPane/parts/items/PlanProposal.tsx | 8 +- .../thread/ChatPane/parts/items/Reasoning.tsx | 12 +- .../parts/items/SubAgentOverlay.test.tsx | 3 +- .../ChatPane/parts/items/SubAgentOverlay.tsx | 31 +- .../ChatPane/parts/items/SubAgentToolCall.tsx | 29 +- .../thread/ChatPane/parts/items/ToolCall.tsx | 10 +- .../ChatPane/parts/items/ToolCallGroup.tsx | 43 +- .../ChatPane/parts/items/UserMessage.tsx | 9 +- .../ChatPane/parts/items/WebSearchItem.tsx | 14 +- .../parts/items/WorkflowOverlayBody.tsx | 42 +- .../parts/items/WorkflowResultGroup.test.tsx | 3 +- .../parts/items/WorkflowResultGroup.tsx | 4 +- .../ChatPane/parts/items/commandSummary.ts | 55 +- .../ChatPane/parts/items/toolDisplay.ts | 119 +- .../parts/items/useReadAbsoluteFile.tsx | 14 +- .../thread/ContinueInProviderDialog.tsx | 35 +- .../thread/PresentationModeTabs.tsx | 8 +- .../components/thread/ProjectSwitchMenu.tsx | 10 +- .../thread/ThreadAgentUpdateDock.tsx | 16 +- .../thread/ThreadAuthRequiredDock.tsx | 32 +- .../components/thread/ThreadCommandPanel.tsx | 4 +- .../components/thread/ThreadComposer.test.tsx | 3 +- .../components/thread/ThreadComposer.tsx | 29 +- .../thread/ThreadComposerSection.test.tsx | 3 +- .../thread/ThreadComposerSection.tsx | 15 +- .../components/thread/ThreadContent.tsx | 5 +- .../components/thread/ThreadContextDock.tsx | 26 +- .../thread/ThreadContextIndicator.test.tsx | 3 +- .../thread/ThreadContextIndicator.tsx | 4 +- .../components/thread/ThreadDockUI.tsx | 7 +- .../components/thread/ThreadDraftChrome.tsx | 6 +- .../thread/ThreadDraftComposerArea.tsx | 27 +- .../thread/ThreadDraftView.test.tsx | 3 +- .../components/thread/ThreadDraftView.tsx | 13 +- .../components/thread/ThreadErrorDock.tsx | 24 +- .../components/thread/ThreadGoalDock.tsx | 41 +- .../components/thread/ThreadHeaderStatus.tsx | 76 +- .../thread/ThreadPendingSteerStrip.tsx | 12 +- .../ThreadRuntimeRequestPanel.tsx | 8 +- .../ThreadRuntimeRequestPanel/helpers.ts | 21 +- .../parts/ApprovalActions.tsx | 4 +- .../parts/PermissionDetailsLine.tsx | 9 +- .../parts/QuestionRows.tsx | 8 +- .../parts/QuestionSwitcher.tsx | 8 +- .../structuredElicitation.tsx | 10 +- .../userInputForm.tsx | 4 +- .../components/thread/ThreadTodoDock.tsx | 27 +- src/renderer/components/thread/ThreadView.tsx | 33 +- .../components/thread/WorktreeModeSelect.tsx | 18 +- .../thread/buildModelPickerControls.tsx | 2 + .../components/thread/threadContextUsage.ts | 24 +- .../thread/threadDraftViewHelpers.ts | 10 +- src/renderer/components/ui/provider.tsx | 188 +- src/renderer/components/ui/toastContent.ts | 4 +- src/renderer/hooks/usePrWriteActions.ts | 6 +- src/renderer/i18n/i18n.test.ts | 39 + src/renderer/i18n/i18n.ts | 77 + src/renderer/i18n/locales.ts | 84 + src/renderer/i18n/po.d.ts | 7 + src/renderer/i18n/sharedMessages.test.ts | 50 + src/renderer/i18n/sharedMessages.ts | 75 + src/renderer/locales/de/messages.po | 5631 +++++++++++++++++ src/renderer/locales/en/messages.po | 5573 ++++++++++++++++ src/renderer/locales/es/messages.po | 5616 ++++++++++++++++ src/renderer/locales/fr/messages.po | 5630 ++++++++++++++++ src/renderer/locales/ja/messages.po | 5523 ++++++++++++++++ src/renderer/locales/ko/messages.po | 5522 ++++++++++++++++ src/renderer/locales/pl/messages.po | 5609 ++++++++++++++++ src/renderer/locales/pt-BR/messages.po | 5616 ++++++++++++++++ src/renderer/locales/ru/messages.po | 5607 ++++++++++++++++ src/renderer/locales/tr/messages.po | 5607 ++++++++++++++++ src/renderer/locales/uk/messages.po | 5601 ++++++++++++++++ src/renderer/locales/vi/messages.po | 5599 ++++++++++++++++ src/renderer/locales/zh-CN/messages.po | 5510 ++++++++++++++++ src/renderer/main.tsx | 10 +- src/renderer/notifications.ts | 16 +- src/renderer/state/sharedSettingsStore.ts | 15 + .../state/slices/runtimeEventSlice.ts | 8 +- src/renderer/testSetup.ts | 4 + src/renderer/testUtils/i18n.tsx | 26 + src/renderer/utils/shellUtils.ts | 4 +- src/renderer/utils/titleGen.ts | 7 + .../FileEditorOverlay/FileEditorOverlay.tsx | 8 +- .../parts/FileEditorModal.tsx | 6 +- .../parts/FileEditorPane/FileEditorPane.tsx | 23 +- .../FileEditorPane/parts/EditorToolbar.tsx | 12 +- .../parts/mergeConflict/codeLensProvider.ts | 8 +- .../parts/FileEditorPanel.tsx | 4 +- .../parts/ProjectTreeView/ProjectTreeView.tsx | 28 +- .../ProjectTreeView/parts/TreeEntryRow.tsx | 18 +- .../GitReviewOverlay/GitReviewOverlay.tsx | 30 +- .../parts/GitDiffContent/GitDiffContent.tsx | 9 +- .../GitDiffContent/parts/DiffSection.tsx | 13 +- .../parts/GitDiffContent/parts/FileHeader.tsx | 3 +- .../GitDiffContent/parts/SingleFileDiff.tsx | 5 +- .../GitReviewOverlay/parts/GitReviewPanel.tsx | 22 +- .../GitReviewSidebar.test.tsx | 3 +- .../GitReviewSidebar/GitReviewSidebar.tsx | 86 +- .../parts/CommitSyncPanel.tsx | 66 +- .../parts/ConflictFileCard.tsx | 20 +- .../GitReviewSidebar/parts/ConflictGroup.tsx | 8 +- .../parts/ConflictResolutionActions.tsx | 5 +- .../GitReviewSidebar/parts/CreatePrModal.tsx | 32 +- .../GitReviewSidebar/parts/FileGroup.tsx | 20 +- .../parts/GitReviewSidebar/parts/FileRow.tsx | 16 +- .../GitReviewSidebar/parts/PrSection.tsx | 92 +- .../GitReviewSidebar/parts/commitActions.ts | 11 +- .../parts/useGitReviewActions.ts | 19 +- .../GitReviewOverlay/parts/GitStackedDiff.tsx | 30 +- src/renderer/views/HomeView.test.tsx | 3 +- src/renderer/views/HomeView.tsx | 5 +- .../LoginTerminalOverlay.tsx | 14 +- .../MainView/parts/AppContent/AppContent.tsx | 22 +- .../Thread/parts/ContinueInProviderDialog.tsx | 29 +- .../views/MainView/parts/AppOverlays.tsx | 18 +- .../MainView/parts/AppShell/AppShell.tsx | 9 +- .../CreateProject/CloneProjectModal.test.tsx | 3 +- .../parts/CreateProject/CloneProjectModal.tsx | 126 +- .../CreateProject/CreateProjectMenu.test.tsx | 3 +- .../parts/CreateProject/CreateProjectMenu.tsx | 22 +- .../CreateProject/CreateProjectModal.test.tsx | 3 +- .../CreateProject/CreateProjectModal.tsx | 50 +- .../MainView/parts/ProjectAuxiliaryPanel.tsx | 8 +- .../MainView/parts/PullFromSourceDialog.tsx | 15 +- .../parts/BrowserPanel/BrowserPanel.test.tsx | 3 +- .../parts/BrowserPanel/BrowserPanel.tsx | 41 +- .../BrowserPanel/hooks/useElementPicker.ts | 37 +- .../BrowserPanel/parts/BrowserEmptyState.tsx | 7 +- .../BrowserPanel/parts/BrowserTabStrip.tsx | 8 +- .../BrowserPanel/parts/BrowserToolbar.tsx | 82 +- .../DevTerminalPanel.test.tsx | 3 +- .../DevTerminalPanel/DevTerminalPanel.tsx | 32 +- .../parts/BottomTerminalLayout.tsx | 6 +- .../parts/RightTerminalLayout.tsx | 6 +- .../parts/TerminalSurfaces.test.tsx | 3 +- .../parts/TerminalSurfaces.tsx | 6 +- .../parts/NotesPanel/NotesEditor.tsx | 14 +- .../parts/NotesPanel/NotesPanel.tsx | 10 +- .../RightPanel/parts/NotesPanel/TodoList.tsx | 14 +- .../RightPanel/parts/NotesPanel/TodoRow.tsx | 19 +- .../parts/UsagePanel/UsagePanel.tsx | 25 +- .../parts/UsagePanelHeaderActions.tsx | 8 +- .../UsagePanel/parts/UsageProviderCard.tsx | 28 +- .../views/MainView/parts/Sidebar/Sidebar.tsx | 32 +- .../Sidebar/parts/DeleteWorktreeDialog.tsx | 25 +- .../Sidebar/parts/ForceDeleteBranchDialog.tsx | 16 +- .../parts/Sidebar/parts/GitBadge.test.tsx | 3 +- .../MainView/parts/Sidebar/parts/GitBadge.tsx | 20 +- .../parts/Sidebar/parts/InlineRenameInput.tsx | 4 +- .../parts/Sidebar/parts/NewThreadButton.tsx | 9 +- .../Sidebar/parts/SidebarProjectHeader.tsx | 22 +- .../parts/SidebarProjectThreadList.tsx | 7 +- .../Sidebar/parts/SidebarThreadGroup.tsx | 40 +- .../Sidebar/parts/SidebarWorktreeGroup.tsx | 20 +- .../SortableThreadItem.test.tsx | 2 +- .../SortableThreadItem/SortableThreadItem.tsx | 62 +- .../parts/ThreadItemSuffix.tsx | 12 +- .../parts/Sidebar/parts/SyncBadge.test.tsx | 3 +- .../parts/Sidebar/parts/SyncBadge.tsx | 13 +- .../Sidebar/parts/WorktreeGroupHeader.tsx | 10 +- .../parts/Sidebar/parts/sidebarProjectRows.ts | 6 +- .../MainView/parts/Sidebar/parts/sortMode.ts | 11 +- .../parts/Sidebar/parts/useWorktreeActions.ts | 22 +- .../MainView/parts/SidebarHeaderControls.tsx | 25 +- .../views/PrReviewOverlay/PrReviewOverlay.tsx | 14 +- .../PrReviewOverlay/parts/PrChecksTab.tsx | 12 +- .../PrReviewOverlay/parts/PrCommitsTab.tsx | 11 +- .../parts/PrConversationTab.tsx | 37 +- .../PrReviewOverlay/parts/PrHeaderCard.tsx | 3 +- .../views/PrReviewOverlay/parts/PrMetaRow.tsx | 24 +- .../PrReviewOverlay/parts/PrReviewSidebar.tsx | 18 +- .../PrReviewOverlay/parts/PrTabsPill.test.tsx | 3 +- .../PrReviewOverlay/parts/PrTabsPill.tsx | 18 +- .../parts/SubmitReviewPopover.tsx | 36 +- .../ProjectSettingsOverlay.tsx | 6 +- .../parts/ActionIconPicker.tsx | 4 +- .../parts/ActionsSection.tsx | 24 +- .../parts/GeneralSection.tsx | 16 +- .../parts/ScriptsSection.test.tsx | 3 +- .../parts/ScriptsSection.tsx | 40 +- .../parts/SearchSection.tsx | 20 +- .../parts/SettingsSidebar.tsx | 18 +- .../SettingsOverlay/SettingsOverlay.test.tsx | 3 +- .../views/SettingsOverlay/SettingsOverlay.tsx | 4 +- .../SettingsOverlay/parts/AISettings.tsx | 47 +- .../SettingsOverlay/parts/AboutSettings.tsx | 58 +- .../parts/AcpRegistrySettings.test.tsx | 3 +- .../parts/AcpRegistrySettings.tsx | 144 +- .../parts/AgentsGeneralSettings.tsx | 4 +- .../parts/AppearanceSettings.tsx | 34 +- .../parts/ArchivedThreadsSettings.tsx | 24 +- .../SettingsOverlay/parts/AudioSettings.tsx | 102 +- .../SettingsOverlay/parts/BrowserSettings.tsx | 45 +- .../parts/ClaudeProfileSettings.test.tsx | 3 +- .../parts/ClaudeProfileSettings.tsx | 49 +- .../SettingsOverlay/parts/DevSettings.tsx | 10 +- .../SettingsOverlay/parts/GeneralSettings.tsx | 91 +- .../SettingsOverlay/parts/GitSettings.tsx | 31 +- .../parts/ModelOrderSection.tsx | 13 +- .../parts/ModelVisibilitySection.tsx | 26 +- .../parts/NotificationSettings.tsx | 65 +- .../parts/SearchExcludeBody.tsx | 76 +- .../SettingsOverlay/parts/SearchSettings.tsx | 15 +- .../SettingsOverlay/parts/SettingsSidebar.tsx | 88 +- .../parts/SingleAgentSettings.test.tsx | 3 +- .../parts/SingleAgentSettings.tsx | 258 +- .../parts/TerminalSettings.tsx | 71 +- .../SettingsOverlay/parts/ThreadSettings.tsx | 37 +- .../SettingsOverlay/parts/UsageSettings.tsx | 50 +- .../parts/agentRegistryNative.ts | 16 +- .../SettingsOverlay/parts/settingsOptions.ts | 78 +- .../ThreadSearchOverlay.tsx | 20 +- src/renderer/views/WelcomeOverlay.tsx | 11 +- src/shared/contracts/git.ts | 6 + src/shared/locale.test.ts | 51 + src/shared/locale.ts | 155 + src/shared/messages.ts | Bin 9242 -> 10298 bytes src/shared/settings.ts | 17 + src/supervisor/commitMessageGenerator.test.ts | 39 + src/supervisor/commitMessageGenerator.ts | 24 +- src/supervisor/prSummaryGenerator.ts | 59 +- src/supervisor/runtime.ts | 3 + src/supervisor/titleGenerator.ts | 35 +- tsconfig.json | 1 + vite.config.ts | 14 +- vitest.config.ts | 7 + 285 files changed, 77524 insertions(+), 2087 deletions(-) create mode 100644 lingui.config.ts create mode 100644 src/renderer/i18n/i18n.test.ts create mode 100644 src/renderer/i18n/i18n.ts create mode 100644 src/renderer/i18n/locales.ts create mode 100644 src/renderer/i18n/po.d.ts create mode 100644 src/renderer/i18n/sharedMessages.test.ts create mode 100644 src/renderer/i18n/sharedMessages.ts create mode 100644 src/renderer/locales/de/messages.po create mode 100644 src/renderer/locales/en/messages.po create mode 100644 src/renderer/locales/es/messages.po create mode 100644 src/renderer/locales/fr/messages.po create mode 100644 src/renderer/locales/ja/messages.po create mode 100644 src/renderer/locales/ko/messages.po create mode 100644 src/renderer/locales/pl/messages.po create mode 100644 src/renderer/locales/pt-BR/messages.po create mode 100644 src/renderer/locales/ru/messages.po create mode 100644 src/renderer/locales/tr/messages.po create mode 100644 src/renderer/locales/uk/messages.po create mode 100644 src/renderer/locales/vi/messages.po create mode 100644 src/renderer/locales/zh-CN/messages.po create mode 100644 src/renderer/testUtils/i18n.tsx create mode 100644 src/shared/locale.test.ts create mode 100644 src/shared/locale.ts diff --git a/lingui.config.ts b/lingui.config.ts new file mode 100644 index 00000000..39665c92 --- /dev/null +++ b/lingui.config.ts @@ -0,0 +1,19 @@ +import { defineConfig } from "@lingui/cli"; +import { formatter } from "@lingui/format-po"; + +// i18n is renderer-only: macros are expanded by Babel in the Vite pipeline, and +// the main process (tsdown) carries no catalogs. Adding a language is two steps: +// add its code to `locales` here (and to SUPPORTED_LOCALES in +// src/shared/locale.ts), then run `pnpm i18n:extract`. +export default defineConfig({ + sourceLocale: "en", + locales: ["en", "es", "ru", "uk", "zh-CN", "ja", "pt-BR", "de", "fr", "ko", "pl", "vi", "tr"], + catalogs: [ + { + path: "src/renderer/locales/{locale}/messages", + include: ["src/renderer"], + exclude: ["**/*.test.*", "**/node_modules/**"], + }, + ], + format: formatter({ lineNumbers: false }), +}); diff --git a/package.json b/package.json index 213b97cb..59d2c1cb 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,8 @@ "test:integration:providers": "vitest run --config vitest.integration.config.ts", "update-server": "node scripts/update-server.mjs", "db:clone": "node scripts/clone-db.mjs", + "i18n:extract": "lingui extract", + "i18n:extract:clean": "lingui extract --clean", "prepare": "husky" }, "dependencies": { @@ -66,6 +68,8 @@ "@heroui/styles": "^3.0.3", "@huggingface/transformers": "^4.2.0", "@lightcode/agents-usage": "workspace:*", + "@lingui/core": "6.2.0", + "@lingui/react": "6.2.0", "@monaco-editor/react": "^4.7.0", "@opencode-ai/sdk": "^1.14.48", "@sentry/electron": "^7.13.0", @@ -107,6 +111,10 @@ "devDependencies": { "@babel/core": "^7.29.0", "@electron/rebuild": "^4.0.4", + "@lingui/babel-plugin-lingui-macro": "6.2.0", + "@lingui/cli": "6.2.0", + "@lingui/format-po": "6.2.0", + "@lingui/vite-plugin": "6.2.0", "@rolldown/plugin-babel": "^0.2.3", "@sentry/cli": "^3.4.2", "@tailwindcss/postcss": "^4.2.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 714a2b13..2571d979 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,12 @@ importers: '@lightcode/agents-usage': specifier: workspace:* version: link:packages/agents-usage + '@lingui/core': + specifier: 6.2.0 + version: 6.2.0 + '@lingui/react': + specifier: 6.2.0 + version: 6.2.0(react@19.2.5) '@monaco-editor/react': specifier: ^4.7.0 version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -177,6 +183,18 @@ importers: '@electron/rebuild': specifier: ^4.0.4 version: 4.0.4 + '@lingui/babel-plugin-lingui-macro': + specifier: 6.2.0 + version: 6.2.0 + '@lingui/cli': + specifier: 6.2.0 + version: 6.2.0 + '@lingui/format-po': + specifier: 6.2.0 + version: 6.2.0 + '@lingui/vite-plugin': + specifier: 6.2.0 + version: 6.2.0(@babel/core@7.29.0)(@lingui/babel-plugin-lingui-macro@6.2.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.7)(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0)))(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0)) '@rolldown/plugin-babel': specifier: ^0.2.3 version: 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.7)(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0)) @@ -281,7 +299,7 @@ importers: dependencies: '@heroui/react': specifier: latest - version: 3.1.0(@react-spectrum/provider@3.11.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0) + version: 3.1.0(@react-spectrum/provider@3.11.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0) '@heroui/styles': specifier: latest version: 3.1.0(tailwind-merge@3.4.0)(tailwindcss@4.3.0) @@ -309,13 +327,13 @@ importers: version: 4.3.0 '@types/node': specifier: latest - version: 25.9.1 + version: 25.9.2 '@types/react': specifier: latest - version: 19.2.16 + version: 19.2.17 '@types/react-dom': specifier: latest - version: 19.2.3(@types/react@19.2.16) + version: 19.2.3(@types/react@19.2.17) typescript: specifier: latest version: 6.0.3 @@ -576,6 +594,10 @@ packages: '@chevrotain/utils@12.0.0': resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==} + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + '@csstools/color-helpers@6.0.2': resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} @@ -1133,6 +1155,14 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1153,6 +1183,69 @@ packages: resolution: {integrity: sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==} engines: {node: '>=12'} + '@lingui/babel-plugin-extract-messages@6.2.0': + resolution: {integrity: sha512-/iPL0DSvq6oZQjxLSHXofaxq18joGKEYzJjaZUWw9jdyWpRp1kNFMHPQVPbemVBR2PSJUNS7jlOu1iph7qtkrQ==} + engines: {node: '>=22.19.0'} + + '@lingui/babel-plugin-lingui-macro@6.2.0': + resolution: {integrity: sha512-sZqrd+RFLOePcimYoyNWs7n8brfkD5MXKb9trYNaV+/obt3C02LEpLrdIqUvykpn5+NgeZY6H0x/HqaXTil5vA==} + engines: {node: '>=22.19.0'} + + '@lingui/cli@6.2.0': + resolution: {integrity: sha512-47j2VnDCRAuxlwFnBshQqTGBmkiRvr2jUs7FzEHT0z/drCOhNwHoNCDDAGC/1SwsiK9kaeNZcObyZfrpe3z3PA==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@lingui/conf@6.2.0': + resolution: {integrity: sha512-SVZlXH0MJgS6Cu9NFCtJeqT7VzbKp5VDQcd9j51x6lRCzknPJS4CjJ4GWzu0RHoTmtQyoADLzuQTPrzD0ommLA==} + engines: {node: '>=22.19.0'} + + '@lingui/core@6.2.0': + resolution: {integrity: sha512-iNxDy9+GfwBGi9YVgAt7q0V7hA7kwt5t+akScFWvIyzII0KpMd4oLq0JyPsedsKloWVmL8V0PuvtThM0Ja4SWg==} + engines: {node: '>=22.19.0'} + peerDependencies: + babel-plugin-macros: 2 || 3 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + '@lingui/format-po@6.2.0': + resolution: {integrity: sha512-rchqiXGySJagw/sN+fL6Wi9A1hAooSO7B/5qPmHbH2DP5kGnMbE61MwqvhGaJQrWMtnac9AGz1PC4AKBWKVAZw==} + engines: {node: '>=22.19.0'} + + '@lingui/message-utils@6.2.0': + resolution: {integrity: sha512-oFmD6xYO40kC/7qBdxieEVekoYNI0KWtizCOanrlT6RXYPSwn5hH4Hf6u0mErEf23m/zNGDMO+D6YNQ4z7IxwQ==} + engines: {node: '>=22.19.0'} + + '@lingui/react@6.2.0': + resolution: {integrity: sha512-DDdzKgHG+j13qA4In3nqBCSJDhdW9baIx+0a9/5znoHlnJsR2VG3AqlAtCmuTMr7uhjzwuZYJgG2Ms718Rr39A==} + engines: {node: '>=22.19.0'} + peerDependencies: + babel-plugin-macros: 2 || 3 + react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + '@lingui/vite-plugin@6.2.0': + resolution: {integrity: sha512-Ti1wFsY40LP7+aXoooZ/fp1HqKLABBibaBNlboUYogsw6vZFatc3ZRYjOs6WcKozzlcawz8IeznGbCtAn3WVnA==} + engines: {node: '>=22.19.0'} + peerDependencies: + '@babel/core': ^7.29.0 || ^8.0.0-rc.1 + '@lingui/babel-plugin-lingui-macro': ^5 || ^6 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + rolldown: ^1.0.0-rc.5 + vite: ^6.3.0 || ^7 || ^8 + peerDependenciesMeta: + '@babel/core': + optional: true + '@lingui/babel-plugin-lingui-macro': + optional: true + '@rolldown/plugin-babel': + optional: true + rolldown: + optional: true + '@malept/cross-spawn-promise@2.0.0': resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==} engines: {node: '>= 12.13.0'} @@ -1164,6 +1257,12 @@ packages: '@mermaid-js/parser@1.1.0': resolution: {integrity: sha512-gxK9ZX2+Fex5zu8LhRQoMeMPEHbc73UKZ0FQ54YrQtUxE1VVhMwzeNtKRPAu5aXks4FasbMe4xB4bWrmq6Jlxw==} + '@messageformat/date-skeleton@1.1.0': + resolution: {integrity: sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==} + + '@messageformat/parser@5.1.1': + resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} + '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} @@ -2270,6 +2369,9 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -2807,6 +2909,15 @@ packages: '@types/http-cache-semantics@4.2.0': resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} @@ -2834,6 +2945,9 @@ packages: '@types/node@25.9.1': resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} + '@types/node@25.9.2': + resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} + '@types/pg-pool@2.0.7': resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} @@ -2851,8 +2965,8 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - '@types/react@19.2.16': - resolution: {integrity: sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -2875,6 +2989,12 @@ packages: '@types/verror@1.10.11': resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -3261,6 +3381,10 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + caniuse-lite@1.0.30001787: resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} @@ -3279,6 +3403,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -3300,6 +3428,10 @@ packages: resolution: {integrity: sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==} engines: {node: '>=22.0.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -3328,6 +3460,14 @@ packages: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + cli-truncate@2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} engines: {node: '>=8'} @@ -3364,6 +3504,14 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@5.1.0: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} @@ -4411,6 +4559,10 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -4425,6 +4577,10 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} @@ -4449,6 +4605,14 @@ packages: engines: {node: '>=10'} hasBin: true + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -4460,6 +4624,9 @@ packages: jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + js-sha256@0.10.1: + resolution: {integrity: sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4544,6 +4711,10 @@ packages: lazy-val@1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -4618,6 +4789,10 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + linkifyjs@4.3.3: resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==} @@ -4646,6 +4821,10 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} @@ -4948,6 +5127,9 @@ packages: monaco-editor@0.55.1: resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==} + moo@0.5.3: + resolution: {integrity: sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==} + motion-dom@12.40.0: resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} @@ -5027,6 +5209,10 @@ packages: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -5076,6 +5262,10 @@ packages: onnxruntime-web@1.26.0-dev.20260416-b7804b056c: resolution: {integrity: sha512-MD6Ss4GSpQBo6zqoJzyT9LRbKYs7x/JVN23FT24EcEvlqF4VuzPOeH6X38orZPKHQDbprn7K+SBpu0/mj2CQiw==} + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} + engines: {node: '>=20'} + orderedmap@2.1.1: resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} @@ -5183,6 +5373,10 @@ packages: resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} engines: {node: '>=10.4.0'} + pofile-ts@4.0.3: + resolution: {integrity: sha512-sz1pnjgEfPyZ+QvaeX3NtCmbYnEvG01LZRLoN/uXoLtPZtxCIH5IctL7yXXc0fFyk/fqV6K8g3hlNfr6IJwupA==} + engines: {node: '>=20'} + points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -5228,6 +5422,10 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + proc-log@6.1.0: resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -5303,6 +5501,11 @@ packages: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} + pseudolocale@2.2.0: + resolution: {integrity: sha512-O+D2eU7fO9wVLqrohvt9V/9fwMadnJQ4jxwiK+LeNEqhMx8JYx4xQHkArDCJFAdPPOp/pQq6z5L37eBvAoc8jw==} + engines: {node: '>=16.0.0'} + hasBin: true + pump@3.0.4: resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} @@ -5361,6 +5564,9 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: @@ -5399,6 +5605,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -5678,6 +5888,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -5701,6 +5915,10 @@ packages: std-env@4.0.0: resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} + engines: {node: '>=18'} + streamdown@2.5.0: resolution: {integrity: sha512-/tTnURfIOxZK/pqJAxsfCvETG/XCJHoWnk3jq9xLcuz6CSpnjjuxSRBTTL4PKGhxiZQf0lqPxGhImdpwcZ2XwA==} peerDependencies: @@ -6293,6 +6511,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -6626,6 +6848,9 @@ snapshots: '@chevrotain/utils@12.0.0': {} + '@colors/colors@1.5.0': + optional: true + '@csstools/color-helpers@6.0.2': {} '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -7000,10 +7225,10 @@ snapshots: - '@types/react' - '@types/react-dom' - '@heroui/react@3.1.0(@react-spectrum/provider@3.11.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0)': + '@heroui/react@3.1.0(@react-spectrum/provider@3.11.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.0)': dependencies: '@heroui/styles': 3.1.0(tailwind-merge@3.4.0)(tailwindcss@4.3.0) - '@radix-ui/react-avatar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-aria/i18n': 3.13.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-aria/ssr': 3.10.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-aria/utils': 3.34.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -7179,6 +7404,19 @@ snapshots: dependencies: minipass: 7.1.3 + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.10 + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.9.2 + '@types/yargs': 17.0.35 + chalk: 4.1.2 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7202,6 +7440,93 @@ snapshots: dependencies: jsbi: 4.3.2 + '@lingui/babel-plugin-extract-messages@6.2.0': {} + + '@lingui/babel-plugin-lingui-macro@6.2.0': + dependencies: + '@babel/core': 7.29.0 + '@babel/types': 7.29.0 + '@lingui/conf': 6.2.0 + '@lingui/message-utils': 6.2.0 + transitivePeerDependencies: + - supports-color + + '@lingui/cli@6.2.0': + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@lingui/babel-plugin-extract-messages': 6.2.0 + '@lingui/babel-plugin-lingui-macro': 6.2.0 + '@lingui/conf': 6.2.0 + '@lingui/core': 6.2.0 + '@lingui/format-po': 6.2.0 + '@lingui/message-utils': 6.2.0 + chokidar: 5.0.0 + cli-table3: 0.6.5 + commander: 14.0.3 + esbuild: 0.25.12 + jiti: 2.6.1 + micromatch: 4.0.8 + ms: 2.1.3 + normalize-path: 3.0.0 + ora: 9.4.0 + pseudolocale: 2.2.0 + source-map: 0.7.6 + tinypool: 2.1.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + '@lingui/conf@6.2.0': + dependencies: + jest-validate: 29.7.0 + jiti: 2.6.1 + lilconfig: 3.1.3 + normalize-path: 3.0.0 + + '@lingui/core@6.2.0': + dependencies: + '@lingui/babel-plugin-lingui-macro': 6.2.0 + '@lingui/message-utils': 6.2.0 + transitivePeerDependencies: + - supports-color + + '@lingui/format-po@6.2.0': + dependencies: + '@lingui/conf': 6.2.0 + '@lingui/message-utils': 6.2.0 + pofile-ts: 4.0.3 + + '@lingui/message-utils@6.2.0': + dependencies: + '@messageformat/date-skeleton': 1.1.0 + '@messageformat/parser': 5.1.1 + js-sha256: 0.10.1 + + '@lingui/react@6.2.0(react@19.2.5)': + dependencies: + '@lingui/babel-plugin-lingui-macro': 6.2.0 + '@lingui/core': 6.2.0 + react: 19.2.5 + transitivePeerDependencies: + - supports-color + + '@lingui/vite-plugin@6.2.0(@babel/core@7.29.0)(@lingui/babel-plugin-lingui-macro@6.2.0)(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.7)(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0)))(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0))': + dependencies: + '@lingui/cli': 6.2.0 + '@lingui/conf': 6.2.0 + vite: 8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0) + optionalDependencies: + '@babel/core': 7.29.0 + '@lingui/babel-plugin-lingui-macro': 6.2.0 + '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.0)(@babel/runtime@7.29.7)(rolldown@1.0.0)(vite@8.0.10(@types/node@25.6.0)(jiti@2.6.1)(yaml@2.9.0)) + rolldown: 1.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + '@malept/cross-spawn-promise@2.0.0': dependencies: cross-spawn: 7.0.6 @@ -7219,6 +7544,12 @@ snapshots: dependencies: langium: 4.2.3 + '@messageformat/date-skeleton@1.1.0': {} + + '@messageformat/parser@5.1.1': + dependencies: + moo: 0.5.3 + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.2)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.23) @@ -7719,18 +8050,18 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.16)(react@19.2.7) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.16)(react@19.2.7) + '@radix-ui/react-context': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7738,11 +8069,11 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@radix-ui/react-context@1.1.3(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7750,11 +8081,11 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-context@1.1.3(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-context@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: @@ -7765,14 +8096,14 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.16)(react@19.2.7) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.16 - '@types/react-dom': 19.2.3(@types/react@19.2.16) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7781,12 +8112,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.16)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7794,11 +8125,11 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7807,12 +8138,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.5)': dependencies: @@ -7820,11 +8151,11 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.16)(react@19.2.7)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@react-aria/color@3.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: @@ -8261,6 +8592,8 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@sinclair/typebox@0.27.10': {} + '@sindresorhus/is@4.6.0': {} '@spectrum-icons/ui@3.7.0(@adobe/react-spectrum@3.47.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': @@ -8680,7 +9013,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -8690,7 +9023,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/d3-array@3.2.2': {} @@ -8823,7 +9156,7 @@ snapshots: '@types/fs-extra@9.0.13': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/geojson@7946.0.16': {} @@ -8833,11 +9166,21 @@ snapshots: '@types/http-cache-semantics@4.2.0': {} + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + '@types/jsesc@2.5.1': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/mdast@4.0.4': dependencies: @@ -8851,7 +9194,7 @@ snapshots: '@types/mysql@2.15.27': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/node@24.12.2': dependencies: @@ -8865,19 +9208,23 @@ snapshots: dependencies: undici-types: 7.24.6 + '@types/node@25.9.2': + dependencies: + undici-types: 7.24.6 + '@types/pg-pool@2.0.7': dependencies: '@types/pg': 8.15.6 '@types/pg@8.15.6': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 pg-protocol: 1.13.0 pg-types: 2.2.0 '@types/plist@3.0.5': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 xmlbuilder: 15.1.1 optional: true @@ -8885,25 +9232,25 @@ snapshots: dependencies: '@types/react': 19.2.14 - '@types/react-dom@19.2.3(@types/react@19.2.16)': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/react': 19.2.16 + '@types/react': 19.2.17 '@types/react@19.2.14': dependencies: csstype: 3.2.3 - '@types/react@19.2.16': + '@types/react@19.2.17': dependencies: csstype: 3.2.3 '@types/responselike@1.0.3': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/tedious@4.0.14': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/trusted-types@2.0.7': optional: true @@ -8917,9 +9264,15 @@ snapshots: '@types/verror@1.10.11': optional: true + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.35': + dependencies: + '@types/yargs-parser': 21.0.3 + '@types/yauzl@2.10.3': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 optional: true '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260501.1': @@ -9338,6 +9691,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + camelcase@6.3.0: {} + caniuse-lite@1.0.30001787: {} ccount@2.0.1: {} @@ -9354,6 +9709,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -9375,6 +9732,10 @@ snapshots: '@chevrotain/types': 12.0.0 '@chevrotain/utils': 12.0.0 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chownr@1.1.4: {} chownr@3.0.0: {} @@ -9393,6 +9754,14 @@ snapshots: dependencies: restore-cursor: 5.1.0 + cli-spinners@3.4.0: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + cli-truncate@2.1.0: dependencies: slice-ansi: 3.0.0 @@ -9430,6 +9799,10 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} + + commander@14.0.3: {} + commander@5.1.0: {} commander@7.2.0: {} @@ -10536,6 +10909,8 @@ snapshots: is-hexadecimal@2.0.1: {} + is-interactive@2.0.0: {} + is-number@7.0.0: {} is-plain-obj@4.1.0: {} @@ -10544,6 +10919,8 @@ snapshots: is-promise@4.0.0: {} + is-unicode-supported@2.1.0: {} + isbinaryfile@4.0.10: {} isbinaryfile@5.0.7: {} @@ -10560,6 +10937,17 @@ snapshots: filelist: 1.0.6 picocolors: 1.1.1 + jest-get-type@29.6.3: {} + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + jiti@2.6.1: {} joi@18.1.2: @@ -10574,6 +10962,8 @@ snapshots: jose@6.2.3: {} + js-sha256@0.10.1: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -10673,6 +11063,8 @@ snapshots: lazy-val@1.0.5: {} + leven@3.1.0: {} + lightningcss-android-arm64@1.32.0: optional: true @@ -10722,6 +11114,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lilconfig@3.1.3: {} + linkifyjs@4.3.3: {} lint-staged@17.0.4: @@ -10751,6 +11145,11 @@ snapshots: lodash@4.18.1: {} + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 @@ -11258,6 +11657,8 @@ snapshots: dompurify: 3.4.5 marked: 14.0.0 + moo@0.5.3: {} + motion-dom@12.40.0: dependencies: motion-utils: 12.39.0 @@ -11342,6 +11743,8 @@ snapshots: dependencies: remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} + normalize-url@6.1.0: {} object-assign@4.1.1: {} @@ -11391,6 +11794,17 @@ snapshots: platform: 1.3.6 protobufjs: 7.6.1 + ora@9.4.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 3.4.0 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.1 + orderedmap@2.1.1: {} oxfmt@0.47.0: @@ -11521,6 +11935,8 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 + pofile-ts@4.0.3: {} + points-on-curve@0.2.0: {} points-on-path@0.2.1: @@ -11576,6 +11992,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + proc-log@6.1.0: {} progress@2.0.3: {} @@ -11697,6 +12119,10 @@ snapshots: proxy-from-env@2.1.0: {} + pseudolocale@2.2.0: + dependencies: + commander: 10.0.1 + pump@3.0.4: dependencies: end-of-stream: 1.4.5 @@ -11792,6 +12218,8 @@ snapshots: react-is@17.0.2: {} + react-is@18.3.1: {} + react-markdown@10.1.0(@types/react@19.2.14)(react@19.2.5): dependencies: '@types/hast': 3.0.4 @@ -11871,6 +12299,8 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdirp@5.0.0: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -12258,6 +12688,8 @@ snapshots: source-map@0.6.1: {} + source-map@0.7.6: {} + space-separated-tokens@2.0.2: {} sprintf-js@1.1.3: {} @@ -12272,6 +12704,8 @@ snapshots: std-env@4.0.0: {} + stdin-discarder@0.3.2: {} + streamdown@2.5.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: clsx: 2.1.1 @@ -12829,6 +13263,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.1.2: {} + zod-to-json-schema@3.25.2(zod@4.4.2): dependencies: zod: 4.4.2 diff --git a/src/main/sharedSettingsFile.test.ts b/src/main/sharedSettingsFile.test.ts index d29374bb..633b3e32 100644 --- a/src/main/sharedSettingsFile.test.ts +++ b/src/main/sharedSettingsFile.test.ts @@ -29,6 +29,8 @@ describe("sharedSettingsFile", () => { writeSharedSettingsFile(settingsPath, { themeMode: "dark", themePreset: "default", + locale: "system", + gitTextLanguage: "en", terminalPosition: "right", commitGenProvider: "auto", commitGenModel: "", @@ -116,6 +118,8 @@ describe("sharedSettingsFile", () => { expect(readSharedSettingsFile(settingsPath)).toEqual({ themeMode: "dark", themePreset: "default", + locale: "system", + gitTextLanguage: "en", terminalPosition: "right", commitGenProvider: "auto", commitGenModel: "", diff --git a/src/renderer/RendererCrashScreen.tsx b/src/renderer/RendererCrashScreen.tsx index 1891ee51..65a614b9 100644 --- a/src/renderer/RendererCrashScreen.tsx +++ b/src/renderer/RendererCrashScreen.tsx @@ -1,7 +1,10 @@ import { Component, useState, type ErrorInfo, type ReactNode } from "react"; import { Copy, RefreshCw } from "lucide-react"; +import { msg } from "@lingui/core/macro"; +import type { MessageDescriptor } from "@lingui/core"; import { Button } from "./components/common"; import { captureRendererException } from "./diagnostics/sentry"; +import { i18n } from "./i18n/i18n"; export type RendererCrashKind = "bootstrap" | "react" | "uncaught" | "unhandled-rejection"; @@ -117,16 +120,16 @@ export function formatRendererCrashReport(report: RendererCrashReport): string { return lines.filter((line): line is string => line !== null).join("\n"); } -function crashTitle(kind: RendererCrashKind): string { +function crashTitle(kind: RendererCrashKind): MessageDescriptor { switch (kind) { case "bootstrap": - return "Renderer failed during startup"; + return msg`Renderer failed during startup`; case "react": - return "Renderer hit a React error"; + return msg`Renderer hit a React error`; case "unhandled-rejection": - return "Renderer hit an unhandled promise rejection"; + return msg`Renderer hit an unhandled promise rejection`; case "uncaught": - return "Renderer hit an uncaught error"; + return msg`Renderer hit an uncaught error`; } } @@ -153,25 +156,26 @@ export function RendererCrashScreen(props: RendererCrashScreenProps) {
-

Renderer crashed

-

{crashTitle(report.kind)}

+

{i18n._(msg`Renderer crashed`)}

+

{i18n._(crashTitle(report.kind))}

- The normal app shell could not render. The diagnostics below are shown before reload - so the failure can be investigated. + {i18n._( + msg`The normal app shell could not render. The diagnostics below are shown before reload so the failure can be investigated.`, + )}

diff --git a/src/renderer/actions/agentLoginActions.ts b/src/renderer/actions/agentLoginActions.ts index 1c19f9cd..f9223b1c 100644 --- a/src/renderer/actions/agentLoginActions.ts +++ b/src/renderer/actions/agentLoginActions.ts @@ -1,7 +1,9 @@ import { toast } from "@heroui/react"; +import { msg } from "@lingui/core/macro"; import type { Project } from "@/shared/contracts"; import { stripAnsi } from "@/shared/ansi"; import { readBridge } from "@/renderer/bridge"; +import { i18n } from "@/renderer/i18n/i18n"; import { useAppStore } from "@/renderer/state/appStore"; import { useDevTerminalStore } from "@/renderer/state/devTerminalStore"; import { useLoginTerminalStore } from "@/renderer/state/loginTerminalStore"; @@ -45,7 +47,7 @@ export function runAgentLoginCommand(input: { }): boolean { const project = input.project ?? resolveLoginProject(); if (!project) { - toast.warning("Add a project before signing in."); + toast.warning(i18n._(msg`Add a project before signing in.`)); return false; } @@ -119,7 +121,9 @@ export function runAgentLoginCommand(input: { // (and leave callers' pending UI stuck). Tear it down and report failure. stopWatching(); fireOnce(-1); - toast.danger(error instanceof Error ? error.message : `Unable to open ${input.label} login.`); + toast.danger( + error instanceof Error ? error.message : i18n._(msg`Unable to open ${input.label} login.`), + ); useLoginTerminalStore.getState().close(); }); writeScriptToShell(shellId, script); @@ -143,7 +147,7 @@ export function runAgentInstallCommand(input: { }): boolean { const project = input.project ?? resolveLoginProject(); if (!project) { - toast.warning("Add a project before installing an agent."); + toast.warning(i18n._(msg`Add a project before installing an agent.`)); return false; } @@ -199,7 +203,9 @@ export function runAgentInstallCommand(input: { .catch((error) => { stopWatching(); fireOnce(-1); - toast.danger(error instanceof Error ? error.message : `Unable to install ${input.label}.`); + toast.danger( + error instanceof Error ? error.message : i18n._(msg`Unable to install ${input.label}.`), + ); useLoginTerminalStore.getState().close(); }); writeScriptToShell(shellId, script); diff --git a/src/renderer/actions/worktreeActions.ts b/src/renderer/actions/worktreeActions.ts index 3fb2f213..ec2e5bff 100644 --- a/src/renderer/actions/worktreeActions.ts +++ b/src/renderer/actions/worktreeActions.ts @@ -1,8 +1,10 @@ import { toast } from "@heroui/react"; +import { msg } from "@lingui/core/macro"; import { buildWorktreeLocation } from "@/shared/worktree"; import { errorDetail } from "@/shared/messages"; import type { Project } from "@/shared/contracts"; import { readBridge } from "@/renderer/bridge"; +import { i18n } from "@/renderer/i18n/i18n"; import { useAppStore } from "@/renderer/state/appStore"; import { useDevTerminalStore } from "@/renderer/state/devTerminalStore"; import { useFileEditorStore } from "@/renderer/state/fileEditorStore"; @@ -59,7 +61,7 @@ export async function performWorktreeRemoval( } catch (err: unknown) { const detail = errorDetail(err); console.warn(`[renderer] failed to remove worktree ${worktreePath}:`, detail); - toast.danger(detail || "Unable to remove worktree."); + toast.danger(detail || i18n._(msg`Unable to remove worktree.`)); return; } diff --git a/src/renderer/app.test.tsx b/src/renderer/app.test.tsx index bb453684..88ba3c13 100644 --- a/src/renderer/app.test.tsx +++ b/src/renderer/app.test.tsx @@ -1,5 +1,6 @@ import { Fragment, type ReactNode } from "react"; -import { act, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { act, fireEvent, screen, waitFor } from "@testing-library/react"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { useAppStore } from "./state/appStore"; import { useGitStore } from "./state/gitStore"; diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 052bd08b..6b77bf74 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -1,4 +1,5 @@ import { toast } from "@heroui/react"; +import { Trans } from "@lingui/react/macro"; import { useEffect } from "react"; import { PixelLoader } from "./components/common"; import { msg } from "@/shared/messages"; @@ -267,7 +268,9 @@ export function App() {
-

Loading…

+

+ Loading… +

diff --git a/src/renderer/commands/CommandPalette.tsx b/src/renderer/commands/CommandPalette.tsx index 4768a880..cc614562 100644 --- a/src/renderer/commands/CommandPalette.tsx +++ b/src/renderer/commands/CommandPalette.tsx @@ -1,5 +1,7 @@ import { useEffect, useRef, useState } from "react"; import { Input, Label, Modal } from "@heroui/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import type { MessageDescriptor } from "@lingui/core"; import { Command, Search } from "lucide-react"; import { readBridge } from "@/renderer/bridge"; import { useAppStore } from "@/renderer/state/appStore"; @@ -18,6 +20,7 @@ import { const MAX_VISIBLE_COMMANDS = 80; export function CommandPalette() { + const { t } = useLingui(); const isOpen = useCommandPaletteStore((state) => state.isOpen); const close = useCommandPaletteStore((state) => state.close); const keybindings = useKeybindingStore((state) => state.keybindings); @@ -37,7 +40,9 @@ export function CommandPalette() { const commands = buildCommandRegistry().filter((command) => isCommandAvailable(command, whenContext), ); - const filteredCommands = filterCommands(commands, query).slice(0, MAX_VISIBLE_COMMANDS); + const resolve = (value: string | MessageDescriptor): string => + typeof value === "string" ? value : t(value); + const filteredCommands = filterCommands(commands, query, resolve).slice(0, MAX_VISIBLE_COMMANDS); const activeCommand = filteredCommands[activeIndex]; useEffect(() => { @@ -76,9 +81,12 @@ export function CommandPalette() { { setQuery(event.target.value); @@ -101,7 +109,7 @@ export function CommandPalette() {
{filteredCommands.length > 0 ? ( -
+
{filteredCommands.map((command, index) => { const shortcut = shortcutForCommand(command.id, keybindings); return ( @@ -118,9 +126,9 @@ export function CommandPalette() { > - + - {command.subtitle ?? command.group} + {resolve(command.subtitle ?? command.group)} {shortcut ? ( @@ -133,7 +141,9 @@ export function CommandPalette() { })}
) : ( -
No commands found
+
+ No commands found +
)}
@@ -142,16 +152,20 @@ export function CommandPalette() { ); } -function filterCommands(commands: AppCommand[], query: string): AppCommand[] { +function filterCommands( + commands: AppCommand[], + query: string, + resolve: (value: string | MessageDescriptor) => string, +): AppCommand[] { const normalized = query.trim().toLowerCase(); if (!normalized) return commands; const terms = normalized.split(/\s+/); return commands.filter((command) => { const haystack = [ command.id, - command.title, - command.group, - command.subtitle ?? "", + resolve(command.title), + resolve(command.group), + command.subtitle ? resolve(command.subtitle) : "", ...(command.keywords ?? []), ] .join(" ") diff --git a/src/renderer/commands/registry.ts b/src/renderer/commands/registry.ts index 3390ffca..2064ff96 100644 --- a/src/renderer/commands/registry.ts +++ b/src/renderer/commands/registry.ts @@ -1,7 +1,10 @@ import { toast } from "@heroui/react"; +import { msg } from "@lingui/core/macro"; +import type { MessageDescriptor } from "@lingui/core"; import { buildWorktreeLocation } from "@/shared/worktree"; import type { AgentSlashCommand, Project, Thread } from "@/shared/contracts"; import { readBridge } from "@/renderer/bridge"; +import { i18n } from "@/renderer/i18n/i18n"; import { captureThreadInputSubmitted } from "@/renderer/analytics/posthog"; import { getCurrentProjectId } from "@/renderer/actions/currentProject"; import { @@ -24,9 +27,9 @@ import { evaluateWhenClause } from "./when"; export interface AppCommand { id: string; - title: string; - group: string; - subtitle?: string; + title: string | MessageDescriptor; + group: string | MessageDescriptor; + subtitle?: string | MessageDescriptor; keywords?: string[]; when?: string; run: (args?: unknown) => void | Promise; @@ -82,20 +85,20 @@ function baseCommands(): AppCommand[] { return [ { id: "palette.open", - title: "Open Command Palette", + title: msg`Open Command Palette`, group: "Lightcode", run: () => useCommandPaletteStore.getState().open(), }, { id: "settings.open", - title: "Open Settings", + title: msg`Open Settings`, group: "Lightcode", run: openSettings, }, { id: "project.settings.open", - title: "Open Project Settings", - group: "Project", + title: msg`Open Project Settings`, + group: msg`Project`, when: "hasProject", run: () => { const project = resolveActiveContext().project; @@ -104,21 +107,21 @@ function baseCommands(): AppCommand[] { }, { id: "thread.new", - title: "New Thread", - group: "Thread", + title: msg`New Thread`, + group: msg`Thread`, when: "hasProject", run: () => openNewThread(resolveActiveContext().project?.id), }, { id: "thread.search.open", - title: "Search Threads", - group: "Thread", + title: msg`Search Threads`, + group: msg`Thread`, run: () => usePanelStore.getState().openThreadSearch(), }, { id: "terminal.toggle", - title: "Toggle Terminal", - group: "Terminal", + title: msg`Toggle Terminal`, + group: msg`Terminal`, when: "hasProject", run: () => { const active = resolveActiveContext(); @@ -132,15 +135,15 @@ function baseCommands(): AppCommand[] { }, { id: "terminal.command.run", - title: "Run Terminal Command", - group: "Terminal", + title: msg`Run Terminal Command`, + group: msg`Terminal`, when: "hasProject", run: (args) => runTerminalCommand(args), }, { id: "files.open", - title: "Open Files", - group: "Project", + title: msg`Open Files`, + group: msg`Project`, when: "hasProject", run: () => { const active = resolveActiveContext(); @@ -149,8 +152,8 @@ function baseCommands(): AppCommand[] { }, { id: "git.open", - title: "Open Git Review", - group: "Project", + title: msg`Open Git Review`, + group: msg`Project`, when: "hasProject", run: () => { const active = resolveActiveContext(); @@ -159,15 +162,15 @@ function baseCommands(): AppCommand[] { }, { id: "pane.close", - title: "Close Pane", - group: "Thread", + title: msg`Close Pane`, + group: msg`Thread`, when: "threadView", run: closeFocusedPane, }, { id: "editor.save", - title: "Save File", - group: "Editor", + title: msg`Save File`, + group: msg`Editor`, when: "editorOpen", run: () => { const editor = useFileEditorStore.getState(); @@ -176,22 +179,23 @@ function baseCommands(): AppCommand[] { }, { id: "editor.close", - title: "Close Editor Tab", - group: "Editor", + title: msg`Close Editor Tab`, + group: msg`Editor`, when: "editorOpen", run: () => { const editor = useFileEditorStore.getState(); const path = editor.activePath; if (!path) return; const buffer = editor.buffers[path]; - if (buffer?.isDirty && !window.confirm(`Discard unsaved changes in ${path}?`)) return; + if (buffer?.isDirty && !window.confirm(i18n._(msg`Discard unsaved changes in ${path}?`))) + return; editor.closeTab(path); }, }, { id: "editor.open", - title: "Open File", - group: "Editor", + title: msg`Open File`, + group: msg`Editor`, when: "hasProject", run: (args) => openFileFromArgs(args), }, @@ -205,7 +209,7 @@ function projectScriptCommands(): AppCommand[] { const command: AppCommand = { id: `script.${action.id}.run`, title: action.name, - group: "Scripts", + group: msg`Scripts`, keywords: [action.command, "project action", "script"], when: "hasProject", run: () => @@ -227,7 +231,7 @@ function chatCommand(command: AgentSlashCommand, thread: Thread): AppCommand { return { id: `chat.command.${command.id}`, title: `/${command.id}`, - group: "Chat Commands", + group: msg`Chat Commands`, subtitle: command.description ?? command.label, keywords: [command.label, command.description ?? ""], when: "hasThread", @@ -287,7 +291,9 @@ function openFileFromArgs(args: unknown): void { void useFileEditorStore .getState() .openFile(args.path, "fullscreen", false, readLineNumber(args)) - .catch((error) => toast.danger(error instanceof Error ? error.message : "Unable to open file")); + .catch((error) => + toast.danger(error instanceof Error ? error.message : i18n._(msg`Unable to open file`)), + ); } function readLineNumber(args: Record): { lineNumber?: number } | undefined { diff --git a/src/renderer/components/common/BranchSelector/BranchSelector.test.tsx b/src/renderer/components/common/BranchSelector/BranchSelector.test.tsx index 002b7b1e..be241746 100644 --- a/src/renderer/components/common/BranchSelector/BranchSelector.test.tsx +++ b/src/renderer/components/common/BranchSelector/BranchSelector.test.tsx @@ -1,4 +1,5 @@ -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { fireEvent, screen, waitFor } from "@testing-library/react"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { BranchSelector } from "./BranchSelector"; diff --git a/src/renderer/components/common/BranchSelector/BranchSelector.tsx b/src/renderer/components/common/BranchSelector/BranchSelector.tsx index 6cbc19bb..7925d169 100644 --- a/src/renderer/components/common/BranchSelector/BranchSelector.tsx +++ b/src/renderer/components/common/BranchSelector/BranchSelector.tsx @@ -1,4 +1,5 @@ import { type ReactNode, useEffect, useRef, useState } from "react"; +import { Plural, Trans, useLingui } from "@lingui/react/macro"; import { ChevronDown, GitBranch, GitFork, Search } from "lucide-react"; import { Popover, toast, Tooltip } from "@heroui/react"; import type { GitBranchInfo } from "@/shared/contracts"; @@ -77,6 +78,7 @@ export function BranchSelector(props: BranchSelectorProps) { compact = false, } = props; const triggerIconSize = compact ? "size-3" : "size-3.5"; + const { t } = useLingui(); const [isOpen, setIsOpen] = useState(false); const [search, setSearch] = useState(""); @@ -166,7 +168,7 @@ export function BranchSelector(props: BranchSelectorProps) { setPendingDelete({ branch, ...(worktreePath ? { worktreePath } : {}), - threadIds: threads.map((t) => t.id), + threadIds: threads.map((thread) => thread.id), threadCount: threads.length, }); } @@ -246,11 +248,11 @@ export function BranchSelector(props: BranchSelectorProps) { }); if (result.changesTransferred === false) { toast.danger( - `Created a worktree on "${newBranch}", but the changes conflicted and remain in a git stash — resolve them in the worktree.`, + t`Created a worktree on "${newBranch}", but the changes conflicted and remain in a git stash — resolve them in the worktree.`, ); } else { toast.success( - `Moved your changes into a new worktree on "${newBranch}". "${currentBranch}" is now clean.`, + t`Moved your changes into a new worktree on "${newBranch}". "${currentBranch}" is now clean.`, ); } } catch (error) { @@ -268,7 +270,7 @@ export function BranchSelector(props: BranchSelectorProps) { {trigger ?? (
{items.length === 0 ? ( -
No models found
+
+ No models found +
) : ( void; }) { const { domIdPrefix, items, selectedKeys, scrollRef, toggleFavorite, onSelect } = props; + const { t } = useLingui(); const [visibleRow, setVisibleRow] = useState(0); const [scrollTop, setScrollTop] = useState(0); const [activeRowId, setActiveRowId] = useState(() => { @@ -596,7 +601,7 @@ function WindowedProviderModelList(props: {
= 0 ? `${domIdPrefix}-${items[activeIndex]?.id}` : undefined } @@ -765,7 +770,7 @@ function WindowedProviderModelList(props: { {item.hideFavoriteToggle ? null : (
); } diff --git a/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts b/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts index 3b766d8f..b3f0d6fb 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/buildItems.ts @@ -1,3 +1,5 @@ +import { msg } from "@lingui/core/macro"; +import type { MessageDescriptor } from "@lingui/core"; import { baseAgentKind, type AgentCapability, @@ -396,7 +398,7 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr function pushShortcutSection( sectionId: string, - headerLabel: string, + headerLabel: MessageDescriptor, refs: readonly ModelRef[], ): void { // Favorites/recents store one entry per (agentKind, modelId, presentationMode). @@ -447,14 +449,14 @@ export function buildProviderModelItems(input: BuildProviderModelItemsInput): Pr if (!singleProviderMode) { if (favorites?.length) { - pushShortcutSection("fav", "Favorites", favorites); + pushShortcutSection("fav", msg`Favorites`, favorites); } if (recents?.length) { const filteredRecents = recents .filter((r) => !sectionFavoriteSet.has(refKey(r))) .slice(0, recentsLimit); if (filteredRecents.length > 0) { - pushShortcutSection("recent", "Recent", filteredRecents); + pushShortcutSection("recent", msg`Recent`, filteredRecents); } } } diff --git a/src/renderer/components/common/ProviderModelMenu/parts/types.ts b/src/renderer/components/common/ProviderModelMenu/parts/types.ts index af3b689f..124b79b4 100644 --- a/src/renderer/components/common/ProviderModelMenu/parts/types.ts +++ b/src/renderer/components/common/ProviderModelMenu/parts/types.ts @@ -1,9 +1,10 @@ +import type { MessageDescriptor } from "@lingui/core"; import type { ThreadPresentationMode } from "@/shared/contracts"; export interface ProviderModelHeaderPlain { type: "header-plain"; id: string; - label: string; + label: MessageDescriptor; } export interface ProviderModelHeaderProvider { diff --git a/src/renderer/components/composer/AttachmentBar.test.tsx b/src/renderer/components/composer/AttachmentBar.test.tsx index 4c018084..28f14c26 100644 --- a/src/renderer/components/composer/AttachmentBar.test.tsx +++ b/src/renderer/components/composer/AttachmentBar.test.tsx @@ -1,5 +1,6 @@ -import { fireEvent, render, screen } from "@testing-library/react"; +import { fireEvent, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; +import { renderWithI18n as render } from "@/renderer/testUtils/i18n"; import { AttachmentBar } from "./AttachmentBar"; import type { Attachment } from "./useAttachments"; diff --git a/src/renderer/components/composer/AttachmentBar.tsx b/src/renderer/components/composer/AttachmentBar.tsx index 96f9a2a3..ce4d2d24 100644 --- a/src/renderer/components/composer/AttachmentBar.tsx +++ b/src/renderer/components/composer/AttachmentBar.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from "react"; import { Tooltip } from "@heroui/react"; import { Globe, X } from "lucide-react"; +import { useLingui } from "@lingui/react/macro"; import { getEntryIconUrl } from "@/renderer/components/common/fileIcons"; import { toLocalFileUrl } from "@/shared/promptContent"; import type { Attachment } from "./useAttachments"; @@ -10,7 +11,9 @@ export function BrowserChip(props: { title?: string; variant?: "chip" | "header"; }) { - const { onRemove, title = "Browser MCP enabled for this thread", variant = "chip" } = props; + const { t } = useLingui(); + const { onRemove, variant = "chip" } = props; + const title = props.title ?? t`Browser MCP enabled for this thread`; if (variant === "header") { // Same structure as the other header buttons (CircleCheck / ArrowRightLeft // / Bug / X) so the indicator slots into the row without alignment drift. @@ -41,12 +44,12 @@ export function BrowserChip(props: { role={onRemove ? "group" : "img"} >