@surrealdb/ui 1.2.16 → 1.2.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/1299e4695ad0bd61.svg +5 -0
- package/dist/assets/408117e904653548.svg +48 -0
- package/dist/assets/b516b020352f6369.svg +1 -0
- package/dist/assets/dfde8de566c3c2b9.svg +11 -0
- package/dist/assets/e9eae7bc863f211b.svg +1 -0
- package/dist/assets/f74ae3707a5d418f.svg +1 -0
- package/dist/icons.d.ts +6 -0
- package/dist/icons.js +198 -195
- package/dist/icons.js.map +1 -1
- package/dist/ui.d.ts +18 -0
- package/dist/ui.js +1441 -1426
- package/dist/ui.js.map +1 -1
- package/package.json +8 -2
- package/.claude/launch.json +0 -17
- package/.oxfmtrc.json +0 -27
- package/.oxlintrc.json +0 -23
- package/.picto-fix-png/Cursor.png +0 -0
- package/.picto-fix-png/DocumentGradient.png +0 -0
- package/.picto-fix-png/FileGradient.png +0 -0
- package/.picto-fix-png/HealthBubbleGradient.png +0 -0
- package/.picto-fix-png/LearnGradient.png +0 -0
- package/.picto-fix-png/MediaFileGradient.png +0 -0
- package/.zed/settings.json +0 -36
- package/AGENTS.md +0 -111
- package/CLAUDE.md +0 -111
- package/REVIEW.md +0 -81
- package/doc/code-style.md +0 -264
- package/doc/release.md +0 -102
- package/doc/stacker-plan.md +0 -219
- package/release-notes.md +0 -39
- package/tests/_setup/e2e-helpers.tsx +0 -169
- package/tests/_setup/markdown-classes.ts +0 -3
- package/tests/_setup/portable-stories.ts +0 -33
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-continues-an-ordered-list-when-Enter-is-pressed-at-the-end-of-a-numbered-line-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-diagram-for-the-fenced-mermaid-block-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-horizontal-rule-as-a-separator-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-read-only-table-preview-when-the-table-block-is-inactive-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-blockquote-text-from-the-sample-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-fenced-TypeScript-sample-code-in-the-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-auto-continues-a-bullet-list-when-Enter-is-pressed-at-the-end-of-a-list-item-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-expands-triple-backtick-into-a-fenced-code-block-with-the-caret-on-the-empty-body-line-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-reflects-typed-characters-in-the-underlying-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/heading-fold.test.tsx/MarkdownEditor---heading-folds-viewer--folds-and-unfolds-a-heading-section-via-the-margin-control-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-clicking-a-callout-header-focuses-the-editor-and-parks-the-caret-inside-the-callout--REASONING--4--1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-keeps-list-bullets-consistent-after-hopping-the-caret-across-items-several-times-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-reveals-heading-marks-when-the-caret-enters-the-heading-line-and-hides-them-when-it-leaves-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---hybrid-widgets-shows-the-right-callout-title-when-moving-focus-between-consecutive-callouts-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-clicking-a-callout-header-focuses-the-editor-with-the-caret-in-that-callout-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-keeps-list-bullets-consistent-after-hopping-the-caret-across-items-several-times-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/hybrid-widgets.test.tsx/MarkdownEditor---preview-widgets-shows-the-right-callout-title-when-moving-focus-between-consecutive-callouts-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---block-JSX-components-clicking-the-edit-source-action-selects-the-component-source-range-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---block-JSX-components-renders-block-components-via-JsxBlockWidget-with-an-edit-source-action-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-block-content.test.tsx/MarkdownEditor---inline-JSX-rendering-block-content-keeps-multiple-inline-JSX-widgets-on-the-same-line-side-by-side-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-highlight.test.tsx/MarkdownEditor---JSX-attribute-highlighting-does-not-let-the-nested-HTML-parser-mis-highlight-attributes-after-an-expression-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-2.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-inline-click.test.tsx/MarkdownEditor---inline-JSX-click-reveal-reveals-the-raw-source-when-an-inline-component-badge-is-clicked-3.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/jsx-selection.test.tsx/MarkdownEditor---JSX-selection-reveal-shows-JSX-widget-when-inactive-and-raw-source-when-caret-is-inside-the-tag-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-images-and-videos-do-not-exceed-the-editor-content-width-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-matches-MarkdownViewer-image-width-in-the-split-playground-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-standalone-MarkdownViewer-is-not-a-CodeMirror-surface-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-toggling-the-SegmentedControl-swaps-mode-without-remounting-the-editor-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-repeated-checkbox-toggles-do-not-duplicate-or-drift-task-markers-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-scroll-position-is-preserved-when-the-document-is-edited--chat-92584463--1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-task-checkbox-toggles-between-checked-and-unchecked-in-the-source-document-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-toggle-via-checkbox-preserves-task-marker-widgets-visible-from-the-previous-caret-position-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-dismisses-on-Escape-and-leaves-the-slash-as-literal-text-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-highlights-the-slash-and-shows-an-Enter-command-placeholder-until-text-is-typed-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/slash-commands.test.tsx/MarkdownEditor---slash-commands-opens-a-filtered--keyboard-navigable-menu-and-inserts-boilerplate-on-Enter-1.png +0 -0
- package/tests/e2e/MarkdownEditor/__screenshots__/undo-redo.test.tsx/MarkdownEditor---undo-and-redo-restores-undone-document-edits-via-redo-1.png +0 -0
- package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +0 -153
- package/tests/e2e/MarkdownEditor/edits.test.tsx +0 -112
- package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +0 -45
- package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +0 -193
- package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +0 -73
- package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +0 -243
- package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +0 -69
- package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +0 -60
- package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +0 -54
- package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +0 -44
- package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +0 -68
- package/tests/e2e/MarkdownEditor/media-align.test.tsx +0 -58
- package/tests/e2e/MarkdownEditor/media-edit.test.tsx +0 -64
- package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +0 -124
- package/tests/e2e/MarkdownEditor/modes.test.tsx +0 -94
- package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +0 -50
- package/tests/e2e/MarkdownEditor/regressions.test.tsx +0 -223
- package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +0 -100
- package/tests/e2e/MarkdownEditor/table-click.test.tsx +0 -48
- package/tests/e2e/MarkdownEditor/table-controls.test.tsx +0 -57
- package/tests/e2e/MarkdownEditor/table-format.test.tsx +0 -42
- package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +0 -39
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-computed-font-size-on-first-heading-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-counts-for-shared-structural-classes-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-visible-text-between-preview-editor--blurred--and-MarkdownViewer-1.png +0 -0
- package/tests/e2e/MarkdownViewer/__screenshots__/render.test.tsx/MarkdownViewer---render-exercises-shared-preview-class-names-without-mounting-CodeMirror-1.png +0 -0
- package/tests/e2e/MarkdownViewer/parity.test.tsx +0 -191
- package/tests/e2e/MarkdownViewer/render.test.tsx +0 -36
- package/tests/e2e/Stacker/stacker.test.tsx +0 -304
- package/tests/unit/Editor/helpers.test.ts +0 -42
- package/tests/unit/MarkdownConformance/error-isolation.test.tsx +0 -44
- package/tests/unit/MarkdownConformance/indentation.test.tsx +0 -30
- package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +0 -255
- package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +0 -57
- package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +0 -165
- package/tests/unit/MarkdownConformance/links.test.tsx +0 -95
- package/tests/unit/MarkdownConformance/render-helpers.tsx +0 -88
- package/tests/unit/MarkdownEditor/code-info.test.ts +0 -63
- package/tests/unit/MarkdownEditor/decorations.test.ts +0 -490
- package/tests/unit/MarkdownEditor/editor-ready.test.ts +0 -36
- package/tests/unit/MarkdownEditor/html-descriptors.test.ts +0 -94
- package/tests/unit/MarkdownEditor/indented-fence.test.ts +0 -103
- package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +0 -115
- package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +0 -92
- package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +0 -88
- package/tests/unit/MarkdownEditor/list-indent.test.ts +0 -95
- package/tests/unit/MarkdownEditor/slash-commands.test.ts +0 -213
- package/tests/unit/MarkdownEditor/table-format.test.ts +0 -83
- package/tests/unit/MarkdownEditor/table.test.ts +0 -119
- package/tests/unit/MarkdownEditor/triggers.test.ts +0 -244
- package/tests/unit/MarkdownEditor/widget-store.test.ts +0 -105
- package/tests/unit/MarkdownViewer/callout.test.tsx +0 -60
- package/tests/unit/MarkdownViewer/code-highlight.test.tsx +0 -61
- package/tests/unit/MarkdownViewer/code-title.test.tsx +0 -62
- package/tests/unit/MarkdownViewer/features.test.tsx +0 -164
- package/tests/unit/MarkdownViewer/headings.test.tsx +0 -40
- package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +0 -98
- package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +0 -234
- package/tests/unit/MarkdownViewer/jsx.test.tsx +0 -211
- package/tests/unit/MarkdownViewer/list-bullets.test.tsx +0 -49
- package/tests/unit/MarkdownViewer/list-code.test.tsx +0 -65
- package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +0 -143
- package/tests/unit/MarkdownViewer/read-time.test.ts +0 -21
- package/tests/unit/MarkdownViewer/renderers.test.tsx +0 -95
- package/tests/unit/MarkdownViewer/runnable.test.tsx +0 -69
- package/tests/unit/MarkdownViewer/ssr.test.tsx +0 -94
- package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +0 -74
- package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +0 -196
- package/tests/unit/MarkdownViewer/tabs.test.tsx +0 -116
- package/tests/unit/Stacker/drag.test.ts +0 -21
- package/tests/unit/Stacker/helpers.test.ts +0 -105
- package/tests/unit/Stacker/reducer.test.ts +0 -107
- package/tests/unit/Syntax/language.test.ts +0 -63
- package/tools/duplicate-vector-mappings.json +0 -143
- package/tools/glass-export-overrides.json +0 -8
- package/tools/icon-parser.ts +0 -78
- package/tools/integration-gradient-exports.json +0 -57
- package/tools/picto-compare.ts +0 -64
- package/tools/picto-merge-urls.ts +0 -43
- package/tools/picto-sync.ts +0 -630
- package/tools/picto-verify-gradients.ts +0 -86
- package/tools/vector-export-nodes.json +0 -977
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { MantineProvider, Paper, v8CssVariablesResolver } from "@mantine/core";
|
|
2
|
-
import { MarkdownEditor } from "@src/primitives/MarkdownEditor";
|
|
3
|
-
import { Since } from "@src/primitives/Since";
|
|
4
|
-
import { MANTINE_THEME } from "@src/theme/mantine";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
|
|
7
|
-
import { getEditorView, mountStory, nextFrame } from "../../_setup/e2e-helpers";
|
|
8
|
-
|
|
9
|
-
const JSX_DOC = `<Since v="9.9.9" />
|
|
10
|
-
|
|
11
|
-
Paragraph after component.
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
describe("MarkdownEditor / JSX selection reveal", () => {
|
|
15
|
-
it("shows JSX widget when inactive and raw source when caret is inside the tag", async () => {
|
|
16
|
-
mountStory(
|
|
17
|
-
<MantineProvider
|
|
18
|
-
theme={MANTINE_THEME}
|
|
19
|
-
cssVariablesResolver={v8CssVariablesResolver}
|
|
20
|
-
forceColorScheme="dark"
|
|
21
|
-
>
|
|
22
|
-
<Paper
|
|
23
|
-
p="md"
|
|
24
|
-
withBorder
|
|
25
|
-
>
|
|
26
|
-
<MarkdownEditor
|
|
27
|
-
mode="preview"
|
|
28
|
-
document={JSX_DOC}
|
|
29
|
-
autoFocus={false}
|
|
30
|
-
jsxMode="render"
|
|
31
|
-
components={{ Since }}
|
|
32
|
-
/>
|
|
33
|
-
</Paper>
|
|
34
|
-
</MantineProvider>,
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const view = await getEditorView();
|
|
38
|
-
view.contentDOM.blur();
|
|
39
|
-
await nextFrame();
|
|
40
|
-
|
|
41
|
-
expect(document.body.textContent).toContain("9.9.9");
|
|
42
|
-
expect(document.querySelector(".cm-content")?.textContent).not.toContain("<Since");
|
|
43
|
-
});
|
|
44
|
-
});
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import { MantineProvider, Paper, v8CssVariablesResolver } from "@mantine/core";
|
|
3
|
-
import { setMarkdownEditorFocused } from "@src/lib/markdown/editor/decorations/editor-focus";
|
|
4
|
-
import { MarkdownEditor } from "@src/primitives/MarkdownEditor";
|
|
5
|
-
import { MANTINE_THEME } from "@src/theme/mantine";
|
|
6
|
-
import { describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { getEditorView, mountStory, nextFrame } from "../../_setup/e2e-helpers";
|
|
9
|
-
|
|
10
|
-
function shell(doc: string) {
|
|
11
|
-
return (
|
|
12
|
-
<MantineProvider
|
|
13
|
-
theme={MANTINE_THEME}
|
|
14
|
-
cssVariablesResolver={v8CssVariablesResolver}
|
|
15
|
-
forceColorScheme="dark"
|
|
16
|
-
>
|
|
17
|
-
<Paper p="md">
|
|
18
|
-
<MarkdownEditor
|
|
19
|
-
mode="preview"
|
|
20
|
-
document={doc}
|
|
21
|
-
autoFocus={false}
|
|
22
|
-
/>
|
|
23
|
-
</Paper>
|
|
24
|
-
</MantineProvider>
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function caretAt(doc: string, index: number) {
|
|
29
|
-
mountStory(shell(doc));
|
|
30
|
-
const view = await getEditorView();
|
|
31
|
-
view.focus();
|
|
32
|
-
view.dispatch({
|
|
33
|
-
selection: EditorSelection.cursor(index),
|
|
34
|
-
effects: setMarkdownEditorFocused.of(true),
|
|
35
|
-
});
|
|
36
|
-
await nextFrame();
|
|
37
|
-
await nextFrame();
|
|
38
|
-
return view;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
describe("MarkdownEditor / link placeholders", () => {
|
|
42
|
-
it("shows an 'alt text' placeholder for an empty link label", async () => {
|
|
43
|
-
const doc = "[](https://surrealdb.com)";
|
|
44
|
-
await caretAt(doc, 1); // caret between the empty []
|
|
45
|
-
expect(document.querySelector(".cm-content")?.textContent).toContain("alt text");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("shows a 'link' placeholder for an empty link destination", async () => {
|
|
49
|
-
const doc = "[label]()";
|
|
50
|
-
await caretAt(doc, doc.indexOf("(") + 1); // caret between the empty ()
|
|
51
|
-
const text = document.querySelector(".cm-content")?.textContent ?? "";
|
|
52
|
-
expect(text).toContain("link");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("shows both placeholders for a fully empty link", async () => {
|
|
56
|
-
const doc = "[]()";
|
|
57
|
-
await caretAt(doc, 1);
|
|
58
|
-
const text = document.querySelector(".cm-content")?.textContent ?? "";
|
|
59
|
-
expect(text).toContain("alt text");
|
|
60
|
-
expect(text).toContain("link");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("does not show placeholders once the sections have content", async () => {
|
|
64
|
-
const doc = "[label](https://surrealdb.com)";
|
|
65
|
-
await caretAt(doc, 3);
|
|
66
|
-
expect(document.querySelector(".cm-content")?.textContent).not.toContain("alt text");
|
|
67
|
-
});
|
|
68
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import { MantineProvider, Paper, v8CssVariablesResolver } from "@mantine/core";
|
|
3
|
-
import { setMarkdownEditorFocused } from "@src/lib/markdown/editor/decorations/editor-focus";
|
|
4
|
-
import { MarkdownEditor } from "@src/primitives/MarkdownEditor";
|
|
5
|
-
import { MANTINE_THEME } from "@src/theme/mantine";
|
|
6
|
-
import { describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { getEditorView, mountStory, nextFrame, sleep } from "../../_setup/e2e-helpers";
|
|
9
|
-
|
|
10
|
-
const DOC =
|
|
11
|
-
"Regular paragraph line.\n\n\n\nAnother paragraph.\n";
|
|
12
|
-
|
|
13
|
-
/** Left edge of a line's rendered text content (text nodes have no element box). */
|
|
14
|
-
function textLeft(line: HTMLElement): number {
|
|
15
|
-
const range = document.createRange();
|
|
16
|
-
range.selectNodeContents(line);
|
|
17
|
-
return range.getBoundingClientRect().left;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
describe("MarkdownEditor / media alignment", () => {
|
|
21
|
-
it("aligns the image preview's left edge with regular text", async () => {
|
|
22
|
-
mountStory(
|
|
23
|
-
<MantineProvider
|
|
24
|
-
theme={MANTINE_THEME}
|
|
25
|
-
cssVariablesResolver={v8CssVariablesResolver}
|
|
26
|
-
forceColorScheme="dark"
|
|
27
|
-
>
|
|
28
|
-
<Paper p="md">
|
|
29
|
-
<MarkdownEditor
|
|
30
|
-
mode="preview"
|
|
31
|
-
document={DOC}
|
|
32
|
-
autoFocus={false}
|
|
33
|
-
/>
|
|
34
|
-
</Paper>
|
|
35
|
-
</MantineProvider>,
|
|
36
|
-
);
|
|
37
|
-
const view = await getEditorView();
|
|
38
|
-
view.focus();
|
|
39
|
-
view.dispatch({
|
|
40
|
-
selection: EditorSelection.cursor(view.state.doc.length),
|
|
41
|
-
effects: setMarkdownEditorFocused.of(true),
|
|
42
|
-
});
|
|
43
|
-
await sleep(200);
|
|
44
|
-
await nextFrame();
|
|
45
|
-
|
|
46
|
-
const para = Array.from(document.querySelectorAll<HTMLElement>(".cm-line")).find((l) =>
|
|
47
|
-
(l.textContent ?? "").includes("Regular"),
|
|
48
|
-
);
|
|
49
|
-
const img = document.querySelector<HTMLElement>('[data-md-widget="ImageWidget"] img');
|
|
50
|
-
expect(para).toBeTruthy();
|
|
51
|
-
expect(img).toBeTruthy();
|
|
52
|
-
|
|
53
|
-
const paraLeft = textLeft(para as HTMLElement);
|
|
54
|
-
const imageLeft = (img as HTMLElement).getBoundingClientRect().left;
|
|
55
|
-
// Within a pixel of the text inset (previously 6px to the left).
|
|
56
|
-
expect(Math.abs(imageLeft - paraLeft)).toBeLessThanOrEqual(1.5);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import { MantineProvider, Paper, v8CssVariablesResolver } from "@mantine/core";
|
|
3
|
-
import { setMarkdownEditorFocused } from "@src/lib/markdown/editor/decorations/editor-focus";
|
|
4
|
-
import { MarkdownEditor } from "@src/primitives/MarkdownEditor";
|
|
5
|
-
import { MANTINE_THEME } from "@src/theme/mantine";
|
|
6
|
-
import { describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { getEditorView, mountStory, nextFrame, sleep } from "../../_setup/e2e-helpers";
|
|
9
|
-
|
|
10
|
-
const IMAGE_DOC = "\n\nParagraph after.\n";
|
|
11
|
-
|
|
12
|
-
function shell(doc: string) {
|
|
13
|
-
return (
|
|
14
|
-
<MantineProvider
|
|
15
|
-
theme={MANTINE_THEME}
|
|
16
|
-
cssVariablesResolver={v8CssVariablesResolver}
|
|
17
|
-
forceColorScheme="dark"
|
|
18
|
-
>
|
|
19
|
-
<Paper
|
|
20
|
-
p="md"
|
|
21
|
-
withBorder
|
|
22
|
-
>
|
|
23
|
-
<MarkdownEditor
|
|
24
|
-
mode="preview"
|
|
25
|
-
document={doc}
|
|
26
|
-
autoFocus={false}
|
|
27
|
-
/>
|
|
28
|
-
</Paper>
|
|
29
|
-
</MantineProvider>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe("MarkdownEditor / media edit action", () => {
|
|
34
|
-
it("shows an image preview with an edit-source action that reveals the source", async () => {
|
|
35
|
-
mountStory(shell(IMAGE_DOC));
|
|
36
|
-
const view = await getEditorView();
|
|
37
|
-
|
|
38
|
-
// Focused, with the caret away from the image, so the image renders as a
|
|
39
|
-
// preview widget rather than raw source.
|
|
40
|
-
view.focus();
|
|
41
|
-
view.dispatch({
|
|
42
|
-
selection: EditorSelection.cursor(view.state.doc.length),
|
|
43
|
-
effects: setMarkdownEditorFocused.of(true),
|
|
44
|
-
});
|
|
45
|
-
await nextFrame();
|
|
46
|
-
await nextFrame();
|
|
47
|
-
|
|
48
|
-
expect(document.querySelector(".cm-content")?.textContent).not.toContain("![A logo]");
|
|
49
|
-
expect(document.querySelector("img")).not.toBeNull();
|
|
50
|
-
|
|
51
|
-
const action = document.querySelector<HTMLButtonElement>(
|
|
52
|
-
'button[aria-label="Edit source"]',
|
|
53
|
-
);
|
|
54
|
-
expect(action).not.toBeNull();
|
|
55
|
-
|
|
56
|
-
action?.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, button: 0 }));
|
|
57
|
-
await sleep(120);
|
|
58
|
-
await nextFrame();
|
|
59
|
-
|
|
60
|
-
// The image markdown source becomes visible and is selected.
|
|
61
|
-
expect(document.querySelector(".cm-content")?.textContent).toContain("![A logo]");
|
|
62
|
-
expect(view.state.selection.main.empty).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import { EditorView } from "@codemirror/view";
|
|
3
|
-
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
|
|
4
|
-
import { composeStories } from "@storybook/react-vite";
|
|
5
|
-
import { describe, expect, it } from "vitest";
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
mountStory,
|
|
9
|
-
nextFrame,
|
|
10
|
-
preparePreviewEditor,
|
|
11
|
-
scrollEditorTo,
|
|
12
|
-
} from "../../_setup/e2e-helpers";
|
|
13
|
-
import { md, mdViewer } from "../../_setup/markdown-classes";
|
|
14
|
-
|
|
15
|
-
const Stories = composeStories(MarkdownStories);
|
|
16
|
-
|
|
17
|
-
function findLineStart(doc: string, prefix: string): number {
|
|
18
|
-
const idx = doc.indexOf(prefix);
|
|
19
|
-
if (idx < 0) throw new Error(`prefix not found in doc: ${prefix}`);
|
|
20
|
-
return idx;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("MarkdownEditor / media sizing", () => {
|
|
24
|
-
it("images and videos do not exceed the editor content width", async () => {
|
|
25
|
-
mountStory(<Stories.Editor />);
|
|
26
|
-
const view = await preparePreviewEditor();
|
|
27
|
-
|
|
28
|
-
const docStr = view.state.doc.toString();
|
|
29
|
-
const imageLine = findLineStart(docStr, "![SurrealDB logo]");
|
|
30
|
-
const videoLine = findLineStart(docStr, "![Product demo]");
|
|
31
|
-
|
|
32
|
-
// Caret away from media lines so replace widgets render.
|
|
33
|
-
view.dispatch({ selection: EditorSelection.cursor(0) });
|
|
34
|
-
await scrollEditorTo(view, imageLine);
|
|
35
|
-
await nextFrame();
|
|
36
|
-
|
|
37
|
-
const scroller = view.scrollDOM;
|
|
38
|
-
const scrollerWidth = scroller.clientWidth;
|
|
39
|
-
expect(scrollerWidth).toBeGreaterThan(0);
|
|
40
|
-
|
|
41
|
-
let imgs: NodeListOf<HTMLImageElement> = document.querySelectorAll(
|
|
42
|
-
`.cm-editor [data-md-widget="ImageWidget"] img, .cm-editor .${md.image}`,
|
|
43
|
-
);
|
|
44
|
-
for (let i = 0; i < 40 && imgs.length === 0; i++) {
|
|
45
|
-
await nextFrame();
|
|
46
|
-
imgs = document.querySelectorAll(
|
|
47
|
-
`.cm-editor [data-md-widget="ImageWidget"] img, .cm-editor .${md.image}`,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
expect(imgs.length).toBeGreaterThan(0);
|
|
52
|
-
for (const img of Array.from(imgs)) {
|
|
53
|
-
const rect = img.getBoundingClientRect();
|
|
54
|
-
expect(
|
|
55
|
-
rect.width,
|
|
56
|
-
`image ${img.src} width ${rect.width} vs scroller ${scrollerWidth}`,
|
|
57
|
-
).toBeLessThanOrEqual(scrollerWidth + 2);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
await scrollEditorTo(view, videoLine);
|
|
61
|
-
await nextFrame();
|
|
62
|
-
|
|
63
|
-
let videos: NodeListOf<HTMLElement> = document.querySelectorAll(
|
|
64
|
-
`.cm-editor [data-md-widget="VideoWidget"] video, .cm-editor [data-md-widget="VideoWidget"] .${md.image}`,
|
|
65
|
-
);
|
|
66
|
-
for (let i = 0; i < 40 && videos.length === 0; i++) {
|
|
67
|
-
await nextFrame();
|
|
68
|
-
videos = document.querySelectorAll(
|
|
69
|
-
`.cm-editor [data-md-widget="VideoWidget"] video, .cm-editor [data-md-widget="VideoWidget"] .${md.image}`,
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
expect(videos.length).toBeGreaterThan(0);
|
|
74
|
-
for (const video of Array.from(videos)) {
|
|
75
|
-
const host =
|
|
76
|
-
video.closest("[data-md-widget='VideoWidget']") ?? video.closest(`.${md.image}`);
|
|
77
|
-
const rect = (host ?? video).getBoundingClientRect();
|
|
78
|
-
expect(
|
|
79
|
-
rect.width,
|
|
80
|
-
`video width ${rect.width} vs scroller ${scrollerWidth}`,
|
|
81
|
-
).toBeLessThanOrEqual(scrollerWidth + 2);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it("matches MarkdownViewer image width in the split playground", async () => {
|
|
86
|
-
mountStory(<Stories.SideBySide />);
|
|
87
|
-
const view = await preparePreviewEditor();
|
|
88
|
-
|
|
89
|
-
const docStr = view.state.doc.toString();
|
|
90
|
-
const imagePos = findLineStart(docStr, "![SurrealDB logo]");
|
|
91
|
-
view.dispatch({
|
|
92
|
-
selection: EditorSelection.cursor(0),
|
|
93
|
-
effects: EditorView.scrollIntoView(imagePos, { y: "center" }),
|
|
94
|
-
});
|
|
95
|
-
await nextFrame();
|
|
96
|
-
|
|
97
|
-
let editorImg: HTMLImageElement | null = null;
|
|
98
|
-
let viewerImg: HTMLImageElement | null = null;
|
|
99
|
-
let editorW = 0;
|
|
100
|
-
let viewerW = 0;
|
|
101
|
-
|
|
102
|
-
for (let i = 0; i < 40; i++) {
|
|
103
|
-
editorImg = document.querySelector<HTMLImageElement>(
|
|
104
|
-
`.cm-editor [data-md-widget="ImageWidget"] img`,
|
|
105
|
-
);
|
|
106
|
-
viewerImg = document.querySelector<HTMLImageElement>(
|
|
107
|
-
`.${mdViewer.viewerRoot} img.${md.image}`,
|
|
108
|
-
);
|
|
109
|
-
if (editorImg && viewerImg) {
|
|
110
|
-
editorW = editorImg.getBoundingClientRect().width;
|
|
111
|
-
viewerW = viewerImg.getBoundingClientRect().width;
|
|
112
|
-
if (editorW > 0 && viewerW > 0 && Math.abs(editorW - viewerW) <= 12) {
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
await nextFrame();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
expect(editorImg).not.toBeNull();
|
|
120
|
-
expect(viewerImg).not.toBeNull();
|
|
121
|
-
// Editor `.cm-line` uses 6px inline padding mirrored in viewer.module.scss.
|
|
122
|
-
expect(Math.abs(editorW - viewerW)).toBeLessThanOrEqual(12);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { EditorSelection } from "@codemirror/state";
|
|
2
|
-
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
|
|
3
|
-
import { composeStories } from "@storybook/react-vite";
|
|
4
|
-
import { describe, expect, it } from "vitest";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
getEditorView,
|
|
8
|
-
mountStory,
|
|
9
|
-
nextFrame,
|
|
10
|
-
page,
|
|
11
|
-
readDoc,
|
|
12
|
-
userEvent,
|
|
13
|
-
} from "../../_setup/e2e-helpers";
|
|
14
|
-
import { md, mdViewer } from "../../_setup/markdown-classes";
|
|
15
|
-
|
|
16
|
-
const Stories = composeStories(MarkdownStories);
|
|
17
|
-
|
|
18
|
-
async function getMode(): Promise<string | null> {
|
|
19
|
-
const editor = document.querySelector<HTMLElement>(".cm-editor");
|
|
20
|
-
return editor?.getAttribute("data-markdown-mode") ?? null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe("MarkdownEditor / modes", () => {
|
|
24
|
-
it("toggling the SegmentedControl swaps mode without remounting the editor", async () => {
|
|
25
|
-
mountStory(<Stories.Editor />);
|
|
26
|
-
await getEditorView();
|
|
27
|
-
|
|
28
|
-
const initialEditor = document.querySelector(".cm-editor");
|
|
29
|
-
expect(initialEditor).not.toBeNull();
|
|
30
|
-
expect(await getMode()).toBe("preview");
|
|
31
|
-
|
|
32
|
-
// Mantine's SegmentedControl hides the underlying radio inputs and
|
|
33
|
-
// styles a sibling label, so we click the visible label text.
|
|
34
|
-
await userEvent.click(page.getByText("Source", { exact: true }));
|
|
35
|
-
await nextFrame();
|
|
36
|
-
expect(await getMode()).toBe("source");
|
|
37
|
-
expect(document.querySelector(".cm-editor")).toBe(initialEditor);
|
|
38
|
-
|
|
39
|
-
await userEvent.click(page.getByText("Preview", { exact: true }));
|
|
40
|
-
await nextFrame();
|
|
41
|
-
expect(await getMode()).toBe("preview");
|
|
42
|
-
expect(document.querySelector(".cm-editor")).toBe(initialEditor);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("standalone MarkdownViewer is not a CodeMirror surface", async () => {
|
|
46
|
-
mountStory(<Stories.Viewer />);
|
|
47
|
-
await nextFrame();
|
|
48
|
-
await nextFrame();
|
|
49
|
-
|
|
50
|
-
expect(document.querySelector(`.${mdViewer.viewerRoot}`)).not.toBeNull();
|
|
51
|
-
expect(document.querySelector(".cm-editor")).toBeNull();
|
|
52
|
-
expect(document.querySelector(".cm-content")).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("source mode renders raw markdown markers (no hidden marks)", async () => {
|
|
56
|
-
mountStory(<Stories.Editor />);
|
|
57
|
-
await getEditorView();
|
|
58
|
-
await userEvent.click(page.getByText("Source", { exact: true }));
|
|
59
|
-
await nextFrame();
|
|
60
|
-
await getEditorView();
|
|
61
|
-
|
|
62
|
-
await nextFrame();
|
|
63
|
-
const hidden = document.querySelectorAll(`.${md.markHidden}`);
|
|
64
|
-
expect(hidden.length).toBe(0);
|
|
65
|
-
|
|
66
|
-
const text = document.querySelector(".cm-content")?.textContent ?? "";
|
|
67
|
-
expect(text).toContain("# Welcome to the live preview editor");
|
|
68
|
-
expect(text).toContain("**bold**");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("keeps edits when cycling Source then Preview after a document change", async () => {
|
|
72
|
-
mountStory(<Stories.Editor />);
|
|
73
|
-
const view = await getEditorView();
|
|
74
|
-
|
|
75
|
-
const token = `\nuniq-e2e-${Date.now()}`;
|
|
76
|
-
view.dispatch({
|
|
77
|
-
selection: EditorSelection.cursor(view.state.doc.length),
|
|
78
|
-
changes: { from: view.state.doc.length, insert: token },
|
|
79
|
-
});
|
|
80
|
-
await nextFrame();
|
|
81
|
-
|
|
82
|
-
await userEvent.click(page.getByText("Source", { exact: true }));
|
|
83
|
-
await nextFrame();
|
|
84
|
-
expect((await readDoc()).includes(token.trim())).toBe(true);
|
|
85
|
-
|
|
86
|
-
await userEvent.click(page.getByText("Preview", { exact: true }));
|
|
87
|
-
await nextFrame();
|
|
88
|
-
|
|
89
|
-
expect((await readDoc()).includes(token.trim())).toBe(true);
|
|
90
|
-
expect(document.querySelector(".cm-content")?.getAttribute("aria-readonly")).not.toBe(
|
|
91
|
-
"true",
|
|
92
|
-
);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as MarkdownStories from "@src/stories/playground/Markdown.stories";
|
|
2
|
-
import { composeStories } from "@storybook/react-vite";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
import { mountStory, nextFrame } from "../../_setup/e2e-helpers";
|
|
6
|
-
|
|
7
|
-
const Stories = composeStories(MarkdownStories);
|
|
8
|
-
|
|
9
|
-
// Resolved by the story's shared onImage resolver. The optional extension on the
|
|
10
|
-
// code selects the media type: no extension → `.auto` image, `.webp` → image,
|
|
11
|
-
// `.mp4` → video.
|
|
12
|
-
const AUTO_IMAGE_SRC = "cu72bvkattbs738rle0g.auto";
|
|
13
|
-
const WEBP_IMAGE_SRC = "cu72bvkattbs738rle0g.webp";
|
|
14
|
-
const VIDEO_SRC = "cu72bvkattbs738rle0g.mp4";
|
|
15
|
-
|
|
16
|
-
async function waitFor<T>(get: () => T | null | undefined, tries = 60): Promise<T | null> {
|
|
17
|
-
for (let i = 0; i < tries; i++) {
|
|
18
|
-
const value = get();
|
|
19
|
-
if (value) return value;
|
|
20
|
-
await nextFrame();
|
|
21
|
-
}
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe("MarkdownEditor / onImage parity (side-by-side story)", () => {
|
|
26
|
-
it("resolves CDN codes to URLs by extension in both the editor preview and the viewer", async () => {
|
|
27
|
-
mountStory(<Stories.SideBySideOnImage />);
|
|
28
|
-
|
|
29
|
-
// A bare code resolves to a `.auto` image; a `.webp` code resolves to a
|
|
30
|
-
// webp image. Both the editor widget and the viewer render each — the same
|
|
31
|
-
// onImage resolver feeds both surfaces. The editor widget mounts a frame
|
|
32
|
-
// after the viewer, so poll until parity.
|
|
33
|
-
const autoImages = await waitFor(() => {
|
|
34
|
-
const found = document.querySelectorAll(`img[src*="${AUTO_IMAGE_SRC}"]`);
|
|
35
|
-
return found.length >= 2 ? found : null;
|
|
36
|
-
});
|
|
37
|
-
expect(autoImages?.length ?? 0).toBeGreaterThanOrEqual(2);
|
|
38
|
-
expect(
|
|
39
|
-
document.querySelectorAll(`img[src*="${WEBP_IMAGE_SRC}"]`).length,
|
|
40
|
-
).toBeGreaterThanOrEqual(2);
|
|
41
|
-
|
|
42
|
-
// A `.mp4` code resolves to a video container, so both surfaces render a player.
|
|
43
|
-
const videos = await waitFor(() => {
|
|
44
|
-
const found = document.querySelectorAll("video");
|
|
45
|
-
return found.length >= 2 ? found : null;
|
|
46
|
-
});
|
|
47
|
-
expect(videos?.length ?? 0).toBeGreaterThanOrEqual(2);
|
|
48
|
-
expect(document.body.innerHTML).toContain(VIDEO_SRC);
|
|
49
|
-
});
|
|
50
|
-
});
|