fix: fix WebGL addon dispose error and add Ctrl+W shortcut to close tab

This commit is contained in:
Ethanfly 2025-12-31 15:48:07 +08:00
parent 2387e6e5f4
commit 0e20b0df3a
2 changed files with 43 additions and 24 deletions

View File

@ -54,13 +54,44 @@ function App() {
checkRemoteStatus(); checkRemoteStatus();
}, [loadHosts, checkRemoteStatus]); }, [loadHosts, checkRemoteStatus]);
// 关闭标签页 (需要在 useEffect 之前定义)
const closeTab = useCallback((tabId) => {
setActiveTabs((prev) => {
const newTabs = prev.filter((t) => t.id !== tabId);
return newTabs;
});
setActiveTabId((prevActiveId) => {
if (prevActiveId === tabId) {
// 找到要关闭的标签的索引
const tabIndex = activeTabs.findIndex(t => t.id === tabId);
const remainingTabs = activeTabs.filter((t) => t.id !== tabId);
if (remainingTabs.length > 0) {
// 优先切换到右边的标签,否则切换到左边的
const newIndex = Math.min(tabIndex, remainingTabs.length - 1);
return remainingTabs[newIndex].id;
}
return null;
}
return prevActiveId;
});
}, [activeTabs]);
// 键盘快捷键 // 键盘快捷键
useEffect(() => { useEffect(() => {
const handleKeyDown = (e) => { const handleKeyDown = (e) => {
// Ctrl+K: 打开命令面板
if ((e.ctrlKey || e.metaKey) && e.key === 'k') { if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault(); e.preventDefault();
setShowCommandPalette(true); setShowCommandPalette(true);
} }
// Ctrl+W: 关闭当前标签页
if ((e.ctrlKey || e.metaKey) && e.key === 'w') {
e.preventDefault();
if (activeTabId) {
closeTab(activeTabId);
}
}
// Escape: 关闭弹窗
if (e.key === 'Escape') { if (e.key === 'Escape') {
setShowCommandPalette(false); setShowCommandPalette(false);
setShowHostManager(false); setShowHostManager(false);
@ -70,7 +101,7 @@ function App() {
window.addEventListener('keydown', handleKeyDown); window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown);
}, []); }, [activeTabId, closeTab]);
// 连接主机 // 连接主机
const connectHost = useCallback((host) => { const connectHost = useCallback((host) => {
@ -90,21 +121,6 @@ function App() {
setSelectedHost(null); // 关闭右侧编辑面板 setSelectedHost(null); // 关闭右侧编辑面板
}, []); }, []);
// 关闭标签页
const closeTab = useCallback((tabId) => {
setActiveTabs((prev) => {
const newTabs = prev.filter((t) => t.id !== tabId);
return newTabs;
});
setActiveTabId((prevActiveId) => {
if (prevActiveId === tabId) {
const remainingTabs = activeTabs.filter((t) => t.id !== tabId);
return remainingTabs.length > 0 ? remainingTabs[remainingTabs.length - 1].id : null;
}
return prevActiveId;
});
}, [activeTabs]);
// 更新连接状态 // 更新连接状态
const handleConnectionChange = useCallback((tabId, connected) => { const handleConnectionChange = useCallback((tabId, connected) => {
setActiveTabs((prev) => setActiveTabs((prev) =>

View File

@ -319,16 +319,19 @@ function Terminal({ tabId, hostId, onConnectionChange, onShowCommandPalette, onT
connectionIdRef.current = null; connectionIdRef.current = null;
} }
// 清理 WebGL 渲染器 // 安全清理终端及其插件
if (webglAddonRef.current) { // 注意:必须先 dispose terminal它会自动清理所有加载的 addon
webglAddonRef.current.dispose(); try {
webglAddonRef.current = null; if (xtermRef.current) {
xtermRef.current.dispose();
xtermRef.current = null;
}
} catch (e) {
console.warn('终端清理时出错:', e);
} }
if (xtermRef.current) { // 清理引用
xtermRef.current.dispose(); webglAddonRef.current = null;
xtermRef.current = null;
}
fitAddonRef.current = null; fitAddonRef.current = null;
}; };
}, [initTerminal, connect]); }, [initTerminal, connect]);