Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions packages/app/src/components/JsonCodeEditor.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { render, waitFor } from "@testing-library/react";
import { JsonCodeEditor } from "./JsonCodeEditor";

describe("JsonCodeEditor", () => {
it("uses theme-aware token and selection colors", async () => {
const { container } = render(
<JsonCodeEditor
label="Synthetic headers"
onChange={() => {}}
value={'{"endpoint":"https://example.test","enabled":true,"retries":2,"optional":null}'}
/>
);

await waitFor(() => {
expect(container.querySelector(".cm-markra-json-property")).toBeInTheDocument();
});

const tokenText = (selector: string) =>
Array.from(
container.querySelectorAll(selector),
(element) => element.textContent ?? "",
).join("");

expect(tokenText(".cm-markra-json-property")).toContain('"endpoint"');
expect(tokenText(".cm-markra-json-string")).toContain("https://example.test");
expect(tokenText(".cm-markra-json-literal")).toContain("true");
expect(tokenText(".cm-markra-json-literal")).toContain("2");
expect(tokenText(".cm-markra-json-literal")).toContain("null");

const themeStyles = Array.from(
document.head.querySelectorAll("style"),
(style) => style.textContent ?? "",
).filter((styles) => styles.includes(".cm-markra-json-property"));

expect(themeStyles.some((styles) => styles.includes("var(--text-heading)"))).toBe(true);
expect(themeStyles.some((styles) => styles.includes("var(--text-primary)"))).toBe(true);
expect(
themeStyles.some((styles) =>
styles.includes(
"background-color: color-mix(in srgb, var(--accent) 22%, transparent) !important;",
),
),
).toBe(true);
});
});
16 changes: 16 additions & 0 deletions packages/app/src/components/JsonCodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { linter, lintGutter } from "@codemirror/lint";
import { EditorState } from "@codemirror/state";
import { EditorView } from "@codemirror/view";
import { basicSetup } from "codemirror";
import { jsonSyntaxHighlighting } from "@markra/editor/codemirror";

