@signal9/era-ui 4.8.0 → 4.9.1

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 (49) hide show
  1. package/dist/ai/swarm/index.d.ts +2 -1
  2. package/dist/ai/swarm/index.js +2 -1
  3. package/dist/ai/swarm/swarm-actions.svelte +86 -0
  4. package/dist/ai/swarm/swarm-actions.svelte.d.ts +31 -0
  5. package/dist/ai/swarm/swarm-agent.svelte +9 -43
  6. package/dist/ai/swarm/swarm-artifact.svelte +2 -6
  7. package/dist/ai/swarm/swarm-artifacts.svelte +1 -10
  8. package/dist/ai/swarm/swarm-artifacts.svelte.d.ts +0 -2
  9. package/dist/ai/swarm/swarm-barriers.svelte +39 -0
  10. package/dist/ai/swarm/swarm-barriers.svelte.d.ts +13 -0
  11. package/dist/ai/swarm/swarm-counts.svelte +1 -4
  12. package/dist/ai/swarm/swarm-counts.svelte.d.ts +0 -2
  13. package/dist/ai/swarm/swarm-footer.svelte +20 -13
  14. package/dist/ai/swarm/swarm-header.svelte +3 -5
  15. package/dist/ai/swarm/swarm-node.svelte +8 -64
  16. package/dist/ai/swarm/swarm-role.svelte +2 -9
  17. package/dist/ai/swarm/swarm-sandbox.svelte +45 -0
  18. package/dist/ai/swarm/swarm-sandbox.svelte.d.ts +10 -0
  19. package/dist/ai/swarm/swarm-tree.svelte +22 -8
  20. package/dist/ai/swarm/swarm.d.ts +8 -0
  21. package/dist/ai/swarm/swarm.js +11 -2
  22. package/dist/ai/swarm/swarm.svelte.d.ts +0 -1
  23. package/dist/ai/swarm/swarm.svelte.js +10 -12
  24. package/dist/apps/llm-shell/llm-shell.svelte +2 -36
  25. package/dist/apps/llm-shell/llm-swarm.svelte +64 -0
  26. package/dist/apps/llm-shell/llm-swarm.svelte.d.ts +23 -0
  27. package/dist/apps/llm-shell/types.d.ts +2 -0
  28. package/dist/apps/notes/editor/bubble-toolbar.svelte +2 -1
  29. package/dist/apps/notes/notes.svelte +9 -18
  30. package/dist/dev/audit/audits/clipped-glyphs.js +33 -11
  31. package/dist/docs/llms-txt.js +1 -1
  32. package/dist/era-ui.css +1 -1
  33. package/dist/generated-docs/bar.md +3 -0
  34. package/dist/generated-docs/llms-full.txt +12 -5
  35. package/dist/generated-docs/llms.txt +1 -1
  36. package/dist/generated-docs/manifest.json +4 -4
  37. package/dist/generated-docs/utilities.json +2 -2
  38. package/dist/generated-docs/utilities.md +8 -4
  39. package/dist/os/pane.svelte +12 -17
  40. package/dist/styles/density.css +42 -9
  41. package/dist/styles/index.css +8 -4
  42. package/dist/ui/bar/bar.svelte +37 -3
  43. package/dist/ui/bar/bar.svelte.d.ts +61 -1
  44. package/dist/ui/pane/pane-close.svelte +11 -16
  45. package/dist/ui/pane/pane-handle.svelte +16 -9
  46. package/dist/ui/pane/pane-root.svelte +8 -6
  47. package/dist/ui/pane/pane-tab-list.svelte +4 -3
  48. package/dist/ui/pane/pane-tab.svelte +1 -1
  49. package/package.json +1 -1
@@ -15,7 +15,7 @@ Named exports wrap a single primitive (`Button`, `Badge`, `Chip`, `Input`, …).
15
15
 
16
16
  Five orthogonal axes are exposed as HTML attributes on any ancestor. The entire subtree re-tiers through CSS inheritance — no JS, no context provider.
17
17
 
18
- - `data-mode`: **dense** (default) · balanced · spacious · touch — adjusts the spacing atom (`--era-sp`).
18
+ - `data-mode`: **dense** (default) · balanced · spacious · touch — adjusts the spacing atom (`--era-sp`) and the type size (`--era-text`: 12px at dense, 14px above).
19
19
  - `data-surface`: **flat** (default) · glass · bevel — swaps the background treatment.
20
20
  - `data-corners`: **rounded** (default) · square — scales all radii toward 0.
21
21
  - `data-font`: **mono** (default) · sans · serif — switches the font stack.
@@ -249,7 +249,9 @@ centering the x-height band — the visual mass of MIXED-CASE text (caps and
249
249
  descenders cancel above/below). cap alphabetic is the spec-example recipe
250
250
  but is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.
251
251
  Where text-box is supported it fully determines the box, so the two
252
- declarations never fight. Worst-case residual: 0.44px.
252
+ declarations never fight. Worst-case residual: 0.44px (measured at 14px; the
253
+ mechanism is font-relative, so dense's 12px inherits the same guarantee with
254
+ a proportionally smaller residual).
253
255
 
254
256
  The hard rules concern overflow clipping, and there are TWO of them.
255
257
 
@@ -371,9 +373,11 @@ The class also owns `overflow: hidden` — animating height without it just
371
373
  spills the content out of the shrinking box.
372
374
 
373
375
  Timing rides the motion axis, so `instant` collapses it to a cut (0s) and
