@surrealdb/ui 1.2.16 → 1.2.17

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 (149) 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/ui.d.ts +12 -0
  8. package/dist/ui.js +659 -647
  9. package/dist/ui.js.map +1 -1
  10. package/package.json +8 -2
  11. package/.claude/launch.json +0 -17
  12. package/.oxfmtrc.json +0 -27
  13. package/.oxlintrc.json +0 -23
  14. package/.picto-fix-png/Cursor.png +0 -0
  15. package/.picto-fix-png/DocumentGradient.png +0 -0
  16. package/.picto-fix-png/FileGradient.png +0 -0
  17. package/.picto-fix-png/HealthBubbleGradient.png +0 -0
  18. package/.picto-fix-png/LearnGradient.png +0 -0
  19. package/.picto-fix-png/MediaFileGradient.png +0 -0
  20. package/.zed/settings.json +0 -36
  21. package/AGENTS.md +0 -111
  22. package/CLAUDE.md +0 -111
  23. package/REVIEW.md +0 -81
  24. package/doc/code-style.md +0 -264
  25. package/doc/release.md +0 -102
  26. package/doc/stacker-plan.md +0 -219
  27. package/release-notes.md +0 -39
  28. package/tests/_setup/e2e-helpers.tsx +0 -169
  29. package/tests/_setup/markdown-classes.ts +0 -3
  30. package/tests/_setup/portable-stories.ts +0 -33
  31. 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
  32. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-diagram-for-the-fenced-mermaid-block-1.png +0 -0
  33. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-renders-a-horizontal-rule-as-a-separator-1.png +0 -0
  34. 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
  35. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-blockquote-text-from-the-sample-document-1.png +0 -0
  36. package/tests/e2e/MarkdownEditor/__screenshots__/content-blocks.test.tsx/MarkdownEditor---content-blocks-shows-fenced-TypeScript-sample-code-in-the-document-1.png +0 -0
  37. 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
  38. 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
  39. package/tests/e2e/MarkdownEditor/__screenshots__/edits.test.tsx/MarkdownEditor---edits-reflects-typed-characters-in-the-underlying-document-1.png +0 -0
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. package/tests/e2e/MarkdownEditor/__screenshots__/media-sizing.test.tsx/MarkdownEditor---media-sizing-matches-MarkdownViewer-image-width-in-the-split-playground-1.png +0 -0
  58. package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-standalone-MarkdownViewer-is-not-a-CodeMirror-surface-1.png +0 -0
  59. package/tests/e2e/MarkdownEditor/__screenshots__/modes.test.tsx/MarkdownEditor---modes-toggling-the-SegmentedControl-swaps-mode-without-remounting-the-editor-1.png +0 -0
  60. package/tests/e2e/MarkdownEditor/__screenshots__/regressions.test.tsx/MarkdownEditor---regressions-repeated-checkbox-toggles-do-not-duplicate-or-drift-task-markers-1.png +0 -0
  61. 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
  62. 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
  63. 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
  64. 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
  65. 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
  66. 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
  67. package/tests/e2e/MarkdownEditor/__screenshots__/undo-redo.test.tsx/MarkdownEditor---undo-and-redo-restores-undone-document-edits-via-redo-1.png +0 -0
  68. package/tests/e2e/MarkdownEditor/content-blocks.test.tsx +0 -153
  69. package/tests/e2e/MarkdownEditor/edits.test.tsx +0 -112
  70. package/tests/e2e/MarkdownEditor/heading-fold.test.tsx +0 -45
  71. package/tests/e2e/MarkdownEditor/hybrid-widgets.test.tsx +0 -193
  72. package/tests/e2e/MarkdownEditor/image-on-image.test.tsx +0 -73
  73. package/tests/e2e/MarkdownEditor/jsx-block-content.test.tsx +0 -243
  74. package/tests/e2e/MarkdownEditor/jsx-highlight.test.tsx +0 -69
  75. package/tests/e2e/MarkdownEditor/jsx-inline-badges.test.tsx +0 -60
  76. package/tests/e2e/MarkdownEditor/jsx-inline-click.test.tsx +0 -54
  77. package/tests/e2e/MarkdownEditor/jsx-selection.test.tsx +0 -44
  78. package/tests/e2e/MarkdownEditor/link-placeholder.test.tsx +0 -68
  79. package/tests/e2e/MarkdownEditor/media-align.test.tsx +0 -58
  80. package/tests/e2e/MarkdownEditor/media-edit.test.tsx +0 -64
  81. package/tests/e2e/MarkdownEditor/media-sizing.test.tsx +0 -124
  82. package/tests/e2e/MarkdownEditor/modes.test.tsx +0 -94
  83. package/tests/e2e/MarkdownEditor/onimage-parity.test.tsx +0 -50
  84. package/tests/e2e/MarkdownEditor/regressions.test.tsx +0 -223
  85. package/tests/e2e/MarkdownEditor/slash-commands.test.tsx +0 -100
  86. package/tests/e2e/MarkdownEditor/table-click.test.tsx +0 -48
  87. package/tests/e2e/MarkdownEditor/table-controls.test.tsx +0 -57
  88. package/tests/e2e/MarkdownEditor/table-format.test.tsx +0 -42
  89. package/tests/e2e/MarkdownEditor/undo-redo.test.tsx +0 -39
  90. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-computed-font-size-on-first-heading-1.png +0 -0
  91. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-counts-for-shared-structural-classes-1.png +0 -0
  92. package/tests/e2e/MarkdownViewer/__screenshots__/parity.test.tsx/MarkdownViewer---editor-parity-matches-visible-text-between-preview-editor--blurred--and-MarkdownViewer-1.png +0 -0
  93. package/tests/e2e/MarkdownViewer/__screenshots__/render.test.tsx/MarkdownViewer---render-exercises-shared-preview-class-names-without-mounting-CodeMirror-1.png +0 -0
  94. package/tests/e2e/MarkdownViewer/parity.test.tsx +0 -191
  95. package/tests/e2e/MarkdownViewer/render.test.tsx +0 -36
  96. package/tests/e2e/Stacker/stacker.test.tsx +0 -304
  97. package/tests/unit/Editor/helpers.test.ts +0 -42
  98. package/tests/unit/MarkdownConformance/error-isolation.test.tsx +0 -44
  99. package/tests/unit/MarkdownConformance/indentation.test.tsx +0 -30
  100. package/tests/unit/MarkdownConformance/inline-code-fuzz.test.tsx +0 -255
  101. package/tests/unit/MarkdownConformance/jsx-attributes.test.tsx +0 -57
  102. package/tests/unit/MarkdownConformance/jsx-fuzz.test.tsx +0 -165
  103. package/tests/unit/MarkdownConformance/links.test.tsx +0 -95
  104. package/tests/unit/MarkdownConformance/render-helpers.tsx +0 -88
  105. package/tests/unit/MarkdownEditor/code-info.test.ts +0 -63
  106. package/tests/unit/MarkdownEditor/decorations.test.ts +0 -490
  107. package/tests/unit/MarkdownEditor/editor-ready.test.ts +0 -36
  108. package/tests/unit/MarkdownEditor/html-descriptors.test.ts +0 -94
  109. package/tests/unit/MarkdownEditor/indented-fence.test.ts +0 -103
  110. package/tests/unit/MarkdownEditor/jsx-attr-scan.test.ts +0 -115
  111. package/tests/unit/MarkdownEditor/jsx-block-widget.test.ts +0 -92
  112. package/tests/unit/MarkdownEditor/jsx-tag-grammar.test.ts +0 -88
  113. package/tests/unit/MarkdownEditor/list-indent.test.ts +0 -95
  114. package/tests/unit/MarkdownEditor/slash-commands.test.ts +0 -213
  115. package/tests/unit/MarkdownEditor/table-format.test.ts +0 -83
  116. package/tests/unit/MarkdownEditor/table.test.ts +0 -119
  117. package/tests/unit/MarkdownEditor/triggers.test.ts +0 -244
  118. package/tests/unit/MarkdownEditor/widget-store.test.ts +0 -105
  119. package/tests/unit/MarkdownViewer/callout.test.tsx +0 -60
  120. package/tests/unit/MarkdownViewer/code-highlight.test.tsx +0 -61
  121. package/tests/unit/MarkdownViewer/code-title.test.tsx +0 -62
  122. package/tests/unit/MarkdownViewer/features.test.tsx +0 -164
  123. package/tests/unit/MarkdownViewer/headings.test.tsx +0 -40
  124. package/tests/unit/MarkdownViewer/indented-jsx-children.test.tsx +0 -98
  125. package/tests/unit/MarkdownViewer/jsx-block-nesting.test.tsx +0 -234
  126. package/tests/unit/MarkdownViewer/jsx.test.tsx +0 -211
  127. package/tests/unit/MarkdownViewer/list-bullets.test.tsx +0 -49
  128. package/tests/unit/MarkdownViewer/list-code.test.tsx +0 -65
  129. package/tests/unit/MarkdownViewer/preserve-newlines.test.tsx +0 -143
  130. package/tests/unit/MarkdownViewer/read-time.test.ts +0 -21
  131. package/tests/unit/MarkdownViewer/renderers.test.tsx +0 -95
  132. package/tests/unit/MarkdownViewer/runnable.test.tsx +0 -69
  133. package/tests/unit/MarkdownViewer/ssr.test.tsx +0 -94
  134. package/tests/unit/MarkdownViewer/syntax-highlighting.test.tsx +0 -74
  135. package/tests/unit/MarkdownViewer/tabs-fenced-code.test.tsx +0 -196
  136. package/tests/unit/MarkdownViewer/tabs.test.tsx +0 -116
  137. package/tests/unit/Stacker/drag.test.ts +0 -21
  138. package/tests/unit/Stacker/helpers.test.ts +0 -105
  139. package/tests/unit/Stacker/reducer.test.ts +0 -107
  140. package/tests/unit/Syntax/language.test.ts +0 -63
  141. package/tools/duplicate-vector-mappings.json +0 -143
  142. package/tools/glass-export-overrides.json +0 -8
  143. package/tools/icon-parser.ts +0 -78
  144. package/tools/integration-gradient-exports.json +0 -57
  145. package/tools/picto-compare.ts +0 -64
  146. package/tools/picto-merge-urls.ts +0 -43
  147. package/tools/picto-sync.ts +0 -630
  148. package/tools/picto-verify-gradients.ts +0 -86
  149. package/tools/vector-export-nodes.json +0 -977
