feat(editor): polish SQL autocomplete UI

This commit is contained in:
t8y2 2026-05-19 12:33:05 +08:00
parent cf46aec158
commit 653feb7c65
3 changed files with 123 additions and 2 deletions

View File

@ -14,7 +14,7 @@ import {
} from "@/lib/sqlCompletion";
import { extractIdentifierAt, isSqlKeyword, matchTable } from "@/lib/sqlNavigation";
import { lineColumnToOffset, parseSqlErrorLocation } from "@/lib/sqlDiagnostics";
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
import { loadEditorTheme, editorFontTheme, sqlCompletionTheme } from "@/lib/editorThemes";
import { shortcutToCodeMirrorKey } from "@/lib/shortcutRegistry";
import type { SqlCompletionColumn } from "@/lib/sqlCompletion";
@ -485,7 +485,7 @@ async function provideSqlCompletions(
})
: {
label: item.label,
type: item.type === "keyword" ? "keyword" : item.type === "table" ? "class" : "property",
type: item.type,
detail: item.detail,
boost: item.boost,
},
@ -595,6 +595,7 @@ onMounted(async () => {
async (context: CompletionContext) => provideSqlCompletions(context.state, context.pos, context.explicit),
],
}),
sqlCompletionTheme(EditorView),
codeMirrorTheme.of(theme),
closeBrackets(),
bracketMatching(),

View File

@ -1,6 +1,8 @@
import type { Extension } from "@codemirror/state";
import type { EditorTheme } from "@/stores/settingsStore";
type CodeMirrorStyleSpec = Parameters<typeof import("@codemirror/view").EditorView.theme>[0];
/** Load a CodeMirror theme extension by theme name. */
export async function loadEditorTheme(theme: EditorTheme): Promise<Extension> {
switch (theme) {
@ -41,5 +43,95 @@ export function editorFontTheme(
},
...(opts?.scrollable ? { ".cm-scroller": { overflow: "auto" } } : {}),
".cm-content": { fontFamily: family },
".cm-gutters": {
borderRight: "0 !important",
position: "relative",
},
".cm-gutters:after": {
background: "rgba(148, 163, 184, 0.38)",
bottom: "0",
content: "''",
pointerEvents: "none",
position: "absolute",
right: "0",
top: "0",
width: "1px",
zIndex: "10",
},
".cm-lineNumbers .cm-gutterElement": {
paddingRight: "16px",
},
});
}
export function buildSqlCompletionThemeRules(): CodeMirrorStyleSpec {
return {
".cm-tooltip.cm-tooltip-autocomplete": {
background: "#24272c",
border: "1px solid rgba(10, 12, 16, 0.95)",
borderRadius: "10px",
boxShadow:
"0 18px 42px rgba(0, 0, 0, 0.46), 0 0 0 1px rgba(255, 255, 255, 0.08) inset, 0 1px 0 rgba(255, 255, 255, 0.06) inset",
color: "rgba(202, 207, 217, 0.95)",
fontFamily: "var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace)",
minWidth: "420px",
overflow: "hidden",
padding: "6px 0",
},
".cm-tooltip.cm-tooltip-autocomplete > ul": {
maxHeight: "340px",
minWidth: "420px",
padding: "0 7px 0 !important",
scrollbarColor: "rgba(148, 153, 162, 0.42) transparent",
scrollbarWidth: "thin",
},
".cm-tooltip.cm-tooltip-autocomplete > ul > li": {
alignItems: "center",
borderRadius: "6px",
color: "rgba(199, 204, 214, 0.92)",
display: "flex",
fontSize: "16px",
fontWeight: "760",
height: "34px",
letterSpacing: "0",
lineHeight: "34px",
padding: "0 18px !important",
transition: "background-color 90ms ease, color 90ms ease",
},
".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
background: "rgba(70, 75, 84, 0.86) !important",
color: "rgba(231, 235, 243, 0.98) !important",
},
".cm-completionIcon": {
display: "none !important",
height: "0",
margin: "0",
paddingRight: "0 !important",
width: "0",
},
".cm-completionLabel": {
color: "inherit",
fontFamily: "var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace)",
fontSize: "16px",
fontWeight: "760",
letterSpacing: "0",
},
".cm-completionMatchedText": {
color: "#5794f9",
fontWeight: "860",
textDecoration: "none",
},
".cm-completionDetail": {
color: "rgba(184, 188, 198, 0.92)",
fontSize: "16px",
fontWeight: "760",
fontStyle: "normal",
marginLeft: "12px",
opacity: "1",
},
};
}
export function sqlCompletionTheme(EditorView: typeof import("@codemirror/view").EditorView): Extension {
return EditorView.theme(buildSqlCompletionThemeRules());
}

View File

@ -0,0 +1,28 @@
import { strict as assert } from "node:assert";
import test from "node:test";
import { buildSqlCompletionThemeRules } from "../../apps/desktop/src/lib/editorThemes.ts";
test("sql completion theme styles the autocomplete popup", () => {
const rules = buildSqlCompletionThemeRules();
assert.ok(rules[".cm-tooltip.cm-tooltip-autocomplete"]);
assert.deepEqual(rules[".cm-completionIcon"], {
display: "none !important",
height: "0",
margin: "0",
paddingRight: "0 !important",
width: "0",
});
assert.deepEqual(rules[".cm-completionLabel"], {
color: "inherit",
fontFamily: "var(--font-mono, 'JetBrains Mono', 'SF Mono', monospace)",
fontSize: "16px",
fontWeight: "760",
letterSpacing: "0",
});
assert.equal(rules[".cm-completionMatchedText"]?.color, "#5794f9");
assert.equal(
rules[".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]"]?.background,
"rgba(70, 75, 84, 0.86) !important",
);
});