374
- `extra` stretches it, for free. Three components had a byte-identical copy of
375
- these keyframes under three different names before this existed; the panel
376
- that needs different motion should override `animation`, not re-declare it.
376
+ `extra` stretches it, for free. Two components carried a byte-identical copy of
377
+ these keyframes under two different names before this existed (a Step's body
378
+ and the error panel's detail), and the swarm tree would have made a third; the
379
+ panel that needs different motion should override `animation`, not re-declare
380
+ it.
377
381
 
378
382
  Declared in `index.css`.
379
383
 
@@ -908,6 +912,9 @@ Inherits all props from `HTMLAttributes<HTMLDivElement>`.
908
912
  |------|------|---------|-------|
909
913
  | `ref?` | `HTMLDivElement \| null` | `null` | bindable |
910
914
  | `size?` | `'sm' \| 'md' \| 'lg'` | `'lg'` | Height tier (scales with density). Default `lg` — a full toolbar. |
915
+ | `content?` | `'tier' \| 'xxs'` | `'tier'` | Tier of the controls inside — drives the bar's padding and gap so they
916
+ stay concentric to them. `tier` (default) is one tier smaller; `xxs` is
917
+ the compact pairing (xxs pills/fields in an sm or md bar). |
911
918
  | `children?` | `Snippet` | — | — |
912
919
 
913
920
  <!-- end: bar -->
@@ -15,7 +15,7 @@ Named exports wrap a single primitive (`Button`, `Badge`, `Chip`, `Input`, …).
15
15
 
16
16
  Five orthogonal axes are exposed as HTML attributes on any ancestor. The entire subtree re-tiers through CSS inheritance — no JS, no context provider.
17
17
 
18
- - `data-mode`: **dense** (default) · balanced · spacious · touch — adjusts the spacing atom (`--era-sp`).
18
+ - `data-mode`: **dense** (default) · balanced · spacious · touch — adjusts the spacing atom (`--era-sp`) and the type size (`--era-text`: 12px at dense, 14px above).
19
19
  - `data-surface`: **flat** (default) · glass · bevel — swaps the background treatment.
20
20
  - `data-corners`: **rounded** (default) · square — scales all radii toward 0.
21
21
  - `data-font`: **mono** (default) · sans · serif — switches the font stack.
@@ -44,7 +44,7 @@
44
44
  "slug": "utilities",
45
45
  "title": "Utilities",
46
46
  "summary": "The CSS classes era ships alongside the components — links, ink-centred text, shimmer, hidden scrollbars.",
47
- "tokenEstimate": 5277,
47
+ "tokenEstimate": 5333,
48
48
  "sections": [
49
49
  "Overview",
50
50
  "era-interactive",
@@ -159,7 +159,7 @@
159
159
  "slug": "bar",
160
160
  "title": "Bar",
161
161
  "summary": "A flex container for toolbars and headers, in three heights.",
162
- "tokenEstimate": 118,
162
+ "tokenEstimate": 181,
163
163
  "sections": [
164
164
  "Import",
165
165
  "Props"
@@ -997,7 +997,7 @@
997
997
  ],
998
998
  "file": "index.css",
999
999
  "useCase": "vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge — fixed-height, non-editable elements only; see era-text-trim-caps for ALL-CAPS strings.",
1000
- "description": "Font-agnostic vertical ink-centering for single-line control text.\nMeasured verdict (4 fonts x 2 dpr, ink from device-pixel screenshots):\nthe offset has TWO causes — a fractional line box (14px x 1.5lh = 21px in\na 24px row places ink on a half pixel, ~1px error) and font ascent/descent\nasymmetry (+-1px, sign flips per font). line-height: 1 fixes the first and\nis the Firefox fallback; text-box ex/alphabetic fixes the second by\ncentering the x-height band — the visual mass of MIXED-CASE text (caps and\ndescenders cancel above/below). cap alphabetic is the spec-example recipe\nbut is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.\nWhere text-box is supported it fully determines the box, so the two\ndeclarations never fight. Worst-case residual: 0.44px.\n\nThe hard rules concern overflow clipping, and there are TWO of them.\n\n1. NEVER on a NATIVE EDITABLE — <input>, <textarea>, <select>. These clip to\n a UA-owned inner editor box that IS the trimmed line box, so a fixed\n height does NOT hold it open: whatever you type gets its ascenders and\n descenders sliced off (a \"d\" renders as an \"o\"). This rule used to read\n the other way round — it claimed a fixed-height <input> was the trim's\n intended home — and the Input component shipped chopped text as a result.\n A tier-height input centres its own text; it needs no trim.\n\n2. NEVER on an AUTO-height element that clips — a `truncate` child, an\n auto-height box with overflow-hidden. With no height to hold it open the\n box collapses to the x-height band (~7.5px at 14px) and the clip chops the\n glyphs into a middle stripe. Put the trim on the fixed-height ROW instead\n and let the child centre via the row's items-center.\n\nA fixed-height NON-editable element is the safe home: the ink overflows the\ntrimmed LINE box but stays inside the padding box, which is what actually\nclips. Both rules are enforced at runtime by typography/collapsed-text-trim.",
1000
+ "description": "Font-agnostic vertical ink-centering for single-line control text.\nMeasured verdict (4 fonts x 2 dpr, ink from device-pixel screenshots):\nthe offset has TWO causes — a fractional line box (14px x 1.5lh = 21px in\na 24px row places ink on a half pixel, ~1px error) and font ascent/descent\nasymmetry (+-1px, sign flips per font). line-height: 1 fixes the first and\nis the Firefox fallback; text-box ex/alphabetic fixes the second by\ncentering the x-height band — the visual mass of MIXED-CASE text (caps and\ndescenders cancel above/below). cap alphabetic is the spec-example recipe\nbut is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.\nWhere text-box is supported it fully determines the box, so the two\ndeclarations never fight. Worst-case residual: 0.44px (measured at 14px; the\nmechanism is font-relative, so dense's 12px inherits the same guarantee with\na proportionally smaller residual).\n\nThe hard rules concern overflow clipping, and there are TWO of them.\n\n1. NEVER on a NATIVE EDITABLE — <input>, <textarea>, <select>. These clip to\n a UA-owned inner editor box that IS the trimmed line box, so a fixed\n height does NOT hold it open: whatever you type gets its ascenders and\n descenders sliced off (a \"d\" renders as an \"o\"). This rule used to read\n the other way round — it claimed a fixed-height <input> was the trim's\n intended home — and the Input component shipped chopped text as a result.\n A tier-height input centres its own text; it needs no trim.\n\n2. NEVER on an AUTO-height element that clips — a `truncate` child, an\n auto-height box with overflow-hidden. With no height to hold it open the\n box collapses to the x-height band (~7.5px at 14px) and the clip chops the\n glyphs into a middle stripe. Put the trim on the fixed-height ROW instead\n and let the child centre via the row's items-center.\n\nA fixed-height NON-editable element is the safe home: the ink overflows the\ntrimmed LINE box but stays inside the padding box, which is what actually\nclips. Both rules are enforced at runtime by typography/collapsed-text-trim.",
1001
1001
  "css": "@utility era-text-trim {\n\tline-height: 1;\n\ttext-box: trim-both ex alphabetic;\n}"
1002
1002
  },
1003
1003
  {
@@ -1037,7 +1037,7 @@
1037
1037
  ],
1038
1038
  "file": "index.css",
1039
1039
  "useCase": "the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).",
1040
- "description": "The open/close animation for a bits-ui Collapsible.Content — the height\nsweep every disclosure in the library uses (a Step's body, an error panel's\ndetail, a swarm node's children).\n\nIt has to be a keyframe animation rather than a transition because the\nnatural height is only knowable at open time: bits measures the content and\npublishes `--bits-collapsible-content-height`, which the `to` frame reads.\nThe class also owns `overflow: hidden` — animating height without it just\nspills the content out of the shrinking box.\n\nTiming rides the motion axis, so `instant` collapses it to a cut (0s) and\n`extra` stretches it, for free. Three components had a byte-identical copy of\nthese keyframes under three different names before this existed; the panel\nthat needs different motion should override `animation`, not re-declare it.",
1040
+ "description": "The open/close animation for a bits-ui Collapsible.Content — the height\nsweep every disclosure in the library uses (a Step's body, an error panel's\ndetail, a swarm node's children).\n\nIt has to be a keyframe animation rather than a transition because the\nnatural height is only knowable at open time: bits measures the content and\npublishes `--bits-collapsible-content-height`, which the `to` frame reads.\nThe class also owns `overflow: hidden` — animating height without it just\nspills the content out of the shrinking box.\n\nTiming rides the motion axis, so `instant` collapses it to a cut (0s) and\n`extra` stretches it, for free. Two components carried a byte-identical copy of\nthese keyframes under two different names before this existed (a Step's body\nand the error panel's detail), and the swarm tree would have made a third; the\npanel that needs different motion should override `animation`, not re-declare\nit.",
1041
1041
  "css": "@utility era-collapse {\n\toverflow: hidden;\n\t&[data-state='open'] {\n\t\tanimation: era-collapse-open var(--era-duration) var(--era-ease);\n\t}\n\t&[data-state='closed'] {\n\t\tanimation: era-collapse-close var(--era-duration) var(--era-ease);\n\t}\n}"
1042
1042
  },
