diff --git a/windows/tauri/src/App.tsx b/windows/tauri/src/App.tsx index 83c1560c..b58a4787 100644 --- a/windows/tauri/src/App.tsx +++ b/windows/tauri/src/App.tsx @@ -6,6 +6,7 @@ import { traceWindowOpen, traceWindowOpenAfterFrame, } from "@/features/window/utils/window-open-diagnostics"; +import { LocaleProvider } from "./i18n/locale-provider"; const WorkbenchApp = lazy(() => import("./workbench-app")); @@ -92,7 +93,9 @@ function App() { return ( }> - + + + ); } diff --git a/windows/tauri/src/config/backend-capabilities.test.ts b/windows/tauri/src/config/backend-capabilities.test.ts new file mode 100644 index 00000000..132dd6e0 --- /dev/null +++ b/windows/tauri/src/config/backend-capabilities.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, test } from "bun:test"; +import { BACKEND_UNAVAILABLE_TOOLTIP, backendCapabilities } from "./backend-capabilities"; +import { defaultSettings } from "@/features/settings/config/default-settings"; + +describe("default Windows workbench capability policy", () => { + test("does not enable unavailable feature families by default", () => { + expect(BACKEND_UNAVAILABLE_TOOLTIP).toBe("待开发"); + + for (const capability of ["github", "remote", "docker", "agent", "collaboration"] as const) { + expect(backendCapabilities[capability]).toBe(false); + } + + expect(defaultSettings.coreFeatures.github).toBe(false); + expect(defaultSettings.coreFeatures.remote).toBe(false); + expect(defaultSettings.coreFeatures.docker).toBe(false); + expect(defaultSettings.coreFeatures.aiChat).toBe(false); + expect(defaultSettings.coreFeatures.teamCollaboration).toBe(false); + }); +}); diff --git a/windows/tauri/src/features/command-palette/components/command-palette.tsx b/windows/tauri/src/features/command-palette/components/command-palette.tsx index a85c1865..e52686b3 100644 --- a/windows/tauri/src/features/command-palette/components/command-palette.tsx +++ b/windows/tauri/src/features/command-palette/components/command-palette.tsx @@ -1,11 +1,9 @@ import { appDataDir } from "@tauri-apps/api/path"; -import { ClockCounterClockwiseIcon as History, PuzzlePieceIcon as Puzzle } from "@/ui/icons"; +import { ClockCounterClockwiseIcon as History } from "@/ui/icons"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { useUIExtensionStore } from "@/extensions/ui/stores/ui-extension-store"; import { IconThemeSelectorContent } from "@/features/command-palette/components/icon-theme-selector"; import { ThemeSelectorContent } from "@/features/command-palette/components/theme-selector"; import { useEditorSettingsStore } from "@/features/editor/stores/settings.store"; -import { DatabaseCommandContent } from "@/features/database/components/database-sidebar"; import { useLspStore } from "@/features/editor/lsp/stores/lsp.store"; import { useBufferStore } from "@/features/editor/stores/buffer.store"; import { isMarkdownFile } from "@/features/editor/utils/lines"; @@ -20,7 +18,6 @@ import { unstageAllFiles, } from "@/features/git/api/git-status-api"; import { useRepositoryStore } from "@/features/git/stores/git-repository.store"; -import { useGitHubStore } from "@/features/github/stores/github.store"; import { useToast } from "@/features/layout/contexts/toast-context"; import { useOnboardingStore } from "@/features/onboarding/stores/onboarding.store"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; @@ -42,11 +39,9 @@ import Command, { import Keybinding from "@/features/keymaps/components/keybinding"; import { matchesSearchQuery } from "@/utils/search-match"; import { createAdvancedActions } from "../constants/advanced-actions"; -import { createDatabaseActions } from "../constants/database-actions"; import { createFileActions } from "../constants/file-actions"; import { createGenerateActions } from "../constants/generate-actions"; import { createGitActions } from "../constants/git-actions"; -import { createGitHubActions } from "../constants/github-actions"; import { createMarkdownActions } from "../constants/markdown-actions"; import { createNavigationActions } from "../constants/navigation-actions"; import { createPaneActions } from "../constants/pane-actions"; @@ -154,8 +149,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont const lspStatus = useLspStore.use.lspStatus(); const rootFolderPath = useFileSystemStore((state) => state.rootFolderPath); const activeRepoPath = useRepositoryStore.use.activeRepoPath(); - const { checkAuth: checkGitHubAuth } = useGitHubStore.use.actions(); - const extensionCommands = useUIExtensionStore.use.commands(); const extensionViews = useCommandPaletteViews(); const { showToast } = useToast(); const openWhatsNew = useWhatsNewStore((state) => state.actions.open); @@ -170,7 +163,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont switchToPreviousBuffer, reopenClosedTab, openWebViewerBuffer, - openGitHubFormBuffer, openContent, } = useBufferStore.use.actions(); const { zoomIn, zoomOut, resetZoom } = useZoomStore.use.actions(); @@ -289,8 +281,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont ...createNavigationActions({ setIsSidebarVisible, setActiveView, - setIsBottomPaneVisible, - setBottomPaneActiveTab, setIsQuickOpenVisible, openCommandPaletteView, openSettingsDialog, @@ -314,26 +304,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont ...createGenerateActions({ onClose, }), - ...Array.from(extensionCommands.values()).map( - (command): Action => ({ - id: `extension-command:${command.id}`, - label: command.title, - description: command.category - ? `${command.category} extension command` - : "Installed extension command", - icon: , - category: command.category ?? "Extensions", - action: () => { - onClose(); - void Promise.resolve(command.execute()).catch((error) => { - showToast({ - message: error instanceof Error ? error.message : "Extension command failed", - type: "error", - }); - }); - }, - }), - ), ...createWindowActions({ onClose, }), @@ -354,27 +324,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont }, onClose, }), - ...createGitHubActions({ - repoPath: activeRepoPath ?? rootFolderPath ?? null, - setIsSidebarVisible, - setActiveView, - settings: { - showGitHubPullRequests: commandSettings.showGitHubPullRequests, - showGitHubIssues: commandSettings.showGitHubIssues, - showGitHubActions: commandSettings.showGitHubActions, - }, - updateSetting: useSettingsStore.getState().actions.updateSetting as ( - key: string, - value: any, - ) => void | Promise, - checkAuth: checkGitHubAuth, - showToast, - openGitHubFormBuffer, - onClose, - }), - ...createDatabaseActions({ - openDatabaseCommand: () => pushView("databases"), - }), ...createAdvancedActions({ lspStatus, vimMode: commandSettings.vimMode, @@ -481,12 +430,6 @@ const CommandPaletteContent = ({ commandPaletteInitialView }: CommandPaletteCont onBack={popView} onClose={onClose} /> - ) : currentView === "databases" ? ( - ) : extensionView ? ( extensionView.render({ isActive: true, diff --git a/windows/tauri/src/features/command-palette/constants/advanced-actions.tsx b/windows/tauri/src/features/command-palette/constants/advanced-actions.tsx index 062fc9b8..cdbcfec0 100644 --- a/windows/tauri/src/features/command-palette/constants/advanced-actions.tsx +++ b/windows/tauri/src/features/command-palette/constants/advanced-actions.tsx @@ -10,7 +10,6 @@ import { stopAllLanguageServers, } from "@/features/keymaps/commands/lsp-command-actions"; import { openLitheLogBuffer } from "@/features/settings/services/lithe-log-service"; -import { useUIState } from "@/features/window/stores/ui-state.store"; import { showAlertDialog } from "@/ui/dialog"; import type { Action } from "../types/action.types"; @@ -36,18 +35,6 @@ export const createAdvancedActions = (params: AdvancedActionsParams): Action[] = const { lspStatus, vimMode, vimCommands, setMode, openQuickEdit, showToast, onClose } = params; const baseActions: Action[] = [ - { - id: "ai-new-agent", - label: "AI: New Agent", - description: "Open the unified agent launcher", - icon: , - category: "AI", - commandId: "workbench.agentLauncher", - action: () => { - useUIState.getState().setIsAgentLauncherVisible(true); - onClose(); - }, - }, { id: "ai-quick-edit", label: "AI: Quick Edit Selection", diff --git a/windows/tauri/src/features/command-palette/constants/navigation-actions.tsx b/windows/tauri/src/features/command-palette/constants/navigation-actions.tsx index 9e39e06b..57876730 100644 --- a/windows/tauri/src/features/command-palette/constants/navigation-actions.tsx +++ b/windows/tauri/src/features/command-palette/constants/navigation-actions.tsx @@ -1,27 +1,19 @@ import { FileTextIcon as FileText, FolderOpenIcon as FolderOpen, - BugBeetleIcon as BugBeetle, GitBranchIcon as GitBranch, - GitPullRequestIcon as GitPullRequest, HashIcon as Hash, ListBulletsIcon as ListBullets, - PackageIcon as Package, MagnifyingGlassIcon as Search, } from "@/ui/icons"; import { useBufferStore } from "@/features/editor/stores/buffer.store"; import type { SidebarView } from "@/features/layout/utils/sidebar-pane-utils"; -import type { - BottomPaneTab, - SettingsTab, -} from "@/features/window/stores/ui-state/types/ui-state.types"; +import type { SettingsTab } from "@/features/window/stores/ui-state/types/ui-state.types"; import type { Action } from "../types/action.types"; interface NavigationActionsParams { setIsSidebarVisible: (v: boolean) => void; setActiveView: (view: SidebarView) => void; - setIsBottomPaneVisible: (v: boolean) => void; - setBottomPaneActiveTab: (tab: BottomPaneTab) => void; setIsQuickOpenVisible: (v: boolean) => void; openCommandPaletteView?: (view: "outline") => void; openSettingsDialog: (tab?: SettingsTab) => void; @@ -33,8 +25,6 @@ export const createNavigationActions = (params: NavigationActionsParams): Action const { setIsSidebarVisible, setActiveView, - setIsBottomPaneVisible, - setBottomPaneActiveTab, setIsQuickOpenVisible, openCommandPaletteView, coreFeatures, @@ -68,32 +58,6 @@ export const createNavigationActions = (params: NavigationActionsParams): Action onClose(); }, }, - { - id: "view-show-github-prs", - label: "View: Show Pull Requests", - description: "Switch to GitHub Pull Requests view", - icon: , - category: "Navigation", - commandId: "workbench.showGitHub", - action: () => { - setIsSidebarVisible(true); - setActiveView("github-prs"); - onClose(); - }, - }, - { - id: "view-show-debugger", - label: "View: Show Run and Debug", - description: "Switch to debugger view", - icon: , - category: "Navigation", - commandId: "workbench.showDebugger", - action: () => { - setBottomPaneActiveTab("debugger"); - setIsBottomPaneVisible(true); - onClose(); - }, - }, ...(coreFeatures.outline ? [ { @@ -123,17 +87,6 @@ export const createNavigationActions = (params: NavigationActionsParams): Action useBufferStore.getState().actions.openGlobalSearchBuffer(); }, }, - { - id: "view-show-extensions", - label: "View: Show Extensions", - description: "Open the extensions tab", - icon: , - category: "Navigation", - action: () => { - onClose(); - useBufferStore.getState().actions.openExtensionsBuffer(); - }, - }, { id: "go-to-line", label: "Go: Go to Line", diff --git a/windows/tauri/src/features/command-palette/constants/settings-actions.tsx b/windows/tauri/src/features/command-palette/constants/settings-actions.tsx index 160d287c..de4eb3ab 100644 --- a/windows/tauri/src/features/command-palette/constants/settings-actions.tsx +++ b/windows/tauri/src/features/command-palette/constants/settings-actions.tsx @@ -79,13 +79,19 @@ const settingsTabLabels: Record = { const settingsTabCommands = (Object.entries(settingsTabLabels) as Array<[SettingsTab, string]>) .map(([tab, label]) => ({ tab, label })) - .filter(({ tab }) => tab !== "language"); + .filter( + ({ tab }) => + !["account", "ai", "collaboration", "enterprise", "language"].includes(tab), + ); function getMatchingSettingsRecords(query: string) { const trimmedQuery = query.trim(); if (trimmedQuery.length < 2) return []; return settingsSearchIndex + .filter( + (record) => !["account", "ai", "collaboration", "enterprise"].includes(record.tab), + ) .filter((record) => record.id !== "editor-vim-mode") .map((record) => { const score = scoreSearchQuery(trimmedQuery, [ diff --git a/windows/tauri/src/features/keymaps/utils/matcher.test.ts b/windows/tauri/src/features/keymaps/utils/matcher.test.ts new file mode 100644 index 00000000..fd38ad7e --- /dev/null +++ b/windows/tauri/src/features/keymaps/utils/matcher.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from "bun:test"; +import { matchKeybinding } from "./matcher"; + +function keyboardEvent(overrides: Partial): KeyboardEvent { + return { + altKey: false, + code: "", + ctrlKey: false, + key: "", + metaKey: false, + shiftKey: false, + ...overrides, + } as KeyboardEvent; +} + +describe("keymap matcher", () => { + test("matches Ctrl+Shift+F by physical key while an IME changes event.key", () => { + const event = keyboardEvent({ + code: "KeyF", + ctrlKey: true, + key: "ㄈ", + shiftKey: true, + }); + + expect(matchKeybinding(event, "cmd+shift+f").matched).toBe(true); + }); + + test("does not treat a modifier-only key as global search", () => { + const event = keyboardEvent({ code: "ShiftLeft", key: "Shift", shiftKey: true }); + + expect(matchKeybinding(event, "cmd+shift+f").matched).toBe(false); + }); +}); diff --git a/windows/tauri/src/features/keymaps/utils/matcher.ts b/windows/tauri/src/features/keymaps/utils/matcher.ts index 251e0d1a..899e55d5 100644 --- a/windows/tauri/src/features/keymaps/utils/matcher.ts +++ b/windows/tauri/src/features/keymaps/utils/matcher.ts @@ -42,7 +42,9 @@ export function eventToKey(event: KeyboardEvent): ParsedKey { // where the character at that physical position differs from the US layout. let key = event.key; const hasModifier = event.metaKey || event.ctrlKey || event.altKey; - if (key === "Dead" || key === "Unidentified" || (hasModifier && CODE_TO_KEY[event.code])) { + if (hasModifier && /^Key[A-Z]$/.test(event.code)) { + key = event.code.slice(3).toLowerCase(); + } else if (key === "Dead" || key === "Unidentified" || (hasModifier && CODE_TO_KEY[event.code])) { key = CODE_TO_KEY[event.code] || event.code; } diff --git a/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx b/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx index 04d7a53f..af0b7d07 100644 --- a/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx +++ b/windows/tauri/src/features/layout/components/bottom-pane/bottom-pane.tsx @@ -1,5 +1,6 @@ import type React from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { isBackendCapabilityAvailable } from "@/config/backend-capabilities"; import DebuggerView from "@/features/debugger/components/debugger-view"; import { useBufferStore } from "@/features/editor/stores/buffer.store"; import { BOTTOM_PANE_ID } from "@/features/panes/constants/pane"; @@ -62,11 +63,25 @@ const BottomPane = () => { }, [bottomPaneActiveTab, isBottomPaneVisible]); useEffect(() => { - if (isBottomPaneVisible && bottomPaneActiveTab === "debugger" && !debuggerEnabled) { + if ( + isBottomPaneVisible && + bottomPaneActiveTab === "debugger" && + (!debuggerEnabled || !isBackendCapabilityAvailable("debugger")) + ) { useUIState.getState().setIsBottomPaneVisible(false); } }, [bottomPaneActiveTab, isBottomPaneVisible, debuggerEnabled]); + useEffect(() => { + if ( + isBottomPaneVisible && + bottomPaneActiveTab === "terminal" && + (!terminalEnabled || !isBackendCapabilityAvailable("terminal")) + ) { + useUIState.getState().setIsBottomPaneVisible(false); + } + }, [bottomPaneActiveTab, isBottomPaneVisible, terminalEnabled]); + useEffect(() => { if ( isBottomPaneVisible && @@ -226,7 +241,7 @@ const BottomPane = () => { >
{/* Terminal Container - Always mounted to preserve terminal sessions */} - {terminalEnabled && ( + {terminalEnabled && isBackendCapabilityAvailable("terminal") && ( { /> )} - {debuggerEnabled && bottomPaneActiveTab === "debugger" && ( + {debuggerEnabled && + isBackendCapabilityAvailable("debugger") && + bottomPaneActiveTab === "debugger" && (
diff --git a/windows/tauri/src/features/layout/components/footer/footer.tsx b/windows/tauri/src/features/layout/components/footer/footer.tsx index 18cda54c..2219431e 100644 --- a/windows/tauri/src/features/layout/components/footer/footer.tsx +++ b/windows/tauri/src/features/layout/components/footer/footer.tsx @@ -5,11 +5,8 @@ import { } from "@/config/backend-capabilities"; import { useDiagnosticsStore } from "@/features/diagnostics/stores/diagnostics.store"; import { useBufferStore } from "@/features/editor/stores/buffer.store"; -import { useExtensionStore } from "@/extensions/registry/extension-store"; -import { useSidebarPaneController } from "@/features/layout/hooks/use-sidebar-pane-controller"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import { useUIState } from "@/features/window/stores/ui-state.store"; -import { useAuthStore } from "@/features/window/stores/auth.store"; import { NotificationsTrigger } from "@/features/notifications/components/notifications-trigger"; import { FOOTER_TRAILING_ITEM_IDS, @@ -18,35 +15,24 @@ import { type FooterTrailingItemId, } from "@/features/layout/config/item-order"; import { orderChromeItems, type ChromeItem } from "@/features/layout/utils/chrome-items"; -import { useFooterDebuggerItem } from "./footer-debugger-item"; import { useFooterGitBranchItem } from "./footer-git-branch-item"; -import { FooterControlBadge, FooterTabControl } from "./footer-tab-control"; +import { FooterTabControl } from "./footer-tab-control"; import { DatabaseIcon, - ExtensionsIcon, - ListIcon, TerminalWindowIcon, - UsersThreeIcon, WarningIcon, } from "@/ui/icons"; import { ChromeBar, ChromeGroup } from "@/ui/chrome"; const Footer = () => { const terminalEnabled = useSettingsStore((state) => state.settings.coreFeatures.terminal); - const debuggerEnabled = useSettingsStore((state) => state.settings.coreFeatures.debugger); const diagnosticsEnabled = useSettingsStore((state) => state.settings.coreFeatures.diagnostics); - const outlineEnabled = useSettingsStore((state) => state.settings.coreFeatures.outline); - const teamCollaborationEnabled = useSettingsStore( - (state) => state.settings.coreFeatures.teamCollaboration, - ); const footerLeadingItemsOrder = useSettingsStore( (state) => state.settings.footerLeadingItemsOrder, ); const footerTrailingItemsOrder = useSettingsStore( (state) => state.settings.footerTrailingItemsOrder, ); - const isRightSidebarVisible = useUIState((state) => state.isRightSidebarVisible); - const activeRightSidebarView = useUIState((state) => state.activeRightSidebarView); const isCommandPaletteVisible = useUIState((state) => state.isCommandPaletteVisible); const commandPaletteInitialView = useUIState((state) => state.commandPaletteInitialView); const isBottomPaneVisible = useUIState((state) => state.isBottomPaneVisible); @@ -54,11 +40,6 @@ const Footer = () => { const setIsBottomPaneVisible = useUIState((state) => state.setIsBottomPaneVisible); const setBottomPaneActiveTab = useUIState((state) => state.setBottomPaneActiveTab); const openCommandPaletteView = useUIState((state) => state.openCommandPaletteView); - const hasTeamsCollaborationAccess = useAuthStore( - (state) => state.subscription?.collaboration?.enabled === true, - ); - const isCollaborationFeatureEnabled = hasTeamsCollaborationAccess && teamCollaborationEnabled; - const { openSidebarView } = useSidebarPaneController(); const isDiagnosticsBufferActive = useBufferStore((state) => { if (!state.activeBufferId) return false; return state.buffers.some( @@ -66,20 +47,7 @@ const Footer = () => { ); }); const openDiagnosticsBuffer = useBufferStore.use.actions().openDiagnosticsBuffer; - const openExtensionsBuffer = useBufferStore.use.actions().openExtensionsBuffer; - const isExtensionsBufferActive = useBufferStore((state) => { - if (!state.activeBufferId) return false; - return state.buffers.some( - (buffer) => buffer.id === state.activeBufferId && buffer.type === "extensions", - ); - }); const branchItem = useFooterGitBranchItem(); - - const debuggerItem = useFooterDebuggerItem( - debuggerEnabled || !isBackendCapabilityAvailable("debugger"), - footerLeadingItemsOrder, - ); - const extensionUpdatesCount = useExtensionStore.use.extensionsWithUpdates().size; const diagnosticsByFile = useDiagnosticsStore.use.diagnosticsByFile(); const diagnosticsCount = Array.from(diagnosticsByFile.values()).reduce( (total, diagnostics) => total + diagnostics.length, @@ -112,7 +80,6 @@ const Footer = () => { ), } : null, - debuggerItem, diagnosticsEnabled ? { id: "diagnostics", @@ -135,33 +102,11 @@ const Footer = () => { ), } : null, - extensionUpdatesCount > 0 - ? { - id: "extensions", - label: "Extension updates", - content: ( - openExtensionsBuffer()} - > - - - {extensionUpdatesCount > 9 ? "9+" : extensionUpdatesCount} - - - ), - } - : null, ]; const footerLeadingItems = footerLeadingItemsSource.filter( (item): item is ChromeItem => item !== null, ); - const shouldShowOutline = outlineEnabled; - const isOutlineActive = isRightSidebarVisible && activeRightSidebarView === "outline"; const isDatabasesActive = isCommandPaletteVisible && commandPaletteInitialView === "databases"; - const isCollaborationActive = isRightSidebarVisible && activeRightSidebarView === "collaboration"; const footerTrailingOrder = useMemo(() => { return normalizeItemOrder( footerTrailingItemsOrder, @@ -170,26 +115,6 @@ const Footer = () => { }, [footerTrailingItemsOrder]); const footerTrailingItems: Array> = [ - ...(shouldShowOutline - ? [ - { - id: "outline" as const, - label: "Outline", - content: ( - { - openSidebarView("outline"); - }} - > - - - ), - }, - ] - : []), { id: "databases", label: "Databases", @@ -209,32 +134,6 @@ const Footer = () => { ), }, - ...(teamCollaborationEnabled - ? [ - { - id: "collaboration" as const, - label: "Collaboration", - content: ( - { - openSidebarView("collaboration"); - }} - > - - - ), - }, - ] - : []), { id: "notifications", label: "Notifications", diff --git a/windows/tauri/src/features/layout/components/main-layout.tsx b/windows/tauri/src/features/layout/components/main-layout.tsx index bf198c0c..74f6de62 100644 --- a/windows/tauri/src/features/layout/components/main-layout.tsx +++ b/windows/tauri/src/features/layout/components/main-layout.tsx @@ -1,20 +1,15 @@ import { lazy, Suspense, useEffect, useRef, useState } from "react"; -import { useChatInitialization } from "@/features/ai/hooks/use-chat-initialization"; -import { useCollaborationPresence } from "@/features/collaboration/hooks/use-collaboration-presence"; import { initializeDebuggerEventBridge } from "@/features/debugger/services/debug-adapter-events"; -import { useBufferStore } from "@/features/editor/stores/buffer.store"; -import { getBufferById } from "@/features/editor/utils/buffer-index"; import { getSymlinkInfo } from "@/features/file-system/controllers/platform"; -import type { FileEntry } from "@/features/file-system/types/app.types"; import { useFileSystemStore } from "@/features/file-system/stores/file-system.store"; import { useFileSystemFolderDrop } from "@/features/file-system/hooks/use-file-system-folder-drop"; import { openDroppedWorkspacePaths } from "@/features/file-system/utils/open-dropped-workspace-paths"; import { useGitStore } from "@/features/git/stores/git.store"; import { isGitChangeRelevant, subscribeToGitChanges } from "@/features/git/events/git-events"; +import { useBufferStore } from "@/features/editor/stores/buffer.store"; import { useOnboardingStore } from "@/features/onboarding/stores/onboarding.store"; import { CachedWorkspaceSplitViews } from "@/features/panes/components/split-view-root"; import { usePaneKeyboard } from "@/features/panes/hooks/use-pane-keyboard"; -import type { PaneContent } from "@/features/panes/types/pane-content.types"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import { useVimStore } from "@/features/vim/stores/vim.store"; import { isWslPath } from "@/features/wsl/utils/wsl-path"; @@ -35,13 +30,8 @@ import { MainSidebar, SidebarActivityRail, } from "./sidebar/main-sidebar"; +import { WelcomeScreen } from "./welcome-screen"; -const AIChat = lazy(() => import("@/features/ai/components/chat/ai-chat")); -const AgentLauncher = lazy(() => - import("@/features/ai/components/agent-launcher").then((module) => ({ - default: module.AgentLauncher, - })), -); const CommandPalette = lazy(() => import("@/features/command-palette/components/command-palette")); const ConnectionDialog = lazy(() => import("@/features/database/components/connection/connection-dialog").then((module) => ({ @@ -79,58 +69,31 @@ const TerminalHost = lazy(() => ); const BottomPane = lazy(() => import("./bottom-pane/bottom-pane")); -const EMPTY_PROJECT_FILES: FileEntry[] = []; -const EMPTY_BUFFERS: PaneContent[] = []; export function MainLayout() { const [deferredSurfacesReady, setDeferredSurfacesReady] = useState(false); - useChatInitialization(); usePaneKeyboard(); - useCollaborationPresence(); const isSidebarVisible = useUIState((state) => state.isSidebarVisible); const activityRailExpanded = useSettingsStore((state) => state.settings.activityRailExpanded); const activityRailWidth = useSettingsStore((state) => state.settings.activityRailWidth); const sidebarWidth = useSettingsStore((state) => state.settings.sidebarWidth); - const aiChatWidth = useSettingsStore((state) => state.settings.aiChatWidth); const showStatusBar = useSettingsStore((state) => state.settings.showStatusBar); - const isRightSidebarVisible = useUIState((state) => state.isRightSidebarVisible); - const activeRightSidebarView = useUIState((state) => state.activeRightSidebarView); const isDatabaseConnectionVisible = useUIState((state) => state.isDatabaseConnectionVisible); const setIsDatabaseConnectionVisible = useUIState( (state) => state.setIsDatabaseConnectionVisible, ); - const showInlineAiChat = useSettingsStore((state) => state.settings.isAIChatVisible); const renderedActivityRailWidth = activityRailExpanded ? activityRailWidth : COLLAPSED_ACTIVITY_RAIL_WIDTH; - const visibleInlineAiChat = showInlineAiChat && deferredSurfacesReady; const leftPaneReservedWidth = - renderedActivityRailWidth + - (isRightSidebarVisible ? sidebarWidth : 0) + - (visibleInlineAiChat ? aiChatWidth : 0); - const aiPaneReservedWidth = - renderedActivityRailWidth + - (isSidebarVisible ? sidebarWidth : 0) + - (isRightSidebarVisible ? sidebarWidth : 0); - const rightPaneReservedWidth = - renderedActivityRailWidth + - (isSidebarVisible ? sidebarWidth : 0) + - (visibleInlineAiChat ? aiChatWidth : 0); + renderedActivityRailWidth + (isSidebarVisible ? sidebarWidth : 0); const vimRelativeLineNumbers = useSettingsStore((state) => state.settings.vimRelativeLineNumbers); const relativeLineNumbers = useVimStore.use.relativeLineNumbers(); const { setRelativeLineNumbers } = useVimStore.use.actions(); - const buffers = useBufferStore((state) => (showInlineAiChat ? state.buffers : EMPTY_BUFFERS)); - const activeBuffer = useBufferStore((state) => { - if (!showInlineAiChat || !state.activeBufferId) return null; - return getBufferById(state.buffers, state.activeBufferId); - }); const handleOpenFolderByPath = useFileSystemStore.use.handleOpenFolderByPath?.(); const handleFileOpen = useFileSystemStore.use.handleFileOpen?.(); const rootFolderPath = useFileSystemStore.use.rootFolderPath?.(); - const allProjectFiles = useFileSystemStore( - (state) => state.projectFilesCache?.files ?? EMPTY_PROJECT_FILES, - ); const switchToProject = useFileSystemStore.use.switchToProject?.(); const setIsSwitchingProject = useFileSystemStore.use.setIsSwitchingProject?.(); const refreshWorkspaceGitStatus = useGitStore((state) => state.actions.refreshWorkspaceGitStatus); @@ -301,83 +264,56 @@ export function MainLayout() { -
-
- - - -
+ {rootFolderPath ? ( + <> +
- -
- {terminalWidthMode === "editor" && deferredSurfacesReady && ( - - - - )} -
+ + + +
+
+ +
+ {terminalWidthMode === "editor" && deferredSurfacesReady && ( + + + + )} +
- {/* Right side panes are ordered from inner to edge. */} - {visibleInlineAiChat ? ( - - - - - - ) : null} - - -
+
- {terminalWidthMode === "full" && deferredSurfacesReady && ( -
- - - + {terminalWidthMode === "full" && deferredSurfacesReady && ( +
+ + + +
+ )}
- )} -
- {showStatusBar ?
: null} + {showStatusBar ?
: null} + + ) : ( + + )} {/* Global modals and overlays */} {deferredSurfacesReady ? ( @@ -385,7 +321,6 @@ export function MainLayout() { - - - {label} - - - ); -} - interface MainSidebarProps { - paneLevel?: "primary" | "edge"; activeView?: SidebarView; isGitActive?: boolean; - isGitHubPRsActive?: boolean; } interface SidebarPaneEntry { @@ -152,29 +103,11 @@ const waitForProjectCarouselPaint = () => export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRailProps) => { const { openSidebarView } = useSidebarPaneController(); const isGitViewActive = useUIState((state) => state.isGitViewActive); - const isGitHubPRsViewActive = useUIState((state) => state.isGitHubPRsViewActive); const isSidebarVisible = useUIState((state) => state.isSidebarVisible); const activeSidebarView = useUIState((state) => state.activeSidebarView); const setIsProjectPickerVisible = useUIState((state) => state.setIsProjectPickerVisible); + const openSettingsDialog = useUIState((state) => state.openSettingsDialog); const openGlobalSearchBuffer = useBufferStore.use.actions().openGlobalSearchBuffer; - const openExtensionsBuffer = useBufferStore.use.actions().openExtensionsBuffer; - const handleNewAgent = useNewAgentAction(); - const handleNewTerminal = useCallback(() => { - const uiState = useUIState.getState(); - uiState.setBottomPaneActiveTab("terminal"); - uiState.setIsBottomPaneVisible(true); - window.dispatchEvent(new CustomEvent("terminal-new")); - }, []); - const handleNewWorktree = useCallback(() => { - openSidebarView("git"); - window.setTimeout(() => { - window.dispatchEvent( - new CustomEvent("lithe:git-palette-action", { - detail: { type: "manage-branches", tab: "worktrees" }, - }), - ); - }, 0); - }, [openSidebarView]); const configuredActivityRailWidth = useSettingsStore((state) => state.settings.activityRailWidth); const openFoldersInNewWindow = useSettingsStore((state) => state.settings.openFoldersInNewWindow); const hiddenSidebarActivityItems = useSettingsStore( @@ -183,15 +116,6 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa const showActivityRailProjectSwitcher = useSettingsStore( (state) => state.settings.showActivityRailProjectSwitcher, ); - const showActivityRailAgentHistory = useSettingsStore( - (state) => state.settings.showActivityRailAgentHistory, - ); - const showActivityRailTerminals = useSettingsStore( - (state) => state.settings.showActivityRailTerminals, - ); - const showActivityRailWorktrees = useSettingsStore( - (state) => state.settings.showActivityRailWorktrees, - ); const showActivityRailProjectIcons = useSettingsStore( (state) => state.settings.showActivityRailProjectIcons, ); @@ -213,12 +137,7 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa const isResizingRef = useRef(false); const isProjectGestureSettlingRef = useRef(false); const projectWheelEndTimerRef = useRef | null>(null); - const isExtensionsBufferActive = useBufferStore((state) => { - const activeBuffer = state.buffers.find((buffer) => buffer.id === state.activeBufferId); - return activeBuffer?.type === "extensions"; - }); const coreFeatures = useSettingsStore((state) => state.settings.coreFeatures); - const extensionViews = useExtensionViews(); const projectTabs = useWorkspaceTabsStore.use.projectTabs(); const activeProject = projectTabs.find((project) => project.isActive); const carouselProject = @@ -246,7 +165,7 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa () => [ { id: "files", - label: "Files", + label: "Project", icon: , }, ...(coreFeatures.search @@ -262,46 +181,25 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa ? [ { id: "git", - label: "Source Control", + label: "Changes", icon: , }, ] : []), - ...(coreFeatures.github - ? [ - { - id: "github-prs", - label: "Pull Requests", - icon: , - }, - ] - : []), - ...(coreFeatures.docker - ? [ - { - id: "docker", - label: "Docker", - icon: , - }, - ] - : []), { - id: "extensions", - label: "Extensions", - icon: , + id: "database", + label: "Database", + icon: , + }, + { + id: "settings", + label: "Settings", + icon: , }, - ...Array.from(extensionViews.values()).map((view) => ({ - id: view.id, - label: view.title, - icon: , - })), ], [ - coreFeatures.docker, coreFeatures.git, - coreFeatures.github, coreFeatures.search, - extensionViews, ], ); @@ -320,17 +218,11 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa const hasHiddenActivityRailItems = hiddenSidebarActivityItems.length > 0 || !showActivityRailProjectSwitcher || - !showActivityRailAgentHistory || - (coreFeatures.terminal && !showActivityRailTerminals) || - (coreFeatures.git && !showActivityRailWorktrees) || !showActivityRailProjectIcons; const showAllActivityRailItems = useCallback(() => { void updateSetting("hiddenSidebarActivityItems", []); void updateSetting("showActivityRailProjectSwitcher", true); - void updateSetting("showActivityRailAgentHistory", true); - void updateSetting("showActivityRailTerminals", true); - void updateSetting("showActivityRailWorktrees", true); void updateSetting("showActivityRailProjectIcons", true); }, [updateSetting]); @@ -741,67 +633,15 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa openGlobalSearchBuffer()} - onExtensionsClick={() => openExtensionsBuffer()} - isExtensionsActive={isExtensionsBufferActive} + onSettingsClick={() => openSettingsDialog()} compact={!expanded} showLabels={expanded} orientation="vertical" /> - - {showActivityRailAgentHistory ? ( - isBackendCapabilityAvailable("agent") ? ( - - ) : ( - } - label="Agents" - /> - ) - ) : null} - {coreFeatures.terminal && showActivityRailTerminals ? ( - isBackendCapabilityAvailable("terminal") ? ( - - ) : ( - } - label="Terminals" - /> - ) - ) : null} - {coreFeatures.git && showActivityRailWorktrees ? ( - isBackendCapabilityAvailable("git") ? ( - - ) : ( - } - label="Worktrees" - /> - ) - ) : null}
)} @@ -858,42 +698,6 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa Actions - - - New Agent - - {coreFeatures.terminal ? ( - - - New Terminal - - ) : null} - {coreFeatures.git ? ( - - - New Worktree - - ) : null} setIsProjectPickerVisible(true)}> Open Project… @@ -902,18 +706,6 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa Search - openExtensionsBuffer()} - disabled={!isBackendCapabilityAvailable("extensions")} - title={ - isBackendCapabilityAvailable("extensions") - ? "Extensions" - : BACKEND_UNAVAILABLE_TOOLTIP - } - > - - Extensions - @@ -943,37 +735,6 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa {item.label} ))} - - void updateSetting("showActivityRailAgentHistory", checked) - } - > - - Agents - - {coreFeatures.terminal ? ( - - void updateSetting("showActivityRailTerminals", checked) - } - > - - Terminals - - ) : null} - {coreFeatures.git ? ( - - void updateSetting("showActivityRailWorktrees", checked) - } - > - - Worktrees - - ) : null} @@ -1001,30 +762,19 @@ export const SidebarActivityRail = memo(({ expanded = false }: SidebarActivityRa }); export const MainSidebar = memo( - ({ paneLevel = "primary", activeView, isGitActive, isGitHubPRsActive }: MainSidebarProps) => { + ({ activeView, isGitActive }: MainSidebarProps) => { const uiGitViewActive = useUIState((state) => state.isGitViewActive); - const uiGitHubPRsViewActive = useUIState((state) => state.isGitHubPRsViewActive); const uiActiveSidebarView = useUIState((state) => state.activeSidebarView); const isGitViewActive = isGitActive ?? uiGitViewActive; - const isGitHubPRsViewActive = isGitHubPRsActive ?? uiGitHubPRsViewActive; const activeSidebarView = activeView ?? uiActiveSidebarView; - const extensionViews = useExtensionViews(); const handleFileSelect = useFileSystemStore.use.handleFileSelect?.(); const rootFolderPath = useFileSystemStore.use.rootFolderPath?.(); const coreFeatures = useSettingsStore((state) => state.settings.coreFeatures); - const hasTeamsCollaborationAccess = useAuthStore( - (state) => state.subscription?.collaboration?.enabled === true, - ); - const isCollaborationFeatureEnabled = - hasTeamsCollaborationAccess && coreFeatures.teamCollaboration; - const isOutlineFeatureEnabled = coreFeatures.outline; const activePaneId: SidebarView = isGitViewActive ? "git" - : isGitHubPRsViewActive - ? "github-prs" - : activeSidebarView; + : activeSidebarView; const allPaneEntries: SidebarPaneEntry[] = [ ...(coreFeatures.git ? [ @@ -1040,57 +790,12 @@ export const MainSidebar = memo( }, ] : []), - ...(coreFeatures.github - ? [ - { - id: "github-prs" as const, - content: , - }, - ] - : []), - ...(coreFeatures.docker - ? [ - { - id: "docker" as const, - content: , - }, - ] - : []), { id: "files", content: , }, - ...(isOutlineFeatureEnabled - ? [ - { - id: "outline" as const, - content: , - }, - ] - : []), - ...(isCollaborationFeatureEnabled - ? [ - { - id: "collaboration" as const, - content: , - }, - ] - : []), - ...Array.from(extensionViews).map( - ([viewId, view]) => - ({ - id: viewId, - content: ( - - {view.render()} - - ), - }) satisfies SidebarPaneEntry, - ), ]; - const paneEntries = allPaneEntries.filter( - (pane) => pane.id === activeSidebarView || getSidebarPaneLevel(pane.id) === paneLevel, - ); + const paneEntries = allPaneEntries; const activePane = (() => { const requestedIndex = paneEntries.findIndex((pane) => pane.id === activePaneId); if (requestedIndex >= 0) return paneEntries[requestedIndex]; diff --git a/windows/tauri/src/features/layout/components/sidebar/sidebar-pane-selector.tsx b/windows/tauri/src/features/layout/components/sidebar/sidebar-pane-selector.tsx index e58d0a2e..6f383b20 100644 --- a/windows/tauri/src/features/layout/components/sidebar/sidebar-pane-selector.tsx +++ b/windows/tauri/src/features/layout/components/sidebar/sidebar-pane-selector.tsx @@ -1,21 +1,16 @@ import { useMemo, type ReactNode } from "react"; -import { - BACKEND_UNAVAILABLE_TOOLTIP, - isBackendCapabilityAvailable, -} from "@/config/backend-capabilities"; +import { BACKEND_UNAVAILABLE_TOOLTIP } from "@/config/backend-capabilities"; +import { useTranslation } from "@/i18n/locale-provider"; import type { CoreFeaturesState } from "@/features/settings/types/feature.types"; -import { useExtensionViews } from "@/extensions/ui/hooks/use-extension-views"; -import { DynamicIcon } from "@/extensions/ui/components/dynamic-icon"; import { normalizeItemOrder } from "@/features/layout/config/item-order"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; import { SidebarListItem } from "@/ui/sidebar"; import { Tabs, TabsList, TabsTrigger } from "@/ui/tabs"; import { - BoxIcon, + DatabaseIcon, + GearIcon, GitBranchIcon, - ExtensionsIcon, FilesIcon, - GitPullRequestIcon, MagnifyingGlassIcon, } from "@/ui/icons"; import Tooltip from "@/ui/tooltip"; @@ -52,14 +47,12 @@ function orderItems(items: T[], orderedIds: string[]) interface SidebarPaneSelectorProps { activeSidebarView: SidebarView; isGitViewActive: boolean; - isGitHubPRsViewActive: boolean; isSidebarVisible?: boolean; coreFeatures: CoreFeaturesState; onViewChange: (view: SidebarView) => void; onSearchClick?: () => void; - onExtensionsClick?: () => void; isSearchActive?: boolean; - isExtensionsActive?: boolean; + onSettingsClick?: () => void; compact?: boolean; showLabels?: boolean; orientation?: "horizontal" | "vertical"; @@ -68,29 +61,26 @@ interface SidebarPaneSelectorProps { export const SidebarPaneSelector = ({ activeSidebarView, isGitViewActive, - isGitHubPRsViewActive, isSidebarVisible = true, coreFeatures, onViewChange, onSearchClick, - onExtensionsClick, isSearchActive = false, - isExtensionsActive = false, + onSettingsClick, compact = false, showLabels = false, orientation = "horizontal", }: SidebarPaneSelectorProps) => { + const { t } = useTranslation(); const isVertical = orientation === "vertical"; const tooltipSide = isVertical ? "right" : "bottom"; const iconClassName = compact || isVertical ? "size-4" : undefined; - const isBufferOwnedSurfaceActive = isSearchActive || isExtensionsActive; + const isBufferOwnedSurfaceActive = isSearchActive; const isPrimarySidebarItemActive = isSidebarVisible && !isBufferOwnedSurfaceActive; const isFilesActive = isPrimarySidebarItemActive && !isGitViewActive && - !isGitHubPRsViewActive && activeSidebarView === "files"; - const extensionViews = useExtensionViews(); const sidebarActivityItemsOrder = useSettingsStore( (state) => state.settings.sidebarActivityItemsOrder, ); @@ -102,13 +92,13 @@ export const SidebarPaneSelector = ({ () => [ { id: "files", - label: showLabels ? "Files" : undefined, + label: showLabels ? t("workbench.project") : undefined, icon: , isActive: isFilesActive, onClick: () => onViewChange("files"), - ariaLabel: "Files", + ariaLabel: t("workbench.project"), tooltip: { - content: "Files", + content: t("workbench.project"), shortcut: "Mod+Shift+E", side: tooltipSide, }, @@ -117,13 +107,13 @@ export const SidebarPaneSelector = ({ ? [ { id: "search", - label: showLabels ? "Search" : undefined, + label: showLabels ? t("workbench.search") : undefined, icon: , isActive: isSearchActive, onClick: onSearchClick, - ariaLabel: "Search", + ariaLabel: t("workbench.search"), tooltip: { - content: "Search", + content: t("workbench.search"), shortcut: "Mod+Shift+F", side: tooltipSide, }, @@ -134,113 +124,59 @@ export const SidebarPaneSelector = ({ ? [ { id: "git", - label: showLabels ? "Source Control" : undefined, + label: showLabels ? t("workbench.changes") : undefined, icon: , isActive: isPrimarySidebarItemActive && isGitViewActive, onClick: () => onViewChange("git"), - disabled: !isBackendCapabilityAvailable("git"), - ariaLabel: "Git Source Control", + ariaLabel: t("workbench.changes"), tooltip: { - content: isBackendCapabilityAvailable("git") - ? "Source Control" - : BACKEND_UNAVAILABLE_TOOLTIP, + content: t("workbench.changes"), shortcut: "Mod+Shift+G", side: tooltipSide, }, } satisfies SidebarPaneItem, ] : []), - ...(coreFeatures.github - ? [ - { - id: "github-prs", - label: showLabels ? "Pull Requests" : undefined, - icon: , - isActive: isPrimarySidebarItemActive && isGitHubPRsViewActive, - onClick: () => onViewChange("github-prs"), - disabled: !isBackendCapabilityAvailable("github"), - ariaLabel: "GitHub Pull Requests", - tooltip: { - content: isBackendCapabilityAvailable("github") - ? "Pull Requests" - : BACKEND_UNAVAILABLE_TOOLTIP, - side: tooltipSide, - }, - } satisfies SidebarPaneItem, - ] - : []), - ...(coreFeatures.docker + { + id: "database", + label: showLabels ? t("workbench.database") : undefined, + icon: , + disabled: true, + ariaLabel: t("workbench.database"), + tooltip: { + content: BACKEND_UNAVAILABLE_TOOLTIP, + side: tooltipSide, + }, + }, + ...(onSettingsClick ? [ { - id: "docker", - label: showLabels ? "Docker" : undefined, - icon: , - isActive: isPrimarySidebarItemActive && activeSidebarView === "docker", - onClick: () => onViewChange("docker"), - disabled: !isBackendCapabilityAvailable("docker"), - ariaLabel: "Docker", + id: "settings", + label: showLabels ? t("workbench.settings") : undefined, + icon: , + onClick: onSettingsClick, + ariaLabel: t("workbench.settings"), tooltip: { - content: isBackendCapabilityAvailable("docker") - ? "Docker" - : BACKEND_UNAVAILABLE_TOOLTIP, + content: t("workbench.settings"), side: tooltipSide, }, } satisfies SidebarPaneItem, ] : []), - { - id: "extensions", - label: showLabels ? "Extensions" : undefined, - icon: , - isActive: isExtensionsActive, - onClick: onExtensionsClick ?? (() => onViewChange("extensions")), - disabled: !isBackendCapabilityAvailable("extensions"), - ariaLabel: "Extensions", - tooltip: { - content: isBackendCapabilityAvailable("extensions") - ? "Extensions" - : BACKEND_UNAVAILABLE_TOOLTIP, - side: tooltipSide, - }, - }, - ...Array.from(extensionViews.values()).map( - (view) => - ({ - id: view.id, - label: showLabels ? view.title : undefined, - icon: , - isActive: isPrimarySidebarItemActive && activeSidebarView === view.id, - onClick: () => onViewChange(view.id), - disabled: !isBackendCapabilityAvailable("extensions"), - ariaLabel: view.title, - tooltip: { - content: isBackendCapabilityAvailable("extensions") - ? view.title - : BACKEND_UNAVAILABLE_TOOLTIP, - side: tooltipSide, - }, - }) satisfies SidebarPaneItem, - ), ], [ - activeSidebarView, coreFeatures.git, - coreFeatures.github, - coreFeatures.docker, coreFeatures.search, - extensionViews, iconClassName, isFilesActive, isPrimarySidebarItemActive, - isGitHubPRsViewActive, isGitViewActive, isSearchActive, - isExtensionsActive, - isSidebarVisible, - onExtensionsClick, onSearchClick, + onSettingsClick, onViewChange, showLabels, + t, tooltipSide, ], ); @@ -263,6 +199,7 @@ export const SidebarPaneSelector = ({ {visibleItems.map((item) => { const itemNode = ( { const tabNode = ( state.recentFolders); + const openRecentFolder = useRecentFoldersStore((state) => state.actions.openRecentFolder); + const removeFromRecents = useRecentFoldersStore((state) => state.actions.removeFromRecents); + const handleOpenFolder = useFileSystemStore((state) => state.handleOpenFolder); + const setIsProjectPickerVisible = useUIState((state) => state.setIsProjectPickerVisible); + const setIsSettingsDialogVisible = useUIState((state) => state.setIsSettingsDialogVisible); + + const visibleRecentFolders = useMemo(() => { + const normalizedQuery = query.trim().toLowerCase(); + const sortedFolders = [...recentFolders].sort( + (left, right) => (right.lastOpenedAt ?? 0) - (left.lastOpenedAt ?? 0), + ); + + if (!normalizedQuery) return sortedFolders; + + return sortedFolders.filter((folder) => { + return ( + folder.name.toLowerCase().includes(normalizedQuery) || + folder.path.toLowerCase().includes(normalizedQuery) + ); + }); + }, [query, recentFolders]); + + return ( +
+ + +
+

+ {t("welcome.title")} +

+ +
+ + + +
+ +
+ {visibleRecentFolders.length > 0 ? ( +
+ {visibleRecentFolders.map((folder) => ( +
+
+ +
+ + +
+ ))} +
+ ) : ( +
+ +
+ {t("welcome.noRecentProjects")} +
+

{t("welcome.openFolderHint")}

+
+ )} +
+
+
+ ); +} diff --git a/windows/tauri/src/features/layout/config/item-order.ts b/windows/tauri/src/features/layout/config/item-order.ts index 02a22c36..ad9aeb7e 100644 --- a/windows/tauri/src/features/layout/config/item-order.ts +++ b/windows/tauri/src/features/layout/config/item-order.ts @@ -1,29 +1,24 @@ -export const HEADER_TRAILING_ITEM_IDS = ["run-actions", "ai-chat", "account"] as const; +export const HEADER_TRAILING_ITEM_IDS = [] as const; export const SIDEBAR_ACTIVITY_ITEM_IDS = [ "files", - "search", "git", - "github-prs", - "docker", - "extensions", + "search", + "database", + "settings", ] as const; export const FOOTER_LEADING_ITEM_IDS = [ "branch", "terminal", - "debugger", "diagnostics", - "extensions", ] as const; export const FOOTER_TRAILING_ITEM_IDS = [ - "outline", "databases", - "collaboration", "notifications", ] as const; -export type HeaderTrailingItemId = (typeof HEADER_TRAILING_ITEM_IDS)[number]; +export type HeaderTrailingItemId = "account"; export type SidebarActivityItemId = (typeof SIDEBAR_ACTIVITY_ITEM_IDS)[number]; -export type FooterLeadingItemId = (typeof FOOTER_LEADING_ITEM_IDS)[number]; +export type FooterLeadingItemId = (typeof FOOTER_LEADING_ITEM_IDS)[number] | "debugger"; export type FooterTrailingItemId = (typeof FOOTER_TRAILING_ITEM_IDS)[number]; export function normalizeItemOrder( diff --git a/windows/tauri/src/features/settings/components/settings-dialog.tsx b/windows/tauri/src/features/settings/components/settings-dialog.tsx index 76f62c17..456f33b6 100644 --- a/windows/tauri/src/features/settings/components/settings-dialog.tsx +++ b/windows/tauri/src/features/settings/components/settings-dialog.tsx @@ -1,6 +1,7 @@ import { CaretDownIcon as CaretDown, MagnifyingGlassIcon as Search } from "@/ui/icons"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useSettingsStore } from "@/features/settings/stores/settings.store"; +import { useTranslation } from "@/i18n/locale-provider"; import { resolveSettingsAccess, resolveVisibleSettingsSection, @@ -10,24 +11,18 @@ import { getSettingSearchTargetKey, SETTINGS_SEARCH_TAB_LABELS, } from "@/features/settings/lib/settings-search"; -import { useAuthStore } from "@/features/window/stores/auth.store"; import { type SettingsTab, useUIState } from "@/features/window/stores/ui-state.store"; import { Card } from "@/ui/card"; import Dialog from "@/ui/dialog"; import { Dropdown, type MenuItem } from "@/ui/dropdown"; import { Empty, EmptyDescription } from "@/ui/empty"; import Input from "@/ui/input"; -import { ScrollArea } from "@/ui/scroll-area"; import type { SearchResult } from "../types/search.types"; import { SETTINGS_TAB_ITEMS, SettingsVerticalTabs } from "./settings-vertical-tabs"; import { AdvancedSettings } from "./tabs/advanced-settings"; -import { AccountSettings } from "./tabs/account-settings"; -import { AISettings } from "./tabs/ai-settings"; import { AppearanceSettings } from "./tabs/appearance-settings"; -import { CollaborationSettings } from "./tabs/collaboration-settings"; import { EditorSettings } from "./tabs/editor-settings"; -import { EnterpriseSettings } from "./tabs/enterprise-settings"; import { GeneralSettings } from "./tabs/general-settings"; import { GitSettings } from "./tabs/git-settings"; import { KeyboardSettings } from "./tabs/keyboard-settings"; @@ -39,13 +34,17 @@ interface SettingsDialogProps { onClose: () => void; } +function getSettingsTabLabelKey(tab: SettingsTab) { + return `settings.tabs.${tab === "file-explorer" ? "files" : tab}`; +} + const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { const { settingsInitialTab, setSettingsInitialTab } = useUIState(); + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState("general"); const lastSettingsTab = useSettingsStore((state) => state.settings.lastSettingsTab); const updateSetting = useSettingsStore((state) => state.actions.updateSetting); - const subscription = useAuthStore((state) => state.subscription); - const settingsAccess = resolveSettingsAccess(subscription); + const settingsAccess = resolveSettingsAccess(null); const { canShowEnterpriseSettings, canShowCollaborationSettings } = settingsAccess; const clearSearch = useSettingsStore((state) => state.actions.clearSearch); @@ -120,7 +119,7 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { const Icon = tab.icon; return { id: tab.id, - label: tab.label, + label: t(getSettingsTabLabelKey(tab.id)), icon: , className: tab.id === activeTab ? "bg-accent text-foreground" : undefined, onClick: () => handleTabChange(tab.id), @@ -194,8 +193,6 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { const renderTabContent = () => { switch (activeTab) { - case "account": - return ; case "general": return ; case "editor": @@ -204,14 +201,8 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { return ; case "appearance": return ; - case "ai": - return ; case "keyboard": return ; - case "collaboration": - return canShowCollaborationSettings ? : ; - case "enterprise": - return canShowEnterpriseSettings ? : ; case "advanced": return ; case "terminal": @@ -234,7 +225,7 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { onClose={onClose} title={ <> - Settings + {t("workbench.settings")} @@ -251,7 +242,7 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => {
{ setSearchQuery(e.target.value); @@ -302,21 +293,17 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { size="flush" className="@container/settings mt-0 mr-2 mb-2 ml-0 min-w-0 flex-1 bg-background max-[720px]:ml-2" > - {renderTabContent()} - +
@@ -362,7 +349,7 @@ const SettingsDialog = ({ isOpen, onClose }: SettingsDialogProps) => { }) ) : ( - No matching settings + {t("settings.noMatching")} )} diff --git a/windows/tauri/src/features/settings/components/settings-vertical-tabs.tsx b/windows/tauri/src/features/settings/components/settings-vertical-tabs.tsx index 1bc11a32..b7e35a3c 100644 --- a/windows/tauri/src/features/settings/components/settings-vertical-tabs.tsx +++ b/windows/tauri/src/features/settings/components/settings-vertical-tabs.tsx @@ -1,5 +1,4 @@ import { - ArrowSquareUpIcon as ArrowSquareUp, CodeBlockIcon as CodeBlock, GearIcon as Gear, GearSixIcon as GearSix, @@ -14,13 +13,9 @@ import { UsersThreeIcon as UsersThree, } from "@/ui/icons"; import type { ComponentType } from "react"; -import { useUpgradeToPro } from "@/features/settings/hooks/use-upgrade-to-pro"; -import { resolveSettingsAccess } from "@/features/settings/lib/settings-access"; import { filterVisibleSettingsTabs } from "@/features/settings/lib/settings-tab-visibility"; -import { useAuthStore } from "@/features/window/stores/auth.store"; +import { useTranslation } from "@/i18n/locale-provider"; import type { SettingsTab } from "@/features/window/stores/ui-state.store"; -import { useProFeature } from "@/extensions/ui/hooks/use-pro-feature"; -import { Button } from "@/ui/button"; import { Empty, EmptyDescription } from "@/ui/empty"; import { ScrollArea } from "@/ui/scroll-area"; import { Tabs, TabsList, TabsTrigger } from "@/ui/tabs"; @@ -110,12 +105,10 @@ export const SettingsVerticalTabs = ({ onTabChange, panelIdForTab = (tab) => `settings-panel-${tab}`, }: SettingsVerticalTabsProps) => { - const subscription = useAuthStore((state) => state.subscription); - const { hasSettingsSync } = useProFeature(); - const { promptUpgrade } = useUpgradeToPro(); - const settingsAccess = resolveSettingsAccess(subscription); + const { t } = useTranslation(); const visibleTabs = filterVisibleSettingsTabs(SETTINGS_TAB_ITEMS, { - ...settingsAccess, + canShowCollaborationSettings: false, + canShowEnterpriseSettings: false, matchingTabs: null, }); @@ -158,7 +151,13 @@ export const SettingsVerticalTabs = ({ )} > - {item.label} + + {t( + `settings.tabs.${ + item.id === "file-explorer" ? "files" : item.id + }`, + )} +
); }) @@ -170,21 +169,6 @@ export const SettingsVerticalTabs = ({ - - {!hasSettingsSync ? ( -
- -
- ) : null} ); }; diff --git a/windows/tauri/src/features/settings/components/tabs/advanced-settings.tsx b/windows/tauri/src/features/settings/components/tabs/advanced-settings.tsx index d090a70e..9b31f76c 100644 --- a/windows/tauri/src/features/settings/components/tabs/advanced-settings.tsx +++ b/windows/tauri/src/features/settings/components/tabs/advanced-settings.tsx @@ -19,12 +19,21 @@ import { Empty, EmptyDescription } from "@/ui/empty"; import Switch from "@/ui/switch"; import Section, { SettingsView, SettingRow } from "../settings-section"; import { getServiceUrls } from "@/config/services"; +import { useTranslation } from "@/i18n/locale-provider"; + +const UNSUPPORTED_FEATURE_IDS = new Set([ + "github", + "remote", + "debugger", + "aiChat", + "teamCollaboration", + "webViewer", +]); -const telemetryDescription = - "Lithe sends anonymous operational metadata for updates and, when enabled, heartbeats, extensions, and crashes; it never sends file paths, project names, prompts, or editor content."; const telemetryLearnMoreUrl = getServiceUrls().telemetryDocsUrl; export const AdvancedSettings = () => { + const { t } = useTranslation(); const coreFeatures = useSettingsStore((state) => state.settings.coreFeatures); const telemetry = useSettingsStore((state) => state.settings.telemetry); const updateSetting = useSettingsStore((state) => state.actions.updateSetting); @@ -40,11 +49,11 @@ export const AdvancedSettings = () => { const handleResetSettings = () => { resetToDefaults(); - showToast({ message: "Settings reset to defaults", type: "success" }); + showToast({ message: t("settings.advanced.settingsReset"), type: "success" }); }; const defaultCoreFeatures = getDefaultSetting("coreFeatures"); const coreFeaturesList = createCoreFeaturesList(coreFeatures).filter( - (feature: CoreFeature) => feature.id !== "git", + (feature: CoreFeature) => feature.id !== "git" && !UNSUPPORTED_FEATURE_IDS.has(feature.id), ); const handleCoreFeatureToggle = (featureId: string, enabled: boolean) => { @@ -63,7 +72,7 @@ export const AdvancedSettings = () => { const handleClearTelemetryLog = async () => { await clearTelemetryLogEntries(); - showToast({ message: "Telemetry log cleared", type: "success" }); + showToast({ message: t("settings.advanced.telemetryCleared"), type: "success" }); }; const handleExportSettings = async () => { @@ -71,8 +80,8 @@ export const AdvancedSettings = () => { const targetPath = await save({ defaultPath: "lithe-settings.json", filters: [ - { name: "JSON", extensions: ["json"] }, - { name: "All Files", extensions: ["*"] }, + { name: t("settings.common.json"), extensions: ["json"] }, + { name: t("settings.common.allFiles"), extensions: ["*"] }, ], }); @@ -82,7 +91,7 @@ export const AdvancedSettings = () => { const payload = createSettingsExportPayload(useSettingsStore.getState().settings); await writeTextFile(targetPath, JSON.stringify(payload, null, 2)); - showToast({ message: "Settings exported", type: "success" }); + showToast({ message: t("settings.advanced.settingsExported"), type: "success" }); } catch (error) { console.error("Failed to export settings:", error); const message = @@ -93,7 +102,7 @@ export const AdvancedSettings = () => { : JSON.stringify(error); showToast({ - message: `Failed to export settings: ${message}`, + message: t("settings.advanced.exportFailed", { error: message }), type: "error", }); } @@ -115,14 +124,17 @@ export const AdvancedSettings = () => { const imported = useSettingsStore.getState().actions.updateSettingsFromJSON(text); if (!imported) { - showToast({ message: "Invalid settings file format", type: "error" }); + showToast({ message: t("settings.advanced.invalidFile"), type: "error" }); return; } - showToast({ message: "Settings imported", type: "success" }); + showToast({ message: t("settings.advanced.settingsImported"), type: "success" }); } catch (error) { console.error("Failed to import settings:", error); - showToast({ message: `Failed to import settings: ${error}`, type: "error" }); + showToast({ + message: t("settings.advanced.importFailed", { error: String(error) }), + type: "error", + }); } }; input.click(); @@ -130,19 +142,22 @@ export const AdvancedSettings = () => { return ( -
+
{coreFeaturesList.map((feature: CoreFeature) => ( - Experimental + {t("settings.advanced.experimental")} ) : undefined } - description={feature.description} + description={t(`settings.advanced.feature.${feature.id}.description`)} onReset={() => handleResetFeature(feature.id)} canReset={ feature.enabled !== @@ -157,37 +172,43 @@ export const AdvancedSettings = () => { ))}
-
- +
+ - - + +
-
+
- {telemetryDescription}{" "} + {t("settings.advanced.telemetryDescription")} {" "} - Learn more + {t("settings.advanced.learnMore")} } @@ -199,8 +220,8 @@ export const AdvancedSettings = () => { />
@@ -219,7 +240,7 @@ export const AdvancedSettings = () => {
{telemetryLog.length === 0 ? ( - No telemetry entries yet. + {t("settings.advanced.noTelemetry")} ) : (
diff --git a/windows/tauri/src/features/settings/components/tabs/appearance-settings.tsx b/windows/tauri/src/features/settings/components/tabs/appearance-settings.tsx index f21471cc..1cea7460 100644 --- a/windows/tauri/src/features/settings/components/tabs/appearance-settings.tsx +++ b/windows/tauri/src/features/settings/components/tabs/appearance-settings.tsx @@ -33,8 +33,10 @@ import { deleteCustomTheme, uploadTheme, } from "@/features/settings/utils/theme-upload"; +import { useTranslation } from "@/i18n/locale-provider"; export const AppearanceSettings = () => { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ autoThemeDark: state.settings.autoThemeDark, @@ -147,7 +149,7 @@ export const AppearanceSettings = () => { chooseThemeFile((file) => { void uploadTheme(file).then((result) => { if (!result.success || !result.theme) { - toast.error(result.error ?? "Failed to import theme", { + toast.error(result.error ?? t("settings.appearance.importThemeFailed"), { description: result.details?.slice(0, 4).join("\n"), }); return; @@ -155,8 +157,8 @@ export const AppearanceSettings = () => { toast.success( result.themes?.length === 1 - ? `Imported ${result.theme.name}` - : `Imported ${result.themes?.length ?? 0} theme variants`, + ? t("settings.appearance.importedTheme", { theme: result.theme.name }) + : t("settings.appearance.importedThemeVariants", { count: result.themes?.length ?? 0 }), ); selectImportedTheme(result.theme.id); }); @@ -177,9 +179,9 @@ export const AppearanceSettings = () => { } await Promise.all(fallbackUpdates); await deleteCustomTheme(themeId); - toast.success("Custom theme removed"); + toast.success(t("settings.appearance.customThemeRemoved")); } catch (error) { - toast.error("Failed to remove custom theme", { + toast.error(t("settings.appearance.removeCustomThemeFailed"), { description: error instanceof Error ? error.message : String(error), }); } @@ -191,10 +193,10 @@ export const AppearanceSettings = () => { return ( -
+
updateSetting("syncSystemTheme", getDefaultSetting("syncSystemTheme"))} canReset={settings.syncSystemTheme !== getDefaultSetting("syncSystemTheme")} > @@ -207,8 +209,8 @@ export const AppearanceSettings = () => { {!settings.syncSystemTheme ? ( updateSetting("theme", getDefaultSetting("theme"))} canReset={settings.theme !== getDefaultSetting("theme")} > @@ -228,8 +230,8 @@ export const AppearanceSettings = () => { {settings.syncSystemTheme ? ( <> updateSetting("autoThemeLight", getDefaultSetting("autoThemeLight"))} canReset={settings.autoThemeLight !== getDefaultSetting("autoThemeLight")} > @@ -246,8 +248,8 @@ export const AppearanceSettings = () => { updateSetting("autoThemeDark", getDefaultSetting("autoThemeDark"))} canReset={settings.autoThemeDark !== getDefaultSetting("autoThemeDark")} > @@ -266,8 +268,8 @@ export const AppearanceSettings = () => { ) : null} updateSetting("iconTheme", getDefaultSetting("iconTheme"))} canReset={settings.iconTheme !== getDefaultSetting("iconTheme")} > @@ -284,17 +286,17 @@ export const AppearanceSettings = () => { - Import Lithe theme JSON or create one from an installed theme.{" "} + {t("settings.appearance.customThemesDescription")} {" "} - Format guide + {t("settings.appearance.formatGuide")} } @@ -302,11 +304,11 @@ export const AppearanceSettings = () => {
@@ -321,7 +323,7 @@ export const AppearanceSettings = () => { type="button" size="icon-xs" variant="danger" - tooltip={`Remove ${theme.name}`} + tooltip={t("settings.appearance.removeTheme", { theme: theme.name })} onClick={() => void handleRemoveCustomTheme(theme.id)} > @@ -330,10 +332,10 @@ export const AppearanceSettings = () => { ))}
-
+
updateSetting("uiFontFamily", getDefaultSetting("uiFontFamily"))} canReset={settings.uiFontFamily !== getDefaultSetting("uiFontFamily")} > @@ -346,8 +348,8 @@ export const AppearanceSettings = () => { updateSetting("uiFontSize", getDefaultSetting("uiFontSize"))} canReset={settings.uiFontSize !== getDefaultSetting("uiFontSize")} > @@ -359,15 +361,17 @@ export const AppearanceSettings = () => { onChange={(value) => updateSetting("uiFontSize", value)} className={cn(SETTINGS_CONTROL_WIDTHS.number, "tabular-nums")} size="sm" - aria-label={`UI font size: ${formatUiFontSize(settings.uiFontSize)} pixels`} + aria-label={t("settings.appearance.uiFontSizeAria", { + size: formatUiFontSize(settings.uiFontSize), + })} />
-
+
updateSetting("reduceMotion", getDefaultSetting("reduceMotion"))} canReset={settings.reduceMotion !== getDefaultSetting("reduceMotion")} > @@ -379,8 +383,8 @@ export const AppearanceSettings = () => { updateSetting("showStatusBar", getDefaultSetting("showStatusBar"))} canReset={settings.showStatusBar !== getDefaultSetting("showStatusBar")} > @@ -392,8 +396,8 @@ export const AppearanceSettings = () => { updateSetting("showTabIcons", getDefaultSetting("showTabIcons"))} canReset={settings.showTabIcons !== getDefaultSetting("showTabIcons")} > @@ -405,8 +409,8 @@ export const AppearanceSettings = () => { updateSetting("tabCloseButtonVisibility", getDefaultSetting("tabCloseButtonVisibility")) } @@ -417,9 +421,9 @@ export const AppearanceSettings = () => { updateSetting("windowChromeDensity", value as WindowChromeDensity)} className={SETTINGS_CONTROL_WIDTHS.wide} @@ -454,8 +458,8 @@ export const AppearanceSettings = () => { updateSetting("activityRailExpanded", getDefaultSetting("activityRailExpanded")) } @@ -469,8 +473,8 @@ export const AppearanceSettings = () => { updateSetting("activityRailWidth", getDefaultSetting("activityRailWidth"))} canReset={settings.activityRailWidth !== getDefaultSetting("activityRailWidth")} > @@ -483,13 +487,13 @@ export const AppearanceSettings = () => { className={SETTINGS_CONTROL_WIDTHS.number} size="sm" disabled={!settings.activityRailExpanded} - aria-label={`Activity bar width: ${settings.activityRailWidth} pixels`} + aria-label={t("settings.appearance.activityBarWidthAria", { size: settings.activityRailWidth })} /> updateSetting("sidebarWidth", getDefaultSetting("sidebarWidth"))} canReset={settings.sidebarWidth !== getDefaultSetting("sidebarWidth")} > @@ -501,14 +505,14 @@ export const AppearanceSettings = () => { onChange={(value) => updateSetting("sidebarWidth", value)} className={SETTINGS_CONTROL_WIDTHS.number} size="sm" - aria-label={`Sidebar width: ${settings.sidebarWidth} pixels`} + aria-label={t("settings.appearance.sidebarWidthAria", { size: settings.sidebarWidth })} /> {!IS_MAC && !IS_WINDOWS && !IS_LINUX && ( updateSetting("nativeMenuBar", getDefaultSetting("nativeMenuBar"))} canReset={settings.nativeMenuBar !== getDefaultSetting("nativeMenuBar")} > @@ -525,8 +529,8 @@ export const AppearanceSettings = () => { {!IS_MAC && ( updateSetting("compactMenuBar", getDefaultSetting("compactMenuBar"))} canReset={settings.compactMenuBar !== getDefaultSetting("compactMenuBar")} > @@ -540,8 +544,8 @@ export const AppearanceSettings = () => { )} updateSetting("windowTransparency", getDefaultSetting("windowTransparency")) } @@ -555,8 +559,8 @@ export const AppearanceSettings = () => { updateSetting("openFoldersInNewWindow", getDefaultSetting("openFoldersInNewWindow")) } diff --git a/windows/tauri/src/features/settings/components/tabs/editor-settings.tsx b/windows/tauri/src/features/settings/components/tabs/editor-settings.tsx index 85b7d5ee..e7315334 100644 --- a/windows/tauri/src/features/settings/components/tabs/editor-settings.tsx +++ b/windows/tauri/src/features/settings/components/tabs/editor-settings.tsx @@ -7,8 +7,10 @@ import Section, { SETTINGS_CONTROL_WIDTHS, SettingsView, SettingRow } from "../s import Select from "@/ui/select"; import Switch from "@/ui/switch"; import { FontSelector } from "../font-selector"; +import { useTranslation } from "@/i18n/locale-provider"; export const EditorSettings = () => { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ autoCompletion: state.settings.autoCompletion, @@ -48,26 +50,26 @@ export const EditorSettings = () => { const updateSetting = useSettingsStore((state) => state.actions.updateSetting); const languageOptions = useMemo( () => [ - { value: "auto", label: "Auto Detect" }, + { value: "auto", label: t("settings.editor.autoDetect") }, ...getAllLanguages().map((language) => ({ value: language.id, label: language.displayName, })), ], - [], + [t], ); const renderWhitespaceOptions = [ - { value: "none", label: "None" }, - { value: "boundary", label: "Boundary" }, - { value: "trailing", label: "Trailing" }, - { value: "all", label: "All" }, + { value: "none", label: t("settings.editor.whitespaceNone") }, + { value: "boundary", label: t("settings.editor.whitespaceBoundary") }, + { value: "trailing", label: t("settings.editor.whitespaceTrailing") }, + { value: "all", label: t("settings.editor.whitespaceAll") }, ]; return ( -
+
updateSetting("fontFamily", getDefaultSetting("fontFamily"))} canReset={settings.fontFamily !== getDefaultSetting("fontFamily")} > @@ -80,8 +82,8 @@ export const EditorSettings = () => { updateSetting("fontSize", getDefaultSetting("fontSize"))} canReset={settings.fontSize !== getDefaultSetting("fontSize")} > @@ -96,8 +98,8 @@ export const EditorSettings = () => { updateSetting("editorFontLigatures", getDefaultSetting("editorFontLigatures")) } @@ -111,8 +113,8 @@ export const EditorSettings = () => { updateSetting("editorItalicComments", getDefaultSetting("editorItalicComments")) } @@ -126,8 +128,8 @@ export const EditorSettings = () => { updateSetting("editorLineHeight", getDefaultSetting("editorLineHeight"))} canReset={settings.editorLineHeight !== getDefaultSetting("editorLineHeight")} > @@ -143,8 +145,8 @@ export const EditorSettings = () => { updateSetting("tabSize", getDefaultSetting("tabSize"))} canReset={settings.tabSize !== getDefaultSetting("tabSize")} > @@ -158,8 +160,8 @@ export const EditorSettings = () => { /> updateSetting("wordWrap", getDefaultSetting("wordWrap"))} canReset={settings.wordWrap !== getDefaultSetting("wordWrap")} > @@ -171,8 +173,8 @@ export const EditorSettings = () => { updateSetting("lineNumbers", getDefaultSetting("lineNumbers"))} canReset={settings.lineNumbers !== getDefaultSetting("lineNumbers")} > @@ -184,8 +186,8 @@ export const EditorSettings = () => { updateSetting("renderWhitespace", getDefaultSetting("renderWhitespace"))} canReset={settings.renderWhitespace !== getDefaultSetting("renderWhitespace")} > @@ -202,8 +204,8 @@ export const EditorSettings = () => { updateSetting("renderIndentGuides", getDefaultSetting("renderIndentGuides")) } @@ -217,8 +219,8 @@ export const EditorSettings = () => { updateSetting("highlightOccurrences", getDefaultSetting("highlightOccurrences")) } @@ -232,8 +234,8 @@ export const EditorSettings = () => { updateSetting("vimRelativeLineNumbers", getDefaultSetting("vimRelativeLineNumbers")) } @@ -248,8 +250,8 @@ export const EditorSettings = () => { updateSetting("showMinimap", getDefaultSetting("showMinimap"))} canReset={settings.showMinimap !== getDefaultSetting("showMinimap")} > @@ -261,8 +263,8 @@ export const EditorSettings = () => { updateSetting("editorStickyScroll", getDefaultSetting("editorStickyScroll")) } @@ -276,8 +278,8 @@ export const EditorSettings = () => { updateSetting( "editorBracketPairColorization", @@ -297,8 +299,8 @@ export const EditorSettings = () => { updateSetting("editorSmoothScrolling", getDefaultSetting("editorSmoothScrolling")) } @@ -312,8 +314,8 @@ export const EditorSettings = () => { updateSetting( "editorScrollBeyondLastLine", @@ -332,20 +334,20 @@ export const EditorSettings = () => { updateSetting("editorCursorStyle", getDefaultSetting("editorCursorStyle"))} canReset={settings.editorCursorStyle !== getDefaultSetting("editorCursorStyle")} > updateSetting("editorCursorBlinking", value as typeof settings.editorCursorBlinking) @@ -383,8 +385,8 @@ export const EditorSettings = () => { updateSetting("maxOpenTabs", getDefaultSetting("maxOpenTabs"))} canReset={settings.maxOpenTabs !== getDefaultSetting("maxOpenTabs")} > @@ -399,8 +401,8 @@ export const EditorSettings = () => { updateSetting("horizontalTabScroll", getDefaultSetting("horizontalTabScroll")) } @@ -413,8 +415,8 @@ export const EditorSettings = () => { /> updateSetting("autoSave", getDefaultSetting("autoSave"))} canReset={settings.autoSave !== getDefaultSetting("autoSave")} > @@ -425,8 +427,8 @@ export const EditorSettings = () => { /> updateSetting("defaultLanguage", getDefaultSetting("defaultLanguage"))} canReset={settings.defaultLanguage !== getDefaultSetting("defaultLanguage")} > @@ -443,8 +445,8 @@ export const EditorSettings = () => { updateSetting("autoDetectLanguage", getDefaultSetting("autoDetectLanguage")) } @@ -458,8 +460,8 @@ export const EditorSettings = () => { updateSetting("formatOnSave", getDefaultSetting("formatOnSave"))} canReset={settings.formatOnSave !== getDefaultSetting("formatOnSave")} > @@ -471,8 +473,8 @@ export const EditorSettings = () => { updateSetting("lintOnSave", getDefaultSetting("lintOnSave"))} canReset={settings.lintOnSave !== getDefaultSetting("lintOnSave")} > @@ -484,8 +486,8 @@ export const EditorSettings = () => { updateSetting("autoCompletion", getDefaultSetting("autoCompletion"))} canReset={settings.autoCompletion !== getDefaultSetting("autoCompletion")} > @@ -497,8 +499,8 @@ export const EditorSettings = () => { updateSetting("parameterHints", getDefaultSetting("parameterHints"))} canReset={settings.parameterHints !== getDefaultSetting("parameterHints")} > @@ -510,8 +512,8 @@ export const EditorSettings = () => { updateSetting("inlayHints", getDefaultSetting("inlayHints"))} canReset={settings.inlayHints !== getDefaultSetting("inlayHints")} > @@ -523,8 +525,8 @@ export const EditorSettings = () => { updateSetting("codeLens", getDefaultSetting("codeLens"))} canReset={settings.codeLens !== getDefaultSetting("codeLens")} > @@ -536,8 +538,8 @@ export const EditorSettings = () => { updateSetting("semanticTokens", getDefaultSetting("semanticTokens"))} canReset={settings.semanticTokens !== getDefaultSetting("semanticTokens")} > @@ -549,8 +551,8 @@ export const EditorSettings = () => { updateSetting("breadcrumbShowSymbols", getDefaultSetting("breadcrumbShowSymbols")) } diff --git a/windows/tauri/src/features/settings/components/tabs/file-tree-settings.tsx b/windows/tauri/src/features/settings/components/tabs/file-tree-settings.tsx index 21ed4066..812833f4 100644 --- a/windows/tauri/src/features/settings/components/tabs/file-tree-settings.tsx +++ b/windows/tauri/src/features/settings/components/tabs/file-tree-settings.tsx @@ -7,8 +7,10 @@ import Select from "@/ui/select"; import Textarea from "@/ui/textarea"; import Section, { SETTINGS_CONTROL_WIDTHS, SettingsView, SettingRow } from "../settings-section"; import Switch from "@/ui/switch"; +import { useTranslation } from "@/i18n/locale-provider"; export const FileTreeSettings = () => { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ autoRevealActiveFileInFileTree: state.settings.autoRevealActiveFileInFileTree, @@ -59,18 +61,18 @@ export const FileTreeSettings = () => { return ( -
+
updateSetting("fileTreeSortOrder", getDefaultSetting("fileTreeSortOrder"))} canReset={settings.fileTreeSortOrder !== getDefaultSetting("fileTreeSortOrder")} > updateSetting("displayLanguage", value as "en-US" | "zh-CN")} + className={SETTINGS_CONTROL_WIDTHS.default} + size="md" + variant="default" + /> + +
{available ? ( @@ -155,10 +177,12 @@ export const GeneralSettings = () => { size="sm" > {downloading - ? "Downloading..." + ? t("settings.general.downloading") : installing - ? "Installing..." - : `Install ${updateInfo?.version ?? "update"}`} + ? t("settings.general.installing") + : t("settings.general.installUpdate", { + version: updateInfo?.version ?? t("settings.general.update"), + })} ) : ( )}
@@ -175,20 +199,26 @@ export const GeneralSettings = () => {
{downloading - ? `Lithe ${appVersion || "..."} · Downloading ${downloadProgress?.percentage ?? 0}%` + ? t("settings.general.updateProgress", { + version: appVersion || "...", + percentage: downloadProgress?.percentage ?? 0, + }) : installing - ? `Lithe ${appVersion || "..."} · Installing update...` + ? t("settings.general.updateInstalling", { version: appVersion || "..." }) : available - ? `Lithe ${appVersion || "..."} · Version ${updateInfo?.version} available` + ? t("settings.general.updateAvailable", { + version: appVersion || "...", + availableVersion: updateInfo?.version ?? "", + }) : error - ? `Lithe ${appVersion || "..."} · Failed to check for updates` - : `Lithe ${appVersion || "..."} · App is up to date`} + ? t("settings.general.updateCheckFailed", { version: appVersion || "..." }) + : t("settings.general.upToDate", { version: appVersion || "..." })}
{downloading && downloadProgress ? ( ) : null} @@ -196,14 +226,14 @@ export const GeneralSettings = () => { {error &&
{error}
}
{cliInstalled ? ( @@ -215,16 +245,16 @@ export const GeneralSettings = () => { variant="default" size="sm" > - {cliInstalling ? "Installing..." : "Install"} + {cliInstalling ? t("settings.general.installing") : t("settings.general.install")} )} @@ -233,24 +263,27 @@ export const GeneralSettings = () => {
{cliChecking - ? "Checking..." + ? t("settings.general.checking") : cliInstalled - ? "CLI command is installed at $HOME/.local/bin/lithe" - : "CLI command is not installed."} + ? t("settings.general.cliInstalled") + : t("settings.general.cliNotInstalled")}
- + @@ -274,6 +307,7 @@ function ReportBugCommandDialog({ onClose: () => void; onSelect: (channel: ReportBugChannel) => void; }) { + const { t } = useTranslation(); const inputRef = useRef(null); const [query, setQuery] = useState(""); const [selectedIndex, setSelectedIndex] = useState(0); @@ -282,12 +316,8 @@ function ReportBugCommandDialog({ inputRef.current?.focus(); }, []); - const channels = useMemo( - () => - REPORT_BUG_CHANNELS.filter((channel) => - matchesSearchQuery(query, [channel.label, channel.detail]), - ), - [query], + const channels = REPORT_BUG_CHANNELS.filter((channel) => + matchesSearchQuery(query, [channel.label, t(channel.detailKey)]), ); useEffect(() => { @@ -316,19 +346,19 @@ function ReportBugCommandDialog({ }; return ( - + {channels.length === 0 ? ( - No report channel matches "{query}". + {t("settings.general.noReportChannel", { query })} ) : ( channels.map((channel, index) => ( onSelect(channel)} onMouseEnter={() => setSelectedIndex(index)} title={channel.label} - description={channel.detail} + description={t(channel.detailKey)} /> )) )} diff --git a/windows/tauri/src/features/settings/components/tabs/git-settings.tsx b/windows/tauri/src/features/settings/components/tabs/git-settings.tsx index 46f51502..35ceef9c 100644 --- a/windows/tauri/src/features/settings/components/tabs/git-settings.tsx +++ b/windows/tauri/src/features/settings/components/tabs/git-settings.tsx @@ -1,10 +1,12 @@ import { useShallow } from "zustand/react/shallow"; import { getDefaultSetting, useSettingsStore } from "@/features/settings/stores/settings.store"; +import { useTranslation } from "@/i18n/locale-provider"; import Section, { SETTINGS_CONTROL_WIDTHS, SettingsView, SettingRow } from "../settings-section"; import Select from "@/ui/select"; import Switch from "@/ui/switch"; export const GitSettings = () => { + const { t } = useTranslation(); const settings = useSettingsStore( useShallow((state) => ({ autoRefreshGitStatus: state.settings.autoRefreshGitStatus, @@ -32,10 +34,10 @@ export const GitSettings = () => { return ( -
+
updateSetting("coreFeatures", getDefaultSetting("coreFeatures"))} canReset={settings.coreFeatures.git !== getDefaultSetting("coreFeatures").git} > @@ -43,8 +45,8 @@ export const GitSettings = () => { updateSetting("autoRefreshGitStatus", getDefaultSetting("autoRefreshGitStatus")) } @@ -58,8 +60,8 @@ export const GitSettings = () => { updateSetting("confirmBeforeDiscard", getDefaultSetting("confirmBeforeDiscard")) } @@ -73,10 +75,10 @@ export const GitSettings = () => {
-
+
updateSetting("gitChangesFolderView", getDefaultSetting("gitChangesFolderView")) } @@ -90,8 +92,8 @@ export const GitSettings = () => { updateSetting("showUntrackedFiles", getDefaultSetting("showUntrackedFiles")) } @@ -105,8 +107,8 @@ export const GitSettings = () => { updateSetting("showStagedFirst", getDefaultSetting("showStagedFirst"))} canReset={settings.showStagedFirst !== getDefaultSetting("showStagedFirst")} > @@ -118,8 +120,8 @@ export const GitSettings = () => { updateSetting("openDiffOnClick", getDefaultSetting("openDiffOnClick"))} canReset={settings.openDiffOnClick !== getDefaultSetting("openDiffOnClick")} > @@ -131,8 +133,8 @@ export const GitSettings = () => { updateSetting("compactGitStatusBadges", getDefaultSetting("compactGitStatusBadges")) } @@ -146,8 +148,8 @@ export const GitSettings = () => { updateSetting("collapseEmptyGitSections", getDefaultSetting("collapseEmptyGitSections")) } @@ -163,8 +165,8 @@ export const GitSettings = () => { updateSetting("rememberLastGitPanelMode", getDefaultSetting("rememberLastGitPanelMode")) } @@ -180,8 +182,8 @@ export const GitSettings = () => { updateSetting("gitDefaultDiffView", getDefaultSetting("gitDefaultDiffView")) } @@ -190,8 +192,8 @@ export const GitSettings = () => { setSearchQuery(event.target.value)} leftIcon={Search} @@ -266,36 +275,36 @@ export const KeyboardSettings = () => { , }, { value: "user", - label: "User", + label: t("settings.keyboard.user"), icon: , }, { value: "default", - label: "Default", + label: t("settings.keyboard.default"), icon: , }, { value: "preset", - label: "Preset", + label: t("settings.keyboard.preset"), icon: , }, { value: "preset-changes", - label: "Preset Changes", + label: t("settings.keyboard.presetChanges"), icon: , }, { value: "extension", - label: "Extension", + label: t("settings.keyboard.extension"), icon: , }, ]} @@ -314,11 +323,11 @@ export const KeyboardSettings = () => { - Command - Keybinding - When - Source - Actions + {t("settings.keyboard.command")} + {t("settings.keyboard.keybinding")} + {t("settings.keyboard.when")} + {t("settings.keyboard.source")} + {t("settings.keyboard.actions")} @@ -326,7 +335,7 @@ export const KeyboardSettings = () => { - No keybindings found + {t("settings.keyboard.noKeybindings")} @@ -346,8 +355,8 @@ export const KeyboardSettings = () => { ) : ( updateSetting("vimMode", getDefaultSetting("vimMode"))} canReset={vimMode !== getDefaultSetting("vimMode")} > @@ -359,8 +368,8 @@ export const KeyboardSettings = () => { updateSetting("keybindingPreset", getDefaultSetting("keybindingPreset")) } @@ -369,12 +378,16 @@ export const KeyboardSettings = () => { { name: event.target.value, }) } - placeholder="My Profile" + placeholder={t("settings.terminal.profileNamePlaceholder")} size="md" /> - Shell + + {t("settings.terminal.profileShell")} + { startupDirectory: event.target.value || undefined, }) } - placeholder="Leave empty to use the current workspace directory" + placeholder={t("settings.terminal.startupDirectoryPlaceholder")} size="md" /> - Leave empty to use the current workspace directory. + {t("settings.terminal.startupDirectoryDescription")} - Startup Commands + {t("settings.terminal.startupCommands")}