From 304a4926d27cb72438ac98af0b9b1b717be1abaa Mon Sep 17 00:00:00 2001 From: Syngnat Date: Fri, 13 Mar 2026 16:22:33 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=F0=9F=94=A7=20fix(query-editor/sidebar):?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=E5=85=A8=E9=80=89=E9=94=99=E4=BD=8D?= =?UTF-8?q?=E5=B9=B6=E5=AE=8C=E5=96=84=E4=BE=A7=E6=A0=8F=E6=8B=96=E6=8B=BD?= =?UTF-8?q?=E8=87=AA=E9=80=82=E5=BA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复新建查询页 Ctrl/Cmd+A 命中空白区域的问题,改为强制触发 Monaco 全选 - 限制全选拦截仅作用于当前活动查询页,避免影响其他编辑区 - 修复 sidebarWidth 声明时序导致的运行时 ReferenceError - 侧栏拖窄时工具按钮按宽度分档自适应(4列→2列→图标模式) - 新建查询/新建连接在窄宽度下改为单列,避免图文重叠 - 补充按钮防溢出样式,保证窄宽度可读与可点击 --- frontend/src/App.tsx | 33 +++++++++++------- frontend/src/components/QueryEditor.tsx | 46 ++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 183106a..3b5a5ff 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -89,6 +89,7 @@ function App() { const [runtimePlatform, setRuntimePlatform] = useState(''); const [isLinuxRuntime, setIsLinuxRuntime] = useState(false); const [isStoreHydrated, setIsStoreHydrated] = useState(() => useStore.persist.hasHydrated()); + const [sidebarWidth, setSidebarWidth] = useState(330); const globalProxyInvalidHintShownRef = React.useRef(false); // 同步 macOS 窗口透明度:opacity=1.0 且 blur=0 时关闭 NSVisualEffectView, @@ -442,7 +443,6 @@ function App() { const floatingLogButtonShadow = darkMode ? '0 8px 22px rgba(0,0,0,0.38)' : '0 8px 20px rgba(0,0,0,0.16)'; - const isOpaqueUtilityMode = resolvedAppearance.opacity >= 0.999 && resolvedAppearance.blur <= 0; const utilityButtonBgAlpha = darkMode ? Math.max(0.28, Math.min(0.76, effectiveOpacity * 0.72)) @@ -462,10 +462,13 @@ function App() { : (darkMode ? `0 8px 18px rgba(0,0,0,${Math.max(0.10, Math.min(0.22, effectiveOpacity * 0.24))})` : `0 8px 18px rgba(15,23,42,${Math.max(0.04, Math.min(0.12, effectiveOpacity * 0.12))})`); + const isSidebarNarrow = sidebarWidth < 360; + const isSidebarCompact = sidebarWidth < 320; + const isSidebarUltraCompact = sidebarWidth < 260; const utilityButtonStyle = useMemo(() => ({ height: Math.max(30, Math.round(32 * effectiveUiScale)), width: '100%', - paddingInline: Math.max(10, Math.round(12 * effectiveUiScale)), + paddingInline: isSidebarCompact ? Math.max(8, Math.round(9 * effectiveUiScale)) : Math.max(10, Math.round(12 * effectiveUiScale)), borderRadius: 10, border: `1px solid ${utilityButtonBorderColor}`, background: utilityButtonBgColor, @@ -476,8 +479,13 @@ function App() { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', - gap: 6, - }), [blurFilter, darkMode, effectiveUiScale, isOpaqueUtilityMode, utilityButtonBgColor, utilityButtonBorderColor, utilityButtonShadow]); + gap: isSidebarCompact ? 4 : 6, + minWidth: 0, + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + fontSize: isSidebarCompact ? 13 : 14, + }), [blurFilter, darkMode, effectiveUiScale, isOpaqueUtilityMode, isSidebarCompact, utilityButtonBgColor, utilityButtonBorderColor, utilityButtonShadow]); const overlayTheme = useMemo(() => buildOverlayWorkbenchTheme(darkMode), [darkMode]); const sidebarQuickActionBaseStyle = useMemo(() => ({ @@ -493,6 +501,8 @@ function App() { backdropFilter: blurFilter, WebkitBackdropFilter: blurFilter, minWidth: 0, + overflow: 'hidden', + textOverflow: 'ellipsis', whiteSpace: 'nowrap', }), [blurFilter, darkMode, effectiveUiScale]); const sidebarQueryActionStyle = useMemo(() => ({ @@ -561,7 +571,7 @@ function App() { marginTop: 2, }), [overlayTheme]); - const sidebarHorizontalPadding = 10; + const sidebarHorizontalPadding = isSidebarCompact ? 8 : 10; const addTab = useStore(state => state.addTab); const activeContext = useStore(state => state.activeContext); @@ -1058,7 +1068,6 @@ function App() { }; // Sidebar Resizing - const [sidebarWidth, setSidebarWidth] = useState(330); const sidebarDragRef = React.useRef<{ startX: number, startWidth: number } | null>(null); const rafRef = React.useRef(null); const ghostRef = React.useRef(null); @@ -1445,15 +1454,15 @@ function App() { >
-
- - - - +
+ + + +
-
+
diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 69294d1..8be3b5d 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -50,6 +50,8 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { const monacoRef = useRef(null); const lastExternalQueryRef = useRef(tab.query || ''); const dragRef = useRef<{ startY: number, startHeight: number } | null>(null); + const queryEditorRootRef = useRef(null); + const editorPaneRef = useRef(null); const tablesRef = useRef<{dbName: string, tableName: string}[]>([]); // Store tables for autocomplete (cross-db) const allColumnsRef = useRef<{dbName: string, tableName: string, name: string, type: string}[]>([]); // Store all columns (cross-db) const visibleDbsRef = useRef([]); // Store visible databases for cross-db intellisense @@ -1341,6 +1343,46 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { } }; + useEffect(() => { + const handleSelectAllInEditor = (event: KeyboardEvent) => { + if (activeTabId !== tab.id) { + return; + } + if (!(event.ctrlKey || event.metaKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== 'a') { + return; + } + + const editor = editorRef.current; + if (!editor) { + return; + } + + const targetNode = event.target instanceof Node ? event.target : null; + const editorHasFocus = !!editor.hasTextFocus?.(); + const inEditorPane = !!(targetNode && editorPaneRef.current?.contains(targetNode)); + const inQueryEditor = !!(targetNode && queryEditorRootRef.current?.contains(targetNode)); + if (!editorHasFocus && !inEditorPane) { + return; + } + if (!editorHasFocus && isEditableElement(event.target) && !inEditorPane) { + return; + } + if (!editorHasFocus && !inQueryEditor) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + editor.focus?.(); + editor.trigger('keyboard', 'editor.action.selectAll', null); + }; + + window.addEventListener('keydown', handleSelectAllInEditor, true); + return () => { + window.removeEventListener('keydown', handleSelectAllInEditor, true); + }; + }, [activeTabId, tab.id]); + useEffect(() => { const binding = shortcutOptions.runQuery; if (!binding?.enabled || !binding.combo) { @@ -1417,7 +1459,7 @@ const QueryEditor: React.FC<{ tab: TabData }> = ({ tab }) => { }; return ( -
+
+