1043
1043
  {
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "file": "index.css",
39
39
  "useCase": "vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge — fixed-height, non-editable elements only; see era-text-trim-caps for ALL-CAPS strings.",
40
- "description": "Font-agnostic vertical ink-centering for single-line control text.\nMeasured verdict (4 fonts x 2 dpr, ink from device-pixel screenshots):\nthe offset has TWO causes — a fractional line box (14px x 1.5lh = 21px in\na 24px row places ink on a half pixel, ~1px error) and font ascent/descent\nasymmetry (+-1px, sign flips per font). line-height: 1 fixes the first and\nis the Firefox fallback; text-box ex/alphabetic fixes the second by\ncentering the x-height band — the visual mass of MIXED-CASE text (caps and\ndescenders cancel above/below). cap alphabetic is the spec-example recipe\nbut is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.\nWhere text-box is supported it fully determines the box, so the two\ndeclarations never fight. Worst-case residual: 0.44px.\n\nThe hard rules concern overflow clipping, and there are TWO of them.\n\n1. NEVER on a NATIVE EDITABLE — <input>, <textarea>, <select>. These clip to\n a UA-owned inner editor box that IS the trimmed line box, so a fixed\n height does NOT hold it open: whatever you type gets its ascenders and\n descenders sliced off (a \"d\" renders as an \"o\"). This rule used to read\n the other way round — it claimed a fixed-height <input> was the trim's\n intended home — and the Input component shipped chopped text as a result.\n A tier-height input centres its own text; it needs no trim.\n\n2. NEVER on an AUTO-height element that clips — a `truncate` child, an\n auto-height box with overflow-hidden. With no height to hold it open the\n box collapses to the x-height band (~7.5px at 14px) and the clip chops the\n glyphs into a middle stripe. Put the trim on the fixed-height ROW instead\n and let the child centre via the row's items-center.\n\nA fixed-height NON-editable element is the safe home: the ink overflows the\ntrimmed LINE box but stays inside the padding box, which is what actually\nclips. Both rules are enforced at runtime by typography/collapsed-text-trim.",
40
+ "description": "Font-agnostic vertical ink-centering for single-line control text.\nMeasured verdict (4 fonts x 2 dpr, ink from device-pixel screenshots):\nthe offset has TWO causes — a fractional line box (14px x 1.5lh = 21px in\na 24px row places ink on a half pixel, ~1px error) and font ascent/descent\nasymmetry (+-1px, sign flips per font). line-height: 1 fixes the first and\nis the Firefox fallback; text-box ex/alphabetic fixes the second by\ncentering the x-height band — the visual mass of MIXED-CASE text (caps and\ndescenders cancel above/below). cap alphabetic is the spec-example recipe\nbut is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.\nWhere text-box is supported it fully determines the box, so the two\ndeclarations never fight. Worst-case residual: 0.44px (measured at 14px; the\nmechanism is font-relative, so dense's 12px inherits the same guarantee with\na proportionally smaller residual).\n\nThe hard rules concern overflow clipping, and there are TWO of them.\n\n1. NEVER on a NATIVE EDITABLE — <input>, <textarea>, <select>. These clip to\n a UA-owned inner editor box that IS the trimmed line box, so a fixed\n height does NOT hold it open: whatever you type gets its ascenders and\n descenders sliced off (a \"d\" renders as an \"o\"). This rule used to read\n the other way round — it claimed a fixed-height <input> was the trim's\n intended home — and the Input component shipped chopped text as a result.\n A tier-height input centres its own text; it needs no trim.\n\n2. NEVER on an AUTO-height element that clips — a `truncate` child, an\n auto-height box with overflow-hidden. With no height to hold it open the\n box collapses to the x-height band (~7.5px at 14px) and the clip chops the\n glyphs into a middle stripe. Put the trim on the fixed-height ROW instead\n and let the child centre via the row's items-center.\n\nA fixed-height NON-editable element is the safe home: the ink overflows the\ntrimmed LINE box but stays inside the padding box, which is what actually\nclips. Both rules are enforced at runtime by typography/collapsed-text-trim.",
41
41
  "css": "@utility era-text-trim {\n\tline-height: 1;\n\ttext-box: trim-both ex alphabetic;\n}"
42
42
  },
43
43
  {
@@ -77,7 +77,7 @@
77
77
  ],
78
78
  "file": "index.css",
79
79
  "useCase": "the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).",
80
- "description": "The open/close animation for a bits-ui Collapsible.Content — the height\nsweep every disclosure in the library uses (a Step's body, an error panel's\ndetail, a swarm node's children).\n\nIt has to be a keyframe animation rather than a transition because the\nnatural height is only knowable at open time: bits measures the content and\npublishes `--bits-collapsible-content-height`, which the `to` frame reads.\nThe class also owns `overflow: hidden` — animating height without it just\nspills the content out of the shrinking box.\n\nTiming rides the motion axis, so `instant` collapses it to a cut (0s) and\n`extra` stretches it, for free. Three components had a byte-identical copy of\nthese keyframes under three different names before this existed; the panel\nthat needs different motion should override `animation`, not re-declare it.",
80
+ "description": "The open/close animation for a bits-ui Collapsible.Content — the height\nsweep every disclosure in the library uses (a Step's body, an error panel's\ndetail, a swarm node's children).\n\nIt has to be a keyframe animation rather than a transition because the\nnatural height is only knowable at open time: bits measures the content and\npublishes `--bits-collapsible-content-height`, which the `to` frame reads.\nThe class also owns `overflow: hidden` — animating height without it just\nspills the content out of the shrinking box.\n\nTiming rides the motion axis, so `instant` collapses it to a cut (0s) and\n`extra` stretches it, for free. Two components carried a byte-identical copy of\nthese keyframes under two different names before this existed (a Step's body\nand the error panel's detail), and the swarm tree would have made a third; the\npanel that needs different motion should override `animation`, not re-declare\nit.",
81
81
  "css": "@utility era-collapse {\n\toverflow: hidden;\n\t&[data-state='open'] {\n\t\tanimation: era-collapse-open var(--era-duration) var(--era-ease);\n\t}\n\t&[data-state='closed'] {\n\t\tanimation: era-collapse-close var(--era-duration) var(--era-ease);\n\t}\n}"
82
82
  },
83
83
  {
@@ -146,7 +146,9 @@ centering the x-height band — the visual mass of MIXED-CASE text (caps and
146
146
  descenders cancel above/below). cap alphabetic is the spec-example recipe
147
147
  but is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.
148
148
  Where text-box is supported it fully determines the box, so the two
149
- declarations never fight. Worst-case residual: 0.44px.
149
+ declarations never fight. Worst-case residual: 0.44px (measured at 14px; the
150
+ mechanism is font-relative, so dense's 12px inherits the same guarantee with
151
+ a proportionally smaller residual).
150
152
 
151
153
  The hard rules concern overflow clipping, and there are TWO of them.
152
154
 
@@ -268,9 +270,11 @@ The class also owns `overflow: hidden` — animating height without it just
268
270
  spills the content out of the shrinking box.
269
271
 
270
272
  Timing rides the motion axis, so `instant` collapses it to a cut (0s) and
271
- `extra` stretches it, for free. Three components had a byte-identical copy of
272
- these keyframes under three different names before this existed; the panel
273
- that needs different motion should override `animation`, not re-declare it.
273
+ `extra` stretches it, for free. Two components carried a byte-identical copy of
274
+ these keyframes under two different names before this existed (a Step's body
275
+ and the error panel's detail), and the swarm tree would have made a third; the
276
+ panel that needs different motion should override `animation`, not re-declare
277
+ it.
274
278
 
275
279
  Declared in `index.css`.
276
280
 
@@ -95,27 +95,22 @@
95
95
  held preview recolours to the action it will commit — yellow for
96
96
  minimize, green for maximize — so the tone and glyph agree on what
97
97
  the release does. -->
98
- <!-- Glyph sizing lives on the BUTTON, not on the icons, and has to be a
99
- [&_svg]: variant: Button's `icon` variant sets the glyph size with
100
- exactly that selector, and a plain size-* on the <svg> loses to it on
101
- specificity. The classes that used to sit on these three icons were
102
- inert — they read "h-xs" (14px) while every one rendered at icon-xs
103
- (10px), including a documented -2px optical tweak on the maximize
104
- bracket that never applied.
98
+ <!-- size="xxs" is the handle's content tier (see Pane.Handle), and it also
99
+ sets the glyph: the xxs button draws icon-xxs, which is what this
100
+ button used to force with a hand-written [&_svg] class. Same size as
101
+ Pane.Close, so the two close affordances match.
105
102
 
106
- Matches Pane.Close at icon-xxs so the two close affordances are the
107
- same size, EXCEPT the maximize bracket, which stays a sub-tier down:
108
- its silhouette fills its whole box, so beside the x and - strokes it
109
- reads bulkier at the same nominal size. icon-xs is exactly 2px under
110
- icon-xxs at every density, which is the adjustment that comment
111
- always wanted. -->
103
+ The ONE exception stays a sub-tier down: the maximize bracket's
104
+ silhouette fills its whole box, so beside the x and strokes it reads
105
+ bulkier at the same nominal size. icon-xs is exactly 2px under
106
+ icon-xxs at every density. It has to be written as [&_svg]: because
107
+ that is the selector the variant uses a plain size-* on the <svg>
108
+ loses to it. -->
112
109
  <Button
113
110
  icon
114
- size="icon"
111
+ size="xxs"
115
112
  tone={armed === 'minimize' ? 'warning' : armed === 'maximize' ? 'success' : 'destructive'}
116
- class={cn(
117
- armed === 'maximize' ? '[&_svg]:size-(--era-icon-xs)' : '[&_svg]:size-(--era-icon-xxs)'
118
- )}
113
+ class={armed === 'maximize' ? '[&_svg]:size-(--era-icon-xs)' : undefined}
119
114
  aria-label="Close (right-click minimizes, middle-click maximizes)"
120
115
  onclick={() => pm.close(pane.id)}
121
116
  oncontextmenu={(e: MouseEvent) => e.preventDefault()}
@@ -13,7 +13,7 @@
13
13
  * lg = 4·sp (bars, containers)
14
14
  *
15
15
  * xs carries a +6 floor so icons stay legible (≥14px) at every sp. xxs sits
16
- * 4px above xs so 14px text has just enough pill around it while fitting
16
+ * 4px above xs so body text has just enough pill around it while fitting
17
17
  * inside an md container with a visible concentric gap (≥3px at dense). sm
18
18
  * keeps the generous 2·sp + 6 that gives switch thumbs their sp/2 slot and
19
19
  * KV rows their padded feel. md and lg are pure sp multiples so button and
@@ -43,6 +43,7 @@
43
43
  * --era-px-{sm,md,lg} sp/2 — tier-to-tier nesting padding
44
44
  * --era-gap sp/2 — inter-element gap
45
45
  * --era-inset-{xxs,sm,md} (T − text)/2 — text-symmetric content padding
46
+ * (text is 12px at dense, 14 above)
46
47
  * --era-xs-inset-sm (sm − xs)/2 = sp/2 — centering an xs element inside sm
47
48
  *
48
49
  * OPTICAL PADDING (unbounded content containers)
@@ -52,7 +53,8 @@
52
53
  * height C inside a container of height T is (T − C)/2, linear in sp. But
53
54
  * cards, dialogs, and content panels have no bounding tier — their padding
54
55
  * is optical, and linear-in-sp padding falls behind as modes loosen: controls
55
- * grow linearly while text stays 14px, so whitespace must grow SUPER-linearly
56
+ * grow linearly while text barely moves (12px at dense, 14 above), so
57
+ * whitespace must grow SUPER-linearly
56
58
  * to hold the same perceived balance. (The tokens that already feel right
57
59
  * across modes — inset-md 5→17, content-p 16→48 — are exactly the convex
58
60
  * ones; the cramped ones — card's old sp/2 = 4→8 — are the linear ones.)
@@ -154,9 +156,9 @@
154
156
 
155
157
  /* Content insets — text-symmetric padding (vertical centering, reused as
156
158
  horizontal only where the content is a single square glyph). */
157
- --era-inset-xxs: max(0px, calc((var(--era-h-xxs) - var(--era-text)) / 2)); /* 2/3/4 */
158
- --era-inset-sm: max(0px, calc((var(--era-h-sm) - var(--era-text)) / 2)); /* 4/6/8 */
159
- --era-inset-md: max(0px, calc((var(--era-h-md) - var(--era-text)) / 2)); /* 5/8/11 */
159
+ --era-inset-xxs: max(0px, calc((var(--era-h-xxs) - var(--era-text)) / 2)); /* 3/3/4/6 */
160
+ --era-inset-sm: max(0px, calc((var(--era-h-sm) - var(--era-text)) / 2)); /* 5/6/8/12 */
161
+ --era-inset-md: max(0px, calc((var(--era-h-md) - var(--era-text)) / 2)); /* 6/8/11/17 */
160
162
 
161
163
  /* Pill side padding — horizontal breathing room for badge/chip text.
162
164
  Text wants MORE horizontal than vertical padding: line-height adds
@@ -167,8 +169,8 @@
167
169
  is narrower than the line, this comes out ~2× the vertical inset on its
168
170
  own — and a lone glyph still measures exactly the tier width, so the
169
171
  "single glyph = square" floor (min-w = h) is preserved, not broken. */
170
- --era-pill-xxs: max(0px, calc((var(--era-h-xxs) - 0.6 * var(--era-text)) / 2)); /* 4.8/5.8/6.8 */
171
- --era-pill-sm: max(0px, calc((var(--era-h-sm) - 0.6 * var(--era-text)) / 2)); /* 6.8/8.8/10.8 */
172
+ --era-pill-xxs: max(0px, calc((var(--era-h-xxs) - 0.6 * var(--era-text)) / 2)); /* 5.4/5.8/6.8 */
173
+ --era-pill-sm: max(0px, calc((var(--era-h-sm) - 0.6 * var(--era-text)) / 2)); /* 7.4/8.8/10.8 */
172
174
 
173
175
  /* Text-field side padding — the horizontal room where typed text starts in an
174
176
  md-tier input (Input, Textarea, combobox/command inputs, date/time fields).
@@ -261,7 +263,8 @@
261
263
  }
262
264
 
263
265
  /* -------------------------------------------------- */
264
- /* Mode inputs — just --era-sp (and static --era-text). */
266
+ /* Mode inputs — --era-sp, and --era-text (12 at dense, */
267
+ /* 14 above; see the dense block). */
265
268
  /* The derivation above re-runs per element wherever */
266
269
  /* --era-sp is overridden. Animation timing is NOT here */
267
270
  /* — it's the motion axis (motion.css), orthogonal to */
@@ -286,7 +289,37 @@
286
289
  [data-mode='dense'] {
287
290
  --era-sp: 0.5rem; /* 8px */
288
291
  --era-density: 1; /* d = sp / 8px */
289
- --era-text: 0.875rem; /* 14px static across modes */
292
+ /* 12px at dense, 14px from balanced up (set in the block below).
293
+ *
294
+ * Type is the SECOND density input, and for a long time it was not one: the
295
+ * token read "14px — static across modes", so dense and balanced differed in
296
+ * their boxes and not in their type. That is what made dense feel like
297
+ * balanced-with-tighter-gaps rather than a genuinely dense mode — the chrome
298
+ * shrank while the thing inside it did not.
299
+ *
300
+ * 12px is the size the reference terminal app is built at, and it makes the
301
+ * whole ladder line up with it: --era-inset-xxs becomes (18−12)/2 = 3px,
302
+ * which is exactly the 3px that app hardcodes for the wall and gap of an
303
+ * 18px control in a 24px bar. Same numbers, derived instead of typed.
304
+ *
305
+ * Everything text-derived follows: inset-xxs 2→3, inset-sm 4→5, inset-md
306
+ * 5→6, pill-xxs 4.8→5.4. Heights do NOT — they are sp-derived, so the tier
307
+ * ladder (14/18/22/24/32) is unchanged and dense stays dimensionally the
308
+ * same mode it was.
309
+ *
310
+ * Only dense moves. Balanced/spacious/touch keep 14px: they exist for
311
+ * reading comfort and pointer size, and shrinking their type would undo the
312
+ * thing they are for. */
313
+ --era-text: 0.75rem; /* 12px */
314
+ }
315
+
316
+ /* Balanced and up: 14px, the comfortable reading size. Declared once here and
317
+ * inherited by spacious/touch so there is a single place the non-dense size
318
+ * lives. */
319
+ [data-mode='balanced'],
320
+ [data-mode='spacious'],
321
+ [data-mode='touch'] {
322
+ --era-text: 0.875rem; /* 14px */
290
323
  }
291
324
 
292
325
  [data-mode='balanced'] {
@@ -126,7 +126,9 @@
126
126
  * descenders cancel above/below). cap alphabetic is the spec-example recipe
127
127
  * but is descender-blind (reads high; CSSWG #9148) — measured 4x worse here.
128
128
  * Where text-box is supported it fully determines the box, so the two
129
- * declarations never fight. Worst-case residual: 0.44px.
129
+ * declarations never fight. Worst-case residual: 0.44px (measured at 14px; the
130
+ * mechanism is font-relative, so dense's 12px inherits the same guarantee with
131
+ * a proportionally smaller residual).
130
132
  *
131
133
  * The hard rules concern overflow clipping, and there are TWO of them.
132
134
  *
@@ -234,9 +236,11 @@
234
236
  * spills the content out of the shrinking box.
235
237
  *
236
238
  * Timing rides the motion axis, so `instant` collapses it to a cut (0s) and
237
- * `extra` stretches it, for free. Three components had a byte-identical copy of
238
- * these keyframes under three different names before this existed; the panel
239
- * that needs different motion should override `animation`, not re-declare it.
239
+ * `extra` stretches it, for free. Two components carried a byte-identical copy of
240
+ * these keyframes under two different names before this existed (a Step's body
241
+ * and the error panel's detail), and the swarm tree would have made a third; the
242
+ * panel that needs different motion should override `animation`, not re-declare
243
+ * it.
240
244
  *
241
245
  * @use the height sweep for a bits-ui Collapsible.Content — put it on the
242
246
  * Content element itself (it carries its own overflow: hidden). */
@@ -10,15 +10,42 @@
10
10
  // Radius stays concentric to the height; gap/px are sp/2 (xs-inset-sm),
11
11
  // the even gap for one-tier-smaller content at every size.
12
12
  export const barVariants = tv({
13
- base: 'flex items-center gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm) era-text-trim',
13
+ base: 'flex items-center era-text-trim',
14
14
  variants: {
15
15
  size: {
16
16
  sm: 'h-(--era-h-sm) rounded-(--era-rd-sm)',
17
17
  md: 'h-(--era-h-md) rounded-(--era-rd-md)',
18
18
  lg: 'h-(--era-h-lg) rounded-(--era-rd-lg)'
19
+ },
20
+ /**
21
+ * The tier of the CONTROLS inside, because that is what the bar's own
22
+ * padding and gap have to be concentric to — (bar − content)/2 on every
23
+ * side. It is a variant rather than a caller's class because the ladder
24
+ * math belongs next to the heights it is derived from: three call sites
25
+ * had each restated the two-tier value by hand, and the geometry spec had
26
+ * been rewritten twice chasing which token they were restating.
27
+ *
28
+ * - `tier` (default) — one tier smaller, the pairing above. sm⊃xs, md⊃sm
29
+ * and lg⊃md all equal sp/2, which is why one token covers every size.
30
+ * - `xxs` — the compact pairing: xxs pills and fields (a filter beside an
31
+ * xxs icon button, a title bar). Two tiers down, so the inset differs
32
+ * per size and comes from the compounds below.
33
+ */
34
+ content: {
35
+ tier: 'gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)',
36
+ xxs: ''
19
37
  }
20
38
  },
21
- defaultVariants: { size: 'lg' }
39
+ compoundVariants: [
40
+ // md ⊃ xxs = (3sp − sp − 10)/2 — the named token.
41
+ { size: 'md', content: 'xxs', class: 'gap-(--era-xxs-inset-md) px-(--era-xxs-inset-md)' },
42
+ // sm ⊃ xxs = (2sp + 6 − sp − 10)/2 = (sp − 4)/2, which is ALSO (h-xxs − text)/2
43
+ // at 14px text — the same number --era-inset-xxs already carries, so the
44
+ // identity is reused rather than a fifth token invented. (lg ⊃ xxs has no
45
+ // token because nothing pairs a page header with pill-tier controls.)
46
+ { size: 'sm', content: 'xxs', class: 'gap-(--era-inset-xxs) px-(--era-inset-xxs)' }
47
+ ],
48
+ defaultVariants: { size: 'lg', content: 'tier' }
22
49
  });
23
50
  </script>
24
51
 
@@ -29,6 +56,7 @@
29
56
  let {
30
57
  ref = $bindable(null),
31
58
  size = 'lg',
59
+ content = 'tier',
32
60
  children,
33
61
  class: className,
34
62
  ...restProps
@@ -36,10 +64,16 @@
36
64
  ref?: HTMLDivElement | null;
37
65
  /** Height tier (scales with density). Default `lg` — a full toolbar. */
38
66
  size?: 'sm' | 'md' | 'lg';
67
+ /**
68
+ * Tier of the controls inside — drives the bar's padding and gap so they
69
+ * stay concentric to them. `tier` (default) is one tier smaller; `xxs` is
70
+ * the compact pairing (xxs pills/fields in an sm or md bar).
71
+ */
72
+ content?: 'tier' | 'xxs';
39
73
  children?: Snippet;
40
74
  } = $props();
41
75
  </script>
42
76
 
43
- <div bind:this={ref} class={cn(barVariants({ size }), className)} {...restProps}>
77
+ <div bind:this={ref} class={cn(barVariants({ size, content }), className)} {...restProps}>
44
78
  {@render children?.()}
45
79
  </div>
@@ -4,18 +4,72 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
4
4
  md: "h-(--era-h-md) rounded-(--era-rd-md)";
5
5
  lg: "h-(--era-h-lg) rounded-(--era-rd-lg)";
6
6
  };
7
- }, undefined, "flex items-center gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm) era-text-trim", {
7
+ /**
8
+ * The tier of the CONTROLS inside, because that is what the bar's own
9
+ * padding and gap have to be concentric to — (bar − content)/2 on every
10
+ * side. It is a variant rather than a caller's class because the ladder
11
+ * math belongs next to the heights it is derived from: three call sites
12
+ * had each restated the two-tier value by hand, and the geometry spec had
13
+ * been rewritten twice chasing which token they were restating.
14
+ *
15
+ * - `tier` (default) — one tier smaller, the pairing above. sm⊃xs, md⊃sm
16
+ * and lg⊃md all equal sp/2, which is why one token covers every size.
17
+ * - `xxs` — the compact pairing: xxs pills and fields (a filter beside an
18
+ * xxs icon button, a title bar). Two tiers down, so the inset differs
19
+ * per size and comes from the compounds below.
20
+ */
21
+ content: {
22
+ tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
23
+ xxs: "";
24
+ };
25
+ }, undefined, "flex items-center era-text-trim", {
8
26
  size: {
9
27
  sm: "h-(--era-h-sm) rounded-(--era-rd-sm)";
10
28
  md: "h-(--era-h-md) rounded-(--era-rd-md)";
11
29
  lg: "h-(--era-h-lg) rounded-(--era-rd-lg)";
12
30
  };
31
+ /**
32
+ * The tier of the CONTROLS inside, because that is what the bar's own
33
+ * padding and gap have to be concentric to — (bar − content)/2 on every
34
+ * side. It is a variant rather than a caller's class because the ladder
35
+ * math belongs next to the heights it is derived from: three call sites
36
+ * had each restated the two-tier value by hand, and the geometry spec had
37
+ * been rewritten twice chasing which token they were restating.
38
+ *
39
+ * - `tier` (default) — one tier smaller, the pairing above. sm⊃xs, md⊃sm
40
+ * and lg⊃md all equal sp/2, which is why one token covers every size.
41
+ * - `xxs` — the compact pairing: xxs pills and fields (a filter beside an
42
+ * xxs icon button, a title bar). Two tiers down, so the inset differs
43
+ * per size and comes from the compounds below.
44
+ */
45
+ content: {
46
+ tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
47
+ xxs: "";
48
+ };
13
49
  }, undefined, import("tailwind-variants").TVReturnTypeLike<{
14
50
  size: {
15
51
  sm: "h-(--era-h-sm) rounded-(--era-rd-sm)";
16
52
  md: "h-(--era-h-md) rounded-(--era-rd-md)";
17
53
  lg: "h-(--era-h-lg) rounded-(--era-rd-lg)";
18
54
  };
55
+ /**
56
+ * The tier of the CONTROLS inside, because that is what the bar's own
57
+ * padding and gap have to be concentric to — (bar − content)/2 on every
58
+ * side. It is a variant rather than a caller's class because the ladder
59
+ * math belongs next to the heights it is derived from: three call sites
60
+ * had each restated the two-tier value by hand, and the geometry spec had
61
+ * been rewritten twice chasing which token they were restating.
62
+ *
63
+ * - `tier` (default) — one tier smaller, the pairing above. sm⊃xs, md⊃sm
64
+ * and lg⊃md all equal sp/2, which is why one token covers every size.
65
+ * - `xxs` — the compact pairing: xxs pills and fields (a filter beside an
66
+ * xxs icon button, a title bar). Two tiers down, so the inset differs
67
+ * per size and comes from the compounds below.
68
+ */
69
+ content: {
70
+ tier: "gap-(--era-xs-inset-sm) px-(--era-xs-inset-sm)";
71
+ xxs: "";
72
+ };
19
73
  }, undefined>>;
20
74
  import type { HTMLAttributes } from 'svelte/elements';
21
75
  import type { Snippet } from 'svelte';
@@ -23,6 +77,12 @@ type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
23
77
  ref?: HTMLDivElement | null;
24
78
  /** Height tier (scales with density). Default `lg` — a full toolbar. */
25
79
  size?: 'sm' | 'md' | 'lg';
80
+ /**
81
+ * Tier of the controls inside — drives the bar's padding and gap so they
82
+ * stay concentric to them. `tier` (default) is one tier smaller; `xxs` is
83
+ * the compact pairing (xxs pills/fields in an sm or md bar).
84
+ */
85
+ content?: 'tier' | 'xxs';
26
86
  children?: Snippet;
27
87
  };
28
88
  declare const Bar: import("svelte").Component<$$ComponentProps, {}, "ref">;
@@ -2,7 +2,6 @@
2
2
  import type { HTMLButtonAttributes } from 'svelte/elements';
3
3
  import { Button } from '../button';
4
4
  import X from '@lucide/svelte/icons/x';
5
- import { cn } from '../../utils/index.js';
6
5
 
7
6
  let {
8
7
  ref = $bindable(null),
@@ -13,31 +12,27 @@
13
12
  } = $props();
14
13
  </script>
15
14
 
16
- <!-- A raw icon Button (size="icon" = h-xs, rd-xs), a tight SQUARE. No
15
+ <!-- A raw icon Button (size="xxs" = h-xxs, rd-xxs), a tight SQUARE. No
17
16
  data-pane-control: it stays in the drag zone, so a press-and-drag moves the
18
17
  pane while a clean click still closes (neodrag starts a drag only on
19
18
  movement and suppresses the trailing click after a real drag). Its natural
20
- rd-xs is what the PANE's corner radius conforms to (rd-sm — see Pane.Root),
19
+ rd-xxs is what the PANE's corner radius conforms to (rd-md — see Pane.Root),
21
20
  not the other way round.
22
21
 
23
- The glyph steps UP one sub-tier, icon-xs -> icon-xxs (10 -> 12px at dense).
24
- Button's `icon` variant drops the glyph below the icon tier at the tight
25
- sizes so it does not fill its box, which is right in general but a close
26
- button is the one control in the chrome you must be able to hit and read at
27
- a glance, and half of a Lucide glyph's box is padding inside its own
28
- viewBox, so 10px rendered only about 5px of actual ink. 12px still leaves
29
- 1px inset per side, so the button is not flush.
30
-
31
- Written as [&_svg]: because that is what the variant uses — a bare
32
- `size-*` on the <X> loses to the variant's descendant selector, which is
33
- why the class that used to sit there had no effect at all. -->
22
+ xxs rather than the tighter `icon` size, which is what the handle's tier
23
+ asks for and it also gets the glyph right for free. A close button is the
24
+ one control in the chrome you must be able to hit and read at a glance, and
25
+ half of a Lucide glyph's box is padding inside its own viewBox, so `icon`'s
26
+ 10px rendered about 5px of actual ink; this tier draws icon-xxs (12px at
27
+ dense), which is what the old hand-written [&_svg] override existed to
28
+ force. One tier declaration now says both things. -->
34
29
  <Button
35
30
  bind:ref
36
31
  icon
37
- size="icon"
32
+ size="xxs"
38
33
  tone="destructive"
39
34
  aria-label="Close"
40
- class={cn('[&_svg]:size-(--era-icon-xxs)', className)}
35
+ class={className}
41
36
  {...restProps}
42
37
  >
43
38
  <X />
@@ -15,19 +15,26 @@
15
15
  } = $props();
16
16
  </script>
17
17
 
18
- <!-- The drag zone. A flush title bar (Bar size="sm") spanning the pane edge to
19
- edge, so the WHOLE top — up to the very top pixel — is draggable.
20
- data-pane-handle marks it as the neodrag allow-zone. Transparent over the
21
- pane's rounded surface: the TOP corners defer to the pane radius, while the
22
- BOTTOM corners round to --era-rd-sm — so the inset divider curves up at the
23
- ends and the bar reads as a long pill. The corners axis collapses rd-sm to
24
- 0 in square mode, so it's flat there and pill-shaped only when rounded. -->
18
+ <!-- The drag zone. A flush title bar spanning the pane edge to edge, so the
19
+ WHOLE top — up to the very top pixel — is draggable. data-pane-handle marks
20
+ it as the neodrag allow-zone. Transparent over the pane's rounded surface:
21
+ the TOP corners defer to the pane radius, while the BOTTOM corners round to
22
+ --era-rd-md — so the inset divider curves up at the ends and the bar reads
23
+ as a long pill. The corners axis collapses the radius to 0 in square mode,
24
+ so it's flat there and pill-shaped only when rounded.
25
+
26
+ md ⊃ xxs, not sm ⊃ xs: a title bar is the densest chrome in the library and
27
+ was a tier too short for it — 22px at dense against the 24 the reference
28
+ terminal app uses, with 14px controls where it uses 18. The pairing is what
29
+ carries that: content="xxs" puts the walls on --era-xxs-inset-md (3/5/7/11),
30
+ which is the 3px that app hardcodes for exactly this bar. -->
25
31
  <Bar
26
32
  bind:ref
27
33
  data-pane-handle
28
- size="sm"
34
+ size="md"
35
+ content="xxs"
29
36
  class={cn(
30
- 'shrink-0 rounded-t-none rounded-b-(--era-rd-sm) [box-shadow:inset_0_-1px_0_var(--color-divider-faded)] select-none',
37
+ 'shrink-0 rounded-t-none rounded-b-(--era-rd-md) [box-shadow:inset_0_-1px_0_var(--color-divider-faded)] select-none',
31
38
  className
32
39
  )}
33
40
  {...restProps}
@@ -298,12 +298,14 @@
298
298
  // negative z sits below the content but above this background (bar clear of
299
299
  // the rails, without lifting the rails above neighbouring UI).
300
300
  //
301
- // rd-sm, NOT rd-lg: a container's radius follows its corner REGION's tier,
302
- // not its overall size. The pane's corners are formed by the h-sm grab bar,
303
- // whose h-xs controls (the ×) sit xs-inset-sm (sp/2) from the corner and
304
- // the ladder guarantees rd-sm = rd-xs + sp/2 at every density, so the ×'s
305
- // natural rd-xs is concentric inside rd-sm by construction.
306
- 'group/pane absolute isolate flex flex-col rounded-(--era-rd-sm) bg-(--era-surface-bg-elevated) shadow-(--era-shadow-lg) glass-blur',
301
+ // rd-md, NOT rd-lg: a container's radius follows its corner REGION's tier,
302
+ // not its overall size. The pane's corners are formed by the grab bar, which
303
+ // is h-md holding h-xxs controls (the ×) and the radius ladder is
304
+ // rd-T = R + (h-T h-xs)/2, so rd-md rd-xxs is exactly (h-md − h-xxs)/2 =
305
+ // --era-xxs-inset-md, the gap the × already sits at from the corner. The ×'s
306
+ // natural rd-xxs is therefore concentric inside rd-md by construction, the
307
+ // same way rd-xs was inside rd-sm before the handle moved up a tier.
308
+ 'group/pane absolute isolate flex flex-col rounded-(--era-rd-md) bg-(--era-surface-bg-elevated) shadow-(--era-shadow-lg) glass-blur',
307
309
  // Auto-size to content until an explicit size is set (by the grip or a preset).
308
310
  size ? '' : 'w-max',
309
311
  // While Ctrl is held the whole pane is grabbable: a grab cursor hints it, and
@@ -36,12 +36,13 @@
36
36
  overflow-x-auto forces overflow-y to clip, so descenders (the logs g, the
37
37
  history y) are at risk at the clip edge. The real fix is on the tab itself —
38
38
  PaneTab cancels Tabs.Trigger's era-text-trim, which had pulled the box up to
39
- the baseline and fed the clip its descenders. py-0.5 stays as a safety margin
40
- for the natural line box. -->
39
+ the baseline and fed the clip its descenders. The py-0.5 that used to back
40
+ that up is gone with it: the tab is now h-xxs, so its own box holds the line
41
+ box with room to spare and the clip edge sits a wall away. -->
41
42
  <Tabs.List
42
43
  bind:ref
43
44
  class={cn(
44
- 'scrollbar-none flex min-w-0 touch-pan-x items-center gap-(--era-xs-inset-sm) overflow-x-auto py-0.5',
45
+ 'scrollbar-none flex min-w-0 touch-pan-x items-center gap-(--era-gap) overflow-x-auto',
45
46
  className
46
47
  )}
47
48
  {...restProps}