From 22697baa3764be392e6c04fb9144bff148b5d98f Mon Sep 17 00:00:00 2001 From: Syngnat Date: Mon, 6 Jul 2026 17:26:41 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(query-editor):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8DSQL=20AI=E8=A1=A5=E5=85=A8=E8=AF=AF=E8=A7=A6=E6=99=AE?= =?UTF-8?q?=E9=80=9A=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QueryEditor.external-sql-save.test.tsx | 40 +++++++++++++++++-- frontend/src/components/QueryEditor.tsx | 17 ++++---- .../QueryEditorToolbar.ai-trigger.test.tsx | 1 + .../src/components/QueryEditorToolbar.tsx | 1 + 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx index 69a1e6a2..04215b42 100644 --- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -1498,7 +1498,7 @@ describe('QueryEditor external SQL save', () => { expect(editorState.value).toBe('SELECT * FROM '); }); - it('uses structured SQL suggestions instead of freeform AI when manual completion is triggered in table-name context', async () => { + it('does not fall back to structured SQL suggestions when manual AI completion is triggered in table-name context', async () => { backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [ @@ -1566,7 +1566,7 @@ describe('QueryEditor external SQL save', () => { } }); - expect(editorState.editor.trigger).toHaveBeenCalledWith( + expect(editorState.editor.trigger).not.toHaveBeenCalledWith( 'gonavi-ai-inline-manual', 'editor.action.triggerSuggest', undefined, @@ -1962,7 +1962,7 @@ describe('QueryEditor external SQL save', () => { } }); - it('strips a stray backslash before opening manual table suggestions from the toolbar AI button', async () => { + it('strips a stray backslash before keeping manual toolbar AI completion on the AI path', async () => { backendApp.DBGetTables.mockResolvedValueOnce({ success: true, data: [ @@ -2013,7 +2013,39 @@ describe('QueryEditor external SQL save', () => { })], ); expect(editorState.value).toBe('SELECT * FROM '); - expect(editorState.editor.trigger).toHaveBeenCalledWith( + expect(editorState.editor.trigger).not.toHaveBeenCalledWith( + 'gonavi-ai-inline-manual', + 'editor.action.triggerSuggest', + undefined, + ); + }); + + it('keeps the AI dropdown completion action on the AI path instead of opening plain suggestions', async () => { + backendApp.DBGetTables.mockResolvedValueOnce({ + success: true, + data: [ + { TABLE_NAME: 'videos' }, + { TABLE_NAME: 'visits' }, + ], + }); + + let renderer: ReactTestRenderer; + await act(async () => { + renderer = create(); + }); + + editorState.value = 'SELECT * FROM '; + editorState.position = { lineNumber: 1, column: 'SELECT * FROM '.length + 1 }; + editorState.editor.trigger.mockClear(); + + await act(async () => { + findButton(renderer!, '触发 SQL AI 自动补全').props.onClick(); + for (let i = 0; i < 8; i += 1) { + await Promise.resolve(); + } + }); + + expect(editorState.editor.trigger).not.toHaveBeenCalledWith( 'gonavi-ai-inline-manual', 'editor.action.triggerSuggest', undefined, diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 617fae9f..2cead968 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -3599,17 +3599,14 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc return; } if (!insertText.trim()) { - if (intent.intent === 'table_name' || intent.intent === 'column_name') { - const shouldTriggerStructuredSuggest = manualTrigger - || shouldTriggerQueryEditorInlineObjectSuggestFallback({ - aiContext: buildQueryEditorAiContext(), - editorSnapshot, - }); + // Keep the manual AI action on the AI path; silently downgrading to plain suggest is misleading. + if (!manualTrigger && (intent.intent === 'table_name' || intent.intent === 'column_name')) { + const shouldTriggerStructuredSuggest = shouldTriggerQueryEditorInlineObjectSuggestFallback({ + aiContext: buildQueryEditorAiContext(), + editorSnapshot, + }); if (shouldTriggerStructuredSuggest) { - triggerStructuredSqlSuggest( - manualTrigger ? 'gonavi-ai-inline-manual' : 'gonavi-ai-inline-auto', - !manualTrigger, - ); + triggerStructuredSqlSuggest('gonavi-ai-inline-auto', true); } } return; diff --git a/frontend/src/components/QueryEditorToolbar.ai-trigger.test.tsx b/frontend/src/components/QueryEditorToolbar.ai-trigger.test.tsx index af4bc60f..0c2dfc53 100644 --- a/frontend/src/components/QueryEditorToolbar.ai-trigger.test.tsx +++ b/frontend/src/components/QueryEditorToolbar.ai-trigger.test.tsx @@ -8,6 +8,7 @@ describe('QueryEditorToolbar AI trigger affordance', () => { expect(source).toContain('onMouseDown={onCaptureEditorCursorPosition}'); expect(source).toContain('onClick={onTriggerSqlAiCompletion}'); expect(source).toContain('triggerSqlAiCompletionLabel'); + expect(source).toMatch(/aria-label="AI more actions"\s+onMouseDown=\{onCaptureEditorCursorPosition\}/); }); it('keeps the secondary AI dropdown for other actions', () => { diff --git a/frontend/src/components/QueryEditorToolbar.tsx b/frontend/src/components/QueryEditorToolbar.tsx index 382fccf0..6ea431ff 100644 --- a/frontend/src/components/QueryEditorToolbar.tsx +++ b/frontend/src/components/QueryEditorToolbar.tsx @@ -400,6 +400,7 @@ const QueryEditorToolbar: React.FC = ({ className={isV2Ui ? "gn-v2-query-toolbar-icon-action" : undefined} icon={} aria-label="AI more actions" + onMouseDown={onCaptureEditorCursorPosition} />