@@ -1,105 +0,0 @@
1
- import { createWidgetStore } from "@src/lib/markdown/editor/widgets/store";
2
- import { describe, expect, it } from "vitest";
3
-
4
- describe("createWidgetStore", () => {
5
- it("unregisterHost removes only the portal whose host identity matches registration", () => {
6
- const store = createWidgetStore();
7
- const host214 = document.createElement("span");
8
- const host227 = document.createElement("span");
9
- store.register({
10
- portalKey: 1,
11
- id: "list-bullet:214",
12
- host: host214,
13
- render: () => null,
14
- });
15
- store.register({
16
- portalKey: 2,
17
- id: "list-bullet:227",
18
- host: host227,
19
- render: () => null,
20
- });
21
- expect(store.getSnapshot()).toHaveLength(2);
22
-
23
- expect(store.hasPortalKey(1)).toBe(true);
24
- expect(store.hasPortalKey(99)).toBe(false);
25
-
26
- store.unregisterHost(host214);
27
- expect(store.getSnapshot()).toHaveLength(1);
28
- expect(store.getSnapshot()[0]?.id).toBe("list-bullet:227");
29
-
30
- store.unregisterHost(host227);
31
- expect(store.getSnapshot()).toHaveLength(0);
32
- });
33
-
34
- it("same logical id on two hosts keeps two portals until each host unregisters", () => {
35
- const store = createWidgetStore();
36
- const hostA = document.createElement("span");
37
- const hostB = document.createElement("span");
38
- store.register({
39
- portalKey: 10,
40
- id: "list-bullet:214",
41
- host: hostA,
42
- render: () => null,
43
- });
44
- store.register({
45
- portalKey: 11,
46
- id: "list-bullet:214",
47
- host: hostB,
48
- render: () => null,
49
- });
50
- expect(store.getSnapshot()).toHaveLength(2);
51
-
52
- store.unregisterHost(hostA);
53
- expect(store.getSnapshot()).toHaveLength(1);
54
- expect(store.getSnapshot()[0]?.host).toBe(hostB);
55
-
56
- store.unregisterHost(hostB);
57
- expect(store.getSnapshot()).toHaveLength(0);
58
- });
59
-
60
- it("re-registering the same host evicts the previous portalKey (one portal per host)", () => {
61
- const store = createWidgetStore();
62
- const host = document.createElement("span");
63
- store.register({
64
- portalKey: 1,
65
- id: "checkbox:2-5",
66
- host,
67
- render: () => null,
68
- });
69
- expect(store.getSnapshot()).toHaveLength(1);
70
-
71
- store.register({
72
- portalKey: 2,
73
- id: "checkbox:2-5",
74
- host,
75
- render: () => null,
76
- });
77
- expect(store.getSnapshot()).toHaveLength(1);
78
- expect(store.getSnapshot()[0]?.portalKey).toBe(2);
79
- expect(store.hasPortalKey(1)).toBe(false);
80
- expect(store.hasPortalKey(2)).toBe(true);
81
- });
82
-
83
- it("unregisterHost with expectedPortalKey skips when a newer registration owns the host", () => {
84
- const store = createWidgetStore();
85
- const host = document.createElement("span");
86
- store.register({
87
- portalKey: 10,
88
- id: "checkbox:2-5",
89
- host,
90
- render: () => null,
91
- });
92
- store.register({
93
- portalKey: 11,
94
- id: "checkbox:2-5",
95
- host,
96
- render: () => null,
97
- });
98
- store.unregisterHost(host, 10);
99
- expect(store.getSnapshot()).toHaveLength(1);
100
- expect(store.getSnapshot()[0]?.portalKey).toBe(11);
101
-
102
- store.unregisterHost(host, 11);
103
- expect(store.getSnapshot()).toHaveLength(0);
104
- });
105
- });
@@ -1,60 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
3
- import { MANTINE_THEME } from "@src/theme/mantine";
4
- import type { ReactElement } from "react";
5
- import { renderToString } from "react-dom/server";
6
- import { beforeAll, describe, expect, it } from "vitest";
7
-
8
- beforeAll(() => {
9
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
10
- Object.defineProperty(window, "matchMedia", {
11
- writable: true,
12
- value: (query: string) => ({
13
- matches: false,
14
- media: query,
15
- onchange: null,
16
- addListener: () => {},
17
- removeListener: () => {},
18
- addEventListener: () => {},
19
- removeEventListener: () => {},
20
- dispatchEvent: () => false,
21
- }),
22
- });
23
- }
24
- });
25
-
26
- function shell(node: ReactElement): string {
27
- return renderToString(
28
- <MantineProvider
29
- theme={MANTINE_THEME}
30
- cssVariablesResolver={v8CssVariablesResolver}
31
- forceColorScheme="dark"
32
- >
33
- {node}
34
- </MantineProvider>,
35
- );
36
- }
37
-
38
- describe("MarkdownViewer / callouts", () => {
39
- it("renders the title and body without a leading <br> between them", () => {
40
- const html = shell(
41
- <MarkdownViewer
42
- content={"> [!NOTE]\n> Sandbox data is **not persistent**: it is reset later."}
43
- />,
44
- );
45
-
46
- expect(html).toContain("Note");
47
- expect(html).toContain("Sandbox data is");
48
- expect(html).toContain("not persistent");
49
-
50
- // The body paragraph must not open with a stray line break.
51
- expect(html).not.toMatch(/<p[^>]*><br\s*\/?>/);
52
- });
53
-
54
- it("supports the title and body on adjacent lines", () => {
55
- const html = shell(<MarkdownViewer content={"> [!WARNING]\n> Be careful here."} />);
56
- expect(html).toContain("Warning");
57
- expect(html).toContain("Be careful here.");
58
- expect(html).not.toMatch(/<p[^>]*><br\s*\/?>/);
59
- });
60
- });
@@ -1,61 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
3
- import { MANTINE_THEME } from "@src/theme/mantine";
4
- import type { ReactElement } from "react";
5
- import { renderToString } from "react-dom/server";
6
- import { beforeAll, describe, expect, it } from "vitest";
7
-
8
- beforeAll(() => {
9
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
10
- Object.defineProperty(window, "matchMedia", {
11
- writable: true,
12
- value: (query: string) => ({
13
- matches: false,
14
- media: query,
15
- onchange: null,
16
- addListener: () => {},
17
- removeListener: () => {},
18
- addEventListener: () => {},
19
- removeEventListener: () => {},
20
- dispatchEvent: () => false,
21
- }),
22
- });
23
- }
24
- });
25
-
26
- function shell(node: ReactElement): string {
27
- return renderToString(
28
- <MantineProvider
29
- theme={MANTINE_THEME}
30
- cssVariablesResolver={v8CssVariablesResolver}
31
- forceColorScheme="dark"
32
- >
33
- {node}
34
- </MantineProvider>,
35
- );
36
- }
37
-
38
- // Dark-scheme syntax colors (see src/syntax/theme.ts).
39
- const STRING_COLOR = "#00ff6e";
40
- const PROPERTY_COLOR = "#e06c75";
41
-
42
- describe("MarkdownViewer / code syntax highlighting", () => {
43
- it("highlights object keys as property names (not bare identifiers)", () => {
44
- const code = ["{", ' "monthly_sales": "DEFINE TABLE monthly_sales TYPE NORMAL"', "}"].join(
45
- "\n",
46
- );
47
- const html = shell(<MarkdownViewer content={`\`\`\`json\n${code}\n\`\`\``} />);
48
- expect(html).toContain(PROPERTY_COLOR);
49
- expect(html).toContain(STRING_COLOR);
50
- });
51
-
52
- it("keeps a multi-line string token highlighted as one string across lines", () => {
53
- const code = ["const q = `DEFINE TABLE wishlist", "SCHEMAFULL PERMISSIONS NONE`;"].join(
54
- "\n",
55
- );
56
- const html = shell(<MarkdownViewer content={`\`\`\`js\n${code}\n\`\`\``} />);
57
- // Continuation line content must be inside a string-colored span — proof
58
- // the whole block was parsed once rather than line by line.
59
- expect(html).toMatch(new RegExp(`color:${STRING_COLOR}[^>]*>SCHEMAFULL PERMISSIONS NONE`));
60
- });
61
- });
@@ -1,62 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
3
- import { MANTINE_THEME } from "@src/theme/mantine";
4
- import type { ReactElement } from "react";
5
- import { renderToString } from "react-dom/server";
6
- import { beforeAll, describe, expect, it } from "vitest";
7
-
8
- beforeAll(() => {
9
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
10
- Object.defineProperty(window, "matchMedia", {
11
- writable: true,
12
- value: (query: string) => ({
13
- matches: false,
14
- media: query,
15
- onchange: null,
16
- addListener: () => {},
17
- removeListener: () => {},
18
- addEventListener: () => {},
19
- removeEventListener: () => {},
20
- dispatchEvent: () => false,
21
- }),
22
- });
23
- }
24
- });
25
-
26
- function shell(node: ReactElement): string {
27
- return renderToString(
28
- <MantineProvider
29
- theme={MANTINE_THEME}
30
- cssVariablesResolver={v8CssVariablesResolver}
31
- forceColorScheme="dark"
32
- >
33
- {node}
34
- </MantineProvider>,
35
- );
36
- }
37
-
38
- describe("MarkdownViewer / code title", () => {
39
- it("renders a title attribute as a caption above the code block", () => {
40
- const html = shell(
41
- <MarkdownViewer content={'```ts title="Example file"\nconst x = 1;\n```'} />,
42
- );
43
- expect(html).toContain("Example file");
44
- });
45
-
46
- it("omits the caption when no title is present", () => {
47
- const html = shell(<MarkdownViewer content={"```ts\nconst x = 1;\n```"} />);
48
- expect(html).not.toContain("code-block-caption");
49
- });
50
-
51
- it("passes the title through the code renderer override", () => {
52
- const html = shell(
53
- <MarkdownViewer
54
- content={'```ts title="From override"\nconst x = 1;\n```'}
55
- renderers={{
56
- code: ({ title }) => <span data-title={title}>{title}</span>,
57
- }}
58
- />,
59
- );
60
- expect(html).toContain("From override");
61
- });
62
- });
@@ -1,164 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import { extractHeadings, markdownSourceFromString, parseMarkdownTree } from "@src/lib/markdown";
3
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
4
- import { MANTINE_THEME } from "@src/theme/mantine";
5
- import type { ReactElement } from "react";
6
- import { renderToString } from "react-dom/server";
7
- import { beforeAll, describe, expect, it } from "vitest";
8
-
9
- beforeAll(() => {
10
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
11
- Object.defineProperty(window, "matchMedia", {
12
- writable: true,
13
- value: (query: string) => ({
14
- matches: false,
15
- media: query,
16
- onchange: null,
17
- addListener: () => {},
18
- removeListener: () => {},
19
- addEventListener: () => {},
20
- removeEventListener: () => {},
21
- dispatchEvent: () => false,
22
- }),
23
- });
24
- }
25
- });
26
-
27
- function shell(node: ReactElement): string {
28
- return renderToString(
29
- <MantineProvider
30
- theme={MANTINE_THEME}
31
- cssVariablesResolver={v8CssVariablesResolver}
32
- forceColorScheme="dark"
33
- >
34
- {node}
35
- </MantineProvider>,
36
- );
37
- }
38
-
39
- describe("MarkdownViewer / features", () => {
40
- it("renders bare URL autolinks as links", () => {
41
- const html = shell(<MarkdownViewer content="Visit https://example.com today." />);
42
- expect(html).toContain('href="https://example.com"');
43
- expect(html).toContain("https://example.com");
44
- });
45
-
46
- it("renders www autolinks with https href", () => {
47
- const html = shell(<MarkdownViewer content="Go to www.example.com" />);
48
- expect(html).toContain('href="https://www.example.com"');
49
- });
50
-
51
- it("renders inline <u> and inline <Since /> with default components", () => {
52
- const html = shell(
53
- <MarkdownViewer content={'Text <u>underlined</u> and <Since v="2.0.0" /> here.'} />,
54
- );
55
- expect(html).toContain("<u>");
56
- expect(html).toContain("underlined");
57
- expect(html).toContain("2.0.0");
58
- expect(html).not.toContain("<Since");
59
- expect(html).not.toMatch(/<p[^>]*>underlined<\/p>/);
60
- });
61
-
62
- it("uses custom heading id from {#suffix}", () => {
63
- const html = shell(<MarkdownViewer content="## Visible {#my-anchor}" />);
64
- expect(html).toMatch(/id="my-anchor"/);
65
- expect(html).toContain('href="#my-anchor"');
66
- expect(html).not.toContain("{#my-anchor}");
67
- });
68
-
69
- it("extractHeadings respects custom {#id}", () => {
70
- const source = "## Title {#custom}\n";
71
- const tree = parseMarkdownTree(source);
72
- const headings = extractHeadings(tree, markdownSourceFromString(source));
73
- expect(headings[0]?.id).toBe("custom");
74
- expect(headings[0]?.text).toBe("Title");
75
- });
76
-
77
- it("preserves ordered list start attribute", () => {
78
- const html = shell(
79
- <MarkdownViewer
80
- content={`3. Third step
81
- 4. Fourth step`}
82
- />,
83
- );
84
- expect(html).toMatch(/start="3"/);
85
- expect(html).toContain("3.");
86
- expect(html).toContain("4.");
87
- });
88
-
89
- it("renders HTML table cells with markdown and inline HTML", () => {
90
- const html = shell(
91
- <MarkdownViewer
92
- content={`<table>
93
- <thead><tr><th>Fn</th></tr></thead>
94
- <tbody><tr><td>**bold** and <code>x</code></td></tr></tbody>
95
- </table>`}
96
- />,
97
- );
98
- expect(html).toContain("<table");
99
- expect(html).toContain("bold");
100
- expect(html).toContain("<code");
101
- });
102
-
103
- it("wraps a GFM table in a horizontal scroll container", () => {
104
- const html = shell(
105
- <MarkdownViewer
106
- content={`| A | B |
107
- | - | - |
108
- | 1 | 2 |`}
109
- />,
110
- );
111
- expect(html).toContain("mantine-TableScrollContainer-scrollContainer");
112
- expect(html).toContain("<table");
113
- expect(html).toContain("1");
114
- });
115
-
116
- it("wraps an HTML table in a horizontal scroll container", () => {
117
- const html = shell(
118
- <MarkdownViewer
119
- content={`<table>
120
- <tbody><tr><td>cell</td></tr></tbody>
121
- </table>`}
122
- />,
123
- );
124
- expect(html).toContain("mantine-TableScrollContainer-scrollContainer");
125
- expect(html).toContain("<table");
126
- expect(html).toContain("cell");
127
- });
128
-
129
- it("renders default Button component", () => {
130
- const html = shell(
131
- <MarkdownViewer content={'<Button label="Go" href="https://surrealdb.com" />'} />,
132
- );
133
- expect(html).toContain("Go");
134
- expect(html).toContain('href="https://surrealdb.com"');
135
- });
136
-
137
- it("resolves image sources through onImage (e.g. CDN code → URL)", () => {
138
- const html = shell(
139
- <MarkdownViewer
140
- content={"![Example](cdn-code-123)"}
141
- onResolveMedia={(node) => ({
142
- ...node,
143
- src: `https://cdn.surrealdb.com/${node.src}.auto`,
144
- })}
145
- />,
146
- );
147
- expect(html).toContain("cdn.surrealdb.com/cdn-code-123.auto");
148
- expect(html).toContain("Example");
149
- });
150
-
151
- it("renders a video player when onImage resolves a source to a video container", () => {
152
- const html = shell(
153
- <MarkdownViewer
154
- content={"![clip](cdn-video-123)"}
155
- onResolveMedia={(node) => ({
156
- ...node,
157
- src: `https://cdn.surrealdb.com/${node.src}.mp4`,
158
- })}
159
- />,
160
- );
161
- expect(html).toContain("cdn.surrealdb.com/cdn-video-123.mp4");
162
- expect(html).toContain("<video");
163
- });
164
- });
@@ -1,40 +0,0 @@
1
- import { extractHeadings, markdownSourceFromString, parseMarkdownTree } from "@src/lib/markdown";
2
- import { describe, expect, it } from "vitest";
3
-
4
- describe("extractHeadings", () => {
5
- it("finds headings nested inside blockquotes", () => {
6
- const source = "> ## Inside quote\n\n## Top";
7
- const tree = parseMarkdownTree(source);
8
- const headings = extractHeadings(tree, markdownSourceFromString(source));
9
-
10
- expect(headings).toHaveLength(2);
11
- expect(headings.map((h) => h.text)).toEqual(["Inside quote", "Top"]);
12
- });
13
-
14
- it("finds headings between details HTML blocks", () => {
15
- const source = `<details>
16
- <summary>Toggle</summary>
17
-
18
- ### Inside details
19
-
20
- </details>`;
21
- const tree = parseMarkdownTree(source);
22
- const headings = extractHeadings(tree, markdownSourceFromString(source), {
23
- depths: [2, 3],
24
- });
25
-
26
- expect(headings).toHaveLength(1);
27
- expect(headings[0]?.text).toBe("Inside details");
28
- expect(headings[0]?.depth).toBe(3);
29
- });
30
-
31
- it("strips inline emphasis marks from heading text", () => {
32
- const source = "## Hello **world**";
33
- const tree = parseMarkdownTree(source);
34
- const headings = extractHeadings(tree, markdownSourceFromString(source));
35
-
36
- expect(headings).toHaveLength(1);
37
- expect(headings[0]?.text).toBe("Hello world");
38
- expect(headings[0]?.id).toBe("hello-world");
39
- });
40
- });
@@ -1,98 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import type { MarkdownComponents } from "@src/lib/markdown";
3
- import { parseMarkdownTree } from "@src/lib/markdown";
4
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
5
- import { MANTINE_THEME } from "@src/theme/mantine";
6
- import type { ReactElement, ReactNode } from "react";
7
- import { renderToString } from "react-dom/server";
8
- import { beforeAll, describe, expect, it } from "vitest";
9
-
10
- beforeAll(() => {
11
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
12
- Object.defineProperty(window, "matchMedia", {
13
- writable: true,
14
- value: (query: string) => ({
15
- matches: false,
16
- media: query,
17
- onchange: null,
18
- addListener: () => {},
19
- removeListener: () => {},
20
- addEventListener: () => {},
21
- removeEventListener: () => {},
22
- dispatchEvent: () => false,
23
- }),
24
- });
25
- }
26
- });
27
-
28
- function shell(node: ReactElement): string {
29
- return renderToString(
30
- <MantineProvider
31
- theme={MANTINE_THEME}
32
- cssVariablesResolver={v8CssVariablesResolver}
33
- forceColorScheme="dark"
34
- >
35
- {node}
36
- </MantineProvider>,
37
- );
38
- }
39
-
40
- function nodeNames(text: string): string[] {
41
- const tree = parseMarkdownTree(text);
42
- const names: string[] = [];
43
- const cursor = tree.cursor();
44
- do {
45
- names.push(cursor.name);
46
- } while (cursor.next());
47
- return names;
48
- }
49
-
50
- function Boxes({ children }: { children?: ReactNode }): ReactElement {
51
- return <div data-boxes>{children}</div>;
52
- }
53
- function IconBox({ title }: { title?: string }): ReactElement {
54
- return <div data-iconbox>{title}</div>;
55
- }
56
-
57
- const components: MarkdownComponents = {
58
- Boxes: { component: Boxes, block: true },
59
- IconBox: { component: IconBox, block: true },
60
- };
61
-
62
- // Children indented with tabs, exactly as authored in docs' index.mdx.
63
- const CONTENT = `<Boxes>
64
- \t<IconBox
65
- \t\ttitle="Querying"
66
- \t\tdescription="Learn to query your data."
67
- \t\thref="/docs/learn/querying"
68
- \t\ticon="@ui/pictoSpeed"
69
- \t/>
70
- \t<IconBox
71
- \t\ttitle="Schema management"
72
- \t\tdescription="Learn how to shape data."
73
- \t\thref="/docs/learn/schema-management"
74
- \t\ticon="@ui/pictoPath"
75
- \t/>
76
- </Boxes>`;
77
-
78
- describe("MarkdownViewer / no indented code blocks", () => {
79
- it("does not parse tab/4-space indented lines as code blocks", () => {
80
- expect(nodeNames("\t<IconBox />")).not.toContain("CodeBlock");
81
- expect(nodeNames(" plain indented text")).not.toContain("CodeBlock");
82
- // Fenced code blocks still work.
83
- expect(nodeNames("```\ncode\n```")).toContain("FencedCode");
84
- });
85
-
86
- it("renders tab-indented IconBox children inside a Boxes block", () => {
87
- const html = shell(
88
- <MarkdownViewer
89
- content={CONTENT}
90
- components={components}
91
- />,
92
- );
93
- expect(html).toContain("data-boxes");
94
- expect(html).toContain("data-iconbox");
95
- expect(html).toContain("Querying");
96
- expect(html).toContain("Schema management");
97
- });
98
- });