@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
package/doc/code-style.md DELETED
@@ -1,264 +0,0 @@
1
- # Code Style
2
-
3
- This document describes how code is written in the SurrealDB UI Kit. It is
4
- derived from analysing the existing source under `src/`, which is the
5
- **leading** authority: when in doubt, match the surrounding code. The goal is
6
- that anyone can write new code indistinguishable from what is already there.
7
-
8
- Formatting (tabs, 100-column width, one-attribute-per-line JSX, import ordering)
9
- is enforced by oxfmt. This document covers the conventions the formatter and
10
- linter do **not** enforce: structure, naming, comments, and idiom.
11
-
12
- ## Files and extensions
13
-
14
- - **`.tsx` everywhere in `/src`** — including files
15
- that contain no JSX (for example `api/helpers.tsx`, `api/keys.tsx`,
16
- `api/publisher.tsx`). The framework packages use `.tsx` as the default source
17
- extension.
18
- - **Component folders** are `PascalCase` and contain an `index.tsx` plus, when
19
- needed, a `style.module.scss`. Sub-parts of a component live beside it as
20
- lowercase files (e.g. `section.tsx`, `view.tsx`, `list.tsx`) or in nested
21
- `PascalCase` folders.
22
- - **Hook and utility files** are `kebab-case`: `use-stable.tsx`,
23
- `use-registry.tsx`, `make-panel-type.tsx`, `clone.ts`.
24
- - **SCSS modules** are always named `style.module.scss` and imported as
25
- `import classes from "./style.module.scss"`.
26
-
27
- ## Components
28
-
29
- **Always define components as typed `const` arrow functions, never as `function`
30
- declarations.** This is the dominant convention across `core`.
31
-
32
- ```tsx
33
- export interface EntryButtonProps
34
- extends ButtonProps, ElementProps<"button">, RefAttributes<HTMLButtonElement> {}
35
-
36
- export const EntryButton: FC<...> = ({ children, className, ...other }) => {
37
- return (
38
- <Button
39
- {...other}
40
- className={clsx(classes.root, className)}
41
- >
42
- {children}
43
- </Button>
44
- );
45
- };
46
- ```
47
-
48
- Conventions:
49
-
50
- - Type the component with `FC` (or `FC<Props>`) imported from `react`, and
51
- destructure props in the parameter list.
52
- - Name the props interface `<Component>Props`. Extend Mantine prop types
53
- (`ButtonProps`, `BoxProps`, `ElementProps<"...">`), `PropsWithChildren`, and
54
- `RefAttributes<...>` where relevant, rather than re-declaring common props.
55
- - **Named exports** are the norm; avoid default exports.
56
- - When a component needs an inner implementation (for example to sit behind a
57
- `<Suspense>` or memo boundary), the exported component is a thin wrapper and the
58
- implementation is a private `const XxxInner: FC<...>` in the same file:
59
-
60
- ```tsx
61
- export const WorkspaceMixin: FC<WorkspaceMixinProps> = memo((props) => (
62
- <Suspense>
63
- <WorkspaceMixinInner {...props} />
64
- </Suspense>
65
- ));
66
-
67
- const WorkspaceMixinInner: FC<WorkspaceMixinProps> = ({ depotKey, name, weight }) => {
68
- // ...
69
- };
70
- ```
71
-
72
- - Boolean props read as flags: `with*` (`withBackground`, `withDebug`), `is*`
73
- (`isActive`, `isFocused`), or `show*` (`showResults`, `showSidebar`).
74
- - JSX attributes are placed one-per-line (oxfmt `singleAttributePerLine: true`);
75
- do not fight the formatter by collapsing them.
76
-
77
- ## Event handlers
78
-
79
- Interactive handlers are named `handle<Event>` and wrapped in `useStable` so they
80
- keep a stable identity across renders:
81
-
82
- ```tsx
83
- const handleSidebarToggle = useStable(() => {
84
- setShowSidebar(!showSidebar);
85
- });
86
- ```
87
-
88
- `useStable` (from `src/hooks`) is preferred over `useCallback` for
89
- callbacks whose dependencies would otherwise churn; it is registered with Biome
90
- as a stable-result hook, so it does not need a dependency array.
91
-
92
- ## Hooks
93
-
94
- - Custom hooks are `use<Thing>` and live in `src/hooks/` (core-wide) or beside the
95
- feature they serve.
96
- - **Context hooks** read a context with `use(SomeContext)` and throw a typed
97
- error when used outside their provider, rather than returning `null`:
98
-
99
- ```tsx
100
- export function useTabs(): TabsContext {
101
- const ctx = use(TabsContext);
102
-
103
- if (!ctx) {
104
- throw new SurrealistError("Component must be wrapped in a <Workspace> component");
105
- }
106
-
107
- return ctx;
108
- }
109
- ```
110
-
111
- - A context, its `use*` accessor, and its `Provider` component typically share a
112
- file. The context value interface and the context object often share the same
113
- name (`interface TabsContext` and `const TabsContext = createContext(...)`).
114
- - Utility hooks are exceptions to the arrow-component rule and are written as
115
- `export function` (e.g. `useStable`, `useRegistry`, `useTabs`). Components use
116
- `const` + `FC`; standalone functions and hooks use `function`.
117
-
118
- ## Functions and utilities
119
-
120
- - Standalone, non-component functions use `export function foo(...)` with explicit
121
- return types where they are part of a public surface.
122
- - Pure helpers live in utility modules and are small and
123
- single-purpose (`generateId`, `delegate`, `dedent`, `extractError`, `bail`,
124
- `sanitizeId`).
125
- - Prefer early returns and guard clauses over deep nesting.
126
-
127
- ## Types and interfaces
128
-
129
- - Use `interface` for object shapes, component props, and context values; use
130
- `type` for unions, aliases, mapped/conditional types, and function-type
131
- aliases.
132
- - Union-of-literals is the norm for enumerations
133
- (`type Platform = "darwin" | "windows" | "linux"`,
134
- `type UpdateType = "metadata" | "state"`).
135
- - Discriminated unions use a `kind` field, with `is*` type guards:
136
-
137
- ```ts
138
- export type ProviderEntry = FolderEntry | ModelEntry;
139
-
140
- export const isFolder = (item: ProviderEntry): item is FolderEntry => item.kind === "folder";
141
- export const isModel = (item: ProviderEntry): item is ModelEntry => item.kind === "model";
142
- ```
143
-
144
- - Generic type parameters are single uppercase letters (`T`, `K`, `A`, `S`, `U`),
145
- matching the panel/registry/depot generics.
146
- - Deliberate exceptions to a lint rule use an inline
147
- `// oxlint-disable-next-line <rule>: <reason>` comment, always with a trailing
148
- reason. The Biome-owned `useExhaustiveDependencies` rule is the exception — it
149
- still reads `// biome-ignore lint/correctness/useExhaustiveDependencies` comments.
150
-
151
- ## Naming
152
-
153
- - **Variables, functions, props, methods:** `camelCase`.
154
- - **Components, classes, interfaces, types:** `PascalCase`.
155
- - **Module-level constants:** `SCREAMING_SNAKE_CASE` — e.g. `LOG_TAG`,
156
- `CONFIG_CACHE`, `DEFAULT_PANELS`, `WORKSPACE_PROVIDER`, `THEME_DATA_RETRIEVAL`,
157
- `OPACITY_STRONG`, `ENTITY_TYPES`, `FullSync`. (A small number of local
158
- singletons like `FullSync` use `PascalCase`; module-wide exported constants use
159
- `SCREAMING_SNAKE_CASE`.)
160
- - **Refs:** suffix with `Ref` (`onChangeInner`, `modelEntryCacheRef`,
161
- `collectRef`).
162
- - **Design tokens:** colours are `THEME_*` (`THEME_DATA_RETRIEVAL`,
163
- `THEME_SCHEMA`, `THEME_ADMIN`, ...); opacities are `OPACITY_*`
164
- (`OPACITY_SUBTLE`, `OPACITY_MEDIUM`, `OPACITY_STRONG`).
165
- - **Icons and pictos:** `icon<Name>` (`iconQuery`, `iconCog`, `iconNamespace`)
166
- and `picto<Name>` (`pictoSurrealist`), imported from the core UI layer.
167
- - **Event names** use a namespaced, double-colon convention on the IPC/electron
168
- boundary (`window::maximize`, `window::toggle-fullscreen`).
169
- - **SCSS class names** are `kebab-case` in the stylesheet and accessed as
170
- `camelCase` via the `classes` object (`.save-box` → `classes.saveBox`).
171
-
172
- ## Comments
173
-
174
- Two distinct comment styles are used, and both matter:
175
-
176
- 1. **Multi-line JSDoc block comments (`/** ... */`)** document exported functions, classes,
177
- interfaces, class members, and interface members. They describe intent, and
178
- use `@param`/`@returns`/`@example` where helpful. Nearly every public method
179
- on `Depot`, `Panel`, `ModelProvider`, and the hooks carries one:
180
-
181
- ```ts
182
- /**
183
- * Get a value from the storage backend.
184
- *
185
- * @param key - The key of the value to get.
186
- * @returns A promise that resolves with the value.
187
- */
188
- async get<K extends keyof T>(key: K): Promise<T[K] | null> { ... }
189
- ```
190
-
191
- 2. **Short single-line comments (`// ...`)** explain _why_ a block of logic exists
192
- or mark the steps of a longer procedure. They sit directly above the code they
193
- describe and read as terse imperative phrases:
194
-
195
- ```ts
196
- // Focus or replace the model if it is already open
197
- for (const key of currentKeys) {
198
- // Focus the existing model
199
- if (models.includes(uriString)) { ... }
200
-
201
- // Replace an existing model
202
- if (replaceUriString && models.includes(replaceUriString)) { ... }
203
- }
204
-
205
- // Create a new region
206
- const newKey = generateId();
207
- ```
208
-
209
- **Do not** restate the code; explain the reason behind logic or explain the use case to a public facing audience.
210
- Longer procedural functions (like tab open/close logic) are essentially outlined by these step-comments.
211
-
212
- **Do not** make any references to the planning phases or phrase comments from a first person perspective.
213
-
214
- ## Whitespace and vertical rhythm
215
-
216
- Blank lines are used deliberately to separate **logical units**, not
217
- arbitrarily. The consistent pattern is:
218
-
219
- - One blank line between the imports block and the first declaration.
220
- - One blank line separating each logical step inside a function — typically a
221
- step introduced by a `// ...` comment is preceded by a blank line:
222
-
223
- ```ts
224
- const newSchema = { ...schema };
225
-
226
- // Synchronize root schema
227
- if (options.root) {
228
- ...
229
- }
230
-
231
- // Synchronize namespace schema
232
- if (surreal.namespace && options.namespace) {
233
- ...
234
- }
235
- ```
236
-
237
- - One blank line between members of a class/interface, and between grouped
238
- declarations.
239
- - Related declarations are grouped together with no blank lines between them,
240
- then separated from the next group by a single blank line. For example, a block
241
- of `useLocalDepotState` calls is grouped, then a blank line precedes the refs:
242
-
243
- ```tsx
244
- const [regionKeys, setRegionKeys] = useLocalDepotState(depot, "regionKeys", [], true);
245
- const [regionModels, setRegionModels] = useLocalDepotState(depot, "regionModels", {}, true);
246
- const [regionActives, setRegionActives] = useLocalDepotState(depot, "regionActives", {});
247
-
248
- const modelEntryCacheRef = useRef(new Map<string, ModelEntry>());
249
- const providers = modelProviders.mapping();
250
- ```
251
-
252
- Never use more than one consecutive blank line.
253
-
254
- ## Imports and exports
255
-
256
- - oxfmt organises imports automatically (`sortImports`); do not hand-sort them.
257
- Imports are grouped — external packages, then internal, then local/relative —
258
- with a blank line between groups, and sorted within each group.
259
- - Use **named exports**. Barrel `index.tsx` files re-export a package's public
260
- surface with `export *` and named re-exports, grouped by area with a short
261
- comment header per group (see `core/src/index.tsx`, whose sections are labelled
262
- `// API exports`, `// Foundation exports`, `// Hooks exports`, etc.).
263
- - Import type-only symbols with `import type { ... }` (`verbatimModuleSyntax` is
264
- on, so this is required for types).
package/doc/release.md DELETED
@@ -1,102 +0,0 @@
1
- # Release process
2
-
3
- This document describes how to cut a release of `@surrealdb/ui`.
4
-
5
- ## 1. Determine the version bump
6
-
7
- Releases follow [semantic versioning](https://semver.org/) (`x.y.z`).
8
-
9
- - **Patch** (`x.y.Z`) — the default. Unless the release is explicitly stated
10
- to be a minor or major, always assume it is a patch.
11
- - **Minor** (`x.Y.0`) — only when explicitly stated (new backwards-compatible
12
- features).
13
- - **Major** (`X.0.0`) — only when explicitly stated (breaking changes).
14
-
15
- ## 2. Determine the next version
16
-
17
- Read the current version from `package.json` (the `version` field) and apply
18
- the bump from step 1.
19
-
20
- For example, if the current version is `1.2.12`:
21
-
22
- | Bump | Next version |
23
- | ----- | ------------ |
24
- | patch | `1.2.13` |
25
- | minor | `1.3.0` |
26
- | major | `2.0.0` |
27
-
28
- The resulting `x.y.z` is used for the branch name, the `package.json` version,
29
- and the pull request title below.
30
-
31
- ## 3. Create the release branch
32
-
33
- Branch off the latest `main`:
34
-
35
- ```sh
36
- git checkout main
37
- git pull
38
- git checkout -b release/x.y.z
39
- ```
40
-
41
- ## 4. Bump the `package.json` version
42
-
43
- Set the `version` field in `package.json` to the new `x.y.z`.
44
-
45
- ## 5. Format, test, and fix
46
-
47
- 1. Run the formatter and apply any fixes:
48
-
49
- ```sh
50
- bun run qa
51
- ```
52
-
53
- If this produces changes (formatting that was previously missing), commit
54
- them.
55
-
56
- 2. Run the test suite:
57
-
58
- ```sh
59
- bun run test
60
- ```
61
-
62
- 3. Iteratively fix any broken tests, re-running the suite until it passes.
63
- Commit the fixes.
64
-
65
- ## 6. Commit and push
66
-
67
- Commit the version bump (and any formatting/test fixes) and push the branch:
68
-
69
- ```sh
70
- git push -u origin release/x.y.z
71
- ```
72
-
73
- ## 7. Open the pull request
74
-
75
- Open a pull request from `release/x.y.z` into `main`.
76
-
77
- - **Title:** `Release x.y.z`
78
- - **Description:** a bullet-point list of the PRs included in this release,
79
- each linking to its PR. Format each entry as:
80
-
81
- ```md
82
- - Did something (#100)
83
- ```
84
-
85
- where `#100` links to the corresponding pull request. Compile the list from
86
- the PRs merged into `main` since the previous release.
87
-
88
- > **The PR description becomes the release notes**, so make sure it is
89
- > complete and well-formed before merging.
90
-
91
- ## 8. Merge the pull request
92
-
93
- Merging the `release/x.y.z` branch into `main` is all that's required — the
94
- [Publish workflow](../.github/workflows/publish.yml) takes over automatically:
95
-
96
- 1. It infers the tag `vx.y.z` from the branch name and asserts it matches the
97
- `package.json` version.
98
- 2. It creates the GitHub release, using the PR description as the release notes.
99
- 3. It builds, packs, and publishes the package to npm.
100
-
101
- No manual GitHub release or tag is needed. (Publishing a GitHub release by hand
102
- still works as a fallback — it triggers the same build-and-publish steps.)
@@ -1,219 +0,0 @@
1
- # Stacker v2 — Responsive modal/drawer overlay manager
2
-
3
- ## Context
4
-
5
- Rewrite `src/lib/stacker` **from scratch** (replacing the previous attempt entirely). Stacker becomes a single imperative API for spawning overlays that render as **modals on desktop and drawers on mobile**, chosen automatically per stack, exposed as `<StackerProvider>` + `useStacker()` (Mantine modals-manager style). It must be built on Mantine's lower-level compound primitives (`Modal.Root`/`Drawer.Root`/`ModalBase*`) rather than the opinionated `<Modal>`/`<Drawer>`.
6
-
7
- Key behaviors (authoritative requirements):
8
-
9
- - **Single `open()`** receives one content + chrome props; kind chosen live from the viewport (mobile → drawer), forceable via optional `variant` on the **first** stack item. Later items' `variant` hints are ignored for rendering. Resizing remounts the matching host immediately.
10
- - **Nested stack** like Mantine Modal Manager: closing the top reveals the previous.
11
- - **Modals**: only ever ONE modal rendered; pushes/pops animate a content swap (scale/fade). ONE scrim (`<Overlay>`) that darkens/fades only — never scales the page.
12
- - **Drawers**: lower drawers stay mounted & peek out behind the top one (shadcn nested look), their **content faded to opacity 0** (sheet surface stays visible). ONE scrim + the page content scales down slightly (fixed amount, no per-depth compounding).
13
- - `title` + `subtitle` (under title); close button pinned top-right even when title/subtitle wrap.
14
- - `side` hint (drawer-only, **default "right"** per user), `draggable` hint (drawer-only, vaul-style velocity drag-to-dismiss).
15
- - Mobile breakpoint: **`sm` = 48em/768px** (per user), overridable via provider `mobileQuery` prop.
16
- - Storybook stories + unit + e2e tests.
17
-
18
- ## Verified Mantine 9.2.2 facts that drive the design
19
-
20
- 1. `Transition` never animates on initial mount (`useDidUpdate`) → every Root mounts `opened=false` and flips true in a mount effect (two-phase mount).
21
- 2. In the `entered` state, Transition keeps its `in` styles **inline on `Modal.Content`/`Drawer.Content` permanently** → our depth/drag/swap transforms must live on inner elements we own, never on Mantine's Content.
22
- 3. Every mounted `ModalBase` registers its own window-capture Escape listener gated only by its own `closeOnEscape` → with N drawer roots mounted, set `closeOnEscape` **only on the top** root.
23
- 4. Theme `defaultProps` do NOT reach compound roots (keys are `ModalRoot`/`DrawerRoot`) → replicate house defaults explicitly (modal: `size "lg"`, `radius "sm"`, `centered`, `padding "xl"`). Theme `Overlay` (opacity .25, blur 4) and `CloseButton` (surreal, custom icon) defaults DO apply — free consistency.
24
- 5. `ModalBase*` parts (`ModalBaseTitle/Body/...`) are exported from `@mantine/core` root and share one context with both Modal and Drawer → one shared header/body chrome component works in both paths with correct `aria-labelledby`/`aria-describedby`.
25
- 6. `onExitTransitionEnd`/`onEnterTransitionEnd` are first-class Root props → use for REMOVE dispatch.
26
- 7. `Drawer.Content` has `overflow-y: auto` and `data-hidden` means `opacity: 0 !important` on the whole content → neutralize Content (transparent, `overflow: visible`), never use `__hidden`; scrolling moves into our `.body`.
27
- 8. `RemoveScroll` is refcounted → per-root `lockScroll` default is safe with N mounted roots.
28
- 9. Drawer `position` auto-picks slide transition (top→slide-down, bottom→slide-up, left→slide-right, right→slide-left); positioning is CSS-vars, not data attributes.
29
- 10. `useDrag` (@mantine/hooks) provides movement/velocity/direction with document-level listeners → synthetic PointerEvents work for e2e drag tests.
30
- 11. `useMediaQuery(query, initial, { getInitialValueInEffect: false })` reads matchMedia synchronously.
31
- 12. Repo: `isolatedDeclarations` (annotate all exports), SCSS modules `dashesOnly`, `clsx` from `@src/helpers/clsx`, `generateRandomId` from `@src/helpers/id`, `useStable` from `src/hooks/use-stable.tsx`, `src/index.ts` already re-exports `./lib/stacker`.
32
-
33
- ## File layout — `src/lib/stacker/` (delete all 11 existing files)
34
-
35
- | File | Responsibility |
36
- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
37
- | `index.ts` | Public re-exports (components, hook, types, `stackerReducer`, pure helpers for tests) |
38
- | `types.ts` | `StackerKind`, `StackerSide`, `StackerSettings`, `StackerItem`, `StackerContextValue` |
39
- | `config.ts` | Constants: `STACKER_MOBILE_QUERY = "(max-width: 48em)"`, z base 200, durations (root 200ms, swap 220ms, drawer 300ms, page 400ms), easing `cubic-bezier(0.32, 0.72, 0, 1)`, `DRAWER_PEEK_PX 12`, `DRAWER_DEPTH_SCALE 0.04`, `PAGE_SCALE 0.98`, `PAGE_RADIUS 12` |
40
- | `helpers.ts` | Pure: `resolveKind(variant, isMobile)`, `resolveLiveKind(items, isMobile)`, `drawerDepthStyle(side, depth)`, `topOpened(items)` |
41
- | `reducer.ts` | `stackerReducer` — OPEN/CLOSE/CLOSE_ALL/REMOVE/UPDATE (items only; no stored kind) |
42
- | `context.ts` | `StackerContext`, `useStacker()` (throws outside provider) |
43
- | `provider.tsx` | `StackerProvider`: reducer, `useMediaQuery`, stable callbacks, page-scale shell, mounts one host by kind |
44
- | `modal-host.tsx` | The single `Modal.Root` tree; REMOVE on root exit |
45
- | `modal-swap.tsx` | Content cross-swap state machine (current/leaving panes, push/pop direction, cleanup, reduced motion) |
46
- | `drawer-host.tsx` | Shared scrim (Portal + Transition + `<Overlay fixed>`) + one `Drawer.Root` per item |
47
- | `drawer-sheet.tsx` | Per-item inner sheet: depth transform, content fade, drag wiring, handle |
48
- | `chrome.tsx` | `StackerHeader` (title/subtitle/close, `live` vs inert mode) + `StackerBody` |
49
- | `use-drag-dismiss.ts` | Port of existing hook nearly as-is (`position`→`side`); pure `shouldDismiss` kept |
50
- | `style.module.scss` | Page shell, sheet, content fade, handle, header, swap panes/keyframes, reduced-motion |
51
- | `AGENTS.md` | Rewritten module doc |
52
-
53
- ### Public API
54
-
55
- ```ts
56
- type StackerKind = "modal" | "drawer";
57
- type StackerSide = "left" | "right" | "top" | "bottom";
58
-
59
- interface StackerSettings {
60
- id?: string; // default generateRandomId()
61
- title?: ReactNode;
62
- subtitle?: ReactNode; // under title
63
- children?: ReactNode; // THE one content
64
- variant?: StackerKind; // first-overlay hint only
65
- side?: StackerSide; // drawer-only, DEFAULT "right"
66
- draggable?: boolean; // drawer-only, default false
67
- size?: MantineSize | (string & {}) | number; // modal default "lg", drawer "md"
68
- padding?: MantineSpacing; // default "xl"
69
- withCloseButton?: boolean; // default true
70
- closeOnEscape?: boolean; // default true
71
- closeOnClickOutside?: boolean; // default true
72
- onClose?: () => void;
73
- }
74
-
75
- useStacker(): { open(settings?): string; close(id?): void; closeAll(): void;
76
- update(id, patch): void; stack: StackerItem[]; kind: StackerKind | null }
77
-
78
- <StackerProvider mobileQuery?: string /* default "(max-width: 48em)" */ zIndex?: number>
79
- ```
80
-
81
- ## State & reducer
82
-
83
- `{ items: StackerItem[] /* bottom→top; opened=false = playing exit */ }`
84
-
85
- - **OPEN** `{item}`: existing id → no-op if open, **revive** (flip opened, merge settings) if closing; else append.
86
- - **CLOSE**: flip `opened=false` (idempotent, same-ref no-op).
87
- - **CLOSE_ALL**: flip all.
88
- - **REMOVE**: filter id.
89
- - **UPDATE**: shallow-merge patch.
90
-
91
- Provider `open()` (stable via `useStable`, state via ref): dispatches `{ item }` only. Live kind is `resolveLiveKind(items, isMobile)` = first item's `variant` ?? viewport.
92
- Provider `close(id?)`: target = id or top opened item; skip if already closing (onClose fires once); fire `onClose`; **non-top modal → immediate REMOVE** (it isn't rendered, no animation); else CLOSE.
93
-
94
- Edge cases: double-close = no-op; reopen mid-exit = revive (Transition reverses); viewport change mid-stack remounts the matching host with the same items.
95
-
96
- ## Modal path — ONE `Modal.Root` for the whole stack
97
-
98
- ```
99
- <Modal.Root opened={entered && anyOpen} onClose={() => close()}
100
- closeOnEscape={top.closeOnEscape} closeOnClickOutside={top.closeOnClickOutside}
101
- trapFocus returnFocus lockScroll centered radius="sm"
102
- padding={top.padding ?? "xl"} size={top.size ?? "lg"}
103
- transitionProps={{ transition: "pop", duration: 200, timingFunction: EASING }}
104
- onExitTransitionEnd={removeAllClosedItems} zIndex={zIndex}>
105
- <Modal.Overlay data-stacker-scrim /> ← THE single scrim (darken/fade only)
106
- <Modal.Content data-stacker="modal">
107
- <ModalSwap top={lastItemRegardlessOfOpened} onLeaveEnd={removeIfStillClosed}>
108
- pane: <StackerHeader live /> + <StackerBody />
109
- </ModalSwap>
110
- </Modal.Content>
111
- </Modal.Root>
112
- ```
113
-
114
- - Escape + overlay click funnel through `onClose` → pops top. Native Mantine focus trap/scroll lock/portal.
115
- - When the LAST item pops, content persists (swap keyed on last item, not last _open_) while the Root plays exit; `onExitTransitionEnd` REMOVEs → kind reset.
116
-
117
- ### Swap mechanism (`modal-swap.tsx`)
118
-
119
- - Derived-state-in-render: when top id ≠ current id, `setLeaving({item: current, direction})`, `setCurrent(top)` during render (no flash frame). Direction: old current still in items → "push", else "pop".
120
- - Layout: `.swap { display:grid }`, `.pane { grid-area: 1/1; min-width: 0 }` (panes overlap; no height animation in v1).
121
- - CSS keyframes 220ms, fill-mode both, direction-aware deck: push = old scales back to 0.94 + fades, new enters from 1.06; pop = reversed.
122
- - Leaving pane is `inert aria-hidden` with `live={false}` chrome (plain markup — avoids duplicate `ModalBaseTitle` aria ids).
123
- - Cleanup: `onAnimationEnd` → `onLeaveEnd(id)` → provider REMOVEs only if item still present with `opened === false`; `setTimeout(duration+100)` fallback. Reduced motion (`useReducedMotion`): skip leaving pane entirely, instant swap.
124
- - Focus: entering pane `tabIndex={-1}`, focused (preventScroll) when `document.activeElement` collapsed to body.
125
-
126
- ## Drawer path — one `Drawer.Root` per item + standalone scrim
127
-
128
- ```
129
- <Portal>
130
- <Transition mounted={anyOpen} transition="fade" duration={200}>
131
- {(styles) => <Overlay fixed data-stacker-scrim style={styles} zIndex={zIndex}
132
- onClick={() => topAllowsClickOutside && close()} />}
133
- </Transition>
134
- </Portal>
135
- {items.map((item, index) =>
136
- <Drawer.Root opened={entered && item.opened} onClose={() => close(item.id)}
137
- position={side /* default "right" */} size={size ?? "md"} padding={padding ?? "xl"}
138
- closeOnEscape={isTop && closeOnEscape} ← CRITICAL (fact 3)
139
- closeOnClickOutside={false} ← scrim owns click-outside
140
- trapFocus={isTop} returnFocus lockScroll
141
- transitionProps={{ duration: 300, timingFunction: EASING }} ← slide-* auto per position
142
- onExitTransitionEnd={() => remove(item.id)} zIndex={zIndex + 1 + index}>
143
- <Drawer.Content className={contentReset} data-stacker="drawer" data-covered={depth > 0}>
144
- <div className={sheet} data-side data-covered style={depthOrDragTransform}>
145
- {draggable && <div className={grip} ref={isTop ? dragRef : undefined}
146
- style={{ touchAction: isTop ? "none" : undefined }}><div className={handle}/></div>}
147
- <StackerHeader live className={fadeItem} />
148
- <StackerBody className={fadeItem} />
149
- </div>
150
- </Drawer.Content>
151
- </Drawer.Root>)}
152
- ```
153
-
154
- - **Standalone scrim** (not `Drawer.Overlay` in root[0]) because a mid-stack/bottom REMOVE must not tear down the scrim. z: scrim at base, roots at base+1+index.
155
- - **Depth visuals** (`drawerDepthStyle(side, depth)`, depth = count of opened items above): transform-origin at the **free edge**, translate away from anchor + scale, net peek = depth × 12px:
156
- - right: `translateX(-12px·depth) scale(1 − 0.04·depth)`, origin `0% 50%`
157
- - left: `translateX(+…)`, origin `100% 50%`; bottom: `translateY(-…)`, origin `50% 0%`; top: `translateY(+…)`, origin `50% 100%`
158
- - Mantine's slide transform lives on Content (outer), ours on `.sheet` (inner) — they compose, never conflict (fact 2). `.sheet { transition: transform .3s EASING }`.
159
- - **Content fade**: `.sheet[data-covered] :is(.header, .body) { opacity: 0 }` — surface + handle stay visible. Covered content gets `pointer-events: none` so clicks on peeking sheets fall through to the scrim.
160
- - **Content neutralization**: `.contentReset { background: transparent; box-shadow: none; overflow: visible }`; scrolling in `.body { flex:1; min-height:0; overflow-y:auto }`; per-side radius on `.sheet` (round free corners only).
161
- - **Drag**: reuse ported `use-drag-dismiss` (flick ≥ 0.5 px/ms toward edge OR past 50% of extent → dismiss); enabled only for top sheet with `draggable`; `transition: none` while dragging; spring-back via sheet transition. Drag and depth transforms never coexist (only depth-0 drags).
162
-
163
- ## Page scale (drawers only)
164
-
165
- Provider wraps children: `<Box className={page} data-pushed={kind === "drawer" && anyOpen}>`. `.page[data-pushed] { transform: scale(0.98); border-radius: 12px; overflow: hidden }`, 0.4s EASING, `transform-origin: top center`, no transform when idle (keeps `position: fixed` descendants working), fixed amount regardless of depth, reduced-motion disables it. Never applied for modals.
166
-
167
- ## Header (`chrome.tsx`)
168
-
169
- Flex row `align-items: flex-start`: left column `.titles { flex:1; min-width:0 }` with `ModalBaseTitle` (live) or plain div (inert) + subtitle `<Text c="dimmed" size="sm">`; right `CloseButton` `.close { flex-shrink:0; margin-inline-start:auto }` → pinned top-right regardless of wrapping. Padding via `--mb-padding`. NOT using `ModalBaseHeader` (sticky/min-height/opaque bg don't fit sheets). Body = `ModalBaseBody` (live) for `aria-describedby`.
170
-
171
- ## Stories (`src/stories/primitives/Stacker.stories.tsx`, CSF3, NO play functions)
172
-
173
- 1. `Default` — auto kind, nested push buttons, title/subtitle demo
174
- 2. `MobileEmulated` — `mobileQuery="(min-width: 0px)"` + explanatory banner
175
- 3. `ForcedModal` / `ForcedDrawer`
176
- 4. `ModalDeck` — recursive push/pop (+ `update` title button)
177
- 5. `StackedDrawers` — nested right drawers, peek + content fade
178
- 6. `DrawerSides` — 4 side buttons
179
- 7. `DraggableDrawer` — `draggable`, `size "60vh"`
180
- 8. `CoercedStack` — drawer first, then `variant:"modal"` push renders as drawer
181
- 9. `DismissControl` — closeOnEscape/closeOnClickOutside/withCloseButton false combos
182
- 10. `LongHeader` — wrapping title+subtitle fixture
183
- 11. `Lifecycle` — visible onClose counters + closeAll
184
-
185
- ## Tests
186
-
187
- **Unit** (`tests/unit/Stacker/`): `reducer.test.ts` (open order/kind lock incl. all-closing stack/revive/idempotent close/remove resets kind/close-all/update), `helpers.test.ts` (`resolveKind` matrix; `drawerDepthStyle` 4 sides × depths), `drag.test.ts` (keep existing 4 `shouldDismiss` cases).
188
-
189
- **E2E** (`tests/e2e/Stacker/stacker.test.tsx`, composeStories + mountStory, selectors via `data-stacker*`):
190
-
191
- - E1 nothing mounts on render; E2 desktop auto → modal, page scale 1; E3 `MobileEmulated` → drawer + page scale < 0.99; E4 forced drawer on desktop; E5 kind coercion (2 sheets, 0 modals); E6 modal push keeps exactly ONE dialog + ONE scrim, old pane leaves DOM; E7 pop reveals previous, scrim stays mounted; E8 drawer peek geometry (lower sheet offset ≈ 12px) + lower body computed opacity 0; E9 page scale not compounding + returns to 1 after closeAll; E10 escape/scrim-click pop only top + respect opt-outs + focus returns into revealed drawer; E11 close button pinned with wrapping header; E12 drag-dismiss via synthetic PointerEvents (past 50% closes; 30px springs back); E13 onClose fires once, closeAll empties, kind resets after stack empties.
192
- - Delete stale `tests/e2e/Stacker/__screenshots__/`.
193
-
194
- ## Implementation sequence
195
-
196
- 1. `types/config/helpers/reducer` → unit tests green
197
- 2. `context` + `provider` (callbacks, kind decision, page shell)
198
- 3. Drawer path (`drawer-host`, `drawer-sheet`, `chrome`, SCSS, `use-drag-dismiss` port)
199
- 4. Modal path (`modal-host`, `modal-swap`, keyframes)
200
- 5. Stories rewrite; manual Storybook pass
201
- 6. E2E rewrite; delete stale screenshots
202
- 7. `AGENTS.md`; verify `src/index.ts` re-export
203
-
204
- ## Verification
205
-
206
- - `npx tsc --noEmit` clean (mind `isolatedDeclarations`)
207
- - `npm run qc` (oxlint + biome exhaustive-deps + oxfmt) clean
208
- - `npx vitest run --project=unit tests/unit/Stacker --project=e2e tests/e2e/Stacker --project=storybook src/stories/primitives/Stacker.stories.tsx` all green
209
- - Live check in Storybook via preview server (spawn on a free port; user's own runs on 6006): no auto-open on load; modal path (single dialog, swap animation, scrim persists, page never scales); drawer path (right default, peek + content fade, page scales once, drag-dismiss); `npm run build` rolls up types
210
-
211
- ## Key risks (mitigations built into design)
212
-
213
- - Transition inline styles on Content → transforms only on inner elements (2)
214
- - No initial-mount animation → two-phase mount everywhere (1)
215
- - Escape multi-close across drawer roots → closeOnEscape top-only (3)
216
- - `data-hidden`/`__hidden` hides sheet surface → never used; fade header/body instead (7)
217
- - Duplicate ARIA ids in swap → inert `live={false}` leaving pane
218
- - Focus tug-of-war on drawer pop → e2e asserts; fallback = delay trapFocus flip one frame or manual focus restore
219
- - Page transform breaks fixed descendants while drawers open → inherent vaul trade-off, no transform when idle, documented
package/release-notes.md DELETED
@@ -1,39 +0,0 @@
1
- Fenced code blocks tagged `tsx` or `jsx` rendered as plain, unhighlighted text in both the markdown viewer and the editor, and TypeScript blocks were silently parsed as plain JavaScript.
2
-
3
- ## Cause
4
-
5
- `src/syntax/language.ts` mapped both `javascript` and `typescript` to the bare `@lezer/javascript` parser. That grammar exposes `dialects: {jsx, ts}`, but Lezer dialects are opt-in and nothing configured them.
6
-
7
- Two consequences:
8
-
9
- - `tsx` and `jsx` were not registered as languages or aliases. `findLanguageDefinition` returned `null`, so `MarkdownCodeLines` fell back to rendering each line as a bare fragment, `createMarkdownLanguage` skipped embedded highlighting in the editor, and neither appeared in the fence language picker.
10
- - `ts` resolved, but to a JavaScript grammar. Type annotations and `interface` declarations parsed as errors.
11
-
12
- Measured against the installed parser:
13
-
14
- ```
15
- ### ts (interface + annotations)
16
- bare parser (before) error nodes: 2
17
- dialect: ts error nodes: 0
18
- ### tsx (JSX element)
19
- bare parser (before) error nodes: 4
20
- dialect: ts error nodes: 2
21
- dialect: jsx ts error nodes: 0
22
- ```
23
-
24
- ## Change
25
-
26
- - `javascript` now uses `dialect: "jsx"` and gains the `jsx` alias.
27
- - `typescript` now uses `dialect: "jsx ts"` and gains the `tsx` alias.
28
-
29
- Registering them as aliases rather than separate entries keeps the exported `Language` union and the fence language picker unchanged.
30
-
31
- Enabling `jsx` on plain JavaScript is safe. The grammar only treats `<` as a tag start where an expression may begin, so comparisons, generic calls, and shift operators parse with zero errors before and after.
32
-
33
- ## Tests
34
-
35
- Adds `tests/unit/Syntax/language.test.ts` covering registration, alias resolution, JSX and TypeScript parsing, and a regression guard that plain JavaScript comparison and shift operators are not read as JSX. Four of the five fail without the fix. Full unit suite passes at 274 tests, and the library builds.
36
-
37
- ## Impact
38
-
39
- Consumers get highlighting for `tsx` and `jsx` blocks, and correct highlighting for the `ts` blocks they already have. On docs.surrealdb.com that is 23 `tsx` and 2 `jsx` blocks currently unhighlighted, plus roughly 1,150 `ts` blocks that were being parsed as JavaScript.