From 95729078b52e89a54d72b6fc1a9a3f452c019603 Mon Sep 17 00:00:00 2001 From: Sammsai Date: Tue, 25 Aug 2026 07:24:48 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=97=E4=B8=8E=E6=95=B0=E5=AD=A6=E5=85=AC=E5=BC=8F=E5=9D=97?= =?UTF-8?q?=E5=BA=95=E6=A1=86=EF=BC=88=E8=83=8C=E6=99=AF=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=E4=B8=8E=E5=9C=86=E8=A7=92=E8=BE=B9=E6=A1=86=EF=BC=89=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/InlineMarkdownEditor.test.tsx | 24 +++---- index.css | 65 +++++++++---------- 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/components/notes/InlineMarkdownEditor.test.tsx b/components/notes/InlineMarkdownEditor.test.tsx index 2963118c7..d5fc70479 100644 --- a/components/notes/InlineMarkdownEditor.test.tsx +++ b/components/notes/InlineMarkdownEditor.test.tsx @@ -517,19 +517,11 @@ test("note code block frame is borderless and language picker is compact", () => assert.match( styles, - /\.netcatty-mdx-editor\s+\[class\*="_codeMirrorWrapper_"\]\s*\{[^}]*border:\s*0\s*!important;[^}]*background:\s*transparent\s*!important;[^}]*padding:\s*0\s*!important;/s, + /\.netcatty-mdx-editor\s+\[class\*="_codeMirrorWrapper_"\]\s*\{[^}]*border:\s*0\s*!important;[^}]*padding:\s*0\.2rem\s+0\s*!important;/s, ); assert.match( styles, - /\.netcatty-mdx-editor\s+\.cm-editor\s*\{[^}]*border:\s*0\s*!important;[^}]*background:\s*transparent\s*!important;/s, - ); - assert.match( - styles, - /\.netcatty-mdx-content\s+pre\s*\{[^}]*border:\s*0;[^}]*background:\s*transparent;[^}]*padding:\s*0;/s, - ); - assert.match( - styles, - /\.netcatty-note-code-copy\s*\{[^}]*border:\s*0\s*!important;[^}]*background:\s*transparent\s*!important;[^}]*box-shadow:\s*none\s*!important;/s, + /\.netcatty-mdx-editor\s+\.cm-editor\s*\{[^}]*border:\s*0\s*!important;/s, ); assert.match( styles, @@ -569,7 +561,7 @@ test("note code block frame is borderless and language picker is compact", () => ); assert.match( styles, - /\.netcatty-mdx-editor:not\(\.netcatty-mdx-editor--preview\)\s+\[class\*="_codeMirrorWrapper_"\]\s*\{[^}]*gap:\s*0;[^}]*margin:\s*0\.25rem\s+0\s+0\.55rem;/s, + /\.netcatty-mdx-editor:not\(\.netcatty-mdx-editor--preview\)\s+\[class\*="_codeMirrorWrapper_"\]\s*\{[^}]*gap:\s*0;[^}]*margin:\s*0\.3rem\s+0\s+0\.65rem;/s, ); assert.match( styles, @@ -577,7 +569,7 @@ test("note code block frame is borderless and language picker is compact", () => ); assert.match( styles, - /\.netcatty-mdx-editor\s+\.cm-gutters\s*\{[^}]*background:\s*transparent\s*!important;[^}]*padding:\s*0\s*!important;/s, + /\.netcatty-mdx-editor\s+\.cm-gutters\s*\{[^}]*padding:\s*0\s*!important;/s, ); assert.match( styles, @@ -585,21 +577,21 @@ test("note code block frame is borderless and language picker is compact", () => ); }); -test("note formulas render without framed surfaces", () => { +test("note formulas render with framed surfaces", () => { const styles = readFileSync(new URL("../../index.css", import.meta.url), "utf8"); assert.doesNotMatch(styles, /data-language="math"/); assert.match( styles, - /\.netcatty-math-formula-preview\s*\{[^}]*background:\s*transparent;[^}]*border:\s*0;/s, + /\.netcatty-math-formula-preview\s*\{[^}]*background:\s*hsl\(var\(--card\)\s*\/\s*0\.85\);[^}]*border:\s*1px solid hsl\(var\(--border\)\s*\/\s*0\.6\);/s, ); assert.match( styles, - /\.netcatty-math-reading-mode\s*\{[^}]*background:\s*transparent\s*!important;[^}]*border:\s*none\s*!important;[^}]*padding:\s*0\s*!important;/s, + /\.netcatty-math-reading-mode\s*\{[^}]*background:\s*transparent\s*!important;[^}]*border:\s*none\s*!important;/s, ); assert.match( styles, - /\.netcatty-math-reading-mode\s+\.netcatty-math-formula-preview\s*\{[^}]*background:\s*transparent;/s, + /\.netcatty-math-reading-mode\s+\.netcatty-math-formula-preview\s*\{[^}]*background:\s*hsl\(var\(--secondary\)\s*\/\s*0\.25\);/s, ); assert.match(styles, /\.netcatty-math-formula-preview\s*\{[^}]*justify-content:\s*safe center;[^}]*overflow-x:\s*auto;/s); assert.match( diff --git a/index.css b/index.css index 52d44715a..f94215780 100644 --- a/index.css +++ b/index.css @@ -1356,26 +1356,26 @@ html[data-theme-transition] *::after { .netcatty-mdx-content pre { border: 0; - border-radius: 0; - background: transparent; - padding: 0; + border-radius: 6px; + background: hsl(var(--secondary) / 0.35); + padding: 0.75rem; } .netcatty-mdx-editor [class*="_codeMirrorWrapper_"] { position: relative !important; - min-height: 2rem !important; - margin: 0.35rem 0 0.65rem; + min-height: 2.85rem !important; + margin: 0.5rem 0 0.85rem; border: 0 !important; - border-radius: 0; - background: transparent !important; - padding: 0 !important; + border-radius: 6px; + background: hsl(var(--secondary) / 0.35) !important; + padding: 0.2rem 0 !important; overflow: visible !important; } .netcatty-note-code-copy { position: absolute !important; - top: 0.2rem !important; - right: 0.15rem !important; + top: 0.35rem !important; + right: 0.35rem !important; z-index: 40 !important; display: inline-flex !important; align-items: center !important; @@ -1383,11 +1383,11 @@ html[data-theme-transition] *::after { width: 1.45rem !important; height: 1.45rem !important; padding: 0 !important; - border: 0 !important; + border: 1px solid hsl(var(--border) / 0.7) !important; border-radius: 4px !important; - background: transparent !important; + background: hsl(var(--background) / 0.95) !important; color: hsl(var(--muted-foreground)) !important; - box-shadow: none !important; + box-shadow: 0 1px 3px rgb(0 0 0 / 0.12) !important; opacity: 0; cursor: pointer !important; transition: opacity 0.15s, color 0.15s, background-color 0.15s; @@ -1445,26 +1445,24 @@ html[data-theme-transition] *::after { .netcatty-mdx-editor [class*="_codeMirrorWrapper_"]:has([data-language="latex"]), .netcatty-mdx-editor [class*="_codeMirrorWrapper_"]:has([data-language="tex"]) { - border: 0 !important; - background: transparent !important; - padding: 0 !important; + border-left: 3px solid hsl(var(--primary)) !important; + background: hsl(var(--secondary) / 0.25) !important; } .netcatty-math-formula-preview { display: flex; align-items: center; justify-content: safe center; - padding: 0.55rem 0; - margin: 0.4rem 0; - border-radius: 0; - background: transparent; - border: 0; + padding: 0.85rem 1.25rem; + margin: 0.35rem 0.5rem 0.35rem; + border-radius: 6px; + background: hsl(var(--card) / 0.85); + border: 1px solid hsl(var(--border) / 0.6); font-size: 1.45em; color: hsl(var(--foreground)); overflow-x: auto; - max-width: 100%; user-select: text; - min-height: 2.25rem; + min-height: 2.75rem; } .netcatty-math-formula-preview .katex-display { @@ -1488,13 +1486,12 @@ html[data-theme-transition] *::after { .netcatty-math-reading-mode { background: transparent !important; border: none !important; - padding: 0 !important; - margin: 0.4rem 0 !important; + margin: 0.5rem 0 !important; } .netcatty-math-reading-mode .netcatty-math-formula-preview { margin: 0; - background: transparent; + background: hsl(var(--secondary) / 0.25); } .netcatty-math-reading-mode > .netcatty-note-code-copy { @@ -1506,9 +1503,9 @@ html[data-theme-transition] *::after { flex-direction: column; align-items: stretch; gap: 0; - margin: 0.25rem 0 0.55rem; - border-radius: 0; - background: transparent !important; + margin: 0.3rem 0 0.65rem; + border-radius: 6px; + background: hsl(var(--secondary) / 0.35) !important; overflow: visible !important; } @@ -1517,7 +1514,7 @@ html[data-theme-transition] *::after { } .netcatty-mdx-editor:not(.netcatty-mdx-editor--preview) [class*="_codeMirrorWrapper_"] .cm-editor { - min-height: 2rem !important; + min-height: 2.2rem !important; background: transparent !important; border-radius: 0 !important; } @@ -1744,8 +1741,8 @@ html[data-theme-transition] *::after { --note-code-token-string: color-mix(in oklab, hsl(var(--primary)) 24%, hsl(var(--foreground))); overflow: hidden; border: 0 !important; - border-radius: 0; - background: transparent !important; + border-radius: 6px; + background: hsl(var(--secondary) / 0.35) !important; color: hsl(var(--foreground) / 0.9) !important; font-family: var(--font-mono); font-size: 13px !important; @@ -1761,7 +1758,7 @@ html[data-theme-transition] *::after { background: transparent !important; color: hsl(var(--foreground) / 0.9) !important; font-family: var(--font-mono); - padding: 0 !important; + padding: 0.25rem 0 !important; } .netcatty-mdx-editor .cm-line { @@ -1812,7 +1809,7 @@ html[data-theme-transition] *::after { .netcatty-mdx-editor .cm-gutters { border-right: 1px solid hsl(var(--border) / 0.35) !important; - background: transparent !important; + background: hsl(var(--secondary) / 0.24) !important; color: hsl(var(--muted-foreground) / 0.75) !important; font-family: var(--font-mono); font-size: 13px !important; From 6c842a05b64731b66f5da2b5027a9390f508a7aa Mon Sep 17 00:00:00 2001 From: Sammsai Date: Tue, 25 Aug 2026 08:59:54 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=97=E4=B8=8E=E6=95=B0=E5=AD=A6=E5=85=AC=E5=BC=8F=E5=9D=97?= =?UTF-8?q?=E5=BA=95=E6=A1=86=EF=BC=88=E8=83=8C=E6=99=AF=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=E4=B8=8E=E5=9C=86=E8=A7=92=E8=BE=B9=E6=A1=86=EF=BC=89=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/InlineMarkdownEditor.test.tsx | 13 +++-- components/notes/InlineMarkdownEditor.tsx | 52 +++++++++++++++++-- index.css | 13 +++++ 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/components/notes/InlineMarkdownEditor.test.tsx b/components/notes/InlineMarkdownEditor.test.tsx index d5fc70479..1aedced2b 100644 --- a/components/notes/InlineMarkdownEditor.test.tsx +++ b/components/notes/InlineMarkdownEditor.test.tsx @@ -39,22 +39,27 @@ test("source undo history coalesces only adjacent edits of the same typing kind" }); test("math language detection does not mistake plain text blocks for TeX", () => { - assert.equal(isNoteMathLanguageLabel("math"), false); - assert.equal(isNoteMathLanguageLabel("Math (LaTeX)"), false); + assert.equal(isNoteMathLanguageLabel("math"), true); + assert.equal(isNoteMathLanguageLabel("Math (LaTeX)"), true); assert.equal(isNoteMathLanguageLabel("language-latex"), true); assert.equal(isNoteMathLanguageLabel("language-tex highlighted"), true); assert.equal(isNoteMathLanguageLabel("latex"), true); assert.equal(isNoteMathLanguageLabel("tex"), true); - assert.equal(isNoteMathLanguageLabel("公式"), false); + assert.equal(isNoteMathLanguageLabel("katex"), true); + assert.equal(isNoteMathLanguageLabel("formula"), true); + assert.equal(isNoteMathLanguageLabel("公式"), true); assert.equal(isNoteMathLanguageLabel("text"), false); assert.equal(isNoteMathLanguageLabel("plaintext"), false); assert.equal(isNoteMathLanguageLabel("typescript"), false); assert.equal(shouldRenderNoteMathFormula("Plain text"), false); assert.equal(shouldRenderNoteMathFormula("plaintext"), false); assert.equal(shouldRenderNoteMathFormula(""), false); - assert.equal(shouldRenderNoteMathFormula("math"), false); + assert.equal(shouldRenderNoteMathFormula("math"), true); assert.equal(shouldRenderNoteMathFormula("latex"), true); assert.equal(shouldRenderNoteMathFormula("tex"), true); + assert.equal(shouldRenderNoteMathFormula("katex"), true); + assert.equal(shouldRenderNoteMathFormula("plaintext", "$$ x + y = z $$"), true); + assert.equal(shouldRenderNoteMathFormula("plaintext", "hello world"), false); }); test("live note decoration scans are debounced while preview mounts stay immediate", () => { diff --git a/components/notes/InlineMarkdownEditor.tsx b/components/notes/InlineMarkdownEditor.tsx index fe16767a0..ef27dcf08 100644 --- a/components/notes/InlineMarkdownEditor.tsx +++ b/components/notes/InlineMarkdownEditor.tsx @@ -284,8 +284,10 @@ const NOTE_CODE_BLOCK_LANGUAGES = { js: "JavaScript", json: "JSON", jsx: "JavaScript (React)", + katex: "KaTeX", latex: "LaTeX", markdown: "Markdown", + math: "Math (LaTeX)", md: "Markdown", nginx: "Nginx", plaintext: "Plain text", @@ -799,14 +801,35 @@ export const annotateNoteCodeBlockDeleteButtons = (container: HTMLElement): void export const isNoteMathLanguageLabel = (value: string): boolean => { const normalized = value.toLowerCase().trim(); if (!normalized) return false; - return normalized === "latex" + return ( + normalized === "latex" || normalized === "tex" - || /(?:^|\s)language-(?:latex|tex)(?:\s|$)/.test(normalized); + || normalized === "math" + || normalized === "katex" + || normalized === "formula" + || normalized === "公式" + || normalized === "math (latex)" + || /(?:^|\s)language-(?:latex|tex|math|katex|formula)(?:\s|$)/.test(normalized) + || /^(?:math|latex|tex|katex|formula|公式)(?:\s|\(|$)/.test(normalized) + ); }; export const shouldRenderNoteMathFormula = ( languageLabel: string, -): boolean => isNoteMathLanguageLabel(languageLabel); + content?: string, +): boolean => { + if (isNoteMathLanguageLabel(languageLabel)) return true; + if (content) { + const trimmed = content.trim(); + if (trimmed.startsWith("$$") && trimmed.endsWith("$$") && trimmed.length >= 4) { + return true; + } + if (trimmed.startsWith("\\[") && trimmed.endsWith("\\]") && trimmed.length >= 4) { + return true; + } + } + return false; +}; export const annotateMathFormulaBlocks = (container: HTMLElement, editorMode: string): void => { container.querySelectorAll('[class*="_codeMirrorWrapper_"], pre').forEach((wrapper) => { @@ -825,7 +848,7 @@ export const annotateMathFormulaBlocks = (container: HTMLElement, editorMode: st const text = getCodeMirrorBlockText(wrapper).trim(); - const isMathBlock = shouldRenderNoteMathFormula(lang); + const isMathBlock = shouldRenderNoteMathFormula(lang, text); if (!isMathBlock) { const existingPreview = wrapper.querySelector(".netcatty-math-formula-preview"); if (existingPreview) existingPreview.remove(); @@ -850,7 +873,11 @@ export const annotateMathFormulaBlocks = (container: HTMLElement, editorMode: st if (preview.dataset.formulaSource !== formulaSource) { preview.dataset.formulaSource = formulaSource; - preview.innerHTML = renderNoteMathFormula(formulaSource); + try { + preview.innerHTML = renderNoteMathFormula(formulaSource); + } catch { + preview.textContent = formulaSource; + } } if (editorMode === "preview") { @@ -860,6 +887,21 @@ export const annotateMathFormulaBlocks = (container: HTMLElement, editorMode: st } }); + if (editorMode === "preview") { + container.querySelectorAll("p").forEach((p) => { + const pText = p.textContent?.trim() || ""; + if (pText.startsWith("$$") && pText.endsWith("$$") && pText.length >= 4) { + if (p.querySelector(".katex, math")) return; + const formula = pText.slice(2, -2).trim(); + try { + p.innerHTML = renderNoteMathFormula(formula); + p.classList.add("netcatty-math-block-p"); + } catch { + // ignore + } + } + }); + } }; const deleteLexicalTextRange = (range: Range, onUpdate: () => void): boolean => { diff --git a/index.css b/index.css index f94215780..d701cba78 100644 --- a/index.css +++ b/index.css @@ -1498,6 +1498,19 @@ html[data-theme-transition] *::after { display: none !important; } +p.netcatty-math-block-p { + display: flex; + align-items: center; + justify-content: center; + padding: 0.85rem 1.25rem; + margin: 0.5rem 0; + border-radius: 6px; + background: hsl(var(--secondary) / 0.25); + font-size: 1.45em; + color: hsl(var(--foreground)); + overflow-x: auto; +} + .netcatty-mdx-editor:not(.netcatty-mdx-editor--preview) [class*="_codeMirrorWrapper_"] { display: flex; flex-direction: column; From 519b2b15028ea96d51de6b2d1c34c4432c4c5d50 Mon Sep 17 00:00:00 2001 From: Sammsai Date: Tue, 25 Aug 2026 12:37:40 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=97=E4=B8=8E=E6=95=B0=E5=AD=A6=E5=85=AC=E5=BC=8F=E5=9D=97?= =?UTF-8?q?=E5=BA=95=E6=A1=86=EF=BC=88=E8=83=8C=E6=99=AF=E5=AE=B9=E5=99=A8?= =?UTF-8?q?=E4=B8=8E=E5=9C=86=E8=A7=92=E8=BE=B9=E6=A1=86=EF=BC=89=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/notes/InlineMarkdownEditor.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/notes/InlineMarkdownEditor.tsx b/components/notes/InlineMarkdownEditor.tsx index ef27dcf08..bba7fe3a0 100644 --- a/components/notes/InlineMarkdownEditor.tsx +++ b/components/notes/InlineMarkdownEditor.tsx @@ -284,10 +284,8 @@ const NOTE_CODE_BLOCK_LANGUAGES = { js: "JavaScript", json: "JSON", jsx: "JavaScript (React)", - katex: "KaTeX", latex: "LaTeX", markdown: "Markdown", - math: "Math (LaTeX)", md: "Markdown", nginx: "Nginx", plaintext: "Plain text", @@ -296,7 +294,6 @@ const NOTE_CODE_BLOCK_LANGUAGES = { sh: "Shell", shell: "Shell", sql: "SQL", - tex: "TeX", toml: "TOML", ts: "TypeScript", tsx: "TypeScript (React)", From d1bd7f6360bc6f1709fe5b83f0e705c1c4afe5fc Mon Sep 17 00:00:00 2001 From: Sammsai Date: Tue, 25 Aug 2026 13:23:32 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E5=9D=97=E4=BB=A3=E7=A0=81=E9=A2=84=E6=B5=8B/=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=E6=A1=86=E9=80=8F=E6=98=8E=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/InlineMarkdownEditor.test.tsx | 8 ++ index.css | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/components/notes/InlineMarkdownEditor.test.tsx b/components/notes/InlineMarkdownEditor.test.tsx index 1aedced2b..b15ba3b49 100644 --- a/components/notes/InlineMarkdownEditor.test.tsx +++ b/components/notes/InlineMarkdownEditor.test.tsx @@ -515,6 +515,14 @@ test("note code block active line is highlighted only while focused", () => { styles, /\.netcatty-mdx-editor\s+\.cm-editor:focus-within\s+\.cm-activeLine,\s*\.netcatty-mdx-editor\s+\.cm-editor:focus-within\s+\.cm-activeLineGutter\s*\{[^}]*background:\s*hsl\(var\(--primary\)\s*\/\s*0\.08\)/s, ); + assert.match( + styles, + /\.cm-tooltip,\s*\.netcatty-mdx-editor\s+\.cm-tooltip\s*\{[^}]*background:\s*hsl\(var\(--popover\)\)/s, + ); + assert.match( + styles, + /\.cm-tooltip\.cm-tooltip-autocomplete\s*>\s*ul\s*\{[^}]*background:\s*hsl\(var\(--popover\)\)/s, + ); }); test("note code block frame is borderless and language picker is compact", () => { diff --git a/index.css b/index.css index d701cba78..6f5bb907a 100644 --- a/index.css +++ b/index.css @@ -1857,6 +1857,81 @@ p.netcatty-math-block-p { border-left-color: hsl(var(--foreground)) !important; } +.cm-tooltip, +.netcatty-mdx-editor .cm-tooltip { + background: hsl(var(--popover)) !important; + color: hsl(var(--popover-foreground)) !important; + border: 1px solid hsl(var(--border) / 0.8) !important; + border-radius: 6px !important; + box-shadow: 0 6px 20px rgb(0 0 0 / 0.3) !important; + z-index: 9999 !important; +} + +.cm-tooltip.cm-tooltip-autocomplete, +.netcatty-mdx-editor .cm-tooltip-autocomplete { + background: hsl(var(--popover)) !important; + border: 1px solid hsl(var(--border) / 0.8) !important; + border-radius: 6px !important; + overflow: hidden !important; +} + +.cm-tooltip.cm-tooltip-autocomplete > ul, +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul { + background: hsl(var(--popover)) !important; + color: hsl(var(--popover-foreground)) !important; + font-family: var(--font-mono) !important; + font-size: 12px !important; + max-height: 14rem !important; + padding: 4px !important; + margin: 0 !important; + list-style: none !important; +} + +.cm-tooltip.cm-tooltip-autocomplete > ul > li, +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul > li { + padding: 3px 8px !important; + border-radius: 4px !important; + display: flex !important; + align-items: center !important; + gap: 6px !important; + cursor: pointer !important; + color: hsl(var(--popover-foreground)) !important; +} + +.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected="true"], +.cm-tooltip.cm-tooltip-autocomplete > ul > li:hover, +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul > li[aria-selected="true"], +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul > li:hover { + background: hsl(var(--accent)) !important; + color: hsl(var(--accent-foreground)) !important; +} + +.cm-tooltip .cm-completionMatchedText, +.netcatty-mdx-editor .cm-tooltip .cm-completionMatchedText { + color: hsl(var(--primary)) !important; + font-weight: 600 !important; + text-decoration: none !important; +} + +.cm-tooltip .cm-completionDetail, +.netcatty-mdx-editor .cm-tooltip .cm-completionDetail { + color: hsl(var(--muted-foreground)) !important; + font-size: 0.85em !important; + font-style: italic !important; + margin-left: auto !important; +} + +.cm-tooltip.cm-completionInfo, +.netcatty-mdx-editor .cm-completionInfo { + background: hsl(var(--popover)) !important; + color: hsl(var(--popover-foreground)) !important; + border: 1px solid hsl(var(--border) / 0.8) !important; + border-radius: 6px !important; + padding: 6px 10px !important; + font-size: 12px !important; + box-shadow: 0 6px 20px rgb(0 0 0 / 0.3) !important; +} + .netcatty-mdx-content blockquote { margin: 0.7rem 0; border-left: 2px solid hsl(var(--border)); From 18d1bd38d7d5875c5a5e69e89ed2bb4c8a1ac76e Mon Sep 17 00:00:00 2001 From: Sammsai Date: Tue, 25 Aug 2026 17:37:34 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E5=AF=B9=E4=BB=A3=E7=A0=81=E5=9D=97?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=A2=84=E6=B5=8B/=E8=A1=A5=E5=85=A8?= =?UTF-8?q?=E6=A1=86=E7=9A=84=E4=BC=98=E5=8C=96=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/notes/InlineMarkdownEditor.test.tsx | 7 +++++-- components/notes/InlineMarkdownEditor.tsx | 8 +++++++- index.css | 17 +++++++++++++---- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/components/notes/InlineMarkdownEditor.test.tsx b/components/notes/InlineMarkdownEditor.test.tsx index b15ba3b49..a28a5d0f6 100644 --- a/components/notes/InlineMarkdownEditor.test.tsx +++ b/components/notes/InlineMarkdownEditor.test.tsx @@ -373,8 +373,7 @@ test("note editor registers a code block editor for pasted fenced code", () => { /codeBlockPlugin\([^)]*\),\s*codeMirrorPlugin\(\{\s*codeBlockLanguages:/s, ); assert.match(source, /codeMirrorExtensions:\s*NOTE_CODE_MIRROR_EXTENSIONS/); - assert.match(source, /syntaxHighlighting\(noteCodeHighlightStyle\)/); -}); + assert.match(source, /syntaxHighlighting\(noteCodeHighlightStyle\)/); assert.match(source, /tooltips\(\{\s*parent:\s*typeof document !== "undefined" \? document\.body : undefined,\s*\}\)/);}); test("note editor enables image plugin for remote markdown images", () => { const source = readFileSync(new URL("./InlineMarkdownEditor.tsx", import.meta.url), "utf8"); @@ -523,6 +522,10 @@ test("note code block active line is highlighted only while focused", () => { styles, /\.cm-tooltip\.cm-tooltip-autocomplete\s*>\s*ul\s*\{[^}]*background:\s*hsl\(var\(--popover\)\)/s, ); + assert.match( + styles, + /\.cm-tooltip\s+\.cm-completionMatchedText,\s*\.netcatty-mdx-editor\s+\.cm-tooltip\s+\.cm-completionMatchedText\s*\{[^}]*color:\s*inherit\s*!important;[^}]*font-weight:\s*700\s*!important;[^}]*text-decoration:\s*underline\s*!important;/s, + ); }); test("note code block frame is borderless and language picker is compact", () => { diff --git a/components/notes/InlineMarkdownEditor.tsx b/components/notes/InlineMarkdownEditor.tsx index bba7fe3a0..8e7497fb0 100644 --- a/components/notes/InlineMarkdownEditor.tsx +++ b/components/notes/InlineMarkdownEditor.tsx @@ -17,6 +17,7 @@ import { thematicBreakPlugin, } from "@mdxeditor/editor"; import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; +import { tooltips } from "@codemirror/view"; import { tags } from "@lezer/highlight"; import { ExternalLink } from "lucide-react"; import { @@ -321,7 +322,12 @@ const noteCodeHighlightStyle = HighlightStyle.define([ { tag: tags.invalid, class: "netcatty-code-token-invalid" }, ]); -const NOTE_CODE_MIRROR_EXTENSIONS = [syntaxHighlighting(noteCodeHighlightStyle)]; +const NOTE_CODE_MIRROR_EXTENSIONS = [ + syntaxHighlighting(noteCodeHighlightStyle), + tooltips({ + parent: typeof document !== "undefined" ? document.body : undefined, + }), +]; type RectLike = Pick; diff --git a/index.css b/index.css index 6f5bb907a..1d5f9155b 100644 --- a/index.css +++ b/index.css @@ -1752,7 +1752,6 @@ p.netcatty-math-block-p { --note-code-token-name: color-mix(in oklab, hsl(var(--primary)) 50%, hsl(var(--foreground))); --note-code-token-value: color-mix(in oklab, hsl(var(--primary)) 38%, hsl(var(--foreground))); --note-code-token-string: color-mix(in oklab, hsl(var(--primary)) 24%, hsl(var(--foreground))); - overflow: hidden; border: 0 !important; border-radius: 6px; background: hsl(var(--secondary) / 0.35) !important; @@ -1908,9 +1907,19 @@ p.netcatty-math-block-p { .cm-tooltip .cm-completionMatchedText, .netcatty-mdx-editor .cm-tooltip .cm-completionMatchedText { - color: hsl(var(--primary)) !important; - font-weight: 600 !important; - text-decoration: none !important; + color: inherit !important; + font-weight: 700 !important; + text-decoration: underline !important; + text-underline-offset: 2px !important; +} + +.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected="true"] .cm-completionMatchedText, +.cm-tooltip.cm-tooltip-autocomplete > ul > li:hover .cm-completionMatchedText, +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul > li[aria-selected="true"] .cm-completionMatchedText, +.netcatty-mdx-editor .cm-tooltip-autocomplete > ul > li:hover .cm-completionMatchedText { + color: inherit !important; + font-weight: 700 !important; + text-decoration: underline !important; } .cm-tooltip .cm-completionDetail,