@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.
Files changed (152) hide show
  1. package/dist/assets/1299e4695ad0bd61.svg +5 -0
  2. package/dist/assets/408117e904653548.svg +48 -0
  3. package/dist/assets/b516b020352f6369.svg +1 -0
  4. package/dist/assets/dfde8de566c3c2b9.svg +11 -0
  5. package/dist/assets/e9eae7bc863f211b.svg +1 -0
  6. package/dist/assets/f74ae3707a5d418f.svg +1 -0
  7. package/dist/icons.d.ts +6 -0
  8. package/dist/icons.js +198 -195
  9. package/dist/icons.js.map +1 -1
  10. package/dist/ui.d.ts +18 -0
  11. package/dist/ui.js +1441 -1426
  12. package/dist/ui.js.map +1 -1
  13. package/package.json +8 -2
  14. package/.claude/launch.json +0 -17
  15. package/.oxfmtrc.json +0 -27
  16. package/.oxlintrc.json +0 -23
  17. package/.picto-fix-png/Cursor.png +0 -0
  18. package/.picto-fix-png/DocumentGradient.png +0 -0
  19. package/.picto-fix-png/FileGradient.png +0 -0
  20. package/.picto-fix-png/HealthBubbleGradient.png +0 -0
  21. package/.picto-fix-png/LearnGradient.png +0 -0
  22. package/.picto-fix-png/MediaFileGradient.png +0 -0
  23. package/.zed/settings.json +0 -36
  24. package/AGENTS.md +0 -111
  25. package/CLAUDE.md +0 -111
  26. package/REVIEW.md +0 -81
  27. package/doc/code-style.md +0 -264
  28. package/doc/release.md +0 -102
  29. package/doc/stacker-plan.md +0 -219
  30. package/release-notes.md +0 -39
  31. package/tests/_setup/e2e-helpers.tsx +0 -169
  32. package/tests/_setup/markdown-classes.ts +0 -3
  33. package/tests/_setup/portable-stories.ts +0 -33
  34. 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
  35. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-diagram-for-the-fenced-mermaid-block-1.png +0 -0
  36. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-horizontal-rule-as-a-separator-1.png +0 -0
  37. 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
  38. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-blockquote-text-from-the-sample-document-1.png +0 -0
  39. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-fenced-TypeScript-sample-code-in-the-document-1.png +0 -0
  40. 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
  41. 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
  42. package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-reflects-typed-characters-in-the-underlying-document-1.png +0 -0
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. 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
  59. 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
  60. package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-matches-MarkdownViewer-image-width-in-the-split-playground-1.png +0 -0
  61. package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-standalone-MarkdownViewer-is-not-a-CodeMirror-surface-1.png +0 -0
  62. package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-toggling-the-SegmentedControl-swaps-mode-without-remounting-the-editor-1.png +0 -0
  63. package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-repeated-checkbox-toggles-do-not-duplicate-or-drift-task-markers-1.png +0 -0
  64. 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
  65. 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
  66. 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
  67. 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
  68. 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
  69. 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
  70. package/tests/e2e/MarkdownEditor/__screenshots__/undo-redo.test.tsx/MarkdownEditor---undo-and-redo-restores-undone-document-edits-via-redo-1.png +0 -0
  71. package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +0 -153
  72. package/tests/e2e/MarkdownEditor/edits.test.tsx +0 -112
  73. package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +0 -45
  74. package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +0 -193
  75. package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +0 -73
  76. package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +0 -243
  77. package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +0 -69
  78. package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +0 -60
  79. package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +0 -54
  80. package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +0 -44
  81. package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +0 -68
  82. package/tests/e2e/MarkdownEditor/media-align.test.tsx +0 -58
  83. package/tests/e2e/MarkdownEditor/media-edit.test.tsx +0 -64
  84. package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +0 -124
  85. package/tests/e2e/MarkdownEditor/modes.test.tsx +0 -94
  86. package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +0 -50
  87. package/tests/e2e/MarkdownEditor/regressions.test.tsx +0 -223
  88. package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +0 -100
  89. package/tests/e2e/MarkdownEditor/table-click.test.tsx +0 -48
  90. package/tests/e2e/MarkdownEditor/table-controls.test.tsx +0 -57
  91. package/tests/e2e/MarkdownEditor/table-format.test.tsx +0 -42
  92. package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +0 -39
  93. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-computed-font-size-on-first-heading-1.png +0 -0
  94. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-counts-for-shared-structural-classes-1.png +0 -0
  95. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-visible-text-between-preview-editor--blurred--and-MarkdownViewer-1.png +0 -0
  96. package/tests/e2e/MarkdownViewer/__screenshots__/render.test.tsx/MarkdownViewer---render-exercises-shared-preview-class-names-without-mounting-CodeMirror-1.png +0 -0
  97. package/tests/e2e/MarkdownViewer/parity.test.tsx +0 -191
  98. package/tests/e2e/MarkdownViewer/render.test.tsx +0 -36
  99. package/tests/e2e/Stacker/stacker.test.tsx +0 -304
  100. package/tests/unit/Editor/helpers.test.ts +0 -42
  101. package/tests/unit/MarkdownConformance/error-isolation.test.tsx +0 -44
  102. package/tests/unit/MarkdownConformance/indentation.test.tsx +0 -30
  103. package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +0 -255
  104. package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +0 -57
  105. package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +0 -165
  106. package/tests/unit/MarkdownConformance/links.test.tsx +0 -95
  107. package/tests/unit/MarkdownConformance/render-helpers.tsx +0 -88
  108. package/tests/unit/MarkdownEditor/code-info.test.ts +0 -63
  109. package/tests/unit/MarkdownEditor/decorations.test.ts +0 -490
  110. package/tests/unit/MarkdownEditor/editor-ready.test.ts +0 -36
  111. package/tests/unit/MarkdownEditor/html-descriptors.test.ts +0 -94
  112. package/tests/unit/MarkdownEditor/indented-fence.test.ts +0 -103
  113. package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +0 -115
  114. package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +0 -92
  115. package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +0 -88
  116. package/tests/unit/MarkdownEditor/list-indent.test.ts +0 -95
  117. package/tests/unit/MarkdownEditor/slash-commands.test.ts +0 -213
  118. package/tests/unit/MarkdownEditor/table-format.test.ts +0 -83
  119. package/tests/unit/MarkdownEditor/table.test.ts +0 -119
  120. package/tests/unit/MarkdownEditor/triggers.test.ts +0 -244
  121. package/tests/unit/MarkdownEditor/widget-store.test.ts +0 -105
  122. package/tests/unit/MarkdownViewer/callout.test.tsx +0 -60
  123. package/tests/unit/MarkdownViewer/code-highlight.test.tsx +0 -61
  124. package/tests/unit/MarkdownViewer/code-title.test.tsx +0 -62
  125. package/tests/unit/MarkdownViewer/features.test.tsx +0 -164
  126. package/tests/unit/MarkdownViewer/headings.test.tsx +0 -40
  127. package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +0 -98
  128. package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +0 -234
  129. package/tests/unit/MarkdownViewer/jsx.test.tsx +0 -211
  130. package/tests/unit/MarkdownViewer/list-bullets.test.tsx +0 -49
  131. package/tests/unit/MarkdownViewer/list-code.test.tsx +0 -65
  132. package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +0 -143
  133. package/tests/unit/MarkdownViewer/read-time.test.ts +0 -21
  134. package/tests/unit/MarkdownViewer/renderers.test.tsx +0 -95
  135. package/tests/unit/MarkdownViewer/runnable.test.tsx +0 -69
  136. package/tests/unit/MarkdownViewer/ssr.test.tsx +0 -94
  137. package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +0 -74
  138. package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +0 -196
  139. package/tests/unit/MarkdownViewer/tabs.test.tsx +0 -116
  140. package/tests/unit/Stacker/drag.test.ts +0 -21
  141. package/tests/unit/Stacker/helpers.test.ts +0 -105
  142. package/tests/unit/Stacker/reducer.test.ts +0 -107
  143. package/tests/unit/Syntax/language.test.ts +0 -63
  144. package/tools/duplicate-vector-mappings.json +0 -143
  145. package/tools/glass-export-overrides.json +0 -8
  146. package/tools/icon-parser.ts +0 -78
  147. package/tools/integration-gradient-exports.json +0 -57
  148. package/tools/picto-compare.ts +0 -64
  149. package/tools/picto-merge-urls.ts +0 -43
  150. package/tools/picto-sync.ts +0 -630
  151. package/tools/picto-verify-gradients.ts +0 -86
  152. 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![Some alt text](https://surrealdb.com/static/logo/light.svg)\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 = "![A logo](https://surrealdb.com/static/logo/light.svg)\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
- });