diff --git a/application/AppHandlers.globalHotkeys.test.ts b/application/AppHandlers.globalHotkeys.test.ts index 78d1448b3..f79b46ad3 100644 --- a/application/AppHandlers.globalHotkeys.test.ts +++ b/application/AppHandlers.globalHotkeys.test.ts @@ -674,3 +674,53 @@ test('connection log host snapshot includes custom host icon fields', () => { }, ); }); + +test('toggleOpacity hotkey invokes toggleWindowOpacity', () => { + let called = false; + const noop = () => {}; + + executeHotkeyActionImpl( + () => ({ + IS_DEV: false, + MOVE_FOCUS_DEBOUNCE_MS: 0, + activeTabStore: { getActiveTabId: () => 'vault' }, + addConnectionLogRef: { current: noop }, + closeSession: noop, + closeTabInFlightRef: { current: false }, + closeWorkspace: noop, + collectSessionIds: () => [], + confirmIfBusyLocalTerminal: async () => true, + createLocalTerminalWithCurrentShell: noop, + editorTabs: [], + fromEditorTabId: () => null, + handleOpenSettingsRef: { current: noop }, + handleRequestCloseEditorTabRef: { current: noop }, + isEditorTabId: () => false, + isQuickSwitcherOpen: false, + lastMoveFocusTimeRef: { current: 0 }, + moveFocusInWorkspace: noop, + orderedTabs: [], + resolveCloseIntent: () => ({ kind: 'noop' }), + resolveSnippetsShortcutIntent: () => ({ kind: 'noop' }), + sessions: [], + setActiveTabId: noop, + setAddToWorkspaceDialog: noop, + setIsQuickSwitcherOpen: noop, + setNavigateToSection: noop, + settings: { showSftpTab: true, shellOnlyTabNumberShortcuts: false }, + splitSessionWithCurrentShell: noop, + systemInfoRef: { current: { username: 'user', hostname: 'host' } }, + toEditorTabId: (id: string) => `editor:${id}`, + toggleBroadcast: noop, + toggleScriptsSidePanelRef: { current: noop }, + toggleSidePanelRef: { current: noop }, + toggleWindowOpacity: () => { called = true; }, + toggleWorkspaceViewMode: noop, + workspaces: [], + }), + 'toggleOpacity', + { key: 'u', code: 'KeyU', ctrlKey: true, shiftKey: true } as KeyboardEvent, + ); + + assert.equal(called, true); +}); diff --git a/application/app/AppHandlers.ts b/application/app/AppHandlers.ts index 75aad2d51..166f2681a 100644 --- a/application/app/AppHandlers.ts +++ b/application/app/AppHandlers.ts @@ -702,7 +702,7 @@ export async function closeTabsBatchImpl(getCtx: AppContextGetter, targetIds: st } export function executeHotkeyActionImpl(getCtx: AppContextGetter, action: string, e: KeyboardEvent) { - const { IS_DEV, MOVE_FOCUS_DEBOUNCE_MS, activeTabStore, addConnectionLogRef, closePluginViewTab, closeSession, closeTabInFlightRef, closeWorkspace, collectSessionIds, confirmIfBusyLocalTerminal, createLocalTerminalWithCurrentShell, editorTabs, fromEditorTabId, handleOpenSettingsRef, handleRequestCloseEditorTabRef, isEditorTabId, isPluginViewTabId, isQuickSwitcherOpen, lastMoveFocusTimeRef, moveFocusInWorkspace, orderedTabs, resolveCloseIntent, resolveSnippetsShortcutIntent, sessions, setActiveTabId, setAddToWorkspaceDialog, setIsQuickSwitcherOpen, setNavigateToSection, settings, sftpPaneMagnificationRef, splitSessionWithCurrentShell, systemInfoRef, terminalPaneMagnificationRef, toEditorTabId, toggleBroadcast, toggleScriptsSidePanelRef, toggleSidePanelRef, workspaces } = getCtx(); + const { IS_DEV, MOVE_FOCUS_DEBOUNCE_MS, activeTabStore, addConnectionLogRef, closePluginViewTab, closeSession, closeTabInFlightRef, closeWorkspace, collectSessionIds, confirmIfBusyLocalTerminal, createLocalTerminalWithCurrentShell, editorTabs, fromEditorTabId, handleOpenSettingsRef, handleRequestCloseEditorTabRef, isEditorTabId, isPluginViewTabId, isQuickSwitcherOpen, lastMoveFocusTimeRef, moveFocusInWorkspace, orderedTabs, resolveCloseIntent, resolveSnippetsShortcutIntent, sessions, setActiveTabId, setAddToWorkspaceDialog, setIsQuickSwitcherOpen, setNavigateToSection, settings, sftpPaneMagnificationRef, splitSessionWithCurrentShell, systemInfoRef, terminalPaneMagnificationRef, toEditorTabId, toggleBroadcast, toggleScriptsSidePanelRef, toggleSidePanelRef, toggleWindowOpacity, workspaces } = getCtx(); { const shortcutTabs = buildNumberShortcutTabTargets({ showSftpTab: settings.showSftpTab ?? true, @@ -892,6 +892,9 @@ export function executeHotkeyActionImpl(getCtx: AppContextGetter, action: string setNavigateToSection('snippets'); } break; + case 'toggleOpacity': + toggleWindowOpacity(); + break; case 'toggleSidePanel': toggleSidePanelRef.current?.(); break; diff --git a/application/app/AppSideEffects.tsx b/application/app/AppSideEffects.tsx index b5172d8be..c432eb2a7 100755 --- a/application/app/AppSideEffects.tsx +++ b/application/app/AppSideEffects.tsx @@ -144,6 +144,7 @@ export function AppSideEffects() { showSftpTab, shellOnlyTabNumberShortcuts, workspaceFocusStyle, + toggleWindowOpacity, } = settings; useTerminalKeyboardFocus(hotkeyScheme !== 'disabled' && !disableTerminalFontZoom); @@ -1076,6 +1077,7 @@ export function AppSideEffects() { toggleBroadcast, toggleScriptsSidePanelRef, toggleSidePanelRef, + toggleWindowOpacity, toggleWorkspaceViewMode, workspaces: workspacesRefForHotkeys.current, }), action, e); @@ -1088,6 +1090,7 @@ export function AppSideEffects() { splitSessionWithCurrentShell, moveFocusInWorkspace, toggleBroadcast, + toggleWindowOpacity, toggleWorkspaceViewMode, confirmIfBusyLocalTerminal, ]); diff --git a/application/i18n/locales/ru/core.ts b/application/i18n/locales/ru/core.ts index c12563009..064f1f419 100644 --- a/application/i18n/locales/ru/core.ts +++ b/application/i18n/locales/ru/core.ts @@ -866,6 +866,7 @@ export const ruCoreMessages: Messages = { 'settings.shortcuts.binding.new-workspace': 'Новая рабочая область', 'settings.shortcuts.binding.snippets': 'Открыть сниппеты', 'settings.shortcuts.binding.broadcast': 'Переключить режим трансляции', + 'settings.shortcuts.binding.toggle-opacity': 'Переключить прозрачность окна', 'settings.shortcuts.binding.toggle-side-panel': 'Переключить боковую панель', 'settings.shortcuts.binding.sftp-copy': 'Копировать файл', 'settings.shortcuts.binding.sftp-cut': 'Вырезать файл', diff --git a/application/i18n/locales/zh-CN/terminal.ts b/application/i18n/locales/zh-CN/terminal.ts index 4a21c5353..0621487c5 100644 --- a/application/i18n/locales/zh-CN/terminal.ts +++ b/application/i18n/locales/zh-CN/terminal.ts @@ -565,6 +565,7 @@ export const zhCNTerminalMessages: Messages = { 'settings.shortcuts.binding.new-workspace': '新建工作区', 'settings.shortcuts.binding.snippets': '打开代码片段', 'settings.shortcuts.binding.broadcast': '切换广播模式', + 'settings.shortcuts.binding.toggle-opacity': '切换窗口透明度', 'settings.shortcuts.binding.toggle-side-panel': '切换侧边栏', 'settings.shortcuts.binding.sftp-copy': '复制文件', 'settings.shortcuts.binding.sftp-cut': '剪切文件', diff --git a/application/i18n/locales/zh-TW/terminal.ts b/application/i18n/locales/zh-TW/terminal.ts index f51fd783a..cae96e31b 100644 --- a/application/i18n/locales/zh-TW/terminal.ts +++ b/application/i18n/locales/zh-TW/terminal.ts @@ -565,6 +565,7 @@ export const zhTWTerminalMessages: Messages = { 'settings.shortcuts.binding.new-workspace': '新增工作區', 'settings.shortcuts.binding.snippets': '開啟程式碼片段', 'settings.shortcuts.binding.broadcast': '切換廣播模式', + 'settings.shortcuts.binding.toggle-opacity': '切換視窗透明度', 'settings.shortcuts.binding.toggle-side-panel': '切換側邊欄', 'settings.shortcuts.binding.sftp-copy': '複製檔案', 'settings.shortcuts.binding.sftp-cut': '剪下檔案', diff --git a/application/state/useGlobalHotkeys.ts b/application/state/useGlobalHotkeys.ts index 0c66cd699..0ea5da9dd 100644 --- a/application/state/useGlobalHotkeys.ts +++ b/application/state/useGlobalHotkeys.ts @@ -39,6 +39,7 @@ export const getAppLevelActions = (): Set => { 'togglePaneZoom', 'openLocal', 'openSettings', + 'toggleOpacity', ]); }; diff --git a/application/state/useSettingsState.ts b/application/state/useSettingsState.ts index fc6e97a86..6963086e4 100644 --- a/application/state/useSettingsState.ts +++ b/application/state/useSettingsState.ts @@ -595,6 +595,18 @@ export const useSettingsState = (options: { enableSettingsSync?: boolean; enable return incoming; }); }, []); + // Tracks the user's preferred transparency level (last value below 100%) so + // Cmd+U can restore it after toggling transparency off. Session-only (useRef), + // never persisted — matches iTerm2's session-level toggle behavior. + const lastNonFullOpacityRef = useRef(windowOpacity < 1.0 ? windowOpacity : null); + useEffect(() => { + if (windowOpacity < 1.0) { + lastNonFullOpacityRef.current = windowOpacity; + } + }, [windowOpacity]); + const toggleWindowOpacity = useCallback(() => { + setWindowOpacity(windowOpacity < 1.0 ? 1.0 : (lastNonFullOpacityRef.current ?? 0.85)); + }, [windowOpacity, setWindowOpacity]); const [appIconVariant, setAppIconVariantState] = useState(() => { const stored = readStoredString(STORAGE_KEY_APP_ICON_VARIANT); return resolveAppIconVariant(stored ?? DEFAULT_APP_ICON_VARIANT); @@ -2110,6 +2122,7 @@ export const useSettingsState = (options: { enableSettingsSync?: boolean; enable setGlobalHotkeyEnabled, windowOpacity, setWindowOpacity, + toggleWindowOpacity, appIconVariant, setAppIconVariant, rehydrateAllFromStorage, diff --git a/components/TopTabsQuickControls.tsx b/components/TopTabsQuickControls.tsx index 8097d7efb..bedeee0f5 100644 --- a/components/TopTabsQuickControls.tsx +++ b/components/TopTabsQuickControls.tsx @@ -107,7 +107,7 @@ export const TopTabsQuickControls: React.FC = ({ onClick={() => setIsOpacityExpanded((current) => !current)} >
- + {t('topTabs.windowOpacity')}
diff --git a/domain/models/keyBindings.ts b/domain/models/keyBindings.ts index c99c2c205..2ee82d033 100644 --- a/domain/models/keyBindings.ts +++ b/domain/models/keyBindings.ts @@ -274,6 +274,7 @@ export const DEFAULT_KEY_BINDINGS: KeyBinding[] = [ { id: 'broadcast', action: 'broadcast', label: 'Switch the Broadcast Mode', mac: '⌘ + B', pc: 'Ctrl + B', category: 'app' }, { id: 'toggle-side-panel', action: 'toggleSidePanel', label: 'Toggle Side Panel', mac: '⌘ + \\', pc: 'Ctrl + \\', category: 'app' }, { id: 'open-settings', action: 'openSettings', label: 'Open Settings', mac: '⌘ + ,', pc: 'Ctrl + ,', category: 'app' }, + { id: 'toggle-opacity', action: 'toggleOpacity', label: 'Toggle Window Transparency', mac: '⌘ + U', pc: 'Ctrl + Shift + U', category: 'app' }, // SFTP Operations { id: 'sftp-copy', action: 'sftpCopy', label: 'Copy Files', mac: '⌘ + C', pc: 'Ctrl + C', category: 'sftp' },