@astryxdesign/core 0.4.3 → 0.4.4-canary.0f4d45f

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 (200) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +79 -45
  3. package/dist/Banner/Banner.d.ts +3 -0
  4. package/dist/Banner/Banner.d.ts.map +1 -1
  5. package/dist/Banner/Banner.js +13 -3
  6. package/dist/BottomSheet/BottomSheet.d.ts +66 -0
  7. package/dist/BottomSheet/BottomSheet.d.ts.map +1 -0
  8. package/dist/BottomSheet/BottomSheet.js +392 -0
  9. package/dist/BottomSheet/BottomSheetPanel.d.ts +60 -0
  10. package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -0
  11. package/dist/BottomSheet/BottomSheetPanel.js +398 -0
  12. package/dist/BottomSheet/BottomSheetSwitcher.d.ts +61 -0
  13. package/dist/BottomSheet/BottomSheetSwitcher.d.ts.map +1 -0
  14. package/dist/BottomSheet/BottomSheetSwitcher.js +410 -0
  15. package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts +21 -0
  16. package/dist/BottomSheet/BottomSheetSwitcherContext.d.ts.map +1 -0
  17. package/dist/BottomSheet/BottomSheetSwitcherContext.js +13 -0
  18. package/dist/BottomSheet/index.d.ts +11 -0
  19. package/dist/BottomSheet/index.d.ts.map +1 -0
  20. package/dist/BottomSheet/index.js +12 -0
  21. package/dist/BottomSheet/snapOffsets.d.ts +72 -0
  22. package/dist/BottomSheet/snapOffsets.d.ts.map +1 -0
  23. package/dist/BottomSheet/snapOffsets.js +130 -0
  24. package/dist/BottomSheet/useMobileKeyboard.d.ts +35 -0
  25. package/dist/BottomSheet/useMobileKeyboard.d.ts.map +1 -0
  26. package/dist/BottomSheet/useMobileKeyboard.js +373 -0
  27. package/dist/BottomSheet/useSheetGestures.d.ts +180 -0
  28. package/dist/BottomSheet/useSheetGestures.d.ts.map +1 -0
  29. package/dist/BottomSheet/useSheetGestures.js +989 -0
  30. package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
  31. package/dist/Chat/ChatComposerInput.js +3 -4
  32. package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
  33. package/dist/ComplexSelector/ComplexSelector.js +12 -6
  34. package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
  35. package/dist/ContextMenu/ContextMenu.js +4 -2
  36. package/dist/DateInput/DateInput.d.ts.map +1 -1
  37. package/dist/DateInput/DateInput.js +9 -1
  38. package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
  39. package/dist/DateTimeInput/DateTimeInput.js +31 -1
  40. package/dist/Dialog/Dialog.d.ts.map +1 -1
  41. package/dist/Dialog/Dialog.js +2 -1
  42. package/dist/Field/InputClearButton.d.ts +2 -1
  43. package/dist/Field/InputClearButton.d.ts.map +1 -1
  44. package/dist/Field/InputClearButton.js +6 -1
  45. package/dist/Lightbox/Lightbox.d.ts.map +1 -1
  46. package/dist/Lightbox/Lightbox.js +9 -3
  47. package/dist/MobileNav/MobileNav.d.ts +12 -0
  48. package/dist/MobileNav/MobileNav.d.ts.map +1 -1
  49. package/dist/MobileNav/MobileNav.js +102 -30
  50. package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
  51. package/dist/MultiSelector/MultiSelector.js +21 -7
  52. package/dist/NumberInput/NumberInput.d.ts.map +1 -1
  53. package/dist/NumberInput/NumberInput.js +9 -1
  54. package/dist/PowerSearch/PowerSearchEditPopover.js +1 -1
  55. package/dist/Selector/Selector.d.ts.map +1 -1
  56. package/dist/Selector/Selector.js +14 -3
  57. package/dist/StatusDot/StatusDot.d.ts +43 -3
  58. package/dist/StatusDot/StatusDot.d.ts.map +1 -1
  59. package/dist/StatusDot/StatusDot.js +42 -5
  60. package/dist/Switch/Switch.d.ts.map +1 -1
  61. package/dist/Switch/Switch.js +5 -1
  62. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
  63. package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +7 -2
  64. package/dist/TimeInput/TimeInput.d.ts.map +1 -1
  65. package/dist/TimeInput/TimeInput.js +8 -1
  66. package/dist/Tokenizer/Tokenizer.js +11 -5
  67. package/dist/Tooltip/useTooltip.d.ts.map +1 -1
  68. package/dist/Tooltip/useTooltip.js +4 -1
  69. package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
  70. package/dist/Typeahead/BaseTypeahead.js +5 -3
  71. package/dist/Typeahead/Typeahead.d.ts.map +1 -1
  72. package/dist/Typeahead/Typeahead.js +10 -1
  73. package/dist/astryx.css +21 -0
  74. package/dist/hooks/index.d.ts +8 -1
  75. package/dist/hooks/index.d.ts.map +1 -1
  76. package/dist/hooks/index.js +8 -1
  77. package/dist/hooks/useFocusTrap.d.ts +0 -11
  78. package/dist/hooks/useFocusTrap.d.ts.map +1 -1
  79. package/dist/hooks/useFocusTrap.js +1 -11
  80. package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
  81. package/dist/hooks/useInputStatusIcon.js +1 -0
  82. package/dist/index.d.ts +1 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +1 -0
  85. package/dist/theme/defineTheme.d.ts +22 -4
  86. package/dist/theme/defineTheme.d.ts.map +1 -1
  87. package/dist/theme/expandColorScale.d.ts +17 -2
  88. package/dist/theme/expandColorScale.d.ts.map +1 -1
  89. package/dist/theme/expandColorScale.js +63 -33
  90. package/dist/utils/ime.d.ts +5 -0
  91. package/dist/utils/ime.d.ts.map +1 -0
  92. package/dist/utils/ime.js +63 -0
  93. package/dist/utils/index.d.ts +1 -0
  94. package/dist/utils/index.d.ts.map +1 -1
  95. package/dist/utils/index.js +2 -1
  96. package/locales/af-ZA.json +958 -0
  97. package/locales/ar-SA.json +998 -0
  98. package/locales/ca-ES.json +974 -0
  99. package/locales/cs-CZ.json +990 -0
  100. package/locales/da-DK.json +966 -0
  101. package/locales/de-DE.json +966 -0
  102. package/locales/el-GR.json +990 -0
  103. package/locales/en.json +48 -0
  104. package/locales/es-ES.json +978 -0
  105. package/locales/fi-FI.json +990 -0
  106. package/locales/fr-FR.json +928 -0
  107. package/locales/he-IL.json +990 -0
  108. package/locales/hu-HU.json +990 -0
  109. package/locales/it-IT.json +978 -0
  110. package/locales/ja-JP.json +998 -0
  111. package/locales/ko-KR.json +990 -0
  112. package/locales/nl-NL.json +954 -0
  113. package/locales/no-NO.json +978 -0
  114. package/locales/pl-PL.json +982 -0
  115. package/locales/pseudo.json +36 -0
  116. package/locales/pt-BR.json +982 -0
  117. package/locales/pt-PT.json +982 -0
  118. package/locales/ro-RO.json +982 -0
  119. package/locales/ru-RU.json +990 -0
  120. package/locales/sr-SP.json +990 -0
  121. package/locales/sv-SE.json +986 -0
  122. package/locales/tr-TR.json +990 -0
  123. package/locales/uk-UA.json +990 -0
  124. package/locales/vi-VN.json +986 -0
  125. package/locales/zh-CN.json +998 -0
  126. package/locales/zh-TW.json +998 -0
  127. package/package.json +9 -8
  128. package/src/Banner/Banner.doc.mjs +3 -3
  129. package/src/Banner/Banner.test.tsx +40 -0
  130. package/src/Banner/Banner.tsx +27 -2
  131. package/src/BottomSheet/BottomSheet.doc.mjs +266 -0
  132. package/src/BottomSheet/BottomSheet.test.tsx +2114 -0
  133. package/src/BottomSheet/BottomSheet.tsx +545 -0
  134. package/src/BottomSheet/BottomSheetPanel.test.tsx +240 -0
  135. package/src/BottomSheet/BottomSheetPanel.tsx +547 -0
  136. package/src/BottomSheet/BottomSheetSwitcher.doc.mjs +219 -0
  137. package/src/BottomSheet/BottomSheetSwitcher.test.tsx +850 -0
  138. package/src/BottomSheet/BottomSheetSwitcher.tsx +628 -0
  139. package/src/BottomSheet/BottomSheetSwitcherContext.ts +51 -0
  140. package/src/BottomSheet/index.ts +15 -0
  141. package/src/BottomSheet/snapOffsets.test.ts +187 -0
  142. package/src/BottomSheet/snapOffsets.ts +159 -0
  143. package/src/BottomSheet/useMobileKeyboard.ts +499 -0
  144. package/src/BottomSheet/useSheetGestures.test.ts +1059 -0
  145. package/src/BottomSheet/useSheetGestures.ts +1459 -0
  146. package/src/Chat/ChatComposerInput.tsx +3 -4
  147. package/src/ComplexSelector/ComplexSelector.tsx +12 -5
  148. package/src/ContextMenu/ContextMenu.tsx +4 -2
  149. package/src/DateInput/DateInput.test.tsx +21 -0
  150. package/src/DateInput/DateInput.tsx +9 -1
  151. package/src/DateTimeInput/DateTimeInput.doc.mjs +4 -0
  152. package/src/DateTimeInput/DateTimeInput.test.tsx +137 -0
  153. package/src/DateTimeInput/DateTimeInput.tsx +39 -2
  154. package/src/Dialog/Dialog.tsx +2 -1
  155. package/src/Field/Field.doc.mjs +1 -0
  156. package/src/Field/InputClearButton.test.tsx +25 -0
  157. package/src/Field/InputClearButton.tsx +4 -1
  158. package/src/FileInput/FileInput.test.tsx +53 -9
  159. package/src/Lightbox/Lightbox.test.tsx +49 -28
  160. package/src/Lightbox/Lightbox.tsx +7 -3
  161. package/src/MobileNav/MobileNav.tsx +128 -37
  162. package/src/MobileNav/MobileNavCloseEdgeCases.test.tsx +426 -0
  163. package/src/MobileNav/MobileNavCloseTiming.test.ts +58 -0
  164. package/src/MobileNav/MobileNavCloseVisibility.test.tsx +172 -0
  165. package/src/MultiSelector/MultiSelector.test.tsx +195 -51
  166. package/src/MultiSelector/MultiSelector.tsx +22 -8
  167. package/src/NumberInput/NumberInput.test.tsx +18 -0
  168. package/src/NumberInput/NumberInput.tsx +9 -1
  169. package/src/PowerSearch/PowerSearchEditPopover.tsx +1 -1
  170. package/src/Selector/Selector.test.tsx +85 -26
  171. package/src/Selector/Selector.tsx +13 -4
  172. package/src/StatusDot/StatusDot.doc.mjs +13 -0
  173. package/src/StatusDot/StatusDot.test.tsx +115 -8
  174. package/src/StatusDot/StatusDot.tsx +74 -9
  175. package/src/Switch/Switch.test.tsx +16 -0
  176. package/src/Switch/Switch.tsx +7 -0
  177. package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +20 -2
  178. package/src/TimeInput/TimeInput.test.tsx +23 -0
  179. package/src/TimeInput/TimeInput.tsx +8 -0
  180. package/src/Tokenizer/Tokenizer.test.tsx +50 -32
  181. package/src/Tokenizer/Tokenizer.tsx +5 -5
  182. package/src/Tooltip/useTooltip.tsx +4 -1
  183. package/src/Typeahead/BaseTypeahead.tsx +10 -3
  184. package/src/Typeahead/Typeahead.test.tsx +116 -7
  185. package/src/Typeahead/Typeahead.tsx +10 -1
  186. package/src/__tests__/stubMatchMedia.test.ts +79 -0
  187. package/src/__tests__/stubMatchMedia.ts +95 -0
  188. package/src/hooks/index.ts +9 -5
  189. package/src/hooks/useFocusTrap.ts +1 -14
  190. package/src/hooks/useInputStatusIcon.test.tsx +19 -0
  191. package/src/hooks/useInputStatusIcon.tsx +7 -0
  192. package/src/index.ts +1 -0
  193. package/src/theme/defineTheme.ts +22 -4
  194. package/src/theme/expandColorScale.test.ts +138 -0
  195. package/src/theme/expandColorScale.ts +89 -44
  196. package/src/utils/ime.test.ts +32 -0
  197. package/src/utils/ime.ts +69 -0
  198. package/src/utils/index.ts +2 -0
  199. package/dist/astryx.umd.js +0 -147
  200. package/dist/astryx.umd.js.map +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,63 @@
1
1
  # @xds/core
2
2
 
3
+ # 0.4.4
4
+
5
+ #### New Components
6
+
7
+ - Promote `BottomSheet` and `BottomSheetSwitcher` from the canary-only Lab package to Core. The stable package now includes their existing native-dialog, drag-detent, transition, and mobile-keyboard behavior, plus Core documentation and examples (#5080).
8
+
9
+ #### New Features
10
+
11
+ - `astryx template --cdn` writes a working no-build-step CDN starter page (#5068).
12
+ A CDN starter is a template, so it joins the template family beside `--skeleton` rather than claiming a top-level command. It is a flag and not the positional `astryx template cdn` because the positional resolves against everything `discoverAll()` finds, where a `cdn` id would shadow a discovered template. `cdn.template.html` loads Astryx from jsDelivr and esm.sh with no bundler, no install and no build step, with every CDN URL pinned to the Astryx version you have installed — an unpinned CDN URL resolves to whatever is latest and is cached hard, so a page written today breaks tomorrow without being edited. An existing file is never clobbered; `--overwrite` replaces it, and `--json` returns the receipt.
13
+
14
+ The annotations are the things that are load-bearing and silent when missing: `?external=react,react-dom` (without it esm.sh bundles a second React and every hook throws `Cannot read properties of null (reading 'useState')`), `react/jsx-runtime` in the import map (the published bundle imports it; omitting it fails the page with `Failed to resolve module specifier`), and a `font-family` on `body` (nothing in the stylesheets sets a document font, so `Button` — which is `font: inherit` — otherwise renders its label in the browser's default serif).
15
+
16
+ Three more lessons came out of building a real app on it. The page now `<link>`s the theme's webfont from Google Fonts, because the theme _names_ Figtree and never loads it, so every viewer silently got the fallback stack (#5015 again). It imports the theme OBJECT and wraps in `<Theme theme={neutralTheme} mode="system">`, so light and dark follow the OS — the `data-astryx-theme` attribute alone scopes the stylesheet but cannot switch modes. And `#root:empty` carries a "Loading…" state, because ESM-from-CDN has real latency and a blank page reads as broken. Markup is `htm`, with a comment saying it is optional and `createElement` is the dependency-free alternative.
17
+
18
+ A recipe that is only read is a recipe that is only assumed to work, so CI renders it: `.github/scripts/cdn-template-smoke-test.mjs` scaffolds the page with the real CLI and opens it in headless Chromium, failing on any console error, page error or failed request, and on a page that loads without rendering.
19
+ - DateTimeInput: expose `date-time-input-toggle-icon` (calendar glyph, with open/closed `state`) and `date-time-input-clock-icon` (leading time glyph) theme targets, so a theme can size and color the leading icons — matching the `date-input-toggle-icon` seam DateInput already offers (#5148).
20
+ - `defineTheme`: `color.accent` accepts a `[light, dark]` tuple (#2279)
21
+ `ColorScaleConfig.accent` now takes either a single hex or a `[light, dark]` tuple, matching `TokenValue`. With a tuple, `expandColorScale` derives the light half of every generated `light-dark()` pair from the light seed's palettes and the dark half from the dark seed's, so each scheme gets a consistent derived palette (muted, on-accent, neutrals) instead of the `tokens['--color-accent']` workaround that skips scale generation. Single-string configs are unchanged, token for token. Also documents the precedence between `color` and `tokens` for accent-derived values: `tokens` entries win token by token, the `var(--color-accent)` reference tokens follow a `--color-accent` override at runtime, and the baked `--color-on-accent` stays derived from the `color.accent` seed.
22
+
23
+ #### Fixes
24
+
25
+ - Banner: `endContent` wraps to its own row on a narrow header instead of squeezing the title to one word per line (#5116).
26
+ - BottomSheet: a swipe that scrolls to the end of the sheet's content and keeps pulling now expands the sheet, instead of stopping dead at the last line. The handoff used to be decided once, when the finger landed: a gesture that started mid-content stayed a scroll for its whole life, so the natural motion — swipe up through the list, reach the bottom, keep pulling — never reached the sheet. Reaching the end of the content is now enough. The sheet is anchored at the point where the content ran out, so only the travel past it moves the sheet, and the pull is left to the content when the finger comes back down or when there is no taller detent to expand into (#5172).
27
+ - BottomSheet: an upward pull at the bottom of scrolled content no longer hijacks the gesture when the sheet is already fully expanded. It used to hand off to a sheet drag with nowhere to expand to, producing a rubber-band the release threw straight back, and — because the handoff swallows the rest of the gesture — leaving the content unscrollable until the finger lifted, so dragging back down collapsed the sheet instead of scrolling. The bottom edge now hands off only when a taller detent exists (#5161).
28
+ - BottomSheet: a sheet resting at a detent now follows the viewport. Its detents were resolved to pixels at gesture time and never revisited, so rotating the device or resizing the window left the sheet frozen at the old geometry — a half-height sheet covering three quarters of a shorter window, and a peek detent whose slide-down could exceed the new viewport entirely, leaving a modal dialog on screen with no sheet in it. Snap fractions are also read from the layout viewport now, so the mobile keyboard no longer moves the detents out from under the sheet it is measuring (#5159).
29
+ - BottomSheet keeps the page still when the mobile keyboard reveals a field the browser focused itself (#5158)
30
+ - Screen-reader announcements are now localizable. MultiSelector, Selector, Typeahead, FileInput, Tokenizer, and Lightbox spoke several live-region messages in hardcoded English — selection and result counts, file selections, token add/remove, and gallery position — so they stayed English under an `InternationalizationProvider`. They now resolve through the message catalog like the rest of the UI, and the counts use ICU plurals instead of appending an English "s", so locales with other plural rules read correctly (#4920).
31
+ - The editable text fields in `Selector`, `MultiSelector`, `Typeahead`, `DateInput`, `DateTimeInput`, `TimeInput`, and `NumberInput` no longer misinterpret the keydown that commits or cancels an IME composition (Korean/Japanese/Chinese input) as a command. Previously a composing Enter would select/toggle the highlighted option or commit a typed date, a composing Escape would exit `Typeahead`'s edit mode, and a composing arrow would step a time or number value — all before the composition finished. Each field now lets the IME finish first, matching the guard already in place for `BaseTypeahead` and the Chat composer (#4908).
32
+ - MobileNav: keep the drawer rendered until the native dialog has actually closed (#4290)
33
+ `display` was driven by the `isOpen` prop, which flips during the commit, while `dialog.close()` only ran afterwards from an effect — so every close called `close()` on a dialog that was already `display: none` but still open and still in the top layer, and an open modal dialog blocks the whole document whether or not it is rendered. Safari 26.1 never un-blocked it, leaving the page inert with no JavaScript error. `display` now takes part in the transition with `transition-behavior: allow-discrete`, including when React's `<Activity mode="hidden">` hides the drawer inside AppShell, and the unmount close moves into its own effect so the deferred close is no longer cut off by its own cleanup. The close delay is derived from the hold in effect rather than assumed, because that hold is `--duration-medium` — a theme value, which the shipped y2k theme sets to exactly the 250ms the delay used to hard-code.
34
+ - `Switch` with `isLabelHidden` no longer reserves the label gap. The hidden label is `sr-only`, but its wrapper stayed a flex item, so the row still painted the 8px gap beside it: the field box measured 8px wider than the track it contains, and a hidden-label switch stopped 8px inside the edge every neighbouring control lined up on. The gap now collapses with the label, so the field is exactly as wide as the painted track — matching `CheckboxInput`, which already did this (#5112).
35
+ - Inputs (`statusVariant="tooltip"`): the focusable status button now opens its tooltip on hover inside `TextArea`, whose absolutely-positioned trailing slot is `pointer-events: none`. Keyboard focus already worked; pointer hover did not (#5147).
36
+
37
+ #### Other Changes
38
+
39
+ - Clear the mechanically fixable ESLint suppressions from the Bottom Sheet promotion: `BottomSheet` and `BottomSheetSwitcher` now use the React 19 context APIs (`<Context>` as provider, `use()`), the panel drops its duplicate body-element ref in favor of the one the gesture hook already tracks, and `useSheetGestures` reads `prefers-reduced-motion` through the shared `useMediaQuery` subscription so an open sheet follows a preference change (#5155).
40
+ - Remove the UMD bundle — it could not work with any React this package supports (#5068).
41
+ `dist/astryx.umd.js` is no longer built or published, and with it go the `unpkg` and `jsdelivr` package fields, the `./astryx.umd.js` export and the `build:umd` step.
42
+
43
+ Nobody has a migration to make, because there was no working configuration to migrate from. The bundle binds Astryx to `window.React` and `window.ReactDOM`, and React 19 does not ship a build that defines them: "UMD builds removed: To load React 19 with a script tag, we recommend using an ESM-based CDN such as esm.sh." `https://unpkg.com/react@19.2.0/umd/react.production.min.js` is a 404 where 18.3.1 is a 200. Our `peerDependencies` are `react >= 19.0.0`, so every supported React is one without a global for the bundle to bind to — it documented a path that never had an entrance.
44
+
45
+ If you were loading it with an older React anyway, load the same components as modules instead: an import map for `react`, `react/jsx-runtime`, `react-dom`, `react-dom/client` and `@astryxdesign/core` (pinned, with `?external=react,react-dom`), then one `<script type="module">`. `astryx template --cdn` writes that page for you, pinned to your installed version and annotated; the recipe is also in the core README under "No build step (CDN)".
46
+ - `isImeKeyEvent` — the guard that stops an IME composition keystroke being read as a command — now lives at `@astryxdesign/core/utils` alongside the other pure helpers, with the reasoning for its two signals written down in one place. It stays exported from `@astryxdesign/core/hooks` for this release but is deprecated there: it is a plain predicate, not a hook, and that barrel is a `'use client'` boundary, so importing it from `hooks` pulls a server-safe function onto a client path. Move imports to `@astryxdesign/core/utils`; the `hooks` re-export will be removed in an upcoming major (#4907).
47
+
48
+ #### Contributors
49
+
50
+ Thanks to everyone who contributed to this release:
51
+
52
+ - @AKnassa
53
+ - @cixzhang
54
+ - @freddymeta
55
+ - @imdreamrunner
56
+ - @jiunshinn
57
+ - @nynexman4464
58
+
59
+ ---
60
+
3
61
  # 0.4.3
4
62
 
5
63
  #### New Features
@@ -20,12 +78,14 @@
20
78
  Under `@astryxdesign/theme-neutral` the info banner had no background at all, light or dark: the override set `background-color` directly and forced `--color-accent-muted` to `transparent`, and a plain CSS property written by a theme lands in `@layer astryx-theme`, which StyleX's `@layer priority4` outranks. Info now goes through `--color-accent-muted` like the other three statuses and like the stone theme already did.
21
79
 
22
80
  Also in this change: `children={false}` (the ordinary `{cond && <ul/>}` idiom) no longer produces an expand toggle that opens an empty box, and `description=""` no longer leaves an empty 20px row, both via `isRenderable`; a long unbroken word in the title or description no longer forces the page into horizontal scrolling at a 320px viewport, measured at `document.scrollWidth` 529px before; and the content area's bottom border uses logical `border-block-end` alongside its inline siblings.
81
+
23
82
  - Count and cut text the way people read it: the TextArea character counter (and its over-limit state and screen-reader announcements) counts user-perceived characters — an emoji is 1, not 2; PowerSearch token truncation no longer cuts an emoji or accented letter in half; Table's auto-generated headers capitalize astral-plane letters correctly; Avatar's initials now use the shared character utilities.
24
83
  - ComplexSelector: honor the `sm`, `md`, and `lg` element-height tokens exactly.
25
84
  - TreeList's `variant` axis is themeable, and a new guard keeps every extensible axis honest. `TreeListVariantMap` invites theme packages to add variants — its own JSDoc shows the module augmentation — but `themeProps('tree-list', {density})` never passed `variant`, so a custom variant type-checked, rendered, and produced no selector to style. It is passed now, and documented in the target's `visualProps` so `astryx theme build` stops calling it an unknown prop.
26
85
  `packages/core/src/theme/extensibleAxes.test.ts` is the third theming-drift guard, beside the ones covering `targets` and `vars`/`derived`. Those two check what a component renders against what it documents; neither looked at the open prop unions, which is why this went unnoticed. For every `*Map` that types a component prop, it now asserts the three places that have to agree: the interface is declared in the index a consumer augments (a re-export is invisible to both module augmentation and the CLI), the prop is reflected through `themeProps`, and it is documented as a visual prop. It reads the TypeScript AST rather than the type checker, and holds the map's OWNER accountable — a component forwarding `actionVariant` or `statusVariant` to the component that owns the map is not separately responsible for it.
27
86
 
28
87
  Registry maps that widen a set of NAMES rather than a visual prop (`IndicatorMap`, `IndicatorFamilyMap`) are out of scope by construction, not by allowlist: the guard only considers maps whose alias types a prop on a `*Props` interface.
88
+
29
89
  - Security: reject `javascript:`, `vbscript:` and `data:text/html` URLs in the Markdown parser, so untrusted markdown can no longer produce an executable link href or image src; and fix `escapeRegExp` in `ChatTokenizedText`, whose character class closed early and left `]` and `\` unescaped, so token values containing them were injected raw into a `RegExp`
30
90
  - `extends` now reaches the CSS. A theme that extended another built a stylesheet holding only the declarations it stated itself: the base's tokens, component overrides and surface rules were all absent, and because each theme is `@scope`d to its own `data-astryx-theme` value, loading the base's stylesheet alongside could not fill the gap either. Every consumer of an inheritance chain silently got stock geometry, elevation and type with a new palette painted over it (#5067). Nothing warned; the loss only showed up by diffing two generated stylesheets token by token.
31
91
  The cause was `theme build` shadowing its own inputs. It writes `<name>.js` next to `<name>.ts`, and the loader resolved a plain `./<name>` specifier to that generated artifact before the source — so the second build of a family read the artifact, which carries no `components` and exports `<name>Theme` rather than whatever the source exports. A named import that missed became `extends: undefined`, and `defineTheme` treated an absent base as no base at all. The loader now resolves source extensions first, which is also the resolution the author's TypeScript sees, so the CSS a build emits matches the theme that type-checked.
package/README.md CHANGED
@@ -252,67 +252,101 @@ Same CSS imports and providers as above. No build plugins needed; Astryx ships p
252
252
  ### No build step (CDN)
253
253
 
254
254
  For prototypes, embeds, or pages without a bundler, load the components straight
255
- from a public CDN. Two delivery options ship in the published package:
255
+ from a public CDN as ES modules. React 19 removed its UMD builds ("To load React
256
+ 19 with a script tag, we recommend using an ESM-based CDN such as esm.sh"), so an
257
+ import map is the way in — there is no `window.React` left for a global bundle to
258
+ bind to.
256
259
 
257
- **1. UMD global (`<script>` tag).** A single pre-bundled file exposes every export
258
- on `window.Astryx`. React and ReactDOM are peer dependencies — load them yourself.
259
- Pair it with the precompiled stylesheet.
260
+ The CLI writes this page for you, annotated and pinned to the version you have
261
+ installed:
262
+
263
+ ```bash
264
+ npx astryx template --cdn # writes cdn.template.html
265
+ ```
260
266
 
261
267
  ```html
262
268
  <!doctype html>
263
- <html data-astryx-theme="neutral">
269
+ <html lang="en" data-astryx-theme="neutral">
264
270
  <head>
271
+ <meta charset="utf-8" />
272
+ <link
273
+ rel="stylesheet"
274
+ href="https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap" />
275
+ <link
276
+ rel="stylesheet"
277
+ href="https://cdn.jsdelivr.net/npm/@astryxdesign/core@0.4.1/src/reset.css" />
265
278
  <link
266
279
  rel="stylesheet"
267
- href="https://cdn.jsdelivr.net/npm/@astryxdesign/core/src/reset.css" />
280
+ href="https://cdn.jsdelivr.net/npm/@astryxdesign/core@0.4.1/dist/astryx.css" />
268
281
  <link
269
282
  rel="stylesheet"
270
- href="https://cdn.jsdelivr.net/npm/@astryxdesign/core/dist/astryx.css" />
283
+ href="https://cdn.jsdelivr.net/npm/@astryxdesign/theme-neutral@0.4.1/dist/theme.css" />
284
+ <script type="importmap">
285
+ {
286
+ "imports": {
287
+ "react": "https://esm.sh/react@19.2.0",
288
+ "react/jsx-runtime": "https://esm.sh/react@19.2.0/jsx-runtime",
289
+ "react-dom": "https://esm.sh/react-dom@19.2.0",
290
+ "react-dom/client": "https://esm.sh/react-dom@19.2.0/client",
291
+ "@astryxdesign/core": "https://esm.sh/@astryxdesign/core@0.4.1?external=react,react-dom"
292
+ }
293
+ }
294
+ </script>
295
+ <style>
296
+ body {
297
+ font-family: var(--font-family-body);
298
+ }
299
+ </style>
271
300
  </head>
272
301
  <body>
273
302
  <div id="root"></div>
274
- <script
275
- crossorigin
276
- src="https://unpkg.com/react@19/umd/react.production.min.js"></script>
277
- <script
278
- crossorigin
279
- src="https://unpkg.com/react-dom@19/umd/react-dom.production.min.js"></script>
280
- <script src="https://cdn.jsdelivr.net/npm/@astryxdesign/core/dist/astryx.umd.js"></script>
281
- <script>
282
- const {Button, Card} = window.Astryx;
303
+ <script type="module">
304
+ import * as React from 'react';
305
+ import {createRoot} from 'react-dom/client';
306
+ import {Card, Stack, Heading, Text, Button} from '@astryxdesign/core';
283
307
  const e = React.createElement;
284
- ReactDOM.createRoot(document.getElementById('root')).render(
285
- e(Card, null, e(Button, {variant: 'primary'}, 'Hello from a CDN')),
308
+ createRoot(document.getElementById('root')).render(
309
+ e(
310
+ Stack,
311
+ {padding: 6, align: 'start'},
312
+ e(
313
+ Card,
314
+ {maxWidth: 480, elevation: 'low'},
315
+ e(
316
+ Stack,
317
+ {gap: 3, align: 'start'},
318
+ e(Heading, {level: 1}, 'Hello from a CDN'),
319
+ e(Text, null, 'No bundler, no install, no build step.'),
320
+ e(Button, {variant: 'primary', label: 'Try me'}),
321
+ ),
322
+ ),
323
+ ),
286
324
  );
287
325
  </script>
288
326
  </body>
289
327
  </html>
290
328
  ```
291
329
 
292
- **2. ES modules (no UMD, no globals).** Use [esm.sh](https://esm.sh), which rewrites
293
- bare imports to browser-resolvable URLs. An import map keeps a single React instance.
294
-
295
- ```html
296
- <link
297
- rel="stylesheet"
298
- href="https://cdn.jsdelivr.net/npm/@astryxdesign/core/dist/astryx.css" />
299
- <script type="importmap">
300
- {
301
- "imports": {
302
- "react": "https://esm.sh/react@19",
303
- "react-dom/client": "https://esm.sh/react-dom@19/client",
304
- "@astryxdesign/core": "https://esm.sh/@astryxdesign/core?external=react,react-dom"
305
- }
306
- }
307
- </script>
308
- <script type="module">
309
- import {createRoot} from 'react-dom/client';
310
- import {Button} from '@astryxdesign/core';
311
- // ...render as usual
312
- </script>
313
- ```
314
-
315
- > Pin a version in production (e.g. `@astryxdesign/core@0.1.1`) — unversioned CDN URLs
316
- > resolve to the latest release and are cached aggressively. The raw ESM entry
317
- > (`dist/index.js`) uses bare `react` imports and will **not** run from a plain
318
- > `<script src>`; use the UMD global or esm.sh as shown above.
330
+ Six details carry the whole recipe:
331
+
332
+ - **`data-astryx-theme` on `<html>`.** Theme CSS is scoped to that attribute, so
333
+ without it the page renders with the built-in defaults instead of the theme you
334
+ just loaded.
335
+ - **`?external=react,react-dom`.** Without it esm.sh bundles its own React and
336
+ every hook throws `Cannot read properties of null (reading 'useState')`.
337
+ - **`react/jsx-runtime` in the import map.** The published bundle imports it;
338
+ omit the entry and the page dies with `Failed to resolve module specifier`.
339
+ - **A font on `body`.** Nothing in the three stylesheets sets a document font:
340
+ the theme styles prose elements and each component styles itself, and `Button`
341
+ is `font: inherit`. Without that one declaration its label renders in the
342
+ browser's default serif.
343
+ - **The webfont itself.** The theme _names_ Figtree; it never loads it. Without
344
+ the Google Fonts link every viewer silently gets the next family in the stack.
345
+ - **No JSX.** Nothing is compiling this file, so elements are created with
346
+ `React.createElement`.
347
+
348
+ > Pin every version. Unversioned CDN URLs resolve to the latest release and are
349
+ > cached aggressively (0.4.1 above is a real pin; `astryx template --cdn` writes
350
+ > yours). The raw ESM entry (`dist/index.js`) uses bare `react` imports and will
351
+ > not run from a plain `<script src>` — the import map is what makes those
352
+ > specifiers resolvable.
@@ -85,6 +85,9 @@ export interface BannerProps extends BaseProps<HTMLDivElement> {
85
85
  * Action button rendered in the header area (end-aligned).
86
86
  * Typically an Button with a secondary or ghost variant.
87
87
  *
88
+ * When the header is too narrow to hold the actions and a readable text
89
+ * column, the whole end area wraps to its own row below the text.
90
+ *
88
91
  * @example
89
92
  * ```
90
93
  * endContent={<Button label="Retry" variant="ghost" onClick={handleRetry} />}
@@ -1 +1 @@
1
- {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAI9C,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAiMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,KAAK,EACL,WAAW,EACX,IAAI,EACJ,aAAqB,EACrB,SAAS,EACT,UAAU,EACV,SAAkB,EAClB,SAAkB,EAClB,iBAAyB,EACzB,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,GAAG,IAAI,EACR,EAAE,WAAW,sCA2Mb;yBA9Ne,MAAM"}
1
+ {"version":3,"file":"Banner.d.ts","sourceRoot":"","sources":["../../src/Banner/Banner.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAA0B,KAAK,SAAS,EAAC,MAAM,OAAO,CAAC;AAE9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAgB5C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAC;AAI9C,OAAO,KAAK,EAAC,eAAe,EAAE,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAMjE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,eAAe,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,WAAY,SAAQ,SAAS,CAAC,cAAc,CAAC;IAC5D,wCAAwC;IACxC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAChC;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;OAEG;IACH,KAAK,EAAE,SAAS,CAAC;IACjB;;OAEG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAmND;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,MAAM,CAAC,EACrB,MAAM,EACN,KAAK,EACL,WAAW,EACX,IAAI,EACJ,aAAqB,EACrB,SAAS,EACT,UAAU,EACV,SAAkB,EAClB,SAAkB,EAClB,iBAAyB,EACzB,QAAQ,EACR,MAAM,EACN,SAAS,EACT,KAAK,EACL,GAAG,EACH,cAAc,EACd,oBAAoB,EACpB,GAAG,IAAI,EACR,EAAE,WAAW,sCA+Mb;yBAlOe,MAAM"}
@@ -115,7 +115,9 @@ const styles = {
115
115
  header: {
116
116
  k1xSpc: "x78zum5",
117
117
  kGNEyG: "x1cy8zhl",
118
- kOIVth: "x1txdalj",
118
+ kwnvtZ: "x1a02dak",
119
+ k1C7PZ: "xak3so",
120
+ khm7nJ: "x4olc9o",
119
121
  k8WAf4: "x8o8v82",
120
122
  kg3NbH: "x1pzlopt",
121
123
  $$css: true
@@ -138,8 +140,11 @@ const styles = {
138
140
  endArea: {
139
141
  k1xSpc: "x78zum5",
140
142
  kGNEyG: "x6s0dn4",
143
+ kwnvtZ: "x1a02dak",
144
+ kjj79g: "x13a6bvl",
141
145
  kOIVth: "x1txdalj",
142
146
  kmuXW: "x2lah0s",
147
+ ks0D6T: "x193iq5w",
143
148
  keTefX: "xvc5jky",
144
149
  kqGvvJ: "x81ka23",
145
150
  $$css: true
@@ -369,8 +374,13 @@ export function Banner({
369
374
  }) : null
370
375
  }), /*#__PURE__*/_jsxs("div", {
371
376
  ...{
372
- className: "x78zum5 xdt5ytf xxhr3t x98rzlu xeuugli"
373
- },
377
+ 0: {
378
+ className: "x78zum5 xdt5ytf xxhr3t x98rzlu xeuugli"
379
+ },
380
+ 1: {
381
+ className: "x78zum5 xdt5ytf xxhr3t x98rzlu xeuugli xgualba"
382
+ }
383
+ }[!!isRenderable(endContent) << 0],
374
384
  children: [/*#__PURE__*/_jsx("div", {
375
385
  ...{
376
386
  className: "x1ghz6dp xjb2p0i xcr08ib x2mo6ok x1kq96og x1tgivj0 xj0a0fe"
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @file BottomSheet.tsx
3
+ * @input Uses React, StyleX, core hooks/utils, BottomSheetPanel, BottomSheetSwitcherContext
4
+ * @output Exports BottomSheet component and BottomSheetProps
5
+ * @position Public BottomSheet router plus private standalone/switcher hosts
6
+ *
7
+ * BottomSheet selects one of two focused hosts. A standalone host owns its
8
+ * native dialog lifecycle; a switcher item participates in the parent's shared
9
+ * dialog and transition state machine. Both render the same BottomSheetPanel,
10
+ * which owns sheet presentation, gestures, mobile-keyboard accommodation, and
11
+ * motion completion.
12
+ *
13
+ * SYNC: When modified, update these files to stay in sync:
14
+ * - /packages/core/src/BottomSheet/BottomSheetPanel.tsx
15
+ * - /packages/core/src/BottomSheet/BottomSheet.doc.mjs
16
+ * - /packages/core/src/BottomSheet/BottomSheet.test.tsx
17
+ * - /packages/core/src/BottomSheet/BottomSheetSwitcher.tsx
18
+ * - /packages/core/src/BottomSheet/BottomSheetSwitcher.test.tsx
19
+ * - /apps/storybook/stories/BottomSheet.stories.tsx
20
+ * - /packages/cli/assets/templates/blocks/components/BottomSheet/BottomSheetShowcase.tsx
21
+ */
22
+ import { type ReactNode } from 'react';
23
+ import type { BaseProps } from '../BaseProps';
24
+ import type { DialogPurpose } from '../Dialog';
25
+ export type { BottomSheetHeight } from './BottomSheetPanel';
26
+ import type { BottomSheetHeight } from './BottomSheetPanel';
27
+ interface BottomSheetSharedProps extends BaseProps<HTMLDivElement> {
28
+ /** Ref forwarded to the visual sheet panel <div>. */
29
+ ref?: React.Ref<HTMLDivElement>;
30
+ /** Accessible label for the sheet. */
31
+ label: string;
32
+ /** Sheet content, rendered below the grab handle in a scrollable area. */
33
+ children: ReactNode;
34
+ /** Height budget or custom CSS length. Only fully expanded Tall is keyboard-aware. @default 'capped' */
35
+ height?: BottomSheetHeight | number | string;
36
+ /**
37
+ * Configures implicit dismissal behavior, matching Dialog.
38
+ * - required: Blocks swipe, scrim click, and Escape
39
+ * - form: Blocks swipe and scrim click, allows Escape
40
+ * - info: Allows swipe, scrim click, and Escape
41
+ * @default 'info'
42
+ */
43
+ purpose?: DialogPurpose;
44
+ }
45
+ interface StandaloneBottomSheetProps extends BottomSheetSharedProps {
46
+ isOpen: boolean;
47
+ onOpenChange: (isOpen: boolean) => void;
48
+ hasScrim?: boolean;
49
+ sheetId?: never;
50
+ }
51
+ interface SwitcherBottomSheetProps extends BottomSheetSharedProps {
52
+ sheetId: string;
53
+ isOpen?: never;
54
+ onOpenChange?: never;
55
+ hasScrim?: never;
56
+ }
57
+ export type BottomSheetProps = StandaloneBottomSheetProps | SwitcherBottomSheetProps;
58
+ /**
59
+ * A mobile touch sheet that either owns a native dialog or participates in a
60
+ * BottomSheetSwitcher shared dialog when given a sheetId inside that context.
61
+ */
62
+ export declare function BottomSheet(props: BottomSheetProps): import("react").JSX.Element;
63
+ export declare namespace BottomSheet {
64
+ var displayName: string;
65
+ }
66
+ //# sourceMappingURL=BottomSheet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BottomSheet.d.ts","sourceRoot":"","sources":["../../src/BottomSheet/BottomSheet.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAc7C,YAAY,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AA2D1D,UAAU,sBAAuB,SAAQ,SAAS,CAAC,cAAc,CAAC;IAChE,qDAAqD;IACrD,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAEhC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IAEd,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,CAAC;IAEpB,wGAAwG;IACxG,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAAC;IAE7C;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,UAAU,0BAA2B,SAAQ,sBAAsB;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC;CACjB;AAED,UAAU,wBAAyB,SAAQ,sBAAsB;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,YAAY,CAAC,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC;CAClB;AAED,MAAM,MAAM,gBAAgB,GAC1B,0BAA0B,GAAG,wBAAwB,CAAC;AAwWxD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BA6BlD;yBA7Be,WAAW"}