diff --git a/web/src/components/MemoEditor/Editor/editor.css b/web/src/components/MemoEditor/Editor/editor.css index 2e741703..de71c2ce 100644 --- a/web/src/components/MemoEditor/Editor/editor.css +++ b/web/src/components/MemoEditor/Editor/editor.css @@ -1,8 +1,6 @@ /* - * Styling for the CodeMirror source editor, in plain CSS with the app's theme - * variables — the same model Obsidian / GitHub use for an app-integrated CM6 - * editor (class-based tokens + CSS, not a CSS-in-JS theme). Co-located with the - * component and scoped to its wrapper so nothing leaks to other CM instances. + * Scoped CodeMirror styling using the app's theme variables. The styles are + * co-located with the component so they do not leak to other editor instances. * * Token classes (.cm-md-*) come from the tagHighlighter in theme.ts; heading * line classes (.cm-md-h*) from headingDecorations.ts; #tag/@mention @@ -22,11 +20,12 @@ outline: none; } & .cm-content { + caret-color: var(--foreground); font-family: inherit; padding: 0; } & .cm-line { - padding: 0; + padding-inline: 0; } & .cm-scroller { font-family: inherit; @@ -35,16 +34,13 @@ } & .cm-placeholder { color: var(--muted-foreground); - opacity: 0.7; } - /* drawSelection() caret + selection (no base color theme is loaded). */ - & .cm-cursor, & .cm-dropCursor { border-left-color: var(--foreground); } - & .cm-selectionBackground, - & .cm-focused .cm-selectionBackground { + & .cm-content::selection, + & .cm-content ::selection { background: var(--accent); } @@ -117,19 +113,14 @@ } & .cm-tooltip-autocomplete > ul { font-family: inherit; - color: var(--popover-foreground); } & .cm-tooltip-autocomplete > ul > li { padding: 0.25rem 0.5rem; - color: var(--popover-foreground); } & .cm-tooltip-autocomplete > ul > li[aria-selected] { background: var(--accent); color: var(--accent-foreground); } - & .cm-completionIcon { - display: none; - } & .cm-completionMatchedText { text-decoration: none; font-weight: 600; diff --git a/web/src/components/MemoEditor/Editor/extensions.ts b/web/src/components/MemoEditor/Editor/extensions.ts index 4d9c4280..52a8a642 100644 --- a/web/src/components/MemoEditor/Editor/extensions.ts +++ b/web/src/components/MemoEditor/Editor/extensions.ts @@ -2,7 +2,7 @@ import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirro import { markdown } from "@codemirror/lang-markdown"; import { indentUnit } from "@codemirror/language"; import { Compartment, type Extension } from "@codemirror/state"; -import { placeholder as cmPlaceholder, drawSelection, dropCursor, EditorView, type KeyBinding, keymap } from "@codemirror/view"; +import { placeholder as cmPlaceholder, dropCursor, EditorView, type KeyBinding, keymap } from "@codemirror/view"; import { GFM } from "@lezer/markdown"; import { headingDecorations } from "./headingDecorations"; import { liftListItem, sinkListItem } from "./listIndent"; @@ -74,7 +74,6 @@ export function buildEditorExtensions({ // Core editing behavior. These are the pieces from CM6 setup that this memo // editor uses, without enabling multi-cursor selection. history(), - drawSelection(), dropCursor(), // Indent with spaces (markdown), matching the 2-space bullet nesting. indentUnit.of(" "), diff --git a/web/src/components/MemoEditor/Editor/tagAutocomplete.ts b/web/src/components/MemoEditor/Editor/tagAutocomplete.ts index 91770f43..f16bba75 100644 --- a/web/src/components/MemoEditor/Editor/tagAutocomplete.ts +++ b/web/src/components/MemoEditor/Editor/tagAutocomplete.ts @@ -21,5 +21,8 @@ export function makeTagCompletionSource(getTags: () => string[]) { } export function tagAutocomplete(getTags: () => string[]): Extension { - return autocompletion({ override: [makeTagCompletionSource(getTags)] }); + return autocompletion({ + override: [makeTagCompletionSource(getTags)], + icons: false, + }); } diff --git a/web/src/components/MemoEditor/Editor/theme.ts b/web/src/components/MemoEditor/Editor/theme.ts index 18589659..3b4e52ec 100644 --- a/web/src/components/MemoEditor/Editor/theme.ts +++ b/web/src/components/MemoEditor/Editor/theme.ts @@ -3,7 +3,7 @@ import { tags as t, tagHighlighter } from "@lezer/highlight"; /** * Map markdown syntax tokens to stable class names. ALL visual styling lives in - * plain CSS (the `.memo-editor-content` block in `src/index.css`) so the editor + * plain CSS (the `.memo-editor-content` block in `Editor/editor.css`) so the editor * is themed like the rest of the app — Tailwind/theme tokens in a stylesheet — * rather than a CodeMirror CSS-in-JS theme object. Headings (`.cm-md-h*`) and * `#tag`/`@mention` (`.cm-memo-*`) classes come from the decoration plugins; diff --git a/web/src/themes/default-dark.css b/web/src/themes/default-dark.css index b9c44990..ade1fb25 100644 --- a/web/src/themes/default-dark.css +++ b/web/src/themes/default-dark.css @@ -11,8 +11,8 @@ --popover-foreground: oklch(0.92 0.006 255); /* Primary - readable blue for selected items, links, and focused controls */ - --primary: oklch(0.62 0.11 250); - --primary-foreground: oklch(0.98 0.004 255); + --primary: oklch(0.66 0.11 250); + --primary-foreground: oklch(0.24 0.008 255); /* Secondary - elevated surface for secondary buttons */ --secondary: oklch(0.33 0.011 255); diff --git a/web/tests/memo-editor-extensions.test.ts b/web/tests/memo-editor-extensions.test.ts index d139d946..dbbea792 100644 --- a/web/tests/memo-editor-extensions.test.ts +++ b/web/tests/memo-editor-extensions.test.ts @@ -13,7 +13,7 @@ describe("MemoEditor CodeMirror extensions", () => { document.body.replaceChildren(); }); - it("uses CodeMirror's selection and placeholder extensions without enabling multi-cursor selection", () => { + it("uses the native selection and CodeMirror placeholder without enabling multi-cursor selection", () => { const parent = document.body.appendChild(document.createElement("div")); const state = EditorState.create({ doc: "", @@ -30,8 +30,8 @@ describe("MemoEditor CodeMirror extensions", () => { views.push(view); expect(view.state.facet(EditorState.allowMultipleSelections)).toBe(false); - expect(view.dom.querySelector(".cm-selectionLayer")).not.toBeNull(); - expect(view.dom.querySelector(".cm-cursorLayer")).not.toBeNull(); + expect(view.dom.querySelector(".cm-selectionLayer")).toBeNull(); + expect(view.dom.querySelector(".cm-cursorLayer")).toBeNull(); expect(view.contentDOM).toHaveAttribute("aria-placeholder", "Any thoughts..."); expect(view.dom.querySelector(".cm-placeholder")).toHaveTextContent("Any thoughts..."); });