fix: allow closing last session tab and restore DevTools shortcut

Closing the sole remaining non-welcome tab now resets to welcome; enable Tauri devtools, menu/shortcut toggle, and copy release artifacts from CARGO_TARGET_DIR.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-12 01:31:20 +08:00
1 parent da95113e6d
commit 188f3556ed
14 files changed
+95 -38

No files matched your search

+2 -1
View File
@@ -143,7 +143,8 @@ export function CommandPalette({
shortcut: 'Ctrl+W',
icon: <X size={16} />,
action: () => {
if (activeTabId && tabs.length > 1) {
const active = tabs.find((tab) => tab.id === activeTabId)
if (activeTabId && (tabs.length > 1 || active?.type !== 'welcome')) {
void disposeAndCloseTab(activeTabId)
}
},
@@ -912,7 +912,8 @@ export default function TerminalWorkspace({ tabId, isActive = true }: TerminalVi
closePane(paneId)
return
}
if (tabs.length <= 1) return
const onlyTab = tabs.length === 1 ? tabs[0] : null
if (tabs.length <= 1 && (!onlyTab || onlyTab.type === 'welcome' || onlyTab.id !== tabId)) return
const connectionId = connectionIdRef.current
const paneShells = panesRef.current.flatMap((pane) => pane.shellId ? [pane.shellId] : [])
@@ -935,7 +936,7 @@ export default function TerminalWorkspace({ tabId, isActive = true }: TerminalVi
}
closeTab(tabId)
})()
}, [closePane, closeTab, connectionIdRef, tabId, tabs.length])
}, [closePane, closeTab, connectionIdRef, tabId, tabs])
const openTerminalContextMenu = useCallback((
paneId: string,
@@ -1246,7 +1247,7 @@ export default function TerminalWorkspace({ tabId, isActive = true }: TerminalVi
hasSelection={contextMenu.hasSelection}
canSplit={connectionState === 'connected'}
canClosePane={panes.length > 1}
canCloseTerminal={panes.length > 1 || tabs.length > 1}
canCloseTerminal={panes.length > 1 || tabs.length > 1 || tabs[0]?.type !== 'welcome'}
closeLabel={panes.length > 1 ? t('toolbar.closePaneLabel') : t('toolbar.closeTabLabel')}
maximized={maximizedPaneId === contextMenu.paneId}
onClose={() => setContextMenu(null)}
+1
View File
@@ -190,6 +190,7 @@ export interface DesktopApiContract {
// Window
toggleMaximize: () => Promise<void>
toggleDevtools: () => Promise<boolean>
// Events
onMenuNewSession: (callback: () => void) => void
+11 -1
View File
@@ -213,6 +213,15 @@ export function useKeyboardShortcuts(): void {
return
}
// ---- Cmd+Option+I / F12: 开发者工具(输入框内也可用)----
if ((ctrlOrMeta && e.altKey && !e.shiftKey && e.key.toLowerCase() === 'i') || e.key === 'F12') {
e.preventDefault()
void window.opsxApi?.toggleDevtools?.().catch(() => {
logger.warn('Failed to toggle developer tools')
})
return
}
// ---- 以下快捷键在输入框中不触发 (除非 fireInInput=true) ----
if (isInputElement(e) && !shouldFireInInput(e)) {
return
@@ -222,7 +231,8 @@ export function useKeyboardShortcuts(): void {
if (matchesKeySequence(e, keys[KeybindingAction.TAB_CLOSE])) {
e.preventDefault()
const { tabs, activeTabId } = useTabStore.getState()
if (activeTabId && tabs.length > 1) {
const active = tabs.find((tab) => tab.id === activeTabId)
if (activeTabId && (tabs.length > 1 || active?.type !== 'welcome')) {
logger.debug('Ctrl+W: closing tab', activeTabId)
void disposeAndCloseTab(activeTabId)
}
+1
View File
@@ -186,6 +186,7 @@ export const desktopApi: DesktopApi = {
clipboardReadText: () => call('clipboard_read_text'),
clipboardWriteText: (text) => call('clipboard_write_text', { text }),
toggleMaximize: () => call('toggle_maximize'),
toggleDevtools: () => call('toggle_devtools'),
sshForwardLocal: (connectionId, localPort, remoteHost, remotePort, forwardId) =>
call('ssh_forward_local', { connectionId, forwardId, localPort, remoteHost, remotePort }),
+18 -5
View File
@@ -176,12 +176,25 @@ export const useTabStore = create<TabState>()(
closeTab: (tabId) => {
const { tabs, activeTabId } = get()
if (tabs.length <= 1) return null
const closedTab = tabs.find(t => t.id === tabId) || null
if (!closedTab) return null
// Last remaining tab: welcome stays put; any other type resets to welcome
// (e.g. after "close others", closing the sole terminal must work).
if (tabs.length <= 1) {
if (closedTab.type === 'welcome') return null
const welcomeTab: Tab = {
id: 'welcome',
title: '欢迎',
type: 'welcome',
isActive: true,
}
set({ tabs: [welcomeTab], activeTabId: 'welcome' })
return closedTab
}
const newTabs = tabs.filter(t => t.id !== tabId)
// If closing active tab, activate another one
let newActiveId = activeTabId
if (activeTabId === tabId) {
@@ -192,7 +205,7 @@ export const useTabStore = create<TabState>()(
newActiveId = newActiveTab.id
}
}
set({ tabs: newTabs, activeTabId: newActiveId })
return closedTab
},
+4 -2
View File
@@ -50,9 +50,11 @@ async function disposeTabs(tabs: Tab[]): Promise<void> {
/** Dispose resources then remove the tab from the store. */
export async function disposeAndCloseTab(tabId: string): Promise<void> {
const { tabs, getTabById, closeTab } = useTabStore.getState()
if (tabs.length <= 1) return
const tab = getTabById(tabId)
if (tab) await disposeTabResources(tab)
if (!tab) return
// Only the lone welcome page is unclosable; a lone session tab resets to welcome.
if (tabs.length <= 1 && tab.type === 'welcome') return
await disposeTabResources(tab)
closeTab(tabId)
}