export function JsonCodeEditor({
label,
Expand All @@ -29,6 +30,7 @@ export function JsonCodeEditor({
basicSetup,
lintGutter(),
json(),
jsonSyntaxHighlighting,
linter(jsonParseLinter()),
EditorView.lineWrapping,
EditorView.contentAttributes.of({
Expand Down Expand Up @@ -75,11 +77,25 @@ export function JsonCodeEditor({
".cm-line": {
padding: "0"
},
".cm-markra-json-literal": {
color: "color-mix(in srgb, var(--link-color) 55%, var(--text-heading)) !important"
},
".cm-markra-json-property": {
color: "var(--text-heading) !important"
},
".cm-markra-json-string": {
color: "var(--text-primary) !important"
},
".cm-scroller": {
fontFamily:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
lineHeight: "1.25rem"
},
".cm-selectionBackground, &.cm-focused .cm-selectionBackground": {
// CodeMirror's focused light fallback is more specific than the
// app theme and otherwise obscures JSON tokens in dark mode.
backgroundColor: "color-mix(in srgb, var(--accent) 22%, transparent) !important"
},
".cm-tooltip": {
backgroundColor: "var(--bg-primary)",
border: "1px solid var(--border-default)",
Expand Down
35 changes: 35 additions & 0 deletions packages/app/src/components/MarkdownSourceEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe("MarkdownSourceEditor", () => {
"const answer = 42;",
"```",
"- Item",
'[link](https://example.test "synthetic title")',
"==highlight=="
].join("\n");
const handleChange = vi.fn();
Expand All @@ -77,12 +78,46 @@ describe("MarkdownSourceEditor", () => {
).join("");
expect(syntaxCharacters).toContain("#");
expect(syntaxCharacters).toContain("====");
const sourceMetadata = Array.from(
container.querySelectorAll(".cm-markra-source-metadata"),
(element) => element.textContent ?? "",
).join("");
expect(sourceMetadata).toContain("ts");
expect(sourceMetadata).toContain("https://example.test");
expect(sourceMetadata).toContain("synthetic title");

replaceCodeMirrorDoc(view, "# Changed");

expect(handleChange).toHaveBeenCalledWith("# Changed");
});

it("keeps the theme-aware drawn selection above CodeMirror's light fallback", () => {
render(
<MarkdownSourceEditor
content="synthetic_value = 2"
onChange={() => {}}
/>
);

const themeStyles = Array.from(
document.head.querySelectorAll("style"),
(style) => style.textContent ?? "",
).filter(
(styles) =>
styles.includes(".cm-selectionBackground") &&
styles.includes("var(--accent)"),
);

expect(themeStyles.length).toBeGreaterThan(0);
expect(
themeStyles.some((styles) =>
styles.includes(
"background-color: color-mix(in srgb, var(--accent) 22%, transparent) !important;",
),
),
).toBe(true);
});

it("keeps source scrolling vertical without pane-level horizontal scroll", () => {
const { container } = render(
<MarkdownSourceEditor
Expand Down
6 changes: 5 additions & 1 deletion packages/app/src/components/MarkdownSourceEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { minimalSetup } from "codemirror";
import { t, type AppLanguage, type SearchRange } from "@markra/shared";
import {
codeMirrorTypewriterMode,
markdownSourceSyntaxHighlighting,
markdownSyntaxHighlighting,
markraHighlight,
reconfigureCodeMirrorVimMode
Expand Down Expand Up @@ -185,7 +186,9 @@ function markdownSourceTheme(): Extension {
overflow: "visible"
},
".cm-selectionBackground, &.cm-focused .cm-selectionBackground": {
backgroundColor: "color-mix(in srgb, var(--accent) 22%, transparent)"
// CodeMirror's focused light fallback is more specific, so this
// theme-aware color must win explicitly on dark writing surfaces.
backgroundColor: "color-mix(in srgb, var(--accent) 22%, transparent) !important"
}
});
}
Expand Down Expand Up @@ -293,6 +296,7 @@ export function MarkdownSourceEditor({
extensions: [markraHighlight]
}),
markdownSyntaxHighlighting,
markdownSourceSyntaxHighlighting,
EditorView.lineWrapping,
contentAttributesCompartmentRef.current.of(markdownSourceContentAttributes(sourceLabel, readOnly)),
editableCompartmentRef.current.of(EditorView.editable.of(!readOnly)),
Expand Down
57 changes: 49 additions & 8 deletions packages/app/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@

--text-primary: #d4d4d4;
--text-heading: #e0e0e0;
--text-secondary: #808080;
--text-secondary: #858585;
--text-md-char: #555555;

--border-default: #333333;
Expand Down Expand Up @@ -326,7 +326,7 @@

--text-primary: #abb2bf;
--text-heading: #e6edf3;
--text-secondary: #5c6370;
--text-secondary: #8f96a3;
--text-md-char: #4b5263;

--border-default: #3b4048;
Expand Down Expand Up @@ -366,7 +366,7 @@

--text-primary: #abb2bf;
--text-heading: #e6edf3;
--text-secondary: #7f848e;
--text-secondary: #8f96a3;
--text-md-char: #5c6370;

--border-default: #3e4451;
Expand Down Expand Up @@ -821,6 +821,34 @@
--editor-text-cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http://www.w3.org/2000/svg%27%20width%3D%2724%27%20height%3D%2724%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20d%3D%27M12%204v16M8.5%204h7M8.5%2020h7%27%20fill%3D%27none%27%20stroke%3D%27%23ffffff%27%20stroke-width%3D%274%27%20stroke-linecap%3D%27round%27/%3E%3Cpath%20d%3D%27M12%204v16M8.5%204h7M8.5%2020h7%27%20fill%3D%27none%27%20stroke%3D%27%231a1c1e%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27/%3E%3C/svg%3E") 12 12, text;
}

.markdown-source-paper {
--source-markdown-syntax-color: var(--editor-markdown-syntax-color);
}

/* Revealed Markdown markers are editable content. Keep them at the
theme's primary text contrast instead of reusing muted chrome tokens. */
.markdown-paper[data-editor-theme="dark"],
.markdown-paper[data-editor-theme="github-dark"],
.markdown-paper[data-editor-theme="night"],
.markdown-paper[data-editor-theme="one-dark"],
.markdown-paper[data-editor-theme="one-dark-pro"],
.markdown-paper[data-editor-theme="solarized-dark"],
.markdown-paper[data-editor-theme="nord"],
.markdown-paper[data-editor-theme="catppuccin-mocha"] {
--editor-markdown-syntax-color: var(--editor-text-primary);
}

[data-theme="dark"] .markdown-source-paper,
[data-theme="github-dark"] .markdown-source-paper,
[data-theme="night"] .markdown-source-paper,
[data-theme="one-dark"] .markdown-source-paper,
[data-theme="one-dark-pro"] .markdown-source-paper,
[data-theme="solarized-dark"] .markdown-source-paper,
[data-theme="nord"] .markdown-source-paper,
[data-theme="catppuccin-mocha"] .markdown-source-paper {
--source-markdown-syntax-color: var(--text-primary);
}

[data-theme="dark"] .markdown-paper,
[data-theme="github-dark"] .markdown-paper,
[data-theme="night"] .markdown-paper,
Expand Down Expand Up @@ -860,11 +888,18 @@
font-family: inherit;
}

.markdown-paper .cm-markra-syntax-character,
.markdown-source-paper .cm-markra-syntax-character {
.markdown-paper .cm-markra-syntax-character {
color: var(--editor-markdown-syntax-color) !important;
}

.markdown-source-paper .cm-markra-syntax-character {
color: var(--source-markdown-syntax-color) !important;
}

.markdown-source-paper .cm-markra-source-metadata {
color: var(--text-primary) !important;
}

.markdown-paper .cm-cursor,
.markdown-paper .cm-dropCursor {
border-left-color: var(--editor-caret-color) !important;
Expand Down Expand Up @@ -2024,7 +2059,7 @@
--editor-paper-bg: #282c34;
--editor-text-primary: #abb2bf;
--editor-text-heading: #e6edf3;
--editor-text-secondary: #5c6370;
--editor-text-secondary: #8f96a3;
--editor-border: #3b4048;
--editor-border-strong: #4b5263;
--editor-bg-secondary: #21252b;
Expand Down Expand Up @@ -2074,7 +2109,7 @@
--editor-paper-bg: #282c34;
--editor-text-primary: #abb2bf;
--editor-text-heading: #e6edf3;
--editor-text-secondary: #7f848e;
--editor-text-secondary: #8f96a3;
--editor-border: #3e4451;
--editor-border-strong: #4b5263;
--editor-bg-secondary: #21252b;
Expand Down Expand Up @@ -4452,7 +4487,13 @@

.markdown-paper::selection,
.markdown-paper *::selection {
background: color-mix(in srgb, var(--accent) 20%, transparent);
background: color-mix(in srgb, var(--editor-caret-color, var(--accent)) 20%, transparent);
}

/* CodeMirror draws selections in a separate layer, so its focused light
fallback must not override the theme-aware native selection color. */
.markdown-paper .cm-selectionBackground {
background: color-mix(in srgb, var(--editor-caret-color, var(--accent)) 20%, transparent) !important;
}

.editor-content-slot[data-document-search-open="true"] .markdown-paper,
Expand Down
Loading
Loading