From c84911647cce9f66ef186476ff281371528f3331 Mon Sep 17 00:00:00 2001 From: Lawrence Chen Date: Tue, 26 May 2026 00:40:58 -0700 Subject: [PATCH 1/4] Remove History mode from right sidebar Drops the History tab from the right sidebar mode picker along with its keyboard shortcut, command palette entries, and the 'Open Full History' menu button. Recently Closed/Focused submenus and Reopen Last Closed still work from the menu bar. --- Resources/Localizable.xcstrings | 28 -- Sources/AppDelegate+HistoryPane.swift | 23 -- ...ntentView+RightSidebarCommandPalette.swift | 6 - Sources/HistoryPanelView.swift | 385 ------------------ Sources/KeyboardShortcutContext.swift | 2 +- Sources/KeyboardShortcutSettings.swift | 7 +- Sources/MainWindowFocusController.swift | 21 +- Sources/RightSidebarMode+Availability.swift | 4 - Sources/RightSidebarPanelView.swift | 119 +----- Sources/RightSidebarToolPanel.swift | 43 +- Sources/cmuxApp+HistoryMenu.swift | 26 -- cmux.xcodeproj/project.pbxproj | 8 - ...ileExplorerStateModePersistenceTests.swift | 1 - .../RightSidebarCommandPaletteTests.swift | 11 +- .../ShortcutAndCommandPaletteTests.swift | 49 --- cmuxTests/WorkspaceUnitTests.swift | 47 +-- web/data/cmux.schema.json | 1 - 17 files changed, 7 insertions(+), 774 deletions(-) delete mode 100644 Sources/AppDelegate+HistoryPane.swift delete mode 100644 Sources/HistoryPanelView.swift diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index 36908317e36f..37247748e32a 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -10106,7 +10106,6 @@ } } }, - "command.openHistoryPane.title": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"ja":{"stringUnit":{"state":"translated","value":"履歴をペインで開く"}},"ar":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"bs":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"da":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"de":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"es":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"fr":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"it":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"ko":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"nb":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"pl":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"ru":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"th":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"tr":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"uk":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Open History as Pane"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Open History as Pane"}}}}, "command.openVaultPane.title": { "extractionState": "manual", "localizations": { @@ -45088,13 +45087,11 @@ "menu.history.noFocusBackHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"No Back History"}},"ja":{"stringUnit":{"state":"translated","value":"戻る履歴がありません"}},"ar":{"stringUnit":{"state":"translated","value":"No Back History"}},"bs":{"stringUnit":{"state":"translated","value":"No Back History"}},"da":{"stringUnit":{"state":"translated","value":"No Back History"}},"de":{"stringUnit":{"state":"translated","value":"No Back History"}},"es":{"stringUnit":{"state":"translated","value":"No Back History"}},"fr":{"stringUnit":{"state":"translated","value":"No Back History"}},"it":{"stringUnit":{"state":"translated","value":"No Back History"}},"ko":{"stringUnit":{"state":"translated","value":"No Back History"}},"nb":{"stringUnit":{"state":"translated","value":"No Back History"}},"pl":{"stringUnit":{"state":"translated","value":"No Back History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"No Back History"}},"ru":{"stringUnit":{"state":"translated","value":"No Back History"}},"th":{"stringUnit":{"state":"translated","value":"No Back History"}},"tr":{"stringUnit":{"state":"translated","value":"No Back History"}},"uk":{"stringUnit":{"state":"translated","value":"No Back History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"No Back History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"No Back History"}}}}, "menu.history.noFocusForwardHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"No Forward History"}},"ja":{"stringUnit":{"state":"translated","value":"進む履歴がありません"}},"ar":{"stringUnit":{"state":"translated","value":"No Forward History"}},"bs":{"stringUnit":{"state":"translated","value":"No Forward History"}},"da":{"stringUnit":{"state":"translated","value":"No Forward History"}},"de":{"stringUnit":{"state":"translated","value":"No Forward History"}},"es":{"stringUnit":{"state":"translated","value":"No Forward History"}},"fr":{"stringUnit":{"state":"translated","value":"No Forward History"}},"it":{"stringUnit":{"state":"translated","value":"No Forward History"}},"ko":{"stringUnit":{"state":"translated","value":"No Forward History"}},"nb":{"stringUnit":{"state":"translated","value":"No Forward History"}},"pl":{"stringUnit":{"state":"translated","value":"No Forward History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"No Forward History"}},"ru":{"stringUnit":{"state":"translated","value":"No Forward History"}},"th":{"stringUnit":{"state":"translated","value":"No Forward History"}},"tr":{"stringUnit":{"state":"translated","value":"No Forward History"}},"uk":{"stringUnit":{"state":"translated","value":"No Forward History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"No Forward History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"No Forward History"}}}}, "menu.history.noFocusHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"No Focus History"}},"ja":{"stringUnit":{"state":"translated","value":"フォーカス履歴がありません"}},"ar":{"stringUnit":{"state":"translated","value":"No Focus History"}},"bs":{"stringUnit":{"state":"translated","value":"No Focus History"}},"da":{"stringUnit":{"state":"translated","value":"No Focus History"}},"de":{"stringUnit":{"state":"translated","value":"No Focus History"}},"es":{"stringUnit":{"state":"translated","value":"No Focus History"}},"fr":{"stringUnit":{"state":"translated","value":"No Focus History"}},"it":{"stringUnit":{"state":"translated","value":"No Focus History"}},"ko":{"stringUnit":{"state":"translated","value":"No Focus History"}},"nb":{"stringUnit":{"state":"translated","value":"No Focus History"}},"pl":{"stringUnit":{"state":"translated","value":"No Focus History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"No Focus History"}},"ru":{"stringUnit":{"state":"translated","value":"No Focus History"}},"th":{"stringUnit":{"state":"translated","value":"No Focus History"}},"tr":{"stringUnit":{"state":"translated","value":"No Focus History"}},"uk":{"stringUnit":{"state":"translated","value":"No Focus History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"No Focus History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"No Focus History"}}}}, - "menu.history.openFullHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"ja":{"stringUnit":{"state":"translated","value":"すべての履歴を開く…"}},"ar":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"bs":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"da":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"de":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"es":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"fr":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"it":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"ko":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"nb":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"pl":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"ru":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"th":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"tr":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"uk":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Open Full History…"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Open Full History…"}}}}, "menu.history.recentlyFocused": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"ja":{"stringUnit":{"state":"translated","value":"最近フォーカスした項目"}},"ar":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"bs":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"da":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"de":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"es":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"fr":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"it":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"ko":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"nb":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"pl":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"ru":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"th":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"tr":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"uk":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Recently Focused"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Recently Focused"}}}}, "menu.history.recentlyFocused.subtitle": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ja":{"stringUnit":{"state":"translated","value":"最近フォーカスした移動先"}},"ar":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"bs":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"da":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"de":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"es":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"fr":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"it":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ko":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"nb":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"pl":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ru":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"th":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"tr":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"uk":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}}}}, "menu.history.menuItemSubtitleFormat": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ja":{"stringUnit":{"state":"translated","value":"%1$@、%2$@"}},"ar":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"bs":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"da":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"de":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"es":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"fr":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"it":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ko":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"nb":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"pl":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"pt-BR":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ru":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"th":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"tr":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"uk":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}}}}, "menu.history.showFullFocusBackHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ja":{"stringUnit":{"state":"translated","value":"戻る履歴をすべて表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full Back History"}}}}, "menu.history.showFullFocusForwardHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ja":{"stringUnit":{"state":"translated","value":"進む履歴をすべて表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}}}}, - "menu.history.showFullFocusHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ja":{"stringUnit":{"state":"translated","value":"すべての履歴を表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full History"}}}}, "menu.history.recentlyClosed": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ja":{"stringUnit":{"state":"translated","value":"最近閉じた項目"}},"ar":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"bs":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"da":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"de":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"es":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"fr":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"it":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ko":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"nb":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"pl":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ru":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"th":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"tr":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"uk":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Recently Closed"}}}}, "menu.history.recentlyClosed.subtitle": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ja":{"stringUnit":{"state":"translated","value":"タブ、ワークスペース、ウィンドウ"}},"ar":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"bs":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"da":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"de":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"es":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"fr":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"it":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ko":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"nb":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"pl":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ru":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"th":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"tr":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"uk":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}}}}, "menu.history.recentlyClosed.clearAll": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Clear All"}},"ja":{"stringUnit":{"state":"translated","value":"すべて消去"}},"ar":{"stringUnit":{"state":"translated","value":"Clear All"}},"bs":{"stringUnit":{"state":"translated","value":"Clear All"}},"da":{"stringUnit":{"state":"translated","value":"Clear All"}},"de":{"stringUnit":{"state":"translated","value":"Clear All"}},"es":{"stringUnit":{"state":"translated","value":"Clear All"}},"fr":{"stringUnit":{"state":"translated","value":"Clear All"}},"it":{"stringUnit":{"state":"translated","value":"Clear All"}},"ko":{"stringUnit":{"state":"translated","value":"Clear All"}},"nb":{"stringUnit":{"state":"translated","value":"Clear All"}},"pl":{"stringUnit":{"state":"translated","value":"Clear All"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Clear All"}},"ru":{"stringUnit":{"state":"translated","value":"Clear All"}},"th":{"stringUnit":{"state":"translated","value":"Clear All"}},"tr":{"stringUnit":{"state":"translated","value":"Clear All"}},"uk":{"stringUnit":{"state":"translated","value":"Clear All"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Clear All"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Clear All"}}}}, @@ -45108,7 +45105,6 @@ "menu.history.recentlyClosed.panel.markdown": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Markdown"}},"ja":{"stringUnit":{"state":"translated","value":"Markdown"}},"ar":{"stringUnit":{"state":"translated","value":"Markdown"}},"bs":{"stringUnit":{"state":"translated","value":"Markdown"}},"da":{"stringUnit":{"state":"translated","value":"Markdown"}},"de":{"stringUnit":{"state":"translated","value":"Markdown"}},"es":{"stringUnit":{"state":"translated","value":"Markdown"}},"fr":{"stringUnit":{"state":"translated","value":"Markdown"}},"it":{"stringUnit":{"state":"translated","value":"Markdown"}},"ko":{"stringUnit":{"state":"translated","value":"Markdown"}},"nb":{"stringUnit":{"state":"translated","value":"Markdown"}},"pl":{"stringUnit":{"state":"translated","value":"Markdown"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Markdown"}},"ru":{"stringUnit":{"state":"translated","value":"Markdown"}},"th":{"stringUnit":{"state":"translated","value":"Markdown"}},"tr":{"stringUnit":{"state":"translated","value":"Markdown"}},"uk":{"stringUnit":{"state":"translated","value":"Markdown"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Markdown"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Markdown"}}}}, "menu.history.recentlyClosed.panel.terminal": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Terminal"}},"ja":{"stringUnit":{"state":"translated","value":"ターミナル"}},"ar":{"stringUnit":{"state":"translated","value":"Terminal"}},"bs":{"stringUnit":{"state":"translated","value":"Terminal"}},"da":{"stringUnit":{"state":"translated","value":"Terminal"}},"de":{"stringUnit":{"state":"translated","value":"Terminal"}},"es":{"stringUnit":{"state":"translated","value":"Terminal"}},"fr":{"stringUnit":{"state":"translated","value":"Terminal"}},"it":{"stringUnit":{"state":"translated","value":"Terminal"}},"ko":{"stringUnit":{"state":"translated","value":"Terminal"}},"nb":{"stringUnit":{"state":"translated","value":"Terminal"}},"pl":{"stringUnit":{"state":"translated","value":"Terminal"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Terminal"}},"ru":{"stringUnit":{"state":"translated","value":"Terminal"}},"th":{"stringUnit":{"state":"translated","value":"Terminal"}},"tr":{"stringUnit":{"state":"translated","value":"Terminal"}},"uk":{"stringUnit":{"state":"translated","value":"Terminal"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Terminal"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Terminal"}}}}, "menu.history.recentlyClosed.panel.tool": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Tool"}},"ja":{"stringUnit":{"state":"translated","value":"ツール"}},"ar":{"stringUnit":{"state":"translated","value":"Tool"}},"bs":{"stringUnit":{"state":"translated","value":"Tool"}},"da":{"stringUnit":{"state":"translated","value":"Tool"}},"de":{"stringUnit":{"state":"translated","value":"Tool"}},"es":{"stringUnit":{"state":"translated","value":"Tool"}},"fr":{"stringUnit":{"state":"translated","value":"Tool"}},"it":{"stringUnit":{"state":"translated","value":"Tool"}},"ko":{"stringUnit":{"state":"translated","value":"Tool"}},"nb":{"stringUnit":{"state":"translated","value":"Tool"}},"pl":{"stringUnit":{"state":"translated","value":"Tool"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Tool"}},"ru":{"stringUnit":{"state":"translated","value":"Tool"}},"th":{"stringUnit":{"state":"translated","value":"Tool"}},"tr":{"stringUnit":{"state":"translated","value":"Tool"}},"uk":{"stringUnit":{"state":"translated","value":"Tool"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Tool"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Tool"}}}}, - "menu.history.recentlyClosed.showFull": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"ja":{"stringUnit":{"state":"translated","value":"最近閉じた項目をすべて表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"da":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"de":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"es":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"it":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"th":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full Recently Closed"}}}}, "menu.history.recentlyClosed.window.oneWorkspace": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ja":{"stringUnit":{"state":"translated","value":"1 個のワークスペース"}},"ar":{"stringUnit":{"state":"translated","value":"1 workspace"}},"bs":{"stringUnit":{"state":"translated","value":"1 workspace"}},"da":{"stringUnit":{"state":"translated","value":"1 workspace"}},"de":{"stringUnit":{"state":"translated","value":"1 workspace"}},"es":{"stringUnit":{"state":"translated","value":"1 workspace"}},"fr":{"stringUnit":{"state":"translated","value":"1 workspace"}},"it":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ko":{"stringUnit":{"state":"translated","value":"1 workspace"}},"nb":{"stringUnit":{"state":"translated","value":"1 workspace"}},"pl":{"stringUnit":{"state":"translated","value":"1 workspace"}},"pt-BR":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ru":{"stringUnit":{"state":"translated","value":"1 workspace"}},"th":{"stringUnit":{"state":"translated","value":"1 workspace"}},"tr":{"stringUnit":{"state":"translated","value":"1 workspace"}},"uk":{"stringUnit":{"state":"translated","value":"1 workspace"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"1 workspace"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"1 workspace"}}}}, "menu.history.recentlyClosed.window.workspaceCount": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"ja":{"stringUnit":{"state":"translated","value":"%d 個のワークスペース"}},"ar":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"bs":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"da":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"de":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"es":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"fr":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"it":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"ko":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"nb":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"pl":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"pt-BR":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"ru":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"th":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"tr":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"uk":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"%d workspaces"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"%d workspaces"}}}}, "menu.history.recentlyClosed.window.workspaceCount.one": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ja":{"stringUnit":{"state":"translated","value":"1 個のワークスペース"}},"ar":{"stringUnit":{"state":"translated","value":"1 workspace"}},"bs":{"stringUnit":{"state":"translated","value":"1 workspace"}},"da":{"stringUnit":{"state":"translated","value":"1 workspace"}},"de":{"stringUnit":{"state":"translated","value":"1 workspace"}},"es":{"stringUnit":{"state":"translated","value":"1 workspace"}},"fr":{"stringUnit":{"state":"translated","value":"1 workspace"}},"it":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ko":{"stringUnit":{"state":"translated","value":"1 workspace"}},"nb":{"stringUnit":{"state":"translated","value":"1 workspace"}},"pl":{"stringUnit":{"state":"translated","value":"1 workspace"}},"pt-BR":{"stringUnit":{"state":"translated","value":"1 workspace"}},"ru":{"stringUnit":{"state":"translated","value":"1 workspace"}},"th":{"stringUnit":{"state":"translated","value":"1 workspace"}},"tr":{"stringUnit":{"state":"translated","value":"1 workspace"}},"uk":{"stringUnit":{"state":"translated","value":"1 workspace"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"1 workspace"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"1 workspace"}}}}, @@ -110428,29 +110424,6 @@ } } }, - "shortcut.switchRightSidebarToHistory.label": { - "extractionState": "manual", - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Show Sidebar History" - } - }, - "ja": { - "stringUnit": { - "state": "translated", - "value": "サイドバーの履歴を表示" - } - }, - "ko": { - "stringUnit": { - "state": "translated", - "value": "사이드바 기록 표시" - } - } - } - }, "menu.view.focusRightSidebar": { "extractionState": "manual", "localizations": { @@ -113190,7 +113163,6 @@ } } }, - "rightSidebar.mode.history": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"History"}},"ja":{"stringUnit":{"state":"translated","value":"履歴"}},"ar":{"stringUnit":{"state":"translated","value":"History"}},"bs":{"stringUnit":{"state":"translated","value":"History"}},"da":{"stringUnit":{"state":"translated","value":"History"}},"de":{"stringUnit":{"state":"translated","value":"History"}},"es":{"stringUnit":{"state":"translated","value":"History"}},"fr":{"stringUnit":{"state":"translated","value":"History"}},"it":{"stringUnit":{"state":"translated","value":"History"}},"ko":{"stringUnit":{"state":"translated","value":"History"}},"nb":{"stringUnit":{"state":"translated","value":"History"}},"pl":{"stringUnit":{"state":"translated","value":"History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"History"}},"ru":{"stringUnit":{"state":"translated","value":"History"}},"th":{"stringUnit":{"state":"translated","value":"History"}},"tr":{"stringUnit":{"state":"translated","value":"History"}},"uk":{"stringUnit":{"state":"translated","value":"History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"History"}}}}, "dock.action.focusControl": { "extractionState": "manual", "localizations": { diff --git a/Sources/AppDelegate+HistoryPane.swift b/Sources/AppDelegate+HistoryPane.swift deleted file mode 100644 index 062e3c0312d6..000000000000 --- a/Sources/AppDelegate+HistoryPane.swift +++ /dev/null @@ -1,23 +0,0 @@ -import AppKit - -extension AppDelegate { - @discardableResult - func openHistoryPane( - preferredTabManager: TabManager? = nil, - preferredWindow: NSWindow? = nil - ) -> Bool { - let manager = preferredTabManager - ?? activeTabManagerForCommands(preferredWindow: preferredWindow ?? NSApp.keyWindow ?? NSApp.mainWindow) - guard let workspace = manager?.selectedWorkspace, - let paneId = workspace.bonsplitController.focusedPaneId ?? workspace.bonsplitController.allPaneIds.first else { - return false - } - - workspace.clearSplitZoom() - return workspace.openOrFocusRightSidebarToolSurface( - inPane: paneId, - mode: .history, - focus: true - ) != nil - } -} diff --git a/Sources/ContentView+RightSidebarCommandPalette.swift b/Sources/ContentView+RightSidebarCommandPalette.swift index 66e17c603a67..5aeb65451087 100644 --- a/Sources/ContentView+RightSidebarCommandPalette.swift +++ b/Sources/ContentView+RightSidebarCommandPalette.swift @@ -131,8 +131,6 @@ extension ContentView { return "palette.showRightSidebarFeed" case .dock: return "palette.showRightSidebarDock" - case .history: - return "palette.showHistoryPane" } } @@ -154,8 +152,6 @@ extension ContentView { return "palette.openFindPane" case .sessions: return "palette.openVaultPane" - case .history: - return "palette.openHistoryPane" case .feed, .dock: return nil } @@ -169,8 +165,6 @@ extension ContentView { return String(localized: "command.openFindPane.title", defaultValue: "Open Find as Pane") case .sessions: return String(localized: "command.openVaultPane.title", defaultValue: "Open Vault as Pane") - case .history: - return String(localized: "command.openHistoryPane.title", defaultValue: "Open History as Pane") case .feed, .dock: return nil } diff --git a/Sources/HistoryPanelView.swift b/Sources/HistoryPanelView.swift deleted file mode 100644 index 5e430fbd9a36..000000000000 --- a/Sources/HistoryPanelView.swift +++ /dev/null @@ -1,385 +0,0 @@ -import AppKit -import SwiftUI - -enum HistoryDayGrouping { - static func matches(query: String, fields: [String]) -> Bool { - let terms = query - .trimmingCharacters(in: .whitespacesAndNewlines) - .split(whereSeparator: { $0.isWhitespace }) - .map(String.init) - guard !terms.isEmpty else { return true } - - let haystack = fields.joined(separator: " ") - return terms.allSatisfy { term in - haystack.localizedCaseInsensitiveContains(term) - } - } - - static func dayTitle( - for date: Date, - now: Date = Date(), - calendar: Calendar = .current - ) -> String { - if calendar.isDate(date, inSameDayAs: now) { - return String(localized: "historyPane.day.today", defaultValue: "Today") - } - - if let yesterday = calendar.date(byAdding: .day, value: -1, to: now), - calendar.isDate(date, inSameDayAs: yesterday) { - return String(localized: "historyPane.day.yesterday", defaultValue: "Yesterday") - } - - return Self.dayFormatter.string(from: date) - } - - private static let dayFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .full - formatter.timeStyle = .none - return formatter - }() -} - -struct HistoryPanelView: View { - @ObservedObject private var closedStore = ClosedItemHistoryStore.shared - @EnvironmentObject private var tabManager: TabManager - - let focusSearchToken: Int - let onFocus: () -> Void - let onOpenClosedItem: (UUID) -> Bool - let onOpenFocusedItem: (FocusHistoryMenuItem) -> Bool - let onClearClosedItems: () -> Void - - @State private var query = "" - @FocusState private var isSearchFieldFocused: Bool - - var body: some View { - let closedSnapshot = closedStore.menuSnapshot() - let focusedBackSnapshot = tabManager.focusHistoryMenuSnapshot(direction: .back) - let focusedForwardSnapshot = tabManager.focusHistoryMenuSnapshot(direction: .forward) - let _ = closedStore.revision - let _ = tabManager.focusHistoryRevision - let closedGroups = groupedRows( - closedSnapshot.items.map(Self.closedRow), - query: query - ) - let focusedGroups = groupedRows( - (focusedBackSnapshot.items + focusedForwardSnapshot.items).map(Self.focusedRow), - query: query - ) - let hasAnyRows = !closedSnapshot.items.isEmpty || - !focusedBackSnapshot.items.isEmpty || - !focusedForwardSnapshot.items.isEmpty - let hasVisibleRows = !closedGroups.isEmpty || !focusedGroups.isEmpty - - VStack(spacing: 0) { - header( - hasClosedItems: !closedSnapshot.items.isEmpty - ) - - searchField - .padding(.horizontal, 12) - .padding(.bottom, 10) - - if !hasAnyRows { - emptyState(String(localized: "historyPane.empty", defaultValue: "No history yet")) - } else if !hasVisibleRows { - emptyState(String(localized: "historyPane.noResults", defaultValue: "No matching history")) - } else { - ScrollView { - LazyVStack(alignment: .leading, spacing: 18) { - if !closedGroups.isEmpty { - historySection( - title: String(localized: "menu.history.recentlyClosed", defaultValue: "Recently Closed"), - groups: closedGroups - ) - } - - if !focusedGroups.isEmpty { - historySection( - title: String(localized: "menu.history.recentlyFocused", defaultValue: "Recently Focused"), - groups: focusedGroups - ) - } - } - .padding(.horizontal, 12) - .padding(.bottom, 18) - } - } - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .contentShape(Rectangle()) - .onTapGesture(perform: onFocus) - .onAppear { - guard focusSearchToken > 0 else { return } - focusSearchField() - } - .onChange(of: focusSearchToken) { _, _ in - focusSearchField() - } - } - - private func header(hasClosedItems: Bool) -> some View { - HStack(spacing: 10) { - Image(systemName: "clock.arrow.circlepath") - .font(.system(size: 13, weight: .medium)) - .foregroundStyle(.secondary) - - Text(String(localized: "menu.history.title", defaultValue: "History")) - .font(.system(size: 13, weight: .semibold)) - - Spacer(minLength: 8) - - Button(String(localized: "historyPane.clearClosed", defaultValue: "Clear Closed")) { - onClearClosedItems() - } - .disabled(!hasClosedItems) - .buttonStyle(.borderless) - .font(.system(size: 12)) - .help(String(localized: "historyPane.clearClosed.help", defaultValue: "Clear recently closed history")) - } - .padding(.horizontal, 12) - .padding(.vertical, 10) - .overlay(alignment: .bottom) { - Rectangle() - .fill(Color.primary.opacity(0.08)) - .frame(height: 1) - } - .accessibilityIdentifier("HistoryPane.Header") - } - - private var searchField: some View { - HStack(spacing: 7) { - Image(systemName: "magnifyingglass") - .font(.system(size: 12, weight: .medium)) - .foregroundStyle(.secondary) - - TextField(String(localized: "historyPane.search.placeholder", defaultValue: "Search history"), text: $query) - .textFieldStyle(.plain) - .font(.system(size: 13)) - .focused($isSearchFieldFocused) - - if !query.isEmpty { - Button { - query = "" - } label: { - Image(systemName: "xmark.circle.fill") - .font(.system(size: 12, weight: .medium)) - .foregroundStyle(.secondary) - } - .buttonStyle(.plain) - .help(String(localized: "historyPane.search.clear", defaultValue: "Clear search")) - } - } - .padding(.horizontal, 9) - .padding(.vertical, 7) - .background( - RoundedRectangle(cornerRadius: 6) - .fill(Color.primary.opacity(0.06)) - ) - .padding(.top, 10) - .accessibilityIdentifier("HistoryPane.SearchField") - } - - private func focusSearchField() { - Task { @MainActor in - isSearchFieldFocused = true - } - } - - private func emptyState(_ title: String) -> some View { - Text(title) - .font(.system(size: 13)) - .foregroundStyle(.secondary) - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) - .padding(20) - .accessibilityIdentifier("HistoryPane.EmptyState") - } - - private func historySection(title: String, groups: [HistoryPaneDayGroup]) -> some View { - VStack(alignment: .leading, spacing: 10) { - Text(title) - .font(.system(size: 12, weight: .semibold)) - .foregroundStyle(.secondary) - .textCase(.uppercase) - .padding(.horizontal, 2) - - ForEach(groups) { group in - VStack(alignment: .leading, spacing: 4) { - Text(group.title) - .font(.system(size: 12, weight: .medium)) - .foregroundStyle(.secondary) - .padding(.horizontal, 2) - .padding(.top, 2) - - LazyVStack(alignment: .leading, spacing: 1) { - ForEach(group.rows) { row in - HistoryPaneRowView(row: row) { - switch row.kind { - case .closed(let id): - if !onOpenClosedItem(id) { - NSSound.beep() - } - case .focused(let item): - if !onOpenFocusedItem(item) { - NSSound.beep() - } - } - } - } - } - } - } - } - } - - private func groupedRows(_ rows: [HistoryPaneRow], query: String) -> [HistoryPaneDayGroup] { - let calendar = Calendar.current - let filteredRows = rows - .filter { row in - HistoryDayGrouping.matches( - query: query, - fields: [row.title, row.subtitle, row.detail, row.badge] - ) - } - .sorted { lhs, rhs in lhs.timestamp > rhs.timestamp } - - let grouped = Dictionary(grouping: filteredRows) { row in - calendar.startOfDay(for: row.timestamp) - } - - return grouped.keys - .sorted(by: >) - .compactMap { day in - guard let rows = grouped[day], !rows.isEmpty else { return nil } - return HistoryPaneDayGroup( - day: day, - title: HistoryDayGrouping.dayTitle(for: day, calendar: calendar), - rows: rows - ) - } - } - - private static func closedRow(_ item: ClosedItemHistoryMenuItem) -> HistoryPaneRow { - let time = item.closedAt.formatted(date: .omitted, time: .shortened) - return HistoryPaneRow( - id: "closed-\(item.id.uuidString)", - title: item.title, - subtitle: item.detail, - detail: String( - format: String(localized: "historyPane.closedAtFormat", defaultValue: "Closed %@"), - time - ), - badge: String(localized: "menu.history.recentlyClosed", defaultValue: "Recently Closed"), - timestamp: item.closedAt, - kind: .closed(item.id) - ) - } - - private static func focusedRow(_ item: FocusHistoryMenuItem) -> HistoryPaneRow { - let time = item.focusedAt.formatted(date: .omitted, time: .shortened) - let badge: String - switch item.position { - case .older: - badge = String(localized: "menu.history.focusBack", defaultValue: "Focus Back") - case .newer: - badge = String(localized: "menu.history.focusForward", defaultValue: "Focus Forward") - } - - return HistoryPaneRow( - id: "focused-\(item.position)-\(item.historyIndex)", - title: item.workspaceTitle, - subtitle: item.panelTitle ?? badge, - detail: String( - format: String(localized: "historyPane.focusedAtFormat", defaultValue: "Focused %@"), - time - ), - badge: badge, - timestamp: item.focusedAt, - kind: .focused(item) - ) - } -} - -private struct HistoryPaneDayGroup: Identifiable { - var id: TimeInterval { day.timeIntervalSinceReferenceDate } - let day: Date - let title: String - let rows: [HistoryPaneRow] -} - -private struct HistoryPaneRow: Identifiable { - enum Kind { - case closed(UUID) - case focused(FocusHistoryMenuItem) - } - - let id: String - let title: String - let subtitle: String - let detail: String - let badge: String - let timestamp: Date - let kind: Kind -} - -private struct HistoryPaneRowView: View { - let row: HistoryPaneRow - let action: () -> Void - - @State private var isHovered = false - - var body: some View { - Button(action: action) { - HStack(alignment: .center, spacing: 10) { - VStack(alignment: .leading, spacing: 2) { - Text(row.title) - .font(.system(size: 13, weight: .medium)) - .foregroundStyle(.primary) - .lineLimit(1) - - Text(row.subtitle) - .font(.system(size: 12)) - .foregroundStyle(.secondary) - .lineLimit(1) - } - - Spacer(minLength: 8) - - VStack(alignment: .trailing, spacing: 2) { - Text(row.badge) - .font(.system(size: 10, weight: .medium)) - .foregroundStyle(.secondary) - .lineLimit(1) - - Text(row.detail) - .font(.system(size: 11)) - .foregroundStyle(.secondary) - .lineLimit(1) - } - } - .padding(.horizontal, 8) - .padding(.vertical, 6) - .contentShape(Rectangle()) - } - .buttonStyle(HistoryPaneRowButtonStyle(isHovered: isHovered)) - .onHover { isHovered = $0 } - .accessibilityIdentifier("HistoryPane.Row.\(row.id)") - } -} - -private struct HistoryPaneRowButtonStyle: ButtonStyle { - let isHovered: Bool - - func makeBody(configuration: Configuration) -> some View { - configuration.label - .background( - RoundedRectangle(cornerRadius: 6) - .fill( - Color.primary.opacity( - configuration.isPressed ? 0.11 : (isHovered ? 0.07 : 0) - ) - ) - ) - } -} diff --git a/Sources/KeyboardShortcutContext.swift b/Sources/KeyboardShortcutContext.swift index 7f7b2fbec2fb..43e9d3f48482 100644 --- a/Sources/KeyboardShortcutContext.swift +++ b/Sources/KeyboardShortcutContext.swift @@ -49,7 +49,7 @@ extension KeyboardShortcutSettings.Action { var shortcutContext: ShortcutContext { switch self { - case .switchRightSidebarToFiles, .switchRightSidebarToFind, .switchRightSidebarToSessions, .switchRightSidebarToFeed, .switchRightSidebarToDock, .switchRightSidebarToHistory: + case .switchRightSidebarToFiles, .switchRightSidebarToFind, .switchRightSidebarToSessions, .switchRightSidebarToFeed, .switchRightSidebarToDock: return .rightSidebarFocus case .renameTab, .renameWorkspace: return .nonBrowserPanel diff --git a/Sources/KeyboardShortcutSettings.swift b/Sources/KeyboardShortcutSettings.swift index 2bc0c54473be..1e925dbf86b8 100644 --- a/Sources/KeyboardShortcutSettings.swift +++ b/Sources/KeyboardShortcutSettings.swift @@ -88,7 +88,6 @@ enum KeyboardShortcutSettings { case switchRightSidebarToSessions case switchRightSidebarToFeed case switchRightSidebarToDock - case switchRightSidebarToHistory case triggerFlash // Navigation @@ -178,7 +177,6 @@ enum KeyboardShortcutSettings { case .switchRightSidebarToSessions: return String(localized: "shortcut.switchRightSidebarToSessions.label", defaultValue: "Show Sidebar Vault") case .switchRightSidebarToFeed: return String(localized: "shortcut.switchRightSidebarToFeed.label", defaultValue: "Show Sidebar Feed") case .switchRightSidebarToDock: return String(localized: "shortcut.switchRightSidebarToDock.label", defaultValue: "Show Sidebar Dock") - case .switchRightSidebarToHistory: return String(localized: "shortcut.switchRightSidebarToHistory.label", defaultValue: "Show Sidebar History") case .triggerFlash: return String(localized: "shortcut.flashFocusedPanel.label", defaultValue: "Flash Focused Panel") case .nextSurface: return String(localized: "shortcut.nextSurface.label", defaultValue: "Next Surface") case .prevSurface: return String(localized: "shortcut.previousSurface.label", defaultValue: "Previous Surface") @@ -239,8 +237,7 @@ enum KeyboardShortcutSettings { .switchRightSidebarToFind, .switchRightSidebarToSessions, .switchRightSidebarToFeed, - .switchRightSidebarToDock, - .switchRightSidebarToHistory: + .switchRightSidebarToDock: return false default: return true @@ -307,8 +304,6 @@ enum KeyboardShortcutSettings { return StoredShortcut(key: "4", command: false, shift: false, option: false, control: true) case .switchRightSidebarToDock: return StoredShortcut(key: "5", command: false, shift: false, option: false, control: true) - case .switchRightSidebarToHistory: - return StoredShortcut(key: "6", command: false, shift: false, option: false, control: true) case .triggerFlash: return StoredShortcut(key: "h", command: true, shift: true, option: false, control: false) case .nextSidebarTab: diff --git a/Sources/MainWindowFocusController.swift b/Sources/MainWindowFocusController.swift index 161c4a7145a3..98b1f9bc7b7d 100644 --- a/Sources/MainWindowFocusController.swift +++ b/Sources/MainWindowFocusController.swift @@ -55,7 +55,6 @@ final class MainWindowFocusController { private weak var fileSearchHost: FileExplorerContainerView? private weak var feedHost: FeedKeyboardFocusView? private weak var dockHost: DockKeyboardFocusView? - private weak var historyHost: RightSidebarHistoryFocusAnchorView? private(set) var intent: MainWindowKeyboardFocusIntent? { didSet { @@ -110,7 +109,7 @@ final class MainWindowFocusController { fileExplorerHost = host case .find: fileSearchHost = host - case .sessions, .feed, .dock, .history: + case .sessions, .feed, .dock: break } focusRegisteredRightSidebarEndpointIfNeeded(mode: mode) @@ -127,11 +126,6 @@ final class MainWindowFocusController { focusRegisteredRightSidebarEndpointIfNeeded(mode: .dock) } - func registerHistoryHost(_ host: RightSidebarHistoryFocusAnchorView) { - historyHost = host - focusRegisteredRightSidebarEndpointIfNeeded(mode: .history) - } - func noteRightSidebarInteraction(mode: RightSidebarMode) { rememberedRightSidebarMode = mode rightSidebarFocusState = .focused(mode: mode, target: .host) @@ -201,9 +195,6 @@ final class MainWindowFocusController { if dockHost?.ownsKeyboardFocus(responder) == true { return true } - if historyHost?.ownsKeyboardFocus(responder) == true { - return true - } return false } @@ -656,8 +647,6 @@ final class MainWindowFocusController { return focusFirstItem ? .firstItem : .host case .dock: return focusFirstItem ? .firstItem : .host - case .history: - return focusFirstItem ? .searchField : .host } } @@ -682,11 +671,6 @@ final class MainWindowFocusController { dockHost?.focusFirstItemFromCoordinator() } return dockHost?.focusHostFromCoordinator() == true - case .history: - if target == .searchField { - return historyHost?.focusSearchFromCoordinator() == true - } - return historyHost?.focusHostFromCoordinator() == true } } @@ -757,9 +741,6 @@ final class MainWindowFocusController { if dockHost?.ownsKeyboardFocus(responder) == true { return .dock } - if historyHost?.ownsKeyboardFocus(responder) == true { - return .history - } return nil } diff --git a/Sources/RightSidebarMode+Availability.swift b/Sources/RightSidebarMode+Availability.swift index 80b7c669020f..7886818e2a9d 100644 --- a/Sources/RightSidebarMode+Availability.swift +++ b/Sources/RightSidebarMode+Availability.swift @@ -13,8 +13,6 @@ extension RightSidebarMode { return .feed case "dock": return .dock - case "history": - return .history default: return nil } @@ -38,8 +36,6 @@ extension RightSidebarMode { return true case .dock: return dockEnabled - case .history: - return true } } } diff --git a/Sources/RightSidebarPanelView.swift b/Sources/RightSidebarPanelView.swift index ae2cffe7e828..c3f65c82cf85 100644 --- a/Sources/RightSidebarPanelView.swift +++ b/Sources/RightSidebarPanelView.swift @@ -15,7 +15,6 @@ nonisolated enum RightSidebarMode: String, CaseIterable, Codable, Sendable { case sessions case feed case dock - case history var label: String { switch self { @@ -24,7 +23,6 @@ nonisolated enum RightSidebarMode: String, CaseIterable, Codable, Sendable { case .sessions: return String(localized: "rightSidebar.mode.sessions", defaultValue: "Vault") case .feed: return String(localized: "rightSidebar.mode.feed", defaultValue: "Feed") case .dock: return String(localized: "rightSidebar.mode.dock", defaultValue: "Dock") - case .history: return String(localized: "rightSidebar.mode.history", defaultValue: "History") } } @@ -35,7 +33,6 @@ nonisolated enum RightSidebarMode: String, CaseIterable, Codable, Sendable { case .sessions: return "books.vertical" case .feed: return "dot.radiowaves.left.and.right" case .dock: return "dock.rectangle" - case .history: return "clock.arrow.circlepath" } } @@ -46,13 +43,12 @@ nonisolated enum RightSidebarMode: String, CaseIterable, Codable, Sendable { case .sessions: return .switchRightSidebarToSessions case .feed: return .switchRightSidebarToFeed case .dock: return .switchRightSidebarToDock - case .history: return .switchRightSidebarToHistory } } } extension RightSidebarMode { - static let paneModes: [RightSidebarMode] = [.files, .find, .sessions, .history] + static let paneModes: [RightSidebarMode] = [.files, .find, .sessions] var canOpenAsPane: Bool { Self.paneModes.contains(self) @@ -79,9 +75,6 @@ extension RightSidebarMode { RightSidebarMode.dock.isAvailable() { return .dock } - if KeyboardShortcutSettings.shortcut(for: .switchRightSidebarToHistory).matches(event: event) { - return .history - } return nil } } @@ -172,7 +165,6 @@ struct RightSidebarPanelView: View { } @State private var focusShortcutHintMonitor = WindowScopedShortcutHintModifierMonitor(activation: .commandOnly) @State private var closeShortcutHintMonitor = WindowScopedShortcutHintModifierMonitor(activation: .commandOnly) - @State private var historySearchFocusToken = 0 @StateObject private var dockStore = DockControlsStore() @ObservedObject private var keyboardShortcutSettingsObserver = KeyboardShortcutSettingsObserver.shared private let alwaysShowShortcutHints = ShortcutHintDebugSettings.alwaysShowHints() @@ -415,40 +407,6 @@ struct RightSidebarPanelView: View { FeedPanelView() case .dock: DockPanelView(rootDirectory: sessionIndexDirectory, workspaceId: workspaceId, store: dockStore) - case .history: - HistoryPanelView( - focusSearchToken: historySearchFocusToken, - onFocus: { - AppDelegate.shared?.noteRightSidebarKeyboardFocusIntent( - mode: .history, - in: NSApp.keyWindow ?? NSApp.mainWindow - ) - }, - onOpenClosedItem: { itemId in - AppDelegate.shared?.reopenClosedHistoryItem( - id: itemId, - preferredTabManager: tabManager - ) == true - }, - onOpenFocusedItem: { item in - tabManager.navigateToFocusHistoryMenuItem(item) - }, - onClearClosedItems: { - if let appDelegate = AppDelegate.shared { - appDelegate.clearRecentlyClosedHistory(preferredTabManager: tabManager) - } else { - ClosedItemHistoryStore.shared.removeAll() - tabManager.clearRecentlyClosedBrowserPanelHistory() - } - } - ) - .environmentObject(tabManager) - .background( - RightSidebarHistoryFocusBridge { - historySearchFocusToken &+= 1 - } - .frame(width: 0, height: 0) - ) } } @@ -481,81 +439,6 @@ struct RightSidebarPanelView: View { } } -private struct RightSidebarHistoryFocusBridge: NSViewRepresentable { - let onFocusSearch: () -> Void - - func makeNSView(context: Context) -> RightSidebarHistoryFocusAnchorView { - let view = RightSidebarHistoryFocusAnchorView() - view.onFocusSearch = onFocusSearch - return view - } - - func updateNSView(_ nsView: RightSidebarHistoryFocusAnchorView, context: Context) { - nsView.onFocusSearch = onFocusSearch - nsView.registerWithKeyboardFocusCoordinatorIfNeeded() - } - - static func dismantleNSView(_ nsView: RightSidebarHistoryFocusAnchorView, coordinator: ()) { - nsView.onFocusSearch = nil - } -} - -final class RightSidebarHistoryFocusAnchorView: NSView { - var onFocusSearch: (() -> Void)? - override var acceptsFirstResponder: Bool { true } - - override func viewDidMoveToWindow() { - super.viewDidMoveToWindow() - registerWithKeyboardFocusCoordinatorIfNeeded() - } - - func registerWithKeyboardFocusCoordinatorIfNeeded() { - guard let window else { return } - AppDelegate.shared?.keyboardFocusCoordinator(for: window)?.registerHistoryHost(self) - } - - func focusSearchFromCoordinator() -> Bool { - onFocusSearch?() - return focusHostFromCoordinator() - } - - func focusHostFromCoordinator() -> Bool { - guard let window else { return false } - return window.makeFirstResponder(self) - } - - func ownsKeyboardFocus(_ responder: NSResponder) -> Bool { - if responder === self { return true } - guard let responderView = Self.view(for: responder), - let root = focusRootView else { return false } - return responderView === root || responderView.isDescendant(of: root) - } - - private static func view(for responder: NSResponder) -> NSView? { - if let view = responder as? NSView { - return view - } - if let textView = responder as? NSTextView, - let delegateView = textView.delegate as? NSView { - return delegateView - } - return nil - } - - private var focusRootView: NSView? { - guard let superview else { return nil } - var current: NSView? = superview - while let view = current { - let typeName = String(describing: type(of: view)) - if typeName.contains("NSHosting") || typeName.contains("ViewHost") { - return view - } - current = view.superview - } - return superview - } -} - private struct RightSidebarKeyboardFocusBridge: NSViewRepresentable { func makeNSView(context: Context) -> RightSidebarKeyboardFocusView { let view = RightSidebarKeyboardFocusView(frame: NSRect(x: 0, y: 0, width: 1, height: 1)) diff --git a/Sources/RightSidebarToolPanel.swift b/Sources/RightSidebarToolPanel.swift index a5832f421dc2..979858f75d9c 100644 --- a/Sources/RightSidebarToolPanel.swift +++ b/Sources/RightSidebarToolPanel.swift @@ -9,12 +9,10 @@ final class RightSidebarToolPanel: Panel, ObservableObject { let mode: RightSidebarMode @Published private(set) var focusFlashToken: Int = 0 - @Published private(set) var historySearchFocusToken: Int = 0 private weak var workspace: Workspace? private weak var fileExplorerContainerView: FileExplorerContainerView? private weak var sessionIndexFocusAnchorView: RightSidebarToolFocusAnchorView? - private weak var historyFocusAnchorView: RightSidebarToolFocusAnchorView? private var fileExplorerStoreStorage: FileExplorerStore? private var fileExplorerStateStorage: FileExplorerState? private var sessionIndexStoreStorage: SessionIndexStore? @@ -75,10 +73,6 @@ final class RightSidebarToolPanel: Panel, ObservableObject { sessionIndexFocusAnchorView = anchor } - fileprivate func attachHistoryFocusAnchor(_ anchor: RightSidebarToolFocusAnchorView?) { - historyFocusAnchorView = anchor - } - func syncWorkspaceRoot(from workspace: Workspace) { switch mode { case .files, .find: @@ -87,7 +81,7 @@ final class RightSidebarToolPanel: Panel, ObservableObject { case .sessions: guard let store = sessionIndexStoreStorage else { return } syncSessionIndexRoot(from: workspace, store: store) - case .feed, .dock, .history: + case .feed, .dock: break } } @@ -112,7 +106,6 @@ final class RightSidebarToolPanel: Panel, ObservableObject { func close() { fileExplorerContainerView = nil sessionIndexFocusAnchorView = nil - historyFocusAnchorView = nil fileExplorerStoreStorage?.applyWorkspaceRoot(.none) sessionIndexStoreStorage?.setCurrentDirectoryIfChanged(nil) workspaceObservationCancellable = nil @@ -128,11 +121,6 @@ final class RightSidebarToolPanel: Panel, ObservableObject { guard let anchor = sessionIndexFocusAnchorView, let window = anchor.window else { return } _ = window.makeFirstResponder(anchor) - case .history: - historySearchFocusToken &+= 1 - guard let anchor = historyFocusAnchorView, - let window = anchor.window else { return } - _ = window.makeFirstResponder(anchor) case .feed, .dock: break } @@ -155,9 +143,6 @@ final class RightSidebarToolPanel: Panel, ObservableObject { case .sessions: guard sessionIndexFocusAnchorView?.ownsKeyboardFocus(responder) == true else { return nil } return .panel - case .history: - guard historyFocusAnchorView?.ownsKeyboardFocus(responder) == true else { return nil } - return .panel case .feed, .dock: return nil } @@ -284,32 +269,6 @@ struct RightSidebarToolPanelView: View { RightSidebarToolFocusAnchor(onViewChange: panel.attachSessionIndexFocusAnchor) .frame(width: 0, height: 0) ) - case .history: - HistoryPanelView( - focusSearchToken: panel.historySearchFocusToken, - onFocus: requestPanelFocusIfNeeded, - onOpenClosedItem: { itemId in - AppDelegate.shared?.reopenClosedHistoryItem( - id: itemId, - preferredTabManager: tabManager - ) == true - }, - onOpenFocusedItem: { item in - tabManager.navigateToFocusHistoryMenuItem(item) - }, - onClearClosedItems: { - if let appDelegate = AppDelegate.shared { - appDelegate.clearRecentlyClosedHistory(preferredTabManager: tabManager) - } else { - ClosedItemHistoryStore.shared.removeAll() - tabManager.clearRecentlyClosedBrowserPanelHistory() - } - } - ) - .background( - RightSidebarToolFocusAnchor(onViewChange: panel.attachHistoryFocusAnchor) - .frame(width: 0, height: 0) - ) case .feed, .dock: EmptyView() } diff --git a/Sources/cmuxApp+HistoryMenu.swift b/Sources/cmuxApp+HistoryMenu.swift index 49c43cc1a3f7..ac283b1b55ef 100644 --- a/Sources/cmuxApp+HistoryMenu.swift +++ b/Sources/cmuxApp+HistoryMenu.swift @@ -39,8 +39,6 @@ extension cmuxApp { snapshot: recentlyClosedSnapshot ) - openFullHistoryButton(manager: historyTabManager) - Divider() splitCommandButton(title: String(localized: "menu.file.restorePreviousAppLaunch", defaultValue: "Restore Previous Launch"), shortcut: menuShortcut(for: .reopenPreviousSession)) { @@ -74,13 +72,6 @@ extension cmuxApp { } .disabled(!item.isNavigable) } - - if snapshot.isLimited { - openFullHistoryButton( - title: String(localized: "menu.history.showFullFocusHistory", defaultValue: "Show Full History"), - manager: manager - ) - } } } @@ -109,13 +100,6 @@ extension cmuxApp { } } } - - if snapshot.isLimited { - openFullHistoryButton( - title: String(localized: "menu.history.recentlyClosed.showFull", defaultValue: "Show Full Recently Closed"), - manager: manager - ) - } } } @@ -151,14 +135,4 @@ extension cmuxApp { ) } - private func openFullHistoryButton( - title: String = String(localized: "menu.history.openFullHistory", defaultValue: "Open Full History…"), - manager: TabManager - ) -> some View { - Button(title) { - if AppDelegate.shared?.openHistoryPane(preferredTabManager: manager) != true { - NSSound.beep() - } - } - } } diff --git a/cmux.xcodeproj/project.pbxproj b/cmux.xcodeproj/project.pbxproj index f70dfe88de5c..7291d30a5d2d 100644 --- a/cmux.xcodeproj/project.pbxproj +++ b/cmux.xcodeproj/project.pbxproj @@ -201,7 +201,6 @@ A5001003 /* TabManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001013 /* TabManager.swift */; }; F0C05170000000000000002 /* FocusHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C05170000000000000001 /* FocusHistory.swift */; }; C10D51700000000000000002 /* ClosedItemHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10D51700000000000000001 /* ClosedItemHistory.swift */; }; - C10D51800000000000000002 /* HistoryPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10D51800000000000000001 /* HistoryPanelView.swift */; }; C3677003000000000000001 /* TabManager+CompatibilityTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3677003000000000000002 /* TabManager+CompatibilityTypes.swift */; }; D7AB00000000000000000013 /* TabManager+DetachedWorkspace.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000000014 /* TabManager+DetachedWorkspace.swift */; }; D7AB00000000000000000015 /* Workspace+DetachedSurfaceTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7AB00000000000000000016 /* Workspace+DetachedSurfaceTransfer.swift */; }; @@ -232,7 +231,6 @@ 2907A0012907A0012907A001 /* AppDelegate+RecoverableMainWindowRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2907A0022907A0022907A002 /* AppDelegate+RecoverableMainWindowRoutes.swift */; }; C4160A030000000000000001 /* AppDelegate+ClosedItemHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4160A030000000000000002 /* AppDelegate+ClosedItemHistory.swift */; }; C4160A040000000000000001 /* AppDelegate+FocusHistoryContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4160A040000000000000002 /* AppDelegate+FocusHistoryContextMenu.swift */; }; - C4160A050000000000000001 /* AppDelegate+HistoryPane.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4160A050000000000000002 /* AppDelegate+HistoryPane.swift */; }; C10D00010000000000000001 /* CloudVMActionLauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10D00020000000000000002 /* CloudVMActionLauncher.swift */; }; A5001094 /* NotificationsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001091 /* NotificationsPage.swift */; }; A5001095 /* TerminalNotificationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5001092 /* TerminalNotificationStore.swift */; }; @@ -756,7 +754,6 @@ A5001013 /* TabManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabManager.swift; sourceTree = ""; }; F0C05170000000000000001 /* FocusHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusHistory.swift; sourceTree = ""; }; C10D51700000000000000001 /* ClosedItemHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedItemHistory.swift; sourceTree = ""; }; - C10D51800000000000000001 /* HistoryPanelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryPanelView.swift; sourceTree = ""; }; C3677003000000000000002 /* TabManager+CompatibilityTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+CompatibilityTypes.swift"; sourceTree = ""; }; E3309A04 /* TabManager+EqualizeSplits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabManager+EqualizeSplits.swift"; sourceTree = ""; }; E3309A0E /* SplitEqualizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitEqualizer.swift; sourceTree = ""; }; @@ -824,7 +821,6 @@ 2907A0022907A0022907A002 /* AppDelegate+RecoverableMainWindowRoutes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+RecoverableMainWindowRoutes.swift"; sourceTree = ""; }; C4160A030000000000000002 /* AppDelegate+ClosedItemHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+ClosedItemHistory.swift"; sourceTree = ""; }; C4160A040000000000000002 /* AppDelegate+FocusHistoryContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+FocusHistoryContextMenu.swift"; sourceTree = ""; }; - C4160A050000000000000002 /* AppDelegate+HistoryPane.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+HistoryPane.swift"; sourceTree = ""; }; A5001091 /* NotificationsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsPage.swift; sourceTree = ""; }; A5001092 /* TerminalNotificationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalNotificationStore.swift; sourceTree = ""; }; A5001097 /* TerminalNotificationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalNotificationPolicy.swift; sourceTree = ""; }; @@ -1352,7 +1348,6 @@ A5001013 /* TabManager.swift */, F0C05170000000000000001 /* FocusHistory.swift */, C10D51700000000000000001 /* ClosedItemHistory.swift */, - C10D51800000000000000001 /* HistoryPanelView.swift */, C3677003000000000000002 /* TabManager+CompatibilityTypes.swift */, E3309A04 /* TabManager+EqualizeSplits.swift */, E3309A0E /* SplitEqualizer.swift */, @@ -1442,7 +1437,6 @@ 2907A0022907A0022907A002 /* AppDelegate+RecoverableMainWindowRoutes.swift */, C4160A030000000000000002 /* AppDelegate+ClosedItemHistory.swift */, C4160A040000000000000002 /* AppDelegate+FocusHistoryContextMenu.swift */, - C4160A050000000000000002 /* AppDelegate+HistoryPane.swift */, C10D00020000000000000002 /* CloudVMActionLauncher.swift */, B37A00000000000000000006 /* MainWindowFocusTypes.swift */, 2F0C05000000000000000001 /* MainWindowFocusController.swift */, @@ -2121,7 +2115,6 @@ A5001003 /* TabManager.swift in Sources */, F0C05170000000000000002 /* FocusHistory.swift in Sources */, C10D51700000000000000002 /* ClosedItemHistory.swift in Sources */, - C10D51800000000000000002 /* HistoryPanelView.swift in Sources */, C3677003000000000000001 /* TabManager+CompatibilityTypes.swift in Sources */, E3309A03 /* TabManager+EqualizeSplits.swift in Sources */, E3309A0D /* SplitEqualizer.swift in Sources */, @@ -2214,7 +2207,6 @@ 2907A0012907A0012907A001 /* AppDelegate+RecoverableMainWindowRoutes.swift in Sources */, C4160A030000000000000001 /* AppDelegate+ClosedItemHistory.swift in Sources */, C4160A040000000000000001 /* AppDelegate+FocusHistoryContextMenu.swift in Sources */, - C4160A050000000000000001 /* AppDelegate+HistoryPane.swift in Sources */, C10D00010000000000000001 /* CloudVMActionLauncher.swift in Sources */, B37A00000000000000000005 /* MainWindowFocusTypes.swift in Sources */, 2F0C05000000000000000002 /* MainWindowFocusController.swift in Sources */, diff --git a/cmuxTests/FileExplorerStateModePersistenceTests.swift b/cmuxTests/FileExplorerStateModePersistenceTests.swift index bc4c8c991ab9..9aab0ee65f40 100644 --- a/cmuxTests/FileExplorerStateModePersistenceTests.swift +++ b/cmuxTests/FileExplorerStateModePersistenceTests.swift @@ -52,7 +52,6 @@ final class FileExplorerStateModePersistenceTests: XCTestCase { XCTAssertEqual(RightSidebarMode.from(cliArgument: "sessions"), .sessions) XCTAssertEqual(RightSidebarMode.from(cliArgument: "feed"), .feed) XCTAssertEqual(RightSidebarMode.from(cliArgument: "dock"), .dock) - XCTAssertEqual(RightSidebarMode.from(cliArgument: "history"), .history) XCTAssertEqual(RightSidebarMode.from(cliArgument: " Vault "), .sessions) XCTAssertNil(RightSidebarMode.from(cliArgument: "unknown")) } diff --git a/cmuxTests/RightSidebarCommandPaletteTests.swift b/cmuxTests/RightSidebarCommandPaletteTests.swift index ee54cc580d00..c98c8b991f2b 100644 --- a/cmuxTests/RightSidebarCommandPaletteTests.swift +++ b/cmuxTests/RightSidebarCommandPaletteTests.swift @@ -35,10 +35,9 @@ final class RightSidebarCommandPaletteTests: XCTestCase { XCTAssertTrue(contribution.enablement(context)) } - XCTAssertEqual(contributions.count, 5) + XCTAssertEqual(contributions.count, 4) XCTAssertNotNil(contributionsByID[ContentView.commandPaletteRightSidebarModeCommandID(.feed)]) XCTAssertNil(contributionsByID[ContentView.commandPaletteRightSidebarModeCommandID(.dock)]) - XCTAssertNotNil(contributionsByID[ContentView.commandPaletteRightSidebarModeCommandID(.history)]) } } @@ -58,14 +57,6 @@ final class RightSidebarCommandPaletteTests: XCTestCase { } } - func testCommandPaletteIncludesHistoryPaneCommand() throws { - let descriptors = ContentView.commandPaletteRightSidebarToolPaneCommandDescriptors() - let historyDescriptor = try XCTUnwrap(descriptors.first { $0.mode == .history }) - - XCTAssertEqual(historyDescriptor.commandId, "palette.openHistoryPane") - XCTAssertEqual(historyDescriptor.title, String(localized: "command.openHistoryPane.title", defaultValue: "Open History as Pane")) - } - func testCommandPaletteUnreadActionsUseConfigurableShortcutActions() { XCTAssertEqual( ContentView.commandPaletteShortcutAction(forCommandID: "palette.toggleUnread"), diff --git a/cmuxTests/ShortcutAndCommandPaletteTests.swift b/cmuxTests/ShortcutAndCommandPaletteTests.swift index bac5af718918..ceff0958f73b 100644 --- a/cmuxTests/ShortcutAndCommandPaletteTests.swift +++ b/cmuxTests/ShortcutAndCommandPaletteTests.swift @@ -1092,7 +1092,6 @@ final class RightSidebarModeShortcutHintTests: XCTestCase { .switchRightSidebarToSessions, .switchRightSidebarToFeed, .switchRightSidebarToDock, - .switchRightSidebarToHistory, ] private var originalSettingsFileStore: KeyboardShortcutSettingsFileStore! private var savedShortcutData: [KeyboardShortcutSettings.Action: Data?] = [:] @@ -1144,7 +1143,6 @@ final class RightSidebarModeShortcutHintTests: XCTestCase { XCTAssertEqual(RightSidebarMode.sessions.shortcutAction, .switchRightSidebarToSessions) XCTAssertEqual(RightSidebarMode.feed.shortcutAction, .switchRightSidebarToFeed) XCTAssertEqual(RightSidebarMode.dock.shortcutAction, .switchRightSidebarToDock) - XCTAssertEqual(RightSidebarMode.history.shortcutAction, .switchRightSidebarToHistory) } func testModeShortcutsUsePrivateControlDigitDefaults() { @@ -1168,10 +1166,6 @@ final class RightSidebarModeShortcutHintTests: XCTestCase { RightSidebarMode.modeShortcut(for: makeKeyDownEvent(key: "5", modifiers: [.control], keyCode: 23)), .dock ) - XCTAssertEqual( - RightSidebarMode.modeShortcut(for: makeKeyDownEvent(key: "6", modifiers: [.control], keyCode: 22)), - .history - ) } func testModeShortcutUsesConfiguredBindings() { @@ -1229,10 +1223,6 @@ final class RightSidebarModeShortcutHintTests: XCTestCase { RightSidebarMode.modeShortcut(for: makeKeyDownEvent(key: "5", modifiers: [.control], keyCode: 23)), .dock ) - XCTAssertEqual( - RightSidebarMode.modeShortcut(for: makeKeyDownEvent(key: "6", modifiers: [.control], keyCode: 22)), - .history - ) } func testFocusRightSidebarShortcutCanBeOverwrittenForHintRendering() { @@ -1418,45 +1408,6 @@ final class MainWindowFocusControllerRightSidebarHideTests: XCTestCase { } @MainActor - func testPendingHistoryFocusCompletesWhenHistorySearchHostRegisters() { - let fileExplorerState = FileExplorerState() - let window = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 240, height: 180), - styleMask: [.titled], - backing: .buffered, - defer: false - ) - let contentView = NSView(frame: window.contentView?.bounds ?? .zero) - window.contentView = contentView - let controller = MainWindowFocusController( - windowId: UUID(), - window: window, - tabManager: TabManager(), - fileExplorerState: fileExplorerState - ) - - XCTAssertTrue(controller.focusRightSidebar(mode: .history, focusFirstItem: true)) - XCTAssertEqual(controller.debugPendingRightSidebarFocusMode, .history) - - var searchFocusRequests = 0 - let focusHost = RightSidebarHistoryFocusAnchorView(frame: NSRect(x: 0, y: 0, width: 24, height: 24)) - focusHost.onFocusSearch = { - searchFocusRequests += 1 - } - defer { - _ = window.makeFirstResponder(nil) - focusHost.removeFromSuperview() - window.contentView = nil - window.orderOut(nil) - } - contentView.addSubview(focusHost) - controller.registerHistoryHost(focusHost) - - XCTAssertNil(controller.debugPendingRightSidebarFocusMode) - XCTAssertTrue(window.firstResponder === focusHost) - XCTAssertEqual(searchFocusRequests, 1) - } - @MainActor func testFocusShortcutToggleClearsRightSidebarIntentWhenTerminalIsUnavailable() { let controller = MainWindowFocusController( diff --git a/cmuxTests/WorkspaceUnitTests.swift b/cmuxTests/WorkspaceUnitTests.swift index 65fcb719c45f..1d4fbf9f7b06 100644 --- a/cmuxTests/WorkspaceUnitTests.swift +++ b/cmuxTests/WorkspaceUnitTests.swift @@ -423,7 +423,6 @@ final class WorkspaceRenameShortcutDefaultsTests: XCTestCase { (.switchRightSidebarToSessions, "3"), (.switchRightSidebarToFeed, "4"), (.switchRightSidebarToDock, "5"), - (.switchRightSidebarToHistory, "6"), ] for (action, key) in modeSwitchActions { @@ -992,8 +991,7 @@ final class KeyboardShortcutSettingsFileStoreTests: XCTestCase { "switchRightSidebarToFind": "ctrl+5", "switchRightSidebarToSessions": "ctrl+6", "switchRightSidebarToFeed": "ctrl+7", - "switchRightSidebarToDock": "ctrl+8", - "switchRightSidebarToHistory": "ctrl+9" + "switchRightSidebarToDock": "ctrl+8" } } """, @@ -1030,10 +1028,6 @@ final class KeyboardShortcutSettingsFileStoreTests: XCTestCase { store.override(for: .switchRightSidebarToDock), StoredShortcut(key: "8", command: false, shift: false, option: false, control: true) ) - XCTAssertEqual( - store.override(for: .switchRightSidebarToHistory), - StoredShortcut(key: "9", command: false, shift: false, option: false, control: true) - ) } func testSettingsFileStoreParsesWorkspaceWorkingDirectoryInheritanceSetting() throws { @@ -5097,45 +5091,6 @@ final class WorkspacePanelGitBranchTests: XCTestCase { XCTAssertEqual(workspace.focusedPanelId, firstPanel.id) } - func testHistoryToolSurfaceOpensAsReusableBonsplitPane() { - let workspace = Workspace() - guard let paneId = workspace.bonsplitController.focusedPaneId else { - XCTFail("Expected focused pane") - return - } - - guard let firstPanel = workspace.openOrFocusRightSidebarToolSurface( - inPane: paneId, - mode: .history, - focus: true - ) else { - XCTFail("Expected History tool surface to be created") - return - } - guard let secondPanel = workspace.openOrFocusRightSidebarToolSurface( - inPane: paneId, - mode: .history, - focus: true - ) else { - XCTFail("Expected existing History tool surface to be focused") - return - } - - XCTAssertEqual(firstPanel.id, secondPanel.id) - XCTAssertEqual(firstPanel.displayTitle, String(localized: "rightSidebar.mode.history", defaultValue: "History")) - XCTAssertEqual(firstPanel.displayIcon, "clock.arrow.circlepath") - XCTAssertGreaterThanOrEqual(firstPanel.historySearchFocusToken, 1) - XCTAssertEqual( - workspace.surfaceIdFromPanelId(firstPanel.id).flatMap { workspace.bonsplitController.tab($0)?.kind }, - Workspace.SurfaceKind.rightSidebarTool - ) - XCTAssertEqual(workspace.focusedPanelId, firstPanel.id) - - let previousFocusToken = firstPanel.historySearchFocusToken - workspace.focusPanel(firstPanel.id) - XCTAssertGreaterThan(firstPanel.historySearchFocusToken, previousFocusToken) - } - func testClosingFocusedSplitRestoresBranchForRemainingFocusedPanel() { let workspace = Workspace() guard let firstPanelId = workspace.focusedPanelId else { diff --git a/web/data/cmux.schema.json b/web/data/cmux.schema.json index 04beff841c79..75fc183b787d 100644 --- a/web/data/cmux.schema.json +++ b/web/data/cmux.schema.json @@ -949,7 +949,6 @@ "switchRightSidebarToSessions", "switchRightSidebarToFeed", "switchRightSidebarToDock", - "switchRightSidebarToHistory", "triggerFlash", "nextSurface", "prevSurface", From 7e9fcb840862ea7bc23c5ea6aa1d916dc0fc13d9 Mon Sep 17 00:00:00 2001 From: Lawrence Chen Date: Tue, 26 May 2026 01:52:36 -0700 Subject: [PATCH 2/4] Drop tests for deleted HistoryDayGrouping helper --- .../TabManagerSessionSnapshotTests.swift | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/cmuxTests/TabManagerSessionSnapshotTests.swift b/cmuxTests/TabManagerSessionSnapshotTests.swift index ef4584021970..5b2b520b3135 100644 --- a/cmuxTests/TabManagerSessionSnapshotTests.swift +++ b/cmuxTests/TabManagerSessionSnapshotTests.swift @@ -1118,28 +1118,6 @@ final class TabManagerSessionSnapshotTests: XCTestCase { XCTAssertTrue(item.menuSubtitle.contains(String(localized: "menu.history.recentlyClosed.kind.tab", defaultValue: "Tab"))) } - func testHistorySearchMatchesAcrossTitleAndKind() { - XCTAssertTrue(HistoryDayGrouping.matches(query: "timed tab", fields: ["Timed Panel", "Tab"])) - XCTAssertTrue(HistoryDayGrouping.matches(query: "workspace", fields: ["Recovered", "Workspace"])) - XCTAssertFalse(HistoryDayGrouping.matches(query: "browser", fields: ["Timed Panel", "Tab"])) - } - - func testHistoryDayGroupingLabelsTodayAndYesterday() throws { - var calendar = Calendar(identifier: .gregorian) - calendar.timeZone = try XCTUnwrap(TimeZone(secondsFromGMT: 0)) - let now = Date(timeIntervalSince1970: 1_700_000_000) - let yesterday = try XCTUnwrap(calendar.date(byAdding: .day, value: -1, to: now)) - - XCTAssertEqual( - HistoryDayGrouping.dayTitle(for: now, now: now, calendar: calendar), - String(localized: "historyPane.day.today", defaultValue: "Today") - ) - XCTAssertEqual( - HistoryDayGrouping.dayTitle(for: yesterday, now: now, calendar: calendar), - String(localized: "historyPane.day.yesterday", defaultValue: "Yesterday") - ) - } - func testReopenSpecificRecentlyClosedRowRestoresOnlyThatRecord() throws { let originalAppDelegate = AppDelegate.shared AppDelegate.shared = nil From 45cc46a67bf369333b700743c2e56f2adcb66e6b Mon Sep 17 00:00:00 2001 From: Lawrence Chen Date: Tue, 26 May 2026 01:58:48 -0700 Subject: [PATCH 3/4] Tolerate obsolete history mode and restore focus-history menu key - SessionRightSidebarToolPanelSnapshot now decodes an unknown mode (e.g. legacy 'history') as nil instead of failing the entire snapshot, so upgraded users can still restore prior sessions. - Restore the 'menu.history.showFullFocusHistory' key still referenced by AppDelegate+FocusHistoryContextMenu for non-sidebar focus history. --- Resources/Localizable.xcstrings | 1 + Sources/SessionPersistence.swift | 16 +++++++++++++++- cmuxTests/TabManagerSessionSnapshotTests.swift | 6 ++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Resources/Localizable.xcstrings b/Resources/Localizable.xcstrings index c3126fb4e674..2c610b7b301a 100644 --- a/Resources/Localizable.xcstrings +++ b/Resources/Localizable.xcstrings @@ -45094,6 +45094,7 @@ "menu.history.recentlyFocused.subtitle": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ja":{"stringUnit":{"state":"translated","value":"最近フォーカスした移動先"}},"ar":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"bs":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"da":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"de":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"es":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"fr":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"it":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ko":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"nb":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"pl":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"ru":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"th":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"tr":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"uk":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Most recent focus targets"}}}}, "menu.history.menuItemSubtitleFormat": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ja":{"stringUnit":{"state":"translated","value":"%1$@、%2$@"}},"ar":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"bs":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"da":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"de":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"es":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"fr":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"it":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ko":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"nb":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"pl":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"pt-BR":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"ru":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"th":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"tr":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"uk":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"%1$@, %2$@"}}}}, "menu.history.showFullFocusBackHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ja":{"stringUnit":{"state":"translated","value":"戻る履歴をすべて表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full Back History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full Back History"}}}}, + "menu.history.showFullFocusHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ja":{"stringUnit":{"state":"translated","value":"すべての履歴を表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full History"}}}}, "menu.history.showFullFocusForwardHistory": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ja":{"stringUnit":{"state":"translated","value":"進む履歴をすべて表示"}},"ar":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"bs":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"da":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"de":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"es":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"fr":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"it":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ko":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"nb":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"pl":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"ru":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"th":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"tr":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"uk":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Show Full Forward History"}}}}, "menu.history.recentlyClosed": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ja":{"stringUnit":{"state":"translated","value":"最近閉じた項目"}},"ar":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"bs":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"da":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"de":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"es":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"fr":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"it":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ko":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"nb":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"pl":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"ru":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"th":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"tr":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"uk":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Recently Closed"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Recently Closed"}}}}, "menu.history.recentlyClosed.subtitle": {"extractionState":"manual","localizations":{"en":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ja":{"stringUnit":{"state":"translated","value":"タブ、ワークスペース、ウィンドウ"}},"ar":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"bs":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"da":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"de":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"es":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"fr":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"it":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ko":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"nb":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"pl":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"pt-BR":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"ru":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"th":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"tr":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"uk":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"zh-Hans":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}},"zh-Hant":{"stringUnit":{"state":"translated","value":"Tabs, workspaces, and windows"}}}}, diff --git a/Sources/SessionPersistence.swift b/Sources/SessionPersistence.swift index 4878dbeace03..279abc1840f5 100644 --- a/Sources/SessionPersistence.swift +++ b/Sources/SessionPersistence.swift @@ -1398,7 +1398,21 @@ struct SessionFilePreviewPanelSnapshot: Codable, Sendable { } struct SessionRightSidebarToolPanelSnapshot: Codable, Sendable { - var mode: RightSidebarMode + var mode: RightSidebarMode? + + init(mode: RightSidebarMode?) { + self.mode = mode + } + + private enum CodingKeys: String, CodingKey { + case mode + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let raw = try container.decodeIfPresent(String.self, forKey: .mode) + self.mode = raw.flatMap { RightSidebarMode(rawValue: $0) } + } } struct SessionNotificationSnapshot: Codable, Sendable { diff --git a/cmuxTests/TabManagerSessionSnapshotTests.swift b/cmuxTests/TabManagerSessionSnapshotTests.swift index 5b2b520b3135..b5554f8e5853 100644 --- a/cmuxTests/TabManagerSessionSnapshotTests.swift +++ b/cmuxTests/TabManagerSessionSnapshotTests.swift @@ -1118,6 +1118,12 @@ final class TabManagerSessionSnapshotTests: XCTestCase { XCTAssertTrue(item.menuSubtitle.contains(String(localized: "menu.history.recentlyClosed.kind.tab", defaultValue: "Tab"))) } + func testRightSidebarToolSnapshotTolerantlyDecodesObsoleteHistoryMode() throws { + let json = #"{"mode":"history"}"#.data(using: .utf8)! + let snapshot = try JSONDecoder().decode(SessionRightSidebarToolPanelSnapshot.self, from: json) + XCTAssertNil(snapshot.mode) + } + func testReopenSpecificRecentlyClosedRowRestoresOnlyThatRecord() throws { let originalAppDelegate = AppDelegate.shared AppDelegate.shared = nil From 33c342d7eb640ab61b0c5440cafcc09f1bea1cc3 Mon Sep 17 00:00:00 2001 From: Lawrence Chen Date: Tue, 26 May 2026 02:06:17 -0700 Subject: [PATCH 4/4] Remove duplicate @MainActor attribute left by prior edit --- cmuxTests/ShortcutAndCommandPaletteTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/cmuxTests/ShortcutAndCommandPaletteTests.swift b/cmuxTests/ShortcutAndCommandPaletteTests.swift index ceff0958f73b..762d733d4b16 100644 --- a/cmuxTests/ShortcutAndCommandPaletteTests.swift +++ b/cmuxTests/ShortcutAndCommandPaletteTests.swift @@ -1407,7 +1407,6 @@ final class MainWindowFocusControllerRightSidebarHideTests: XCTestCase { XCTAssertTrue(window.firstResponder === focusHost) } - @MainActor @MainActor func testFocusShortcutToggleClearsRightSidebarIntentWhenTerminalIsUnavailable() { let controller = MainWindowFocusController(