@surrealdb/ui 1.2.15 → 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 (148) 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 +678 -649
  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 -0
  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/tools/duplicate-vector-mappings.json +0 -143
  141. package/tools/glass-export-overrides.json +0 -8
  142. package/tools/icon-parser.ts +0 -78
  143. package/tools/integration-gradient-exports.json +0 -57
  144. package/tools/picto-compare.ts +0 -64
  145. package/tools/picto-merge-urls.ts +0 -43
  146. package/tools/picto-sync.ts +0 -630
  147. package/tools/picto-verify-gradients.ts +0 -86
  148. package/tools/vector-export-nodes.json +0 -977
@@ -1,21 +0,0 @@
1
- import {
2
- estimateMarkdownReadTimeMinutes,
3
- formatMarkdownReadTime,
4
- markdownToPlainText,
5
- } from "@src/lib/markdown/read-time";
6
- import { describe, expect, it } from "vitest";
7
-
8
- describe("markdown read time", () => {
9
- it("strips markdown syntax for word counting", () => {
10
- expect(markdownToPlainText("# Title\n\n[link](https://example.com)")).toBe("Title link");
11
- });
12
-
13
- it("estimates at least one minute", () => {
14
- expect(estimateMarkdownReadTimeMinutes("")).toBe(1);
15
- });
16
-
17
- it("formats a read-time label", () => {
18
- const words = Array.from({ length: 400 }, () => "word").join(" ");
19
- expect(formatMarkdownReadTime(words)).toBe("2 min read");
20
- });
21
- });
@@ -1,95 +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 / MarkdownViewerRenderers", () => {
39
- it("uses heading renderer override", () => {
40
- const html = shell(
41
- <MarkdownViewer
42
- content="## Section title"
43
- renderers={{
44
- heading: () => <span data-custom-heading>HEADING_MARKER</span>,
45
- }}
46
- />,
47
- );
48
- expect(html).toContain("HEADING_MARKER");
49
- expect(html).not.toContain("Section title");
50
- });
51
-
52
- it("uses link renderer override", () => {
53
- const html = shell(
54
- <MarkdownViewer
55
- content="[Example](https://example.com)"
56
- renderers={{
57
- link: () => <span data-custom-link>LINK_MARKER</span>,
58
- }}
59
- />,
60
- );
61
- expect(html).toContain("LINK_MARKER");
62
- expect(html).not.toContain("Example");
63
- expect(html).not.toContain("https://example.com");
64
- });
65
-
66
- it("uses table renderer override without the scroll wrapper", () => {
67
- const html = shell(
68
- <MarkdownViewer
69
- content={`| A | B |
70
- | - | - |
71
- | 1 | 2 |`}
72
- renderers={{
73
- table: () => <span data-custom-table>TABLE_MARKER</span>,
74
- }}
75
- />,
76
- );
77
- expect(html).toContain("TABLE_MARKER");
78
- expect(html).not.toContain("<table");
79
- expect(html).not.toContain("mantine-TableScrollContainer-scrollContainer");
80
- });
81
-
82
- it("uses image renderer override", () => {
83
- const html = shell(
84
- <MarkdownViewer
85
- content='![Alt text](https://example.com/image.png "Title")'
86
- renderers={{
87
- image: () => <span data-custom-image>IMAGE_MARKER</span>,
88
- }}
89
- />,
90
- );
91
- expect(html).toContain("IMAGE_MARKER");
92
- expect(html).not.toContain("Alt text");
93
- expect(html).not.toContain("https://example.com/image.png");
94
- });
95
- });
@@ -1,69 +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 / runnable code", () => {
39
- it("renders RunnableCodeBlock when fenced info includes runnable", () => {
40
- const html = shell(
41
- <MarkdownViewer content={'```surql runnable="SELECT 1"\nRETURN 1;\n```'} />,
42
- );
43
- expect(html).toContain("Run Query");
44
- });
45
-
46
- it("renders RunnableCodeBlock for a loose runnable flag with no value", () => {
47
- const html = shell(<MarkdownViewer content={"```surql runnable\nRETURN 1;\n```"} />);
48
- expect(html).toContain("Run Query");
49
- });
50
-
51
- it("does not render RunnableCodeBlock for plain fenced code", () => {
52
- const html = shell(<MarkdownViewer content={"```surql\nRETURN 1;\n```"} />);
53
- expect(html).not.toContain("Run Query");
54
- });
55
-
56
- it("preserves runnable attribute casing through the render pipeline", () => {
57
- const html = shell(
58
- <MarkdownViewer
59
- content={'```surql runnable="SELECT 1"\nRETURN 1;\n```'}
60
- codeRenderers={{}}
61
- renderers={{
62
- code: ({ runnable }) => <span data-runnable={runnable}>{runnable}</span>,
63
- }}
64
- />,
65
- );
66
- expect(html).toContain("SELECT 1");
67
- expect(html).not.toContain("select 1");
68
- });
69
- });
@@ -1,94 +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
- import { md, mdViewer } from "../../_setup/markdown-classes";
9
-
10
- // Mantine's `useComputedColorScheme` reaches for `window.matchMedia` even
11
- // during a `renderToString` pass; stub it so the SSR test can exercise the
12
- // full viewer pipeline (including `MarkdownCodeLines`).
13
- beforeAll(() => {
14
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
15
- Object.defineProperty(window, "matchMedia", {
16
- writable: true,
17
- value: (query: string) => ({
18
- matches: false,
19
- media: query,
20
- onchange: null,
21
- addListener: () => {},
22
- removeListener: () => {},
23
- addEventListener: () => {},
24
- removeEventListener: () => {},
25
- dispatchEvent: () => false,
26
- }),
27
- });
28
- }
29
- });
30
-
31
- function shell(node: ReactElement): string {
32
- return renderToString(
33
- <MantineProvider
34
- theme={MANTINE_THEME}
35
- cssVariablesResolver={v8CssVariablesResolver}
36
- forceColorScheme="dark"
37
- >
38
- {node}
39
- </MantineProvider>,
40
- );
41
- }
42
-
43
- describe("MarkdownViewer / SSR", () => {
44
- it("renders to a static HTML string with semantic markdown structure", () => {
45
- const html = shell(
46
- <MarkdownViewer
47
- content={`# Title
48
-
49
- Body with **bold**, *italic* and [a link](https://surrealdb.com).
50
-
51
- - One
52
- - Two
53
-
54
- 1. Alpha
55
- 2. Beta
56
-
57
- \`\`\`ts
58
- return true;
59
- \`\`\`
60
- `}
61
- />,
62
- );
63
-
64
- // Wrapper.
65
- expect(html).toContain(mdViewer.viewerRoot);
66
-
67
- // Semantic heading.
68
- expect(html).toContain(md.h1);
69
- expect(html).toMatch(/id="title"/);
70
- expect(html).toContain("Title");
71
-
72
- // Paragraph and inline marks (paragraph uses bare <p> without module class).
73
- expect(html).toMatch(/<p[^>]*>Body with/);
74
- expect(html).toContain(md.strong);
75
- expect(html).toContain("bold</strong>");
76
- expect(html).toContain(md.emphasis);
77
- expect(html).toContain(md.link);
78
-
79
- // Semantic lists.
80
- expect(html).toContain(md.list);
81
- expect(html).toContain("<li");
82
-
83
- // Semantic code block.
84
- expect(html).toContain(md.codeBlock);
85
- expect(html).toContain("<code");
86
- expect(html).toContain("</code></pre>");
87
-
88
- // Raw markdown markers must not leak through.
89
- expect(html).not.toContain("**bold**");
90
- expect(html).not.toContain("*italic*");
91
- expect(html).not.toContain("## ");
92
- expect(html).not.toMatch(/<p[^>]*>#/);
93
- });
94
- });
@@ -1,74 +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
- import { md } from "../../_setup/markdown-classes";
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
- describe("MarkdownViewer / syntax highlighting", () => {
41
- it("highlights bash and sh fenced blocks", () => {
42
- const html = shell(
43
- <MarkdownViewer
44
- content={`\`\`\`bash
45
- cd project
46
- \`\`\`
47
-
48
- \`\`\`sh
49
- export FOO=bar
50
- \`\`\`
51
- `}
52
- />,
53
- );
54
-
55
- expect(html).toContain(md.codeBlock);
56
- expect(html).toMatch(/style="color:#ffd000"/);
57
- });
58
-
59
- it("highlights json fenced blocks", () => {
60
- const html = shell(
61
- <MarkdownViewer
62
- content={`\`\`\`json
63
- {
64
- "name": "example"
65
- }
66
- \`\`\`
67
- `}
68
- />,
69
- );
70
-
71
- expect(html).toContain(md.codeBlock);
72
- expect(html).toMatch(/style="color:#00ff6e"/);
73
- });
74
- });
@@ -1,196 +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
- function labels(html: string): string[] {
39
- return [...html.matchAll(/mantine-Tabs-tabLabel">([^<]*)</g)].map((m) => m[1] ?? "");
40
- }
41
-
42
- // Tab items whose fenced code sits directly under the tag (no blank-line
43
- // separators) and contains an interior blank line.
44
- const FLUSH = `<Tabs groupId="package-manager">
45
- <TabItem value="esm" label="ESM" default>
46
- \`\`\`ts
47
- import { Surreal } from 'surrealdb';
48
-
49
- // Create a new Surreal instance
50
- const db = new Surreal();
51
- \`\`\`
52
- </TabItem>
53
- <TabItem value="cjs" label="CommonJS">
54
- \`\`\`ts
55
- const { Surreal } = require('surrealdb');
56
-
57
- // Create a new Surreal instance
58
- const db = new Surreal();
59
- \`\`\`
60
- </TabItem>
61
- </Tabs>`;
62
-
63
- // Same content, but the tab items are indented under <Tabs> with tabs.
64
- const INDENTED = `<Tabs groupId="package-manager">
65
- \t<TabItem value="esm" label="ESM" default>
66
- \t\`\`\`ts
67
- \timport { Surreal } from 'surrealdb';
68
-
69
- \t// Create a new Surreal instance
70
- \tconst db = new Surreal();
71
- \t\`\`\`
72
- \t</TabItem>
73
- \t<TabItem value="cjs" label="CommonJS">
74
- \t\`\`\`ts
75
- \tconst { Surreal } = require('surrealdb');
76
-
77
- \t// Create a new Surreal instance
78
- \tconst db = new Surreal();
79
- \t\`\`\`
80
- \t</TabItem>
81
- </Tabs>`;
82
-
83
- describe("MarkdownViewer / Tabs with fenced code", () => {
84
- for (const [name, content] of [
85
- ["flush", FLUSH],
86
- ["indented", INDENTED],
87
- ] as const) {
88
- it(`${name}: renders both tabs, both labels, and highlighted code`, () => {
89
- const html = shell(<MarkdownViewer content={content} />);
90
- const text = html.replace(/<[^>]+>/g, "");
91
-
92
- // Both tab buttons are present, in order.
93
- expect(labels(html)).toEqual(["ESM", "CommonJS"]);
94
- // The fenced code is rendered as a real (highlighted) code block
95
- // whose tokens survive in the markup.
96
- expect(html).toContain('data-language="ts"');
97
- expect(html).toContain("Surreal");
98
- expect(html).toContain("surrealdb");
99
- // Tags and fences are not leaked as visible plain text.
100
- expect(text).not.toContain("</TabItem>");
101
- expect(text).not.toContain("<TabItem");
102
- expect(text).not.toContain("```");
103
- });
104
- }
105
- });
106
-
107
- // A tab item whose content mixes prose paragraphs, blank lines, and a fenced
108
- // code block — all indented under the tags. `bl` builds the blank separator
109
- // lines, optionally carrying the structural tab (some editors keep it).
110
- function mixedItem(bl: "" | "\t"): string {
111
- return [
112
- "<Tabs>",
113
- '\t<TabItem value="esm" label="ESM">',
114
- bl,
115
- "\tTest",
116
- bl,
117
- "\t```ts",
118
- "\timport { Surreal } from 'surrealdb';",
119
- bl,
120
- "\t// Create a new Surreal instance",
121
- "\tconst db = new Surreal();",
122
- "\t```",
123
- bl,
124
- "\ttest2",
125
- bl,
126
- "\t</TabItem>",
127
- "</Tabs>",
128
- ].join("\n");
129
- }
130
-
131
- // Mirrors docs' in-memory.mdx: an indented tab item whose runaway fence used to
132
- // swallow the following flush <Tabs> block whole.
133
- const TWO_TAB_BLOCKS = [
134
- "<Tabs>",
135
- '\t<TabItem value="esm" label="ESM">',
136
- "",
137
- "\tTest",
138
- "",
139
- "\t```ts",
140
- "\timport { Surreal } from 'surrealdb';",
141
- "\t```",
142
- "",
143
- "\t</TabItem>",
144
- "</Tabs>",
145
- "",
146
- "<Tabs>",
147
- "",
148
- '<TabItem label="SurrealDB 3.x">',
149
- "",
150
- "```bash",
151
- "surreal start mem",
152
- "```",
153
- "",
154
- "</TabItem>",
155
- "</Tabs>",
156
- ].join("\n");
157
-
158
- describe("MarkdownViewer / two adjacent Tabs blocks", () => {
159
- it("renders both blocks independently without one swallowing the next", () => {
160
- const html = shell(<MarkdownViewer content={TWO_TAB_BLOCKS} />);
161
- const text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
162
-
163
- // Both tab blocks produced their own labelled tab.
164
- expect(labels(html)).toEqual(["ESM", "SurrealDB 3.x"]);
165
- // Content from both blocks is present…
166
- expect(html).toContain("surrealdb");
167
- expect(text).toContain("surreal start mem");
168
- // …and nothing leaked as raw tags or fences.
169
- expect(text).not.toContain("TabItem");
170
- expect(text).not.toContain("Tabs");
171
- expect(text).not.toContain("```");
172
- });
173
- });
174
-
175
- describe("MarkdownViewer / Tabs with mixed indented content", () => {
176
- for (const blank of ["", "\t"] as const) {
177
- const label = blank === "" ? "empty blank lines" : "tab-filled blank lines";
178
- it(`renders prose + code + prose inside an indented tab item (${label})`, () => {
179
- const html = shell(<MarkdownViewer content={mixedItem(blank)} />);
180
- const text = html.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
181
-
182
- expect(labels(html)).toEqual(["ESM"]);
183
- // Prose before and after the code block renders as prose.
184
- expect(text).toContain("Test");
185
- expect(text).toContain("test2");
186
- // The code renders as a highlighted code block, not leaked prose.
187
- expect(html).toContain('data-language="ts"');
188
- expect(html).toContain("surrealdb");
189
- // Nothing is swallowed: tags and fences never surface as text.
190
- expect(text).not.toContain("TabItem");
191
- expect(text).not.toContain("```");
192
- // The code content must not appear as an indented (tab-prefixed) blob.
193
- expect(text).not.toContain("\timport");
194
- });
195
- }
196
- });
@@ -1,116 +0,0 @@
1
- import { MantineProvider, v8CssVariablesResolver } from "@mantine/core";
2
- import type { MarkdownComponents } from "@src/lib/markdown";
3
- import { MarkdownViewer } from "@src/primitives/MarkdownViewer";
4
- import { MANTINE_THEME } from "@src/theme/mantine";
5
- import { Children, isValidElement, type ReactElement, type ReactNode } from "react";
6
- import { renderToString } from "react-dom/server";
7
- import { beforeAll, describe, expect, it } from "vitest";
8
-
9
- import viewerClasses from "@src/lib/markdown/style/viewer.module.scss";
10
-
11
- beforeAll(() => {
12
- if (typeof window !== "undefined" && typeof window.matchMedia !== "function") {
13
- Object.defineProperty(window, "matchMedia", {
14
- writable: true,
15
- value: (query: string) => ({
16
- matches: false,
17
- media: query,
18
- onchange: null,
19
- addListener: () => {},
20
- removeListener: () => {},
21
- addEventListener: () => {},
22
- removeEventListener: () => {},
23
- dispatchEvent: () => false,
24
- }),
25
- });
26
- }
27
- });
28
-
29
- function shell(node: ReactElement): string {
30
- return renderToString(
31
- <MantineProvider
32
- theme={MANTINE_THEME}
33
- cssVariablesResolver={v8CssVariablesResolver}
34
- forceColorScheme="dark"
35
- >
36
- {node}
37
- </MantineProvider>,
38
- );
39
- }
40
-
41
- const TABS_MARKDOWN = `<Tabs>
42
- <TabItem label="JavaScript">
43
-
44
- \`\`\`javascript
45
- const x = 42;
46
- \`\`\`
47
-
48
- </TabItem>
49
- <TabItem label="Rust">
50
-
51
- \`\`\`rust
52
- fn main() {}
53
- \`\`\`
54
-
55
- </TabItem>
56
- </Tabs>`;
57
-
58
- describe("MarkdownViewer / Tabs", () => {
59
- it("renders one tab button per TabItem with the correct labels", () => {
60
- const html = shell(<MarkdownViewer content={TABS_MARKDOWN} />);
61
-
62
- const tabButtons = html.match(/mantine-Tabs-tab\b/g) ?? [];
63
- expect(tabButtons).toHaveLength(2);
64
-
65
- const labels = [...html.matchAll(/mantine-Tabs-tabLabel">([^<]*)</g)].map((m) => m[1]);
66
- expect(labels).toEqual(["JavaScript", "Rust"]);
67
- });
68
-
69
- it("renders the active tab's content as markdown (code block highlighted)", () => {
70
- const html = shell(<MarkdownViewer content={TABS_MARKDOWN} />);
71
- // A real fenced code block is rendered (not raw text).
72
- expect(html).toContain('data-language="javascript"');
73
- // The code is highlighted into spans, so the plain text survives stripping tags.
74
- const text = html.replace(/<[^>]+>/g, "");
75
- expect(text).toContain("const x = 42");
76
- });
77
-
78
- it("wraps tab sub-content in the shared content-flow class for top-level rhythm", () => {
79
- const contentFlow = viewerClasses.contentFlow;
80
- expect(contentFlow).toBeTruthy();
81
-
82
- const html = shell(<MarkdownViewer content={TABS_MARKDOWN} />);
83
-
84
- // The class is shared: the viewer root composes it, and the tab body
85
- // wrapper applies it — so nested content is spaced like top-level content.
86
- const occurrences = html.split(contentFlow).length - 1;
87
- expect(occurrences).toBeGreaterThanOrEqual(2);
88
- });
89
-
90
- it("exposes bare child elements with their props for custom introspecting components", () => {
91
- // Mirrors how docs' <ContentTabs> reads each child's label/content via
92
- // React.Children — the children must be the bare TabItem elements.
93
- function Group({ children }: { children?: ReactNode }): ReactElement {
94
- const tabs = Children.toArray(children)
95
- .filter(isValidElement)
96
- .map((el) => (el.props as { label?: string }).label ?? "MISSING");
97
- return <div data-labels={tabs.join("|")} />;
98
- }
99
- function Item({ children }: { children?: ReactNode }): ReactElement {
100
- return <>{children}</>;
101
- }
102
- const components: MarkdownComponents = {
103
- Group: { component: Group, block: true },
104
- Item: { component: Item, block: true },
105
- };
106
- const html = shell(
107
- <MarkdownViewer
108
- content={
109
- '<Group>\n<Item label="One">\n\na\n\n</Item>\n<Item label="Two">\n\nb\n\n</Item>\n</Group>'
110
- }
111
- components={components}
112
- />,
113
- );
114
- expect(html).toContain('data-labels="One|Two"');
115
- });
116
- });
@@ -1,21 +0,0 @@
1
- import { shouldDismiss } from "@src/lib/stacker";
2
- import { describe, expect, it } from "vitest";
3
-
4
- describe("shouldDismiss", () => {
5
- it("dismisses on a fast flick towards the edge regardless of distance", () => {
6
- expect(shouldDismiss(10, 400, 0.6)).toBe(true);
7
- });
8
-
9
- it("does not dismiss a slow drag that stays under halfway", () => {
10
- expect(shouldDismiss(100, 400, 0.1)).toBe(false);
11
- });
12
-
13
- it("dismisses once dragged past halfway", () => {
14
- expect(shouldDismiss(210, 400, 0.1)).toBe(true);
15
- expect(shouldDismiss(200, 400, 0.1)).toBe(false);
16
- });
17
-
18
- it("never dismisses when the extent is unknown and there is no flick", () => {
19
- expect(shouldDismiss(0, 0, 0.1)).toBe(false);
20
- });
21
- });