@surrealdb/ui 1.2.11 → 1.2.13

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 (101) hide show
  1. package/.claude/launch.json +17 -0
  2. package/.oxfmtrc.json +27 -0
  3. package/.oxlintrc.json +23 -0
  4. package/.zed/settings.json +34 -34
  5. package/AGENTS.md +21 -19
  6. package/README.md +15 -10
  7. package/dist/assets/245b1ccaa4927818.svg +3 -0
  8. package/dist/assets/333e1fe6e67ca4a2.svg +10 -0
  9. package/dist/assets/4b0e10c655814886.svg +21 -0
  10. package/dist/assets/9932da008ea5dd32.svg +3 -0
  11. package/dist/assets/a372168d5789daa2.svg +4 -0
  12. package/dist/assets/b140e44ee4ae7e35.svg +5 -0
  13. package/dist/assets/e028075817d928e4.svg +3 -0
  14. package/dist/assets/ee1065ad5a8f7f14.svg +6 -0
  15. package/dist/ui.css +1 -1
  16. package/dist/ui.d.ts +579 -1
  17. package/dist/ui.js +11553 -8988
  18. package/dist/ui.js.map +1 -1
  19. package/doc/code-style.md +264 -0
  20. package/doc/stacker-plan.md +219 -0
  21. package/package.json +119 -115
  22. package/res/_mixins.scss +15 -11
  23. package/tests/_setup/e2e-helpers.tsx +85 -85
  24. package/tests/_setup/portable-stories.ts +11 -10
  25. package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +140 -139
  26. package/tests/e2e/MarkdownEditor/edits.test.tsx +101 -100
  27. package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +35 -34
  28. package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +178 -177
  29. package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +50 -49
  30. package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +195 -194
  31. package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +47 -46
  32. package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +35 -34
  33. package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +39 -38
  34. package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +28 -27
  35. package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +49 -48
  36. package/tests/e2e/MarkdownEditor/media-align.test.tsx +40 -39
  37. package/tests/e2e/MarkdownEditor/media-edit.test.tsx +49 -48
  38. package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +108 -107
  39. package/tests/e2e/MarkdownEditor/modes.test.tsx +79 -78
  40. package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +29 -28
  41. package/tests/e2e/MarkdownEditor/regressions.test.tsx +194 -193
  42. package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +81 -80
  43. package/tests/e2e/MarkdownEditor/table-click.test.tsx +36 -35
  44. package/tests/e2e/MarkdownEditor/table-controls.test.tsx +44 -43
  45. package/tests/e2e/MarkdownEditor/table-format.test.tsx +28 -27
  46. package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +22 -21
  47. package/tests/e2e/MarkdownViewer/parity.test.tsx +144 -143
  48. package/tests/e2e/MarkdownViewer/render.test.tsx +20 -19
  49. package/tests/e2e/Stacker/stacker.test.tsx +304 -0
  50. package/tests/unit/Editor/helpers.test.ts +26 -26
  51. package/tests/unit/MarkdownConformance/error-isolation.test.tsx +26 -25
  52. package/tests/unit/MarkdownConformance/indentation.test.tsx +20 -19
  53. package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +255 -0
  54. package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +39 -38
  55. package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +122 -122
  56. package/tests/unit/MarkdownConformance/links.test.tsx +78 -54
  57. package/tests/unit/MarkdownConformance/render-helpers.tsx +60 -60
  58. package/tests/unit/MarkdownEditor/code-info.test.ts +58 -58
  59. package/tests/unit/MarkdownEditor/decorations.test.ts +423 -421
  60. package/tests/unit/MarkdownEditor/editor-ready.test.ts +22 -22
  61. package/tests/unit/MarkdownEditor/html-descriptors.test.ts +70 -70
  62. package/tests/unit/MarkdownEditor/indented-fence.test.ts +80 -80
  63. package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +87 -87
  64. package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +51 -51
  65. package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +63 -63
  66. package/tests/unit/MarkdownEditor/list-indent.test.ts +67 -67
  67. package/tests/unit/MarkdownEditor/slash-commands.test.ts +150 -150
  68. package/tests/unit/MarkdownEditor/table-format.test.ts +67 -67
  69. package/tests/unit/MarkdownEditor/table.test.ts +96 -96
  70. package/tests/unit/MarkdownEditor/triggers.test.ts +167 -167
  71. package/tests/unit/MarkdownEditor/widget-store.test.ts +90 -90
  72. package/tests/unit/MarkdownViewer/callout.test.tsx +42 -42
  73. package/tests/unit/MarkdownViewer/code-highlight.test.tsx +41 -41
  74. package/tests/unit/MarkdownViewer/code-title.test.tsx +45 -45
  75. package/tests/unit/MarkdownViewer/features.test.tsx +129 -129
  76. package/tests/unit/MarkdownViewer/headings.test.tsx +28 -28
  77. package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +53 -53
  78. package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +137 -137
  79. package/tests/unit/MarkdownViewer/jsx.test.tsx +188 -188
  80. package/tests/unit/MarkdownViewer/list-bullets.test.tsx +31 -31
  81. package/tests/unit/MarkdownViewer/list-code.test.tsx +45 -45
  82. package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +109 -109
  83. package/tests/unit/MarkdownViewer/read-time.test.ts +13 -13
  84. package/tests/unit/MarkdownViewer/renderers.test.tsx +75 -75
  85. package/tests/unit/MarkdownViewer/runnable.test.tsx +51 -51
  86. package/tests/unit/MarkdownViewer/ssr.test.tsx +56 -55
  87. package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +43 -42
  88. package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +120 -120
  89. package/tests/unit/MarkdownViewer/tabs.test.tsx +83 -82
  90. package/tests/unit/Stacker/drag.test.ts +21 -0
  91. package/tests/unit/Stacker/helpers.test.ts +105 -0
  92. package/tests/unit/Stacker/reducer.test.ts +107 -0
  93. package/tools/duplicate-vector-mappings.json +141 -141
  94. package/tools/glass-export-overrides.json +6 -6
  95. package/tools/icon-parser.ts +41 -40
  96. package/tools/integration-gradient-exports.json +55 -55
  97. package/tools/picto-compare.ts +32 -32
  98. package/tools/picto-merge-urls.ts +19 -19
  99. package/tools/picto-sync.ts +543 -543
  100. package/tools/picto-verify-gradients.ts +50 -50
  101. package/tools/vector-export-nodes.json +975 -975
package/package.json CHANGED
@@ -1,117 +1,121 @@
1
1
  {
2
- "name": "@surrealdb/ui",
3
- "version": "1.2.11",
4
- "type": "module",
5
- "sideEffects": false,
6
- "exports": {
7
- ".": {
8
- "types": "./dist/index.d.ts",
9
- "import": "./dist/ui.js"
10
- },
11
- "./icons": {
12
- "types": "./dist/icons.d.ts",
13
- "import": "./dist/icons.js"
14
- },
15
- "./styles.css": {
16
- "import": "./dist/ui.css"
17
- },
18
- "./fonts.css": {
19
- "import": "./dist/fonts.css"
20
- },
21
- "./mixins": {
22
- "import": "./res/_mixins.scss"
23
- }
24
- },
25
- "scripts": {
26
- "build": "vite build",
27
- "storybook:dev": "storybook dev -p 6006 --ci",
28
- "storybook:build": "storybook build",
29
- "qc": "biome check .",
30
- "qa": "biome check . --write",
31
- "qau": "biome check . --write --unsafe",
32
- "qts": "tsc --noEmit",
33
- "qtsw": "tsc --noEmit --watch",
34
- "test": "vitest run",
35
- "test:unit": "vitest run --project=unit",
36
- "test:e2e": "vitest run --project=e2e",
37
- "test:storybook": "vitest run --project=storybook",
38
- "test:watch": "vitest",
39
- "icons": "bun run tools/icon-parser.ts"
40
- },
41
- "dependencies": {
42
- "@codemirror/lang-markdown": "^6.5.0",
43
- "@codemirror/legacy-modes": "^6.4.2",
44
- "@fontsource-variable/geist": "^5.2.9",
45
- "@fontsource-variable/jetbrains-mono": "^5.2.8",
46
- "@lezer/common": "^1.2.1",
47
- "@lezer/go": "^1.0.0",
48
- "@lezer/highlight": "^1.2.1",
49
- "@lezer/html": "^1.3.10",
50
- "@lezer/javascript": "^1.4.17",
51
- "@lezer/json": "^1.0.3",
52
- "@lezer/markdown": "^1.6.3",
53
- "@lezer/php": "^1.0.2",
54
- "@lezer/python": "^1.1.14",
55
- "@lezer/rust": "^1.0.2",
56
- "@lezer/yaml": "^1.0.3",
57
- "@replit/codemirror-indentation-markers": "^6.5.3",
58
- "@surrealdb/lezer": "1.0.0-beta.23",
59
- "acorn": "^8.16.0",
60
- "github-slugger": "^2.0.0",
61
- "vite-tsconfig-paths": "^6.0.5"
62
- },
63
- "devDependencies": {
64
- "@biomejs/biome": "2.4.12",
65
- "@chromatic-com/storybook": "^4.1.3",
66
- "@laynezh/vite-plugin-lib-assets": "^2.1.3",
67
- "@storybook/addon-a11y": "^10.1.11",
68
- "@storybook/addon-docs": "^10.1.11",
69
- "@storybook/addon-onboarding": "^10.1.11",
70
- "@storybook/addon-vitest": "^10.1.11",
71
- "@storybook/react-vite": "^10.1.11",
72
- "@surrealdb/codemirror": "^1.0.3",
73
- "@types/bun": "^1.3.6",
74
- "@types/js-yaml": "^4.0.9",
75
- "@types/jsdom": "^27.0.0",
76
- "@types/react": "^19.2.2",
77
- "@types/react-dom": "^19.2.3",
78
- "@vitejs/plugin-react": "^5.0.2",
79
- "@vitest/browser-playwright": "^4.0.16",
80
- "@vitest/coverage-v8": "^4.0.16",
81
- "jsdom": "^28.0.0",
82
- "mermaid": "^11.14.0",
83
- "playwright": "^1.57.0",
84
- "sass-embedded": "^1.92.1",
85
- "slate": "^0.120.0",
86
- "slate-dom": "^0.119.0",
87
- "slate-react": "^0.120.0",
88
- "storybook": "^10.1.11",
89
- "svgo": "^4.0.0",
90
- "typescript": "^5",
91
- "vite": "^7.1.5",
92
- "vite-plugin-dts": "^4.5.4",
93
- "vitest": "^4.0.16"
94
- },
95
- "peerDependencies": {
96
- "@codemirror/autocomplete": "^6.13.0",
97
- "@codemirror/commands": "^6.3.3",
98
- "@codemirror/language": "^6.10.1",
99
- "@codemirror/legacy-modes": "^6.4.2",
100
- "@codemirror/lint": "^6.5.0",
101
- "@codemirror/search": "^6.5.6",
102
- "@codemirror/state": "^6.4.1",
103
- "@codemirror/view": "^6.24.1",
104
- "@mantine/core": "^9.2.2",
105
- "@mantine/hooks": "^9.2.2",
106
- "mermaid": "^11",
107
- "react": "^19",
108
- "slate": "^0.120.0",
109
- "slate-dom": "^0.119.0",
110
- "slate-react": "^0.120.0"
111
- },
112
- "peerDependenciesMeta": {
113
- "mermaid": {
114
- "optional": true
115
- }
116
- }
2
+ "name": "@surrealdb/ui",
3
+ "version": "1.2.13",
4
+ "type": "module",
5
+ "sideEffects": false,
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/ui.js"
10
+ },
11
+ "./icons": {
12
+ "types": "./dist/icons.d.ts",
13
+ "import": "./dist/icons.js"
14
+ },
15
+ "./styles.css": {
16
+ "import": "./dist/ui.css"
17
+ },
18
+ "./fonts.css": {
19
+ "import": "./dist/fonts.css"
20
+ },
21
+ "./mixins": {
22
+ "import": "./res/_mixins.scss"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "vite build",
27
+ "storybook:dev": "storybook dev -p 6006 --ci",
28
+ "storybook:build": "storybook build",
29
+ "qc": "oxlint && biome lint --only=correctness/useExhaustiveDependencies . && oxfmt --check .",
30
+ "qa": "oxlint --fix && biome lint --only=correctness/useExhaustiveDependencies --write . && oxfmt .",
31
+ "qau": "oxlint --fix --fix-suggestions && biome lint --only=correctness/useExhaustiveDependencies --write --unsafe . && oxfmt .",
32
+ "qts": "tsc --noEmit",
33
+ "qtsw": "tsc --noEmit --watch",
34
+ "test": "vitest run",
35
+ "test:unit": "vitest run --project=unit",
36
+ "test:e2e": "vitest run --project=e2e",
37
+ "test:storybook": "vitest run --project=storybook",
38
+ "test:watch": "vitest",
39
+ "icons": "bun run tools/icon-parser.ts"
40
+ },
41
+ "dependencies": {
42
+ "@codemirror/lang-markdown": "^6.5.0",
43
+ "@codemirror/legacy-modes": "^6.4.2",
44
+ "@fontsource-variable/geist": "^5.2.9",
45
+ "@fontsource-variable/jetbrains-mono": "^5.2.8",
46
+ "@lezer/common": "^1.2.1",
47
+ "@lezer/go": "^1.0.0",
48
+ "@lezer/highlight": "^1.2.1",
49
+ "@lezer/html": "^1.3.10",
50
+ "@lezer/javascript": "^1.4.17",
51
+ "@lezer/json": "^1.0.3",
52
+ "@lezer/markdown": "^1.6.3",
53
+ "@lezer/php": "^1.0.2",
54
+ "@lezer/python": "^1.1.14",
55
+ "@lezer/rust": "^1.0.2",
56
+ "@lezer/yaml": "^1.0.3",
57
+ "@replit/codemirror-indentation-markers": "^6.5.3",
58
+ "@surrealdb/lezer": "1.0.0-beta.23",
59
+ "acorn": "^8.16.0",
60
+ "github-slugger": "^2.0.0",
61
+ "react-reverse-portal": "^2.3.0",
62
+ "vite-tsconfig-paths": "^6.0.5"
63
+ },
64
+ "devDependencies": {
65
+ "@biomejs/biome": "2.5.3",
66
+ "@chromatic-com/storybook": "^4.1.3",
67
+ "@laynezh/vite-plugin-lib-assets": "^2.1.3",
68
+ "@storybook/addon-a11y": "^10.1.11",
69
+ "@storybook/addon-docs": "^10.1.11",
70
+ "@storybook/addon-onboarding": "^10.1.11",
71
+ "@storybook/addon-vitest": "^10.1.11",
72
+ "@storybook/react-vite": "^10.1.11",
73
+ "@tanstack/react-virtual": "^3.13.12",
74
+ "@surrealdb/codemirror": "^1.0.3",
75
+ "@types/bun": "^1.3.6",
76
+ "@types/js-yaml": "^4.0.9",
77
+ "@types/jsdom": "^27.0.0",
78
+ "@types/react": "^19.2.2",
79
+ "@types/react-dom": "^19.2.3",
80
+ "@vitejs/plugin-react": "^5.0.2",
81
+ "@vitest/browser-playwright": "^4.0.16",
82
+ "@vitest/coverage-v8": "^4.0.16",
83
+ "jsdom": "^28.0.0",
84
+ "mermaid": "^11.14.0",
85
+ "playwright": "^1.57.0",
86
+ "sass-embedded": "^1.92.1",
87
+ "oxfmt": "0.58.0",
88
+ "oxlint": "1.73.0",
89
+ "slate": "^0.120.0",
90
+ "slate-dom": "^0.119.0",
91
+ "slate-react": "^0.120.0",
92
+ "storybook": "^10.1.11",
93
+ "svgo": "^4.0.0",
94
+ "typescript": "^5",
95
+ "vite": "^7.1.5",
96
+ "vite-plugin-dts": "^4.5.4",
97
+ "vitest": "^4.0.16"
98
+ },
99
+ "peerDependencies": {
100
+ "@codemirror/autocomplete": "^6.13.0",
101
+ "@codemirror/commands": "^6.3.3",
102
+ "@codemirror/language": "^6.10.1",
103
+ "@codemirror/legacy-modes": "^6.4.2",
104
+ "@codemirror/lint": "^6.5.0",
105
+ "@codemirror/search": "^6.5.6",
106
+ "@codemirror/state": "^6.4.1",
107
+ "@codemirror/view": "^6.24.1",
108
+ "@mantine/core": "^9.2.2",
109
+ "@mantine/hooks": "^9.2.2",
110
+ "mermaid": "^11",
111
+ "react": "^19",
112
+ "slate": "^0.120.0",
113
+ "slate-dom": "^0.119.0",
114
+ "slate-react": "^0.120.0"
115
+ },
116
+ "peerDependenciesMeta": {
117
+ "mermaid": {
118
+ "optional": true
119
+ }
120
+ }
117
121
  }
package/res/_mixins.scss CHANGED
@@ -71,17 +71,21 @@
71
71
  }
72
72
 
73
73
  @mixin edge-shadow-x($begin: 0px, $end: 0px) {
74
- mask: linear-gradient(to right,
75
- transparent,
76
- black $begin,
77
- black calc(100% - $end),
78
- transparent);
74
+ mask: linear-gradient(
75
+ to right,
76
+ transparent,
77
+ black $begin,
78
+ black calc(100% - $end),
79
+ transparent
80
+ );
79
81
  }
80
82
 
81
83
  @mixin edge-shadow-y($begin: 0px, $end: 0px) {
82
- mask: linear-gradient(to bottom,
83
- transparent,
84
- black $begin,
85
- black calc(100% - $end),
86
- transparent);
87
- }
84
+ mask: linear-gradient(
85
+ to bottom,
86
+ transparent,
87
+ black $begin,
88
+ black calc(100% - $end),
89
+ transparent
90
+ );
91
+ }
@@ -5,17 +5,17 @@ import { afterEach } from "vitest";
5
5
  import { page, userEvent } from "vitest/browser";
6
6
 
7
7
  interface MountedStory {
8
- container: HTMLElement;
9
- unmount: () => void;
8
+ container: HTMLElement;
9
+ unmount: () => void;
10
10
  }
11
11
 
12
12
  const activeMounts = new Set<MountedStory>();
13
13
 
14
14
  afterEach(() => {
15
- for (const mount of activeMounts) {
16
- mount.unmount();
17
- }
18
- activeMounts.clear();
15
+ for (const mount of activeMounts) {
16
+ mount.unmount();
17
+ }
18
+ activeMounts.clear();
19
19
  });
20
20
 
21
21
  /**
@@ -24,30 +24,30 @@ afterEach(() => {
24
24
  * each test automatically; tests can still call `unmount()` early.
25
25
  */
26
26
  export function mountStory(node: ReactElement | ComponentType): MountedStory {
27
- const container = document.createElement("div");
28
- container.dataset.testRoot = "true";
29
- document.body.appendChild(container);
30
-
31
- let root: Root | null = createRoot(container);
32
- const element = isReactElement(node) ? node : createElement(node);
33
- root.render(element);
34
-
35
- const mount: MountedStory = {
36
- container,
37
- unmount: () => {
38
- if (!root) return;
39
- root.unmount();
40
- root = null;
41
- container.remove();
42
- activeMounts.delete(mount);
43
- },
44
- };
45
- activeMounts.add(mount);
46
- return mount;
27
+ const container = document.createElement("div");
28
+ container.dataset.testRoot = "true";
29
+ document.body.appendChild(container);
30
+
31
+ let root: Root | null = createRoot(container);
32
+ const element = isReactElement(node) ? node : createElement(node);
33
+ root.render(element);
34
+
35
+ const mount: MountedStory = {
36
+ container,
37
+ unmount: () => {
38
+ if (!root) return;
39
+ root.unmount();
40
+ root = null;
41
+ container.remove();
42
+ activeMounts.delete(mount);
43
+ },
44
+ };
45
+ activeMounts.add(mount);
46
+ return mount;
47
47
  }
48
48
 
49
49
  function isReactElement(node: ReactElement | ComponentType): node is ReactElement {
50
- return typeof node !== "function" && typeof node === "object";
50
+ return typeof node !== "function" && typeof node === "object";
51
51
  }
52
52
 
53
53
  /**
@@ -56,27 +56,27 @@ function isReactElement(node: ReactElement | ComponentType): node is ReactElemen
56
56
  * authoritative state (selection, doc, mode) directly inside assertions.
57
57
  */
58
58
  export async function getEditorView(timeoutMs = 5000): Promise<EditorView> {
59
- const start = Date.now();
60
- while (Date.now() - start < timeoutMs) {
61
- const dom = document.querySelector<HTMLElement>(".cm-editor");
62
- if (dom) {
63
- const view = EditorView.findFromDOM(dom);
64
- if (view) return view;
65
- }
66
- await sleep(20);
67
- }
68
- throw new Error("Timed out waiting for the markdown EditorView to mount");
59
+ const start = Date.now();
60
+ while (Date.now() - start < timeoutMs) {
61
+ const dom = document.querySelector<HTMLElement>(".cm-editor");
62
+ if (dom) {
63
+ const view = EditorView.findFromDOM(dom);
64
+ if (view) return view;
65
+ }
66
+ await sleep(20);
67
+ }
68
+ throw new Error("Timed out waiting for the markdown EditorView to mount");
69
69
  }
70
70
 
71
71
  /** Returns the `.cm-content` editable element once it's in the DOM. */
72
72
  export async function getContentElement(timeoutMs = 5000): Promise<HTMLElement> {
73
- const start = Date.now();
74
- while (Date.now() - start < timeoutMs) {
75
- const dom = document.querySelector<HTMLElement>(".cm-content");
76
- if (dom) return dom;
77
- await sleep(20);
78
- }
79
- throw new Error("Timed out waiting for .cm-content");
73
+ const start = Date.now();
74
+ while (Date.now() - start < timeoutMs) {
75
+ const dom = document.querySelector<HTMLElement>(".cm-content");
76
+ if (dom) return dom;
77
+ await sleep(20);
78
+ }
79
+ throw new Error("Timed out waiting for .cm-content");
80
80
  }
81
81
 
82
82
  /**
@@ -86,21 +86,21 @@ export async function getContentElement(timeoutMs = 5000): Promise<HTMLElement>
86
86
  * `view.dispatch({ selection: ... })`.
87
87
  */
88
88
  export async function focusEditor(): Promise<EditorView> {
89
- const view = await getEditorView();
90
- view.focus();
91
- await getContentElement();
92
- return view;
89
+ const view = await getEditorView();
90
+ view.focus();
91
+ await getContentElement();
92
+ return view;
93
93
  }
94
94
 
95
95
  /** Type at the currently focused element. Caller should call `focusEditor()` first. */
96
96
  export async function typeText(text: string): Promise<void> {
97
- await userEvent.keyboard(text);
97
+ await userEvent.keyboard(text);
98
98
  }
99
99
 
100
100
  /** Convenience: focus the editor and type at the current caret. */
101
101
  export async function focusAndType(text: string): Promise<void> {
102
- await focusEditor();
103
- await userEvent.keyboard(text);
102
+ await focusEditor();
103
+ await userEvent.keyboard(text);
104
104
  }
105
105
 
106
106
  /**
@@ -108,62 +108,62 @@ export async function focusAndType(text: string): Promise<void> {
108
108
  * and DOM assertions so CodeMirror's measure cycle has time to run.
109
109
  */
110
110
  export function nextFrame(): Promise<void> {
111
- return new Promise((resolve) => requestAnimationFrame(() => resolve()));
111
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
112
112
  }
113
113
 
114
114
  /** Sleep helper for the polling loops above. */
115
115
  export function sleep(ms: number): Promise<void> {
116
- return new Promise((resolve) => setTimeout(resolve, ms));
116
+ return new Promise((resolve) => setTimeout(resolve, ms));
117
117
  }
118
118
 
119
119
  /**
120
120
  * Read the current markdown source out of the live EditorView.
121
121
  */
122
122
  export async function readDoc(): Promise<string> {
123
- const view = await getEditorView();
124
- return view.state.doc.toString();
123
+ const view = await getEditorView();
124
+ return view.state.doc.toString();
125
125
  }
126
126
 
127
127
  /** Wait until the editor reports `data-markdown-mode="preview"`. */
128
128
  export async function ensurePreviewMode(timeoutMs = 5000): Promise<void> {
129
- const start = Date.now();
130
- while (Date.now() - start < timeoutMs) {
131
- const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
132
- if (mode === "preview") return;
133
- await nextFrame();
134
- }
135
-
136
- const preview = page.getByText("Preview", { exact: true });
137
-
138
- if (preview.elements().length > 0) {
139
- await preview.click();
140
- }
141
-
142
- const retryStart = Date.now();
143
- while (Date.now() - retryStart < timeoutMs) {
144
- const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
145
- if (mode === "preview") return;
146
- await nextFrame();
147
- }
148
-
149
- throw new Error("Timed out waiting for preview mode");
129
+ const start = Date.now();
130
+ while (Date.now() - start < timeoutMs) {
131
+ const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
132
+ if (mode === "preview") return;
133
+ await nextFrame();
134
+ }
135
+
136
+ const preview = page.getByText("Preview", { exact: true });
137
+
138
+ if (preview.elements().length > 0) {
139
+ await preview.click();
140
+ }
141
+
142
+ const retryStart = Date.now();
143
+ while (Date.now() - retryStart < timeoutMs) {
144
+ const mode = document.querySelector(".cm-editor")?.getAttribute("data-markdown-mode");
145
+ if (mode === "preview") return;
146
+ await nextFrame();
147
+ }
148
+
149
+ throw new Error("Timed out waiting for preview mode");
150
150
  }
151
151
 
152
152
  /** Mount helper: editor view + preview mode + focus. */
153
153
  export async function preparePreviewEditor(): Promise<EditorView> {
154
- const view = await getEditorView();
155
- await ensurePreviewMode();
156
- view.focus();
157
- await nextFrame();
158
- return view;
154
+ const view = await getEditorView();
155
+ await ensurePreviewMode();
156
+ view.focus();
157
+ await nextFrame();
158
+ return view;
159
159
  }
160
160
 
161
161
  /** Scroll the editor so `pos` is centred in the viewport. */
162
162
  export async function scrollEditorTo(view: EditorView, pos: number): Promise<void> {
163
- view.dispatch({
164
- effects: EditorView.scrollIntoView(pos, { y: "center" }),
165
- });
166
- await nextFrame();
163
+ view.dispatch({
164
+ effects: EditorView.scrollIntoView(pos, { y: "center" }),
165
+ });
166
+ await nextFrame();
167
167
  }
168
168
 
169
169
  export { page, userEvent };
@@ -1,5 +1,6 @@
1
1
  import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
2
2
  import { setProjectAnnotations } from "@storybook/react-vite";
3
+
3
4
  import * as projectAnnotations from "../../.storybook/preview";
4
5
 
5
6
  // Calling `setProjectAnnotations` here ensures any `composeStories(...)` invocation
@@ -19,14 +20,14 @@ setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
19
20
  // real navigation, so neutralise the browser's default navigation while leaving
20
21
  // the app's own click handlers (which place the caret) untouched.
21
22
  if (typeof window !== "undefined" && !(window as { __e2eNavGuard?: boolean }).__e2eNavGuard) {
22
- (window as { __e2eNavGuard?: boolean }).__e2eNavGuard = true;
23
- window.addEventListener(
24
- "click",
25
- (event) => {
26
- const anchor = (event.target as HTMLElement | null)?.closest?.("a[href]");
27
- if (anchor) event.preventDefault();
28
- },
29
- true,
30
- );
31
- window.addEventListener("submit", (event) => event.preventDefault(), true);
23
+ (window as { __e2eNavGuard?: boolean }).__e2eNavGuard = true;
24
+ window.addEventListener(
25
+ "click",
26
+ (event) => {
27
+ const anchor = (event.target as HTMLElement | null)?.closest?.("a[href]");
28
+ if (anchor) event.preventDefault();
29
+ },
30
+ true,
31
+ );
32
+ window.addEventListener("submit", (event) => event.preventDefault(), true);
32
33
  }