@adea-ai/ui 0.118.3 → 0.119.0

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 (36) hide show
  1. package/README.md +7 -3
  2. package/dist/components/composites/settings/index.d.ts +1 -1
  3. package/dist/components/composites/settings/index.d.ts.map +1 -1
  4. package/dist/components/composites/settings/settings-layout.d.ts +18 -2
  5. package/dist/components/composites/settings/settings-layout.d.ts.map +1 -1
  6. package/dist/components/composites/settings/settings-layout.js +40 -5
  7. package/dist/components/composites/settings/settings-layout.js.map +1 -1
  8. package/dist/components/composites/settings/settings-navigation.d.ts.map +1 -1
  9. package/dist/components/composites/settings/settings-navigation.js +36 -14
  10. package/dist/components/composites/settings/settings-navigation.js.map +1 -1
  11. package/dist/components/ui/code-block/code-block.d.ts +13 -0
  12. package/dist/components/ui/code-block/code-block.d.ts.map +1 -1
  13. package/dist/components/ui/code-block/code-block.js +22 -3
  14. package/dist/components/ui/code-block/code-block.js.map +1 -1
  15. package/dist/components/ui/item/index.d.ts +1 -1
  16. package/dist/components/ui/item/index.d.ts.map +1 -1
  17. package/dist/components/ui/item/item.d.ts +17 -1
  18. package/dist/components/ui/item/item.d.ts.map +1 -1
  19. package/dist/components/ui/item/item.js +17 -10
  20. package/dist/components/ui/item/item.js.map +1 -1
  21. package/dist/r/registry.json +3 -1
  22. package/dist/r/settings.json +3 -1
  23. package/dist/r/src/components/composites/settings/index.ts +5 -1
  24. package/dist/r/src/components/composites/settings/settings-layout.tsx +48 -5
  25. package/dist/r/src/components/composites/settings/settings-navigation.tsx +44 -16
  26. package/dist/r/src/components/ui/code-block/code-block.tsx +27 -2
  27. package/dist/r/src/components/ui/item/index.ts +1 -0
  28. package/dist/r/src/components/ui/item/item.tsx +41 -5
  29. package/package.json +1 -1
  30. package/registry.json +3 -1
  31. package/src/components/composites/settings/index.ts +5 -1
  32. package/src/components/composites/settings/settings-layout.tsx +48 -5
  33. package/src/components/composites/settings/settings-navigation.tsx +44 -16
  34. package/src/components/ui/code-block/code-block.tsx +27 -2
  35. package/src/components/ui/item/index.ts +1 -0
  36. package/src/components/ui/item/item.tsx +41 -5
package/README.md CHANGED
@@ -284,9 +284,13 @@ appearance view, and `ThemeToggle` is the light/dark switch on its own.
284
284
  `SettingsLayout` composes a controlled vertical tab root, grouped `SettingsNavigation`,
285
285
  and a scrollable panel viewport. Supply the selected `value`, `onChange`, navigation
286
286
  groups, and matching `TabsContent` panels. The host owns URL or preference updates
287
- and panel content; the layout contracts its rail at narrow widths, reports repeated
288
- activation through `onReselect`, and reveals the selected row by default. Use
289
- `SettingsNavigation` directly when you already own the surrounding tabs composition.
287
+ and panel content; below 48rem of viewport width the rail becomes a horizontal,
288
+ scrollable strip of tabs above the panels (groups in order, Left/Right keys,
289
+ `aria-orientation="horizontal"`), and `navigationLayout="rail" | "strip"` pins one
290
+ shape at every width. The layout reports repeated activation through `onReselect`
291
+ and reveals the selected row on either axis by default. Use `SettingsNavigation`
292
+ directly when you already own the surrounding tabs composition; it follows the
293
+ enclosing `Tabs` orientation.
290
294
  Shared tabs associate panels with custom trigger IDs in browser and server renders;
291
295
  an explicit panel `aria-labelledby` takes precedence. Independent tab roots keep
292
296
  their associations separate.
@@ -1,4 +1,4 @@
1
1
  export { SettingsField, SettingsPage, SettingsRow, SettingsSection } from './settings';
2
- export { SettingsLayout, type SettingsLayoutProps } from './settings-layout';
2
+ export { SettingsLayout, type SettingsLayoutProps, type SettingsNavigationLayout, } from './settings-layout';
3
3
  export { SettingsNavigation, type SettingsNavigationGroup, type SettingsNavigationItem, type SettingsNavigationProps, } from './settings-navigation';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACtF,OAAO,EACL,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,uBAAuB,CAAA"}
@@ -4,7 +4,7 @@ import { type SettingsNavigationGroup } from './settings-navigation';
4
4
  export type SettingsLayoutProps = Omit<ComponentProps<typeof Tabs>, 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'> & {
5
5
  /** Grouped navigation entries; the host owns their values and panel content. */
6
6
  groups: readonly SettingsNavigationGroup[];
7
- /** Accessible name for the vertical tab list. */
7
+ /** Accessible name for the tab list. */
8
8
  'aria-label': string;
9
9
  /** The host-controlled selected destination. */
10
10
  value: string;
@@ -14,15 +14,31 @@ export type SettingsLayoutProps = Omit<ComponentProps<typeof Tabs>, 'children' |
14
14
  onReselect?: (value: string) => void;
15
15
  /** Reveal the selected row on value changes and window resizes. Defaults to nearest scroll. */
16
16
  revealSelected?: boolean | ((trigger: HTMLButtonElement | undefined) => void);
17
+ /**
18
+ * How the section navigation sits beside the panels. `auto` (the default) is
19
+ * the vertical rail from 48rem of viewport width up and a horizontal,
20
+ * scrollable strip of tabs above the panels below it. `rail` and `strip` pin
21
+ * one shape at every width.
22
+ */
23
+ navigationLayout?: SettingsNavigationLayout;
17
24
  /** Panels must be rendered as `TabsContent` descendants. */
18
25
  children?: JSX.Element;
19
26
  };
27
+ export type SettingsNavigationLayout = 'auto' | 'rail' | 'strip';
20
28
  /**
21
29
  * SettingsLayout.
22
30
  *
23
31
  * A controlled, vertical settings rail beside its scrollable panel viewport.
24
32
  * The host keeps ownership of selection, URL/hash state and panel content.
25
- * At narrow viewports the rail contracts while both columns remain shrinkable.
33
+ *
34
+ * Below 48rem the rail becomes a horizontal strip of tabs above the panels.
35
+ * A 6rem rail at phone width clips every label, so this is the default rather
36
+ * than an opt-in. It changes the tab root's orientation, not just its
37
+ * styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on
38
+ * the rail, Left/Right on the strip) follow the axis the reader sees. The
39
+ * breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,
40
+ * and is read before the first render so a phone never paints the rail. A
41
+ * server render, which has no viewport, renders the rail.
26
42
  */
27
43
  export declare function SettingsLayout(props: SettingsLayoutProps): JSX.Element;
28
44
  //# sourceMappingURL=settings-layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAsB,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAExF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,CAAC,OAAO,IAAI,CAAC,EAC3B,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CACnE,GAAG;IACF,gFAAgF;IAChF,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAA;IACpB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,+FAA+F;IAC/F,cAAc,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KAAK,IAAI,CAAC,CAAA;IAC7E,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,eA+BxD"}
1
+ {"version":3,"file":"settings-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAsB,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AAExF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,cAAc,CAAC,OAAO,IAAI,CAAC,EAC3B,UAAU,GAAG,aAAa,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CACnE,GAAG;IACF,gFAAgF;IAChF,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAA;IACpB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,+FAA+F;IAC/F,cAAc,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KAAK,IAAI,CAAC,CAAA;IAC7E;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,wBAAwB,CAAA;IAC3C,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAWhE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,eAgDxD"}
@@ -2,15 +2,28 @@ import { cn } from "../../../lib/utils.js";
2
2
  import { Tabs } from "../../ui/tabs/tabs.js";
3
3
  import { SettingsNavigation } from "./settings-navigation.js";
4
4
  import { createComponent, insert, mergeProps, template } from "solid-js/web";
5
- import { splitProps } from "solid-js";
5
+ import { createSignal, onCleanup, onMount, splitProps } from "solid-js";
6
6
  //#region src/components/composites/settings/settings-layout.tsx
7
7
  var _tmpl$ = /*#__PURE__*/ template(`<div data-slot=settings-layout-panels class="min-h-0 min-w-0 flex-1 overflow-y-auto p-4">`);
8
+ /** The `md` breakpoint the rail has always widened at; below it the rail becomes a strip. */
9
+ var RAIL_MEDIA_QUERY = "(min-width: 48rem)";
10
+ function railQuery() {
11
+ return typeof window === "undefined" || typeof window.matchMedia !== "function" ? void 0 : window.matchMedia(RAIL_MEDIA_QUERY);
12
+ }
8
13
  /**
9
14
  * SettingsLayout.
10
15
  *
11
16
  * A controlled, vertical settings rail beside its scrollable panel viewport.
12
17
  * The host keeps ownership of selection, URL/hash state and panel content.
13
- * At narrow viewports the rail contracts while both columns remain shrinkable.
18
+ *
19
+ * Below 48rem the rail becomes a horizontal strip of tabs above the panels.
20
+ * A 6rem rail at phone width clips every label, so this is the default rather
21
+ * than an opt-in. It changes the tab root's orientation, not just its
22
+ * styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on
23
+ * the rail, Left/Right on the strip) follow the axis the reader sees. The
24
+ * breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,
25
+ * and is read before the first render so a phone never paints the rail. A
26
+ * server render, which has no viewport, renders the rail.
14
27
  */
15
28
  function SettingsLayout(props) {
16
29
  const [local, rest] = splitProps(props, [
@@ -20,10 +33,30 @@ function SettingsLayout(props) {
20
33
  "aria-label",
21
34
  "value",
22
35
  "onReselect",
23
- "revealSelected"
36
+ "revealSelected",
37
+ "navigationLayout"
24
38
  ]);
39
+ const [wide, setWide] = createSignal(railQuery()?.matches ?? true);
40
+ onMount(() => {
41
+ const query = railQuery();
42
+ if (!query) return;
43
+ const update = () => setWide(query.matches);
44
+ update();
45
+ query.addEventListener("change", update);
46
+ onCleanup(() => query.removeEventListener("change", update));
47
+ });
48
+ const layout = () => {
49
+ const requested = local.navigationLayout ?? "auto";
50
+ if (requested !== "auto") return requested;
51
+ return wide() ? "rail" : "strip";
52
+ };
25
53
  return createComponent(Tabs, mergeProps(rest, {
26
- orientation: "vertical",
54
+ get orientation() {
55
+ return layout() === "rail" ? "vertical" : "horizontal";
56
+ },
57
+ get ["data-navigation-layout"]() {
58
+ return layout();
59
+ },
27
60
  get value() {
28
61
  return local.value;
29
62
  },
@@ -47,7 +80,9 @@ function SettingsLayout(props) {
47
80
  get revealSelected() {
48
81
  return local.revealSelected;
49
82
  },
50
- "class": "w-24 md:w-sidebar"
83
+ get ["class"]() {
84
+ return layout() === "rail" ? "w-24 md:w-sidebar" : void 0;
85
+ }
51
86
  }), (() => {
52
87
  var _el$ = _tmpl$();
53
88
  insert(_el$, () => local.children);
@@ -1 +1 @@
1
- {"version":3,"file":"settings-layout.js","names":["ComponentProps","JSX","splitProps","cn","Tabs","SettingsNavigation","SettingsNavigationGroup","SettingsLayoutProps","Omit","groups","value","onChange","onReselect","revealSelected","trigger","HTMLButtonElement","children","Element","SettingsLayout","props","local","rest","_$createComponent","_$mergeProps","orientation","class","aria-label","_el$","_tmpl$","_$insert","_$createComponent","_$mergeProps"],"sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Tabs } from '../../ui/tabs'\nimport { SettingsNavigation, type SettingsNavigationGroup } from './settings-navigation'\n\nexport type SettingsLayoutProps = Omit<\n ComponentProps<typeof Tabs>,\n 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'\n> & {\n /** Grouped navigation entries; the host owns their values and panel content. */\n groups: readonly SettingsNavigationGroup[]\n /** Accessible name for the vertical tab list. */\n 'aria-label': string\n /** The host-controlled selected destination. */\n value: string\n /** Updates the host-controlled selected destination. */\n onChange: (value: string) => void\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scroll. */\n revealSelected?: boolean | ((trigger: HTMLButtonElement | undefined) => void)\n /** Panels must be rendered as `TabsContent` descendants. */\n children?: JSX.Element\n}\n\n/**\n * SettingsLayout.\n *\n * A controlled, vertical settings rail beside its scrollable panel viewport.\n * The host keeps ownership of selection, URL/hash state and panel content.\n * At narrow viewports the rail contracts while both columns remain shrinkable.\n */\nexport function SettingsLayout(props: SettingsLayoutProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'children',\n 'groups',\n 'aria-label',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n\n return (\n <Tabs\n {...rest}\n orientation=\"vertical\"\n value={local.value}\n class={cn('min-h-0 min-w-0 flex-1 gap-0', local.class)}\n >\n <SettingsNavigation\n aria-label={local['aria-label']}\n groups={local.groups}\n value={local.value}\n onReselect={local.onReselect}\n revealSelected={local.revealSelected}\n class=\"w-24 md:w-sidebar\"\n />\n <div data-slot=\"settings-layout-panels\" class=\"min-h-0 min-w-0 flex-1 overflow-y-auto p-4\">\n {local.children}\n </div>\n </Tabs>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgBkB,eAAeC,OAA4B;CACzD,MAAM,CAACC,OAAOC,QAAQnB,WAAWiB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;CAAgB,CACjB;CAED,OAAAG,gBACGlB,MAAImB,WACCF,MAAI;EACRG,aAAW;EAAA,IACXd,QAAK;GAAA,OAAEU,MAAMV;EAAK;EAAA,KAAA,WAAA;GAAA,OACXP,GAAG,gCAAgCiB,MAAMK,KAAK;EAAC;EAAA,IAAAT,WAAA;GAAA,OAAA,CAAAM,gBAErDjB,oBAAkB;IAAA,KAAA,gBAAA;KAAA,OACLe,MAAM;IAAa;IAAA,IAC/BX,SAAM;KAAA,OAAEW,MAAMX;IAAM;IAAA,IACpBC,QAAK;KAAA,OAAEU,MAAMV;IAAK;IAAA,IAClBE,aAAU;KAAA,OAAEQ,MAAMR;IAAU;IAAA,IAC5BC,iBAAc;KAAA,OAAEO,MAAMP;IAAc;IAAA,SAAA;GAAA,CAAA,UAAA;IAAA,IAAAc,OAAAC,OAAA;IAAAC,OAAAF,YAInCP,MAAMJ,QAAQ;IAAA,OAAAW;GAAA,EAAA,CAAA,CAAA;EAAA;CAAA,CAAA,CAAA;AAIvB"}
1
+ {"version":3,"file":"settings-layout.js","names":["ComponentProps","JSX","createSignal","onCleanup","onMount","splitProps","cn","Tabs","SettingsNavigation","SettingsNavigationGroup","SettingsLayoutProps","Omit","groups","value","onChange","onReselect","revealSelected","trigger","HTMLButtonElement","navigationLayout","SettingsNavigationLayout","children","Element","RAIL_MEDIA_QUERY","railQuery","MediaQueryList","window","matchMedia","undefined","SettingsLayout","props","local","rest","wide","setWide","matches","query","update","addEventListener","removeEventListener","layout","requested","_$createComponent","_$mergeProps","orientation","data-navigation-layout","class","aria-label","_el$","_tmpl$","_$insert","_$createComponent","_$mergeProps"],"sources":["../../../../src/components/composites/settings/settings-layout.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { createSignal, onCleanup, onMount, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Tabs } from '../../ui/tabs'\nimport { SettingsNavigation, type SettingsNavigationGroup } from './settings-navigation'\n\nexport type SettingsLayoutProps = Omit<\n ComponentProps<typeof Tabs>,\n 'children' | 'orientation' | 'value' | 'defaultValue' | 'onChange'\n> & {\n /** Grouped navigation entries; the host owns their values and panel content. */\n groups: readonly SettingsNavigationGroup[]\n /** Accessible name for the tab list. */\n 'aria-label': string\n /** The host-controlled selected destination. */\n value: string\n /** Updates the host-controlled selected destination. */\n onChange: (value: string) => void\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scroll. */\n revealSelected?: boolean | ((trigger: HTMLButtonElement | undefined) => void)\n /**\n * How the section navigation sits beside the panels. `auto` (the default) is\n * the vertical rail from 48rem of viewport width up and a horizontal,\n * scrollable strip of tabs above the panels below it. `rail` and `strip` pin\n * one shape at every width.\n */\n navigationLayout?: SettingsNavigationLayout\n /** Panels must be rendered as `TabsContent` descendants. */\n children?: JSX.Element\n}\n\nexport type SettingsNavigationLayout = 'auto' | 'rail' | 'strip'\n\n/** The `md` breakpoint the rail has always widened at; below it the rail becomes a strip. */\nconst RAIL_MEDIA_QUERY = '(min-width: 48rem)'\n\nfunction railQuery(): MediaQueryList | undefined {\n return typeof window === 'undefined' || typeof window.matchMedia !== 'function'\n ? undefined\n : window.matchMedia(RAIL_MEDIA_QUERY)\n}\n\n/**\n * SettingsLayout.\n *\n * A controlled, vertical settings rail beside its scrollable panel viewport.\n * The host keeps ownership of selection, URL/hash state and panel content.\n *\n * Below 48rem the rail becomes a horizontal strip of tabs above the panels.\n * A 6rem rail at phone width clips every label, so this is the default rather\n * than an opt-in. It changes the tab root's orientation, not just its\n * styling: the list's `aria-orientation` and Kobalte's arrow keys (Up/Down on\n * the rail, Left/Right on the strip) follow the axis the reader sees. The\n * breakpoint is the viewport's, like CatalogBrowser's and ContextualSidebar's,\n * and is read before the first render so a phone never paints the rail. A\n * server render, which has no viewport, renders the rail.\n */\nexport function SettingsLayout(props: SettingsLayoutProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'children',\n 'groups',\n 'aria-label',\n 'value',\n 'onReselect',\n 'revealSelected',\n 'navigationLayout',\n ])\n\n const [wide, setWide] = createSignal(railQuery()?.matches ?? true)\n onMount(() => {\n const query = railQuery()\n if (!query) return\n const update = () => setWide(query.matches)\n update()\n query.addEventListener('change', update)\n onCleanup(() => query.removeEventListener('change', update))\n })\n const layout = (): 'rail' | 'strip' => {\n const requested = local.navigationLayout ?? 'auto'\n if (requested !== 'auto') return requested\n return wide() ? 'rail' : 'strip'\n }\n\n return (\n <Tabs\n {...rest}\n orientation={layout() === 'rail' ? 'vertical' : 'horizontal'}\n data-navigation-layout={layout()}\n value={local.value}\n class={cn('min-h-0 min-w-0 flex-1 gap-0', local.class)}\n >\n <SettingsNavigation\n aria-label={local['aria-label']}\n groups={local.groups}\n value={local.value}\n onReselect={local.onReselect}\n revealSelected={local.revealSelected}\n class={layout() === 'rail' ? 'w-24 md:w-sidebar' : undefined}\n />\n <div data-slot=\"settings-layout-panels\" class=\"min-h-0 min-w-0 flex-1 overflow-y-auto p-4\">\n {local.children}\n </div>\n </Tabs>\n )\n}\n"],"mappings":";;;;;;;;AAoCA,IAAMuB,mBAAmB;AAEzB,SAASC,YAAwC;CAC/C,OAAO,OAAOE,WAAW,eAAe,OAAOA,OAAOC,eAAe,aACjEC,KAAAA,IACAF,OAAOC,WAAWJ,gBAAgB;AACxC;;;;;;;;;;;;;;;;AAiBA,SAAgBM,eAAeC,OAA4B;CACzD,MAAM,CAACC,OAAOC,QAAQ3B,WAAWyB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAkB,CACnB;CAED,MAAM,CAACG,MAAMC,WAAWhC,aAAasB,UAAU,CAAC,EAAEW,WAAW,IAAI;CACjE/B,cAAc;EACZ,MAAMgC,QAAQZ,UAAU;EACxB,IAAI,CAACY,OAAO;EACZ,MAAMC,eAAeH,QAAQE,MAAMD,OAAO;EAC1CE,OAAO;EACPD,MAAME,iBAAiB,UAAUD,MAAM;EACvClC,gBAAgBiC,MAAMG,oBAAoB,UAAUF,MAAM,CAAC;CAC7D,CAAC;CACD,MAAMG,eAAiC;EACrC,MAAMC,YAAYV,MAAMZ,oBAAoB;EAC5C,IAAIsB,cAAc,QAAQ,OAAOA;EACjC,OAAOR,KAAK,IAAI,SAAS;CAC3B;CAEA,OAAAS,gBACGnC,MAAIoC,WACCX,MAAI;EAAA,IACRY,cAAW;GAAA,OAAEJ,OAAO,MAAM,SAAS,aAAa;EAAY;EAAA,KAAA,4BAAA;GAAA,OACpCA,OAAO;EAAC;EAAA,IAChC3B,QAAK;GAAA,OAAEkB,MAAMlB;EAAK;EAAA,KAAA,WAAA;GAAA,OACXP,GAAG,gCAAgCyB,MAAMe,KAAK;EAAC;EAAA,IAAAzB,WAAA;GAAA,OAAA,CAAAqB,gBAErDlC,oBAAkB;IAAA,KAAA,gBAAA;KAAA,OACLuB,MAAM;IAAa;IAAA,IAC/BnB,SAAM;KAAA,OAAEmB,MAAMnB;IAAM;IAAA,IACpBC,QAAK;KAAA,OAAEkB,MAAMlB;IAAK;IAAA,IAClBE,aAAU;KAAA,OAAEgB,MAAMhB;IAAU;IAAA,IAC5BC,iBAAc;KAAA,OAAEe,MAAMf;IAAc;IAAA,KAAA,WAAA;KAAA,OAC7BwB,OAAO,MAAM,SAAS,sBAAsBZ,KAAAA;IAAS;GAAA,CAAA,UAAA;IAAA,IAAAoB,OAAAC,OAAA;IAAAC,OAAAF,YAG3DjB,MAAMV,QAAQ;IAAA,OAAA2B;GAAA,EAAA,CAAA,CAAA;EAAA;CAAA,CAAA,CAAA;AAIvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"settings-navigation.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInC,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAGzE,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,aAAa,EACb,YAAY,GAAG,UAAU,GAAG,aAAa,CAC1C,GAAG;IACF,6EAA6E;IAC7E,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;iGAC6F;IAC7F,cAAc,CAAC,EACX,OAAO,GACP,CAAC,CACC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KACnC,IAAI,CAAC,CAAA,CAAC,8EAA8E;IAC7F,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AA0CD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,eAwFhE"}
1
+ {"version":3,"file":"settings-navigation.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInC,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAGzE,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,kEAAkE;AAClE,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,SAAS,sBAAsB,EAAE,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,IAAI,CACxC,aAAa,EACb,YAAY,GAAG,UAAU,GAAG,aAAa,CAC1C,GAAG;IACF,6EAA6E;IAC7E,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC1C,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC;iGAC6F;IAC7F,cAAc,CAAC,EACX,OAAO,GACP,CAAC,CACC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KACnC,IAAI,CAAC,CAAA,CAAC,8EAA8E;IAC7F,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAkDD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,eA2GhE"}
@@ -2,11 +2,12 @@ import { cn } from "../../../lib/utils.js";
2
2
  import { Text } from "../../ui/typography/typography.js";
3
3
  import { TabsList, TabsTrigger } from "../../ui/tabs/tabs.js";
4
4
  import { sidebarNavItemClass, sidebarNavItemStateClass } from "../../layout/sidebar-nav/sidebar-nav.js";
5
- import { createComponent, insert, memo, mergeProps, setAttribute, template } from "solid-js/web";
6
- import { createEffect, createUniqueId, onCleanup, splitProps } from "solid-js";
5
+ import { className, createComponent, effect, insert, memo, mergeProps, setAttribute, template } from "solid-js/web";
6
+ import { Show, createEffect, createUniqueId, onCleanup, splitProps } from "solid-js";
7
+ import { useTabsContext } from "@kobalte/core/tabs";
7
8
  //#region src/components/composites/settings/settings-navigation.tsx
8
- var _tmpl$ = /*#__PURE__*/ template(`<div role=none data-slot=settings-navigation-group class="flex min-w-0 flex-col gap-0.5"><span class=sr-only>`);
9
- var _tmpl$2 = /*#__PURE__*/ template(`<span class="min-w-0 truncate">`);
9
+ var _tmpl$ = /*#__PURE__*/ template(`<div role=none data-slot=settings-navigation-group><span class=sr-only>`);
10
+ var _tmpl$2 = /*#__PURE__*/ template(`<span>`);
10
11
  /**
11
12
  * SettingsNavigation.
12
13
  *
@@ -15,6 +16,14 @@ var _tmpl$2 = /*#__PURE__*/ template(`<span class="min-w-0 truncate">`);
15
16
  * the controlled value, panel content, icons and any persistence or routing.
16
17
  * Use ordinary tabs for a short, ungrouped row of view choices.
17
18
  *
19
+ * The shape follows the enclosing `Tabs` orientation rather than a prop of its
20
+ * own, so the visual axis can never disagree with the arrow keys Kobalte
21
+ * binds: `vertical` is the grouped rail, `horizontal` is a single scrollable
22
+ * strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the
23
+ * groups in order, separated by a rule; their visible headings drop out, since
24
+ * an overline between tabs reads as another tab, but each tab still carries its
25
+ * group as its accessible description.
26
+ *
18
27
  * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This
19
28
  * component adds no URL, storage or host-specific state. Set
20
29
  * `revealSelected={false}` when the surrounding layout does not use an internal
@@ -52,11 +61,14 @@ function SettingsNavigation(props) {
52
61
  "onReselect",
53
62
  "revealSelected"
54
63
  ]);
64
+ const tabs = useTabsContext();
65
+ const strip = () => tabs.orientation() === "horizontal";
55
66
  const triggers = /* @__PURE__ */ new Map();
56
67
  const groupIdPrefix = `settings-navigation-${createUniqueId()}`;
57
68
  createEffect(() => {
58
69
  const value = local.value;
59
70
  const reveal = local.revealSelected;
71
+ strip();
60
72
  if (reveal === false) return;
61
73
  const revealCurrent = () => {
62
74
  const trigger = triggers.get(value);
@@ -72,9 +84,11 @@ function SettingsNavigation(props) {
72
84
  get ["aria-label"]() {
73
85
  return local["aria-label"];
74
86
  },
75
- orientation: "vertical",
87
+ get orientation() {
88
+ return tabs.orientation();
89
+ },
76
90
  get ["class"]() {
77
- return cn("flex max-h-full min-h-0 min-w-0 shrink-0 flex-col items-stretch justify-start gap-2 overflow-x-hidden overflow-y-auto px-2 py-2", local.class);
91
+ return cn("relative flex min-w-0 shrink-0 justify-start", strip() ? "scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5" : "max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2", local.class);
78
92
  }
79
93
  }, rest, { get children() {
80
94
  return local.groups.map((group, groupIndex) => {
@@ -83,18 +97,24 @@ function SettingsNavigation(props) {
83
97
  var _el$ = _tmpl$(), _el$2 = _el$.firstChild;
84
98
  setAttribute(_el$2, "id", groupId);
85
99
  insert(_el$2, () => group.label);
86
- insert(_el$, createComponent(Text, {
87
- variant: "overline",
88
- tone: "inherit",
89
- "aria-hidden": "true",
90
- "class": "px-2 py-1.5 text-sidebar-muted-foreground",
100
+ insert(_el$, createComponent(Show, {
101
+ get when() {
102
+ return !strip();
103
+ },
91
104
  get children() {
92
- return group.label;
105
+ return createComponent(Text, {
106
+ variant: "overline",
107
+ tone: "inherit",
108
+ "aria-hidden": "true",
109
+ "class": "px-2 py-1.5 text-sidebar-muted-foreground",
110
+ get children() {
111
+ return group.label;
112
+ }
113
+ });
93
114
  }
94
115
  }), null);
95
116
  insert(_el$, () => group.items.map((item) => createComponent(TabsTrigger, {
96
117
  "data-slot": "settings-navigation-trigger",
97
- orientation: "vertical",
98
118
  "aria-describedby": groupId,
99
119
  get value() {
100
120
  return item.value;
@@ -110,16 +130,18 @@ function SettingsNavigation(props) {
110
130
  if (local.value === item.value) local.onReselect?.(item.value);
111
131
  },
112
132
  get ["class"]() {
113
- return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg w-full justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground");
133
+ return cn(sidebarNavItemClass, sidebarNavItemStateClass.idle, "h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground", strip() ? "w-auto shrink-0" : "w-full");
114
134
  },
115
135
  get children() {
116
136
  return [memo(() => item.icon), (() => {
117
137
  var _el$3 = _tmpl$2();
118
138
  insert(_el$3, () => item.label);
139
+ effect(() => className(_el$3, cn("min-w-0", { truncate: !strip() })));
119
140
  return _el$3;
120
141
  })()];
121
142
  }
122
143
  })), null);
144
+ effect(() => className(_el$, cn("flex gap-0.5", strip() ? "shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2" : "min-w-0 flex-col")));
123
145
  return _el$;
124
146
  })();
125
147
  });
@@ -1 +1 @@
1
- {"version":3,"file":"settings-navigation.js","names":["JSX","createEffect","createUniqueId","onCleanup","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","orientation","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation=\"vertical\"\n class={cn(\n 'flex max-h-full min-h-0 min-w-0 shrink-0 flex-col items-stretch justify-start gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class=\"flex min-w-0 flex-col gap-0.5\"\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n orientation=\"vertical\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg w-full justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground'\n )}\n >\n {item.icon}\n <span class=\"min-w-0 truncate\">{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"settings-navigation.js","names":["useTabsContext","JSX","createEffect","createUniqueId","onCleanup","Show","splitProps","cn","sidebarNavItemClass","sidebarNavItemStateClass","TabsList","TabsTrigger","TabsListProps","Text","SettingsNavigationItem","value","label","icon","Element","disabled","SettingsNavigationGroup","items","SettingsNavigationProps","Omit","groups","onReselect","revealSelected","trigger","HTMLButtonElement","scrollContainerNearest","container","HTMLElement","row","style","window","getComputedStyle","box","getBoundingClientRect","rowBox","overflowX","left","scrollLeft","right","overflowY","top","scrollTop","bottom","revealNearest","parentElement","SettingsNavigation","props","local","rest","tabs","strip","orientation","triggers","Map","groupIdPrefix","reveal","revealCurrent","get","addEventListener","removeEventListener","_$createComponent","_$mergeProps","aria-label","class","children","map","group","groupIndex","groupId","_el$","_tmpl$","_el$2","firstChild","_$setAttribute","_$insert","when","variant","tone","item","ref","element","set","delete","onClick","idle","_$memo","_el$3","_tmpl$2","_$effect","_$className","truncate","_$createComponent","_$mergeProps","_$memo","_$className"],"sources":["../../../../src/components/composites/settings/settings-navigation.tsx"],"sourcesContent":["import { useTabsContext } from '@kobalte/core/tabs'\nimport type { JSX } from 'solid-js'\nimport { createEffect, createUniqueId, onCleanup, Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { sidebarNavItemClass, sidebarNavItemStateClass } from '../../layout/sidebar-nav/sidebar-nav'\nimport { TabsList, TabsTrigger, type TabsListProps } from '../../ui/tabs'\nimport { Text } from '../../ui/typography'\n\n/** One host-owned destination in a settings tab list. Values must be unique. */\nexport type SettingsNavigationItem = {\n value: string\n label: string\n icon?: JSX.Element\n disabled?: boolean\n}\n\n/** A visible, accessibly named group of settings destinations. */\nexport type SettingsNavigationGroup = {\n label: string\n items: readonly SettingsNavigationItem[]\n}\n\nexport type SettingsNavigationProps = Omit<\n TabsListProps,\n 'appearance' | 'children' | 'orientation'\n> & {\n /** Groups are presentation only; the host owns the tab values and panels. */\n groups: readonly SettingsNavigationGroup[]\n /** The same controlled value supplied to the enclosing `Tabs`. */\n value: string\n /** Called when the already-selected destination is activated again. */\n onReselect?: (value: string) => void\n /** Reveal the selected row on value changes and window resizes. Defaults to nearest scrolling\n * of the ancestors that expose a scroll affordance; hidden-overflow ancestors never move. */\n revealSelected?:\n | boolean\n | ((\n trigger: HTMLButtonElement | undefined\n ) => void) /** Every tab list needs a label independent of its visible group headings. */\n 'aria-label': string\n}\n\n/**\n * SettingsNavigation.\n *\n * A grouped vertical tab list for sibling settings panels. It composes the\n * shared Tabs behaviour with the shared sidebar row recipe; the host supplies\n * the controlled value, panel content, icons and any persistence or routing.\n * Use ordinary tabs for a short, ungrouped row of view choices.\n *\n * The shape follows the enclosing `Tabs` orientation rather than a prop of its\n * own, so the visual axis can never disagree with the arrow keys Kobalte\n * binds: `vertical` is the grouped rail, `horizontal` is a single scrollable\n * strip of tabs (what `SettingsLayout` shows below 48rem). The strip keeps the\n * groups in order, separated by a rule; their visible headings drop out, since\n * an overline between tabs reads as another tab, but each tab still carries its\n * group as its accessible description.\n *\n * Kobalte owns roving focus, arrow keys, selection and tab/panel ARIA. This\n * component adds no URL, storage or host-specific state. Set\n * `revealSelected={false}` when the surrounding layout does not use an internal\n * scrolling viewport.\n */\n/**\n * Reveal by scrolling only the ancestors that expose a scroll affordance\n * (`auto` or `scroll`), by the exact nearest-edge delta. `scrollIntoView` also\n * pans `overflow: hidden` ancestors, which are programmatically scrollable but\n * give the user no way back, so one over-pan left earlier rows permanently\n * clipped outside the viewport with no scroll to recover them (#182).\n */\nfunction scrollContainerNearest(container: HTMLElement, row: HTMLElement): void {\n const style = window.getComputedStyle(container)\n const box = container.getBoundingClientRect()\n const rowBox = row.getBoundingClientRect()\n if (style.overflowX === 'auto' || style.overflowX === 'scroll') {\n if (rowBox.left < box.left) container.scrollLeft += rowBox.left - box.left\n else if (rowBox.right > box.right) container.scrollLeft += rowBox.right - box.right\n }\n if (style.overflowY === 'auto' || style.overflowY === 'scroll') {\n if (rowBox.top < box.top) container.scrollTop += rowBox.top - box.top\n else if (rowBox.bottom > box.bottom) container.scrollTop += rowBox.bottom - box.bottom\n }\n}\n\nfunction revealNearest(row: HTMLElement): void {\n for (let container = row.parentElement; container; container = container.parentElement) {\n scrollContainerNearest(container, row)\n }\n}\n\nexport function SettingsNavigation(props: SettingsNavigationProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'aria-label',\n 'groups',\n 'value',\n 'onReselect',\n 'revealSelected',\n ])\n const tabs = useTabsContext()\n const strip = () => tabs.orientation() === 'horizontal'\n const triggers = new Map<string, HTMLButtonElement>()\n const groupIdPrefix = `settings-navigation-${createUniqueId()}`\n\n createEffect(() => {\n const value = local.value\n const reveal = local.revealSelected\n // Tracked so a rail that becomes a strip (or back) re-reveals on the new axis.\n strip()\n if (reveal === false) return\n\n const revealCurrent = () => {\n const trigger = triggers.get(value)\n if (typeof reveal === 'function') reveal(trigger)\n else if (trigger) revealNearest(trigger)\n }\n\n revealCurrent()\n window.addEventListener('resize', revealCurrent)\n onCleanup(() => window.removeEventListener('resize', revealCurrent))\n })\n\n return (\n <TabsList\n data-slot=\"settings-navigation\"\n aria-label={local['aria-label']}\n orientation={tabs.orientation()}\n class={cn(\n // Positioned so the visually hidden group names are laid out (and\n // clipped) inside the list: unpositioned, their absolute boxes escape a\n // scrolled strip and widen the page by however far it has scrolled.\n 'relative flex min-w-0 shrink-0 justify-start',\n strip()\n ? // The block padding is the room the focus ring needs: a scroller\n // clips at its padding edge, and this one scrolls sideways only.\n 'scroll-fade w-full flex-row items-center gap-1 overflow-x-auto overflow-y-hidden border-b border-border px-2 py-1.5'\n : 'max-h-full min-h-0 flex-col items-stretch gap-2 overflow-x-hidden overflow-y-auto px-2 py-2',\n local.class\n )}\n {...rest}\n >\n {local.groups.map((group, groupIndex) => {\n const groupId = `${groupIdPrefix}-group-${groupIndex}`\n return (\n <div\n role=\"none\"\n data-slot=\"settings-navigation-group\"\n class={cn(\n 'flex gap-0.5',\n strip()\n ? 'shrink-0 flex-row items-center not-first:ms-1 not-first:border-s not-first:border-border not-first:ps-2'\n : 'min-w-0 flex-col'\n )}\n >\n <span id={groupId} class=\"sr-only\">\n {group.label}\n </span>\n <Show when={!strip()}>\n {/* The overline role on the sidebar's own muted foreground. */}\n <Text\n variant=\"overline\"\n tone=\"inherit\"\n aria-hidden=\"true\"\n class=\"px-2 py-1.5 text-sidebar-muted-foreground\"\n >\n {group.label}\n </Text>\n </Show>\n {group.items.map((item) => (\n <TabsTrigger\n data-slot=\"settings-navigation-trigger\"\n aria-describedby={groupId}\n value={item.value}\n disabled={item.disabled}\n ref={(element: HTMLButtonElement | undefined) => {\n if (element) triggers.set(item.value, element)\n else triggers.delete(item.value)\n }}\n onClick={() => {\n if (local.value === item.value) local.onReselect?.(item.value)\n }}\n class={cn(\n sidebarNavItemClass,\n sidebarNavItemStateClass.idle,\n 'h-row-lg justify-start border-0 px-3 py-2 text-left data-[selected]:bg-primary-subtle data-[selected]:text-foreground',\n strip() ? 'w-auto shrink-0' : 'w-full'\n )}\n >\n {item.icon}\n <span class={cn('min-w-0', { truncate: !strip() })}>{item.label}</span>\n </TabsTrigger>\n ))}\n </div>\n )\n })}\n </TabsList>\n )\n}\n"],"mappings":"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"}
@@ -57,6 +57,19 @@ export type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {
57
57
  showLineNumbers?: boolean;
58
58
  /** The height above which the action row is repeated below the block. */
59
59
  tallThreshold?: number;
60
+ /**
61
+ * Cap the code body's height and scroll it vertically past that. A number is
62
+ * pixels; a string is any CSS length (`'20rem'`, `'50vh'`). Unset, the block
63
+ * grows with its content.
64
+ */
65
+ maxHeight?: number | string;
66
+ /**
67
+ * Soft-wrap long lines inside the block instead of scrolling sideways. Off by
68
+ * default: wrapped code loses its column alignment, so it is for prose-like
69
+ * output (logs, long commands) in a narrow column. Wraps the plain renderer and
70
+ * any `pre` inside `highlight` output; line numbers stay on the first visual line.
71
+ */
72
+ wrap?: boolean;
60
73
  };
61
74
  /** The height a block must exceed before its actions are repeated. */
62
75
  export declare const TALL_CODE_BLOCK_PX = 480;
@@ -1 +1 @@
1
- {"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/code-block/code-block.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,OAAO,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAM,CAAA;AAkBrC,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,eAyH9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,eAYvD"}
1
+ {"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/code-block/code-block.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACnB,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oFAAoF;IACpF,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,OAAO,CAAA;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qDAAqD;IACrD,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,yEAAyE;IACzE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAM,CAAA;AAkBrC,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,eAqI9C;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,eAYvD"}
@@ -1,13 +1,13 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { Button } from "../button/button.js";
3
- import { createComponent, effect, insert, memo, mergeProps, setAttribute, setStyleProperty, spread, template, use } from "solid-js/web";
3
+ import { className, createComponent, effect, insert, memo, mergeProps, setAttribute, setStyleProperty, spread, style, template, use } from "solid-js/web";
4
4
  import { Check, Copy } from "lucide-solid";
5
5
  import { For, Show, createMemo, createSignal, splitProps } from "solid-js";
6
6
  //#region src/components/ui/code-block/code-block.tsx
7
7
  var _tmpl$ = /*#__PURE__*/ template(`<div class="flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100">`);
8
8
  var _tmpl$2 = /*#__PURE__*/ template(`<p class="px-3 pb-2 text-xs text-muted-foreground italic">Generating…`);
9
9
  var _tmpl$3 = /*#__PURE__*/ template(`<div data-code-block-footer class="flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border">`);
10
- var _tmpl$4 = /*#__PURE__*/ template(`<div><div class="flex items-center justify-between gap-2 px-3 py-1"><span class="truncate font-code text-code text-muted-foreground"></span></div><div class="scroll-fade overflow-x-auto"tabindex=0 role=region>`);
10
+ var _tmpl$4 = /*#__PURE__*/ template(`<div><div class="flex items-center justify-between gap-2 px-3 py-1"><span class="truncate font-code text-code text-muted-foreground"></span></div><div data-slot=code-block-body tabindex=0 role=region>`);
11
11
  var _tmpl$5 = /*#__PURE__*/ template(`<pre class="m-0 px-3 py-2"><code class="font-code text-code">`);
12
12
  var _tmpl$6 = /*#__PURE__*/ template(`<span class="grid grid-cols-[3ch_1fr] gap-3"><span class="text-right text-muted-foreground select-none"aria-hidden=true></span><span>`);
13
13
  var _tmpl$7 = /*#__PURE__*/ template(`<div class="px-3 py-2">`);
@@ -42,6 +42,8 @@ function CodeBlock(props) {
42
42
  "lineHeight",
43
43
  "showLineNumbers",
44
44
  "tallThreshold",
45
+ "maxHeight",
46
+ "wrap",
45
47
  "class",
46
48
  "ref"
47
49
  ]);
@@ -51,6 +53,7 @@ function CodeBlock(props) {
51
53
  const label = () => local.title ?? local.language ?? "code";
52
54
  const regionLabel = () => local.language ? `${local.language} code` : "code";
53
55
  const lines = createMemo(() => local.code.replace(/\n$/, "").split("\n"));
56
+ const maxHeight = () => typeof local.maxHeight === "number" ? `${local.maxHeight}px` : local.maxHeight;
54
57
  const copy = async () => {
55
58
  const ok = await copyText(local.code);
56
59
  setCopied(ok);
@@ -123,6 +126,7 @@ function CodeBlock(props) {
123
126
  var _el$0 = _tmpl$6(), _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling;
124
127
  insert(_el$1, () => index() + 1);
125
128
  insert(_el$10, line || " ");
129
+ effect(() => className(_el$10, cn({ "min-w-0": local.wrap })));
126
130
  return _el$0;
127
131
  })()
128
132
  });
@@ -156,7 +160,22 @@ function CodeBlock(props) {
156
160
  return _el$7;
157
161
  }
158
162
  }), null);
159
- effect(() => setAttribute(_el$5, "aria-label", regionLabel()));
163
+ effect((_p$) => {
164
+ var _v$ = local.wrap ? "" : void 0, _v$2 = cn("scroll-fade overflow-x-auto", {
165
+ "overflow-y-auto": local.maxHeight !== void 0,
166
+ "[&_pre]:whitespace-pre-wrap [&_pre]:[overflow-wrap:anywhere]": local.wrap
167
+ }), _v$3 = maxHeight() === void 0 ? void 0 : { "max-height": maxHeight() }, _v$4 = regionLabel();
168
+ _v$ !== _p$.e && setAttribute(_el$5, "data-wrap", _p$.e = _v$);
169
+ _v$2 !== _p$.t && className(_el$5, _p$.t = _v$2);
170
+ _p$.a = style(_el$5, _v$3, _p$.a);
171
+ _v$4 !== _p$.o && setAttribute(_el$5, "aria-label", _p$.o = _v$4);
172
+ return _p$;
173
+ }, {
174
+ e: void 0,
175
+ t: void 0,
176
+ a: void 0,
177
+ o: void 0
178
+ });
160
179
  return _el$2;
161
180
  })();
162
181
  }
@@ -1 +1 @@
1
- {"version":3,"file":"code-block.js","names":["createMemo","createSignal","For","Show","splitProps","ComponentProps","JSX","Check","Copy","Button","cn","CodeBlockProps","Omit","code","language","highlight","Element","complete","title","actions","copyLabel","copiedLabel","lineHeight","showLineNumbers","tallThreshold","TALL_CODE_BLOCK_PX","copyText","text","Promise","navigator","clipboard","writeText","CodeBlock","props","local","rest","copied","setCopied","failed","setFailed","tall","setTall","label","regionLabel","lines","replace","split","copy","ok","setTimeout","Actions","_el$","_tmpl$","_$insert","_$createComponent","variant","size","aria-label","_$memo","undefined","onClick","children","when","fallback","_el$2","_tmpl$4","_el$3","firstChild","_el$4","_el$5","nextSibling","_$use","element","ref","observer","ResizeObserver","offsetHeight","observe","_$spread","_$mergeProps","class","_el$8","_tmpl$5","_el$9","each","line","index","_el$0","_tmpl$6","_el$1","_el$10","_$effect","_$p","_$setStyleProperty","_el$11","_tmpl$7","_tmpl$2","_el$7","_tmpl$3","_$setAttribute","InlineCode","_el$12","_tmpl$8","_$createComponent","_$memo","_$mergeProps","_$setStyleProperty","_$setAttribute"],"sources":["../../../../src/components/ui/code-block/code-block.tsx"],"sourcesContent":["import {\n createMemo,\n createSignal,\n For,\n Show,\n splitProps,\n type ComponentProps,\n type JSX,\n} from 'solid-js'\nimport { Check, Copy } from 'lucide-solid'\nimport { Button } from '../button'\nimport { cn } from '#lib/utils'\n\n/**\n * CodeBlock.\n *\n * A fenced block, rendered as a surface. The library ships no highlighter —\n * syntax colouring is a large dependency with its own theme format, and a\n * component that bundled one would force every consumer to pay for it. This\n * renders the block, its chrome, and its behaviour; a caller that wants\n * highlighting passes the result through `highlight`, which is the seam a\n * Shiki/Pierre/Prism integration plugs into.\n *\n * Four details are the reason this exists rather than a styled `<pre>`:\n *\n * **Copy confirms only on success.** `navigator.clipboard.writeText` rejects\n * outside a secure context, in a sandboxed frame, and when the document is not\n * focused. Flipping to a tick unconditionally confirms a copy that did not\n * happen, and the reader pastes something stale and does not know why. The\n * rejection is caught and the button reports the failure instead.\n *\n * **A tall block repeats its action row at the bottom.** Copying from a block\n * that is taller than the viewport otherwise costs a scroll back to the top and\n * another scroll down to where you were. The threshold is a fixed 480px rather\n * than viewport-relative so the behaviour does not change when the window is\n * resized.\n *\n * **The scroller is focusable and named.** A horizontally scrollable region has\n * to be reachable by keyboard — this is axe's `scrollable-region-focusable`, and\n * a region that scrolls but cannot be focused is a WCAG 2.1.1 failure. It is a\n * `region` with a label rather than a bare `tabindex`, so the tab stop announces\n * what it is.\n *\n * **Resolving a streaming block must not move the reader.** When `complete` is\n * false the block shows the code it has plus a generating indicator; the\n * indicator is inside the scroller's own box and does not add a line, because a\n * block that grows the moment its content resolves shifts everything below it.\n * If a caller's `highlight` changes the line box, that is the caller's height to\n * hold — `lineHeight` is a prop for exactly that reason.\n */\nexport type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {\n code: string\n /** The language, shown in the header and used as the region's label. */\n language?: string\n /** The code, transformed — highlighted HTML or a component. Omit for plain text. */\n highlight?: (code: string) => JSX.Element\n /** False while the block is still streaming. Adds a generating indicator. */\n complete?: boolean\n /** A filename or title, shown instead of the language. */\n title?: string\n /** Extra header controls, before the copy button. */\n actions?: JSX.Element\n /** Copy label. Override for a non-English application. */\n copyLabel?: string\n copiedLabel?: string\n /** Line height of the rendered code. Set it when `highlight` changes the box. */\n lineHeight?: string\n /** A line-number gutter. Off by default; a short snippet rarely needs one. */\n showLineNumbers?: boolean\n /** The height above which the action row is repeated below the block. */\n tallThreshold?: number\n}\n\n/** The height a block must exceed before its actions are repeated. */\nexport const TALL_CODE_BLOCK_PX = 480\n\n/**\n * Copy, reporting whether it worked. `navigator.clipboard` is absent in an\n * insecure context, and `writeText` rejects when the document is not focused —\n * both are normal in an embedded or iframed application, and neither should\n * silently look like success.\n */\nasync function copyText(text: string): Promise<boolean> {\n try {\n if (!navigator.clipboard) return false\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n\nexport function CodeBlock(props: CodeBlockProps) {\n const [local, rest] = splitProps(props, [\n 'code',\n 'language',\n 'highlight',\n 'complete',\n 'title',\n 'actions',\n 'copyLabel',\n 'copiedLabel',\n 'lineHeight',\n 'showLineNumbers',\n 'tallThreshold',\n 'class',\n 'ref',\n ])\n\n const [copied, setCopied] = createSignal(false)\n const [failed, setFailed] = createSignal(false)\n const [tall, setTall] = createSignal(false)\n\n const label = () => local.title ?? local.language ?? 'code'\n const regionLabel = () => (local.language ? `${local.language} code` : 'code')\n const lines = createMemo(() => local.code.replace(/\\n$/, '').split('\\n'))\n\n const copy = async () => {\n const ok = await copyText(local.code)\n setCopied(ok)\n setFailed(!ok)\n if (ok) setTimeout(() => setCopied(false), 1500)\n }\n\n const Actions = () => (\n <div class=\"flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100\">\n {local.actions}\n <Button\n variant=\"ghost\"\n size=\"icon-2xs\"\n aria-label={copied() ? (local.copiedLabel ?? 'Copied') : (local.copyLabel ?? 'Copy code')}\n title={failed() ? 'Copying is unavailable in this context' : undefined}\n onClick={() => void copy()}\n >\n <Show when={copied()} fallback={<Copy aria-hidden=\"true\" />}>\n <Check aria-hidden=\"true\" />\n </Show>\n </Button>\n </div>\n )\n\n return (\n <div\n class={cn('group/code overflow-hidden rounded-xl border border-border bg-card', local.class)}\n {...rest}\n ref={(element) => {\n // Composed rather than replaced: a caller's ref must survive.\n if (typeof local.ref === 'function') local.ref(element)\n else if (local.ref) local.ref = element\n // Measured rather than assumed: the caller controls the content, so only\n // the rendered box can say whether the action row has scrolled away.\n const observer = new ResizeObserver(() => {\n setTall(element.offsetHeight > (local.tallThreshold ?? TALL_CODE_BLOCK_PX))\n })\n observer.observe(element)\n }}\n >\n <div class=\"flex items-center justify-between gap-2 px-3 py-1\">\n <span class=\"truncate font-code text-code text-muted-foreground\">{label()}</span>\n <Actions />\n </div>\n <div\n class=\"scroll-fade overflow-x-auto\"\n // A region that scrolls must be focusable, and a tab stop needs a name.\n tabindex=\"0\"\n role=\"region\"\n aria-label={regionLabel()}\n >\n <Show\n when={local.highlight}\n fallback={\n <pre class=\"m-0 px-3 py-2\" style={{ 'line-height': local.lineHeight ?? '1.5' }}>\n <code class=\"font-code text-code\">\n <Show when={local.showLineNumbers} fallback={local.code.replace(/\\n$/, '')}>\n <For each={lines()}>\n {(line, index) => (\n <span class=\"grid grid-cols-[3ch_1fr] gap-3\">\n <span\n class=\"text-right text-muted-foreground select-none\"\n aria-hidden=\"true\"\n >\n {index() + 1}\n </span>\n <span>{line || ' '}</span>\n </span>\n )}\n </For>\n </Show>\n </code>\n </pre>\n }\n >\n {(highlight) => <div class=\"px-3 py-2\">{highlight()(local.code)}</div>}\n </Show>\n {/*\n In the scroller's box, and a single line: the indicator must not add\n height when it disappears, or a streaming block shifts everything below\n it as it resolves.\n */}\n <Show when={!local.complete}>\n <p class=\"px-3 pb-2 text-xs text-muted-foreground italic\">Generating…</p>\n </Show>\n </div>\n <Show when={tall()}>\n <div\n data-code-block-footer=\"\"\n class=\"flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border\"\n >\n <Actions />\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * InlineCode.\n *\n * The `code` inside a sentence. It is a component rather than a class because it\n * has to hold three properties at once: it uses the shared code family and size\n * preference, and it wraps rather than overflows a narrow column\n * (`overflow-wrap: anywhere` — an identifier with\n * no spaces is one long token), and it must be legible against whatever surface\n * it lands on, which is why the fill is a token and not a fixed grey.\n */\nexport function InlineCode(props: ComponentProps<'code'>) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <code\n class={cn(\n 'rounded-sm bg-muted px-[0.4em] py-[0.15em] font-code text-code text-foreground [overflow-wrap:anywhere]',\n local.class\n )}\n {...rest}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0EA,IAAayB,qBAAqB;;;;;;;AAQlC,eAAeC,SAASC,MAAgC;CACtD,IAAI;EACF,IAAI,CAACE,UAAUC,WAAW,OAAO;EACjC,MAAMD,UAAUC,UAAUC,UAAUJ,IAAI;EACxC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgBK,UAAUC,OAAuB;CAC/C,MAAM,CAACC,OAAOC,QAAQ/B,WAAW6B,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAK,CACN;CAED,MAAM,CAACG,QAAQC,aAAapC,aAAa,KAAK;CAC9C,MAAM,CAACqC,QAAQC,aAAatC,aAAa,KAAK;CAC9C,MAAM,CAACuC,MAAMC,WAAWxC,aAAa,KAAK;CAE1C,MAAMyC,cAAcR,MAAMhB,SAASgB,MAAMpB,YAAY;CACrD,MAAM6B,oBAAqBT,MAAMpB,WAAW,GAAGoB,MAAMpB,SAAQ,SAAU;CACvE,MAAM8B,QAAQ5C,iBAAiBkC,MAAMrB,KAAKgC,QAAQ,OAAO,EAAE,CAAC,CAACC,MAAM,IAAI,CAAC;CAExE,MAAMC,OAAO,YAAY;EACvB,MAAMC,KAAK,MAAMtB,SAASQ,MAAMrB,IAAI;EACpCwB,UAAUW,EAAE;EACZT,UAAU,CAACS,EAAE;EACb,IAAIA,IAAIC,iBAAiBZ,UAAU,KAAK,GAAG,IAAI;CACjD;CAEA,MAAMa,uBAAU;EAAA,IAAAC,OAAAC,OAAA;EAAAC,OAAAF,YAEXjB,MAAMf,SAAO,IAAA;EAAAkC,OAAAF,MAAAG,gBACb7C,QAAM;GACL8C,SAAO;GACPC,MAAI;GAAA,KAAA,gBAAA;IAAA,OACQE,WAAA,CAAA,CAAAtB,OAAO,CAAC,CAAA,CAAA,IAAIF,MAAMb,eAAe,WAAaa,MAAMd,aAAa;GAAY;GAAA,IACzFF,QAAK;IAAA,OAAEoB,OAAO,IAAI,2CAA2CqB,KAAAA;GAAS;GACtEC,eAAe,KAAKb,KAAK;GAAC,IAAAc,WAAA;IAAA,OAAAP,gBAEzBnD,MAAI;KAAA,IAAC2D,OAAI;MAAA,OAAE1B,OAAO;KAAC;KAAA,IAAE2B,WAAQ;MAAA,OAAAT,gBAAG9C,MAAI,EAAA,eAAA,OAAA,CAAA;KAAA;KAAA,IAAAqD,WAAA;MAAA,OAAAP,gBAClC/C,OAAK,EAAA,eAAA,OAAA,CAAA;KAAA;IAAA,CAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA,OAAA4C;CAAA,EAAA,CAAA;CAMd,cAAA;EAAA,IAAAa,QAAAC,QAAA,GAAAC,QAAAF,MAAAG,YAAAC,QAAAF,MAAAC,YAAAE,QAAAH,MAAAI;EAAAC,KAIUC,YAAY;GAEhB,IAAI,OAAOtC,MAAMuC,QAAQ,YAAYvC,MAAMuC,IAAID,OAAO;QACjD,IAAItC,MAAMuC,KAAKvC,MAAMuC,MAAMD;GAMhCE,IAHqBC,qBAAqB;IACxClC,QAAQ+B,QAAQI,gBAAgB1C,MAAMV,iBAAAA,IAAoC;GAC5E,CACAkD,CAAAA,CAASG,QAAQL,OAAO;EAC1B,GAACR,KAAA;EAAAc,OAAAd,OAAAe,WAAA,EAAA,KAAA,WAAA;GAAA,OAZMrE,GAAG,sEAAsEwB,MAAM8C,KAAK;EAAC,EAAA,GACxF7C,IAAI,GAAA,OAAA,IAAA;EAAAkB,OAAAe,OAc4D1B,KAAK;EAAAW,OAAAa,OAAAZ,gBACtEJ,SAAO,CAAA,CAAA,GAAA,IAAA;EAAAG,OAAAgB,OAAAf,gBASPnD,MAAI;GAAA,IACH2D,OAAI;IAAA,OAAE5B,MAAMnB;GAAS;GAAA,IACrBgD,WAAQ;IAAA,cAAA;KAAA,IAAAkB,QAAAC,QAAA,GAAAC,QAAAF,MAAAd;KAAAd,OAAA8B,OAAA7B,gBAGDnD,MAAI;MAAA,IAAC2D,OAAI;OAAA,OAAE5B,MAAMX;MAAe;MAAA,IAAEwC,WAAQ;OAAA,OAAE7B,MAAMrB,KAAKgC,QAAQ,OAAO,EAAE;MAAC;MAAA,IAAAgB,WAAA;OAAA,OAAAP,gBACvEpD,KAAG;QAAA,IAACkF,OAAI;SAAA,OAAExC,MAAM;QAAC;QAAAiB,WACdwB,MAAMC,iBAAK;SAAA,IAAAC,QAAAC,QAAA,GAAAC,QAAAF,MAAApB,YAAAuB,SAAAD,MAAAnB;SAAAjB,OAAAoC,aAMNH,MAAM,IAAI,CAAC;SAAAjC,OAAAqC,QAEPL,QAAQ,GAAG;SAAA,OAAAE;QAAA,EAAA,CAAA;OAErB,CAAA;MAAA;KAAA,CAAA,CAAA;KAAAI,QAAAC,QAAAC,iBAAAZ,OAAA,eAd0C/C,MAAMZ,cAAc,KAAK,CAAA;KAAA,OAAA2D;IAAA,EAAA,CAAA;GAAA;GAAApB,WAqB5E9C,qBAAS;IAAA,IAAA+E,SAAAC,QAAA;IAAA1C,OAAAyC,cAA6B/E,UAAU,CAAC,CAACmB,MAAMrB,IAAI,CAAC;IAAA,OAAAiF;GAAA,EAAA,CAAA;EAAO,CAAA,GAAA,IAAA;EAAAzC,OAAAgB,OAAAf,gBAOvEnD,MAAI;GAAA,IAAC2D,OAAI;IAAA,OAAE,CAAC5B,MAAMjB;GAAQ;GAAA,IAAA4C,WAAA;IAAA,OAAAmC,QAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA3C,OAAAW,OAAAV,gBAI5BnD,MAAI;GAAA,IAAC2D,OAAI;IAAA,OAAEtB,KAAK;GAAC;GAAA,IAAAqB,WAAA;IAAA,IAAAoC,QAAAC,QAAA;IAAA7C,OAAA4C,OAAA3C,gBAKbJ,SAAO,CAAA,CAAA,CAAA;IAAA,OAAA+C;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAN,aAAAQ,aAAA9B,OAAA,cA1CE1B,YAAY,CAAC,CAAA;EAAA,OAAAqB;CAAA,EAAA,CAAA;AA+CjC;;;;;;;;;;;AAYA,SAAgBoC,WAAWnE,OAA+B;CACxD,MAAM,CAACC,OAAOC,QAAQ/B,WAAW6B,OAAO,CAAC,OAAO,CAAC;CAEjD,cAAA;EAAA,IAAAoE,SAAAC,QAAA;EAAAxB,OAAAuB,QAAAtB,WAAA,EAAA,KAAA,WAAA;GAAA,OAEWrE,GACL,2GACAwB,MAAM8C,KACR;EAAC,EAAA,GACG7C,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAkE;CAAA,EAAA,CAAA;AAGd"}
1
+ {"version":3,"file":"code-block.js","names":["createMemo","createSignal","For","Show","splitProps","ComponentProps","JSX","Check","Copy","Button","cn","CodeBlockProps","Omit","code","language","highlight","Element","complete","title","actions","copyLabel","copiedLabel","lineHeight","showLineNumbers","tallThreshold","maxHeight","wrap","TALL_CODE_BLOCK_PX","copyText","text","Promise","navigator","clipboard","writeText","CodeBlock","props","local","rest","copied","setCopied","failed","setFailed","tall","setTall","label","regionLabel","lines","replace","split","copy","ok","setTimeout","Actions","_el$","_tmpl$","_$insert","_$createComponent","variant","size","aria-label","_$memo","undefined","onClick","children","when","fallback","_el$2","_tmpl$4","_el$3","firstChild","_el$4","_el$5","nextSibling","_$use","element","ref","observer","ResizeObserver","offsetHeight","observe","_$spread","_$mergeProps","class","_el$8","_tmpl$5","_el$9","each","line","index","_el$0","_tmpl$6","_el$1","_el$10","_$effect","_$className","_$p","_$setStyleProperty","_el$11","_tmpl$7","_tmpl$2","_el$7","_tmpl$3","_p$","_v$","_v$2","_v$3","_v$4","e","_$setAttribute","t","a","_$style","o","InlineCode","_el$12","_tmpl$8","_$createComponent","_$memo","_$mergeProps","_$className","_$setStyleProperty","_$setAttribute","_$style"],"sources":["../../../../src/components/ui/code-block/code-block.tsx"],"sourcesContent":["import {\n createMemo,\n createSignal,\n For,\n Show,\n splitProps,\n type ComponentProps,\n type JSX,\n} from 'solid-js'\nimport { Check, Copy } from 'lucide-solid'\nimport { Button } from '../button'\nimport { cn } from '#lib/utils'\n\n/**\n * CodeBlock.\n *\n * A fenced block, rendered as a surface. The library ships no highlighter —\n * syntax colouring is a large dependency with its own theme format, and a\n * component that bundled one would force every consumer to pay for it. This\n * renders the block, its chrome, and its behaviour; a caller that wants\n * highlighting passes the result through `highlight`, which is the seam a\n * Shiki/Pierre/Prism integration plugs into.\n *\n * Four details are the reason this exists rather than a styled `<pre>`:\n *\n * **Copy confirms only on success.** `navigator.clipboard.writeText` rejects\n * outside a secure context, in a sandboxed frame, and when the document is not\n * focused. Flipping to a tick unconditionally confirms a copy that did not\n * happen, and the reader pastes something stale and does not know why. The\n * rejection is caught and the button reports the failure instead.\n *\n * **A tall block repeats its action row at the bottom.** Copying from a block\n * that is taller than the viewport otherwise costs a scroll back to the top and\n * another scroll down to where you were. The threshold is a fixed 480px rather\n * than viewport-relative so the behaviour does not change when the window is\n * resized.\n *\n * **The scroller is focusable and named.** A horizontally scrollable region has\n * to be reachable by keyboard — this is axe's `scrollable-region-focusable`, and\n * a region that scrolls but cannot be focused is a WCAG 2.1.1 failure. It is a\n * `region` with a label rather than a bare `tabindex`, so the tab stop announces\n * what it is.\n *\n * **Resolving a streaming block must not move the reader.** When `complete` is\n * false the block shows the code it has plus a generating indicator; the\n * indicator is inside the scroller's own box and does not add a line, because a\n * block that grows the moment its content resolves shifts everything below it.\n * If a caller's `highlight` changes the line box, that is the caller's height to\n * hold — `lineHeight` is a prop for exactly that reason.\n */\nexport type CodeBlockProps = Omit<ComponentProps<'div'>, 'children'> & {\n code: string\n /** The language, shown in the header and used as the region's label. */\n language?: string\n /** The code, transformed — highlighted HTML or a component. Omit for plain text. */\n highlight?: (code: string) => JSX.Element\n /** False while the block is still streaming. Adds a generating indicator. */\n complete?: boolean\n /** A filename or title, shown instead of the language. */\n title?: string\n /** Extra header controls, before the copy button. */\n actions?: JSX.Element\n /** Copy label. Override for a non-English application. */\n copyLabel?: string\n copiedLabel?: string\n /** Line height of the rendered code. Set it when `highlight` changes the box. */\n lineHeight?: string\n /** A line-number gutter. Off by default; a short snippet rarely needs one. */\n showLineNumbers?: boolean\n /** The height above which the action row is repeated below the block. */\n tallThreshold?: number\n /**\n * Cap the code body's height and scroll it vertically past that. A number is\n * pixels; a string is any CSS length (`'20rem'`, `'50vh'`). Unset, the block\n * grows with its content.\n */\n maxHeight?: number | string\n /**\n * Soft-wrap long lines inside the block instead of scrolling sideways. Off by\n * default: wrapped code loses its column alignment, so it is for prose-like\n * output (logs, long commands) in a narrow column. Wraps the plain renderer and\n * any `pre` inside `highlight` output; line numbers stay on the first visual line.\n */\n wrap?: boolean\n}\n\n/** The height a block must exceed before its actions are repeated. */\nexport const TALL_CODE_BLOCK_PX = 480\n\n/**\n * Copy, reporting whether it worked. `navigator.clipboard` is absent in an\n * insecure context, and `writeText` rejects when the document is not focused —\n * both are normal in an embedded or iframed application, and neither should\n * silently look like success.\n */\nasync function copyText(text: string): Promise<boolean> {\n try {\n if (!navigator.clipboard) return false\n await navigator.clipboard.writeText(text)\n return true\n } catch {\n return false\n }\n}\n\nexport function CodeBlock(props: CodeBlockProps) {\n const [local, rest] = splitProps(props, [\n 'code',\n 'language',\n 'highlight',\n 'complete',\n 'title',\n 'actions',\n 'copyLabel',\n 'copiedLabel',\n 'lineHeight',\n 'showLineNumbers',\n 'tallThreshold',\n 'maxHeight',\n 'wrap',\n 'class',\n 'ref',\n ])\n\n const [copied, setCopied] = createSignal(false)\n const [failed, setFailed] = createSignal(false)\n const [tall, setTall] = createSignal(false)\n\n const label = () => local.title ?? local.language ?? 'code'\n const regionLabel = () => (local.language ? `${local.language} code` : 'code')\n const lines = createMemo(() => local.code.replace(/\\n$/, '').split('\\n'))\n const maxHeight = () =>\n typeof local.maxHeight === 'number' ? `${local.maxHeight}px` : local.maxHeight\n\n const copy = async () => {\n const ok = await copyText(local.code)\n setCopied(ok)\n setFailed(!ok)\n if (ok) setTimeout(() => setCopied(false), 1500)\n }\n\n const Actions = () => (\n <div class=\"flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code:opacity-100 group-hover/code:opacity-100\">\n {local.actions}\n <Button\n variant=\"ghost\"\n size=\"icon-2xs\"\n aria-label={copied() ? (local.copiedLabel ?? 'Copied') : (local.copyLabel ?? 'Copy code')}\n title={failed() ? 'Copying is unavailable in this context' : undefined}\n onClick={() => void copy()}\n >\n <Show when={copied()} fallback={<Copy aria-hidden=\"true\" />}>\n <Check aria-hidden=\"true\" />\n </Show>\n </Button>\n </div>\n )\n\n return (\n <div\n class={cn('group/code overflow-hidden rounded-xl border border-border bg-card', local.class)}\n {...rest}\n ref={(element) => {\n // Composed rather than replaced: a caller's ref must survive.\n if (typeof local.ref === 'function') local.ref(element)\n else if (local.ref) local.ref = element\n // Measured rather than assumed: the caller controls the content, so only\n // the rendered box can say whether the action row has scrolled away.\n const observer = new ResizeObserver(() => {\n setTall(element.offsetHeight > (local.tallThreshold ?? TALL_CODE_BLOCK_PX))\n })\n observer.observe(element)\n }}\n >\n <div class=\"flex items-center justify-between gap-2 px-3 py-1\">\n <span class=\"truncate font-code text-code text-muted-foreground\">{label()}</span>\n <Actions />\n </div>\n <div\n data-slot=\"code-block-body\"\n data-wrap={local.wrap ? '' : undefined}\n class={cn('scroll-fade overflow-x-auto', {\n // A capped body scrolls on both axes; the region is already a tab stop,\n // so the keyboard reaches the vertical overflow the same way.\n 'overflow-y-auto': local.maxHeight !== undefined,\n '[&_pre]:whitespace-pre-wrap [&_pre]:[overflow-wrap:anywhere]': local.wrap,\n })}\n style={maxHeight() === undefined ? undefined : { 'max-height': maxHeight() }}\n // A region that scrolls must be focusable, and a tab stop needs a name.\n tabindex=\"0\"\n role=\"region\"\n aria-label={regionLabel()}\n >\n <Show\n when={local.highlight}\n fallback={\n <pre class=\"m-0 px-3 py-2\" style={{ 'line-height': local.lineHeight ?? '1.5' }}>\n <code class=\"font-code text-code\">\n <Show when={local.showLineNumbers} fallback={local.code.replace(/\\n$/, '')}>\n <For each={lines()}>\n {(line, index) => (\n <span class=\"grid grid-cols-[3ch_1fr] gap-3\">\n <span\n class=\"text-right text-muted-foreground select-none\"\n aria-hidden=\"true\"\n >\n {index() + 1}\n </span>\n <span class={cn({ 'min-w-0': local.wrap })}>{line || ' '}</span>\n </span>\n )}\n </For>\n </Show>\n </code>\n </pre>\n }\n >\n {(highlight) => <div class=\"px-3 py-2\">{highlight()(local.code)}</div>}\n </Show>\n {/*\n In the scroller's box, and a single line: the indicator must not add\n height when it disappears, or a streaming block shifts everything below\n it as it resolves.\n */}\n <Show when={!local.complete}>\n <p class=\"px-3 pb-2 text-xs text-muted-foreground italic\">Generating…</p>\n </Show>\n </div>\n <Show when={tall()}>\n <div\n data-code-block-footer=\"\"\n class=\"flex items-center justify-end border-t border-transparent px-3 py-1 transition-colors group-focus-within/code:border-border group-hover/code:border-border\"\n >\n <Actions />\n </div>\n </Show>\n </div>\n )\n}\n\n/**\n * InlineCode.\n *\n * The `code` inside a sentence. It is a component rather than a class because it\n * has to hold three properties at once: it uses the shared code family and size\n * preference, and it wraps rather than overflows a narrow column\n * (`overflow-wrap: anywhere` — an identifier with\n * no spaces is one long token), and it must be legible against whatever surface\n * it lands on, which is why the fill is a token and not a fixed grey.\n */\nexport function InlineCode(props: ComponentProps<'code'>) {\n const [local, rest] = splitProps(props, ['class'])\n\n return (\n <code\n class={cn(\n 'rounded-sm bg-muted px-[0.4em] py-[0.15em] font-code text-code text-foreground [overflow-wrap:anywhere]',\n local.class\n )}\n {...rest}\n />\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;AAuFA,IAAa2B,qBAAqB;;;;;;;AAQlC,eAAeC,SAASC,MAAgC;CACtD,IAAI;EACF,IAAI,CAACE,UAAUC,WAAW,OAAO;EACjC,MAAMD,UAAUC,UAAUC,UAAUJ,IAAI;EACxC,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgBK,UAAUC,OAAuB;CAC/C,MAAM,CAACC,OAAOC,QAAQjC,WAAW+B,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAK,CACN;CAED,MAAM,CAACG,QAAQC,aAAatC,aAAa,KAAK;CAC9C,MAAM,CAACuC,QAAQC,aAAaxC,aAAa,KAAK;CAC9C,MAAM,CAACyC,MAAMC,WAAW1C,aAAa,KAAK;CAE1C,MAAM2C,cAAcR,MAAMlB,SAASkB,MAAMtB,YAAY;CACrD,MAAM+B,oBAAqBT,MAAMtB,WAAW,GAAGsB,MAAMtB,SAAQ,SAAU;CACvE,MAAMgC,QAAQ9C,iBAAiBoC,MAAMvB,KAAKkC,QAAQ,OAAO,EAAE,CAAC,CAACC,MAAM,IAAI,CAAC;CACxE,MAAMvB,kBACJ,OAAOW,MAAMX,cAAc,WAAW,GAAGW,MAAMX,UAAS,MAAOW,MAAMX;CAEvE,MAAMwB,OAAO,YAAY;EACvB,MAAMC,KAAK,MAAMtB,SAASQ,MAAMvB,IAAI;EACpC0B,UAAUW,EAAE;EACZT,UAAU,CAACS,EAAE;EACb,IAAIA,IAAIC,iBAAiBZ,UAAU,KAAK,GAAG,IAAI;CACjD;CAEA,MAAMa,uBAAU;EAAA,IAAAC,OAAAC,OAAA;EAAAC,OAAAF,YAEXjB,MAAMjB,SAAO,IAAA;EAAAoC,OAAAF,MAAAG,gBACb/C,QAAM;GACLgD,SAAO;GACPC,MAAI;GAAA,KAAA,gBAAA;IAAA,OACQE,WAAA,CAAA,CAAAtB,OAAO,CAAC,CAAA,CAAA,IAAIF,MAAMf,eAAe,WAAae,MAAMhB,aAAa;GAAY;GAAA,IACzFF,QAAK;IAAA,OAAEsB,OAAO,IAAI,2CAA2CqB,KAAAA;GAAS;GACtEC,eAAe,KAAKb,KAAK;GAAC,IAAAc,WAAA;IAAA,OAAAP,gBAEzBrD,MAAI;KAAA,IAAC6D,OAAI;MAAA,OAAE1B,OAAO;KAAC;KAAA,IAAE2B,WAAQ;MAAA,OAAAT,gBAAGhD,MAAI,EAAA,eAAA,OAAA,CAAA;KAAA;KAAA,IAAAuD,WAAA;MAAA,OAAAP,gBAClCjD,OAAK,EAAA,eAAA,OAAA,CAAA;KAAA;IAAA,CAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA,OAAA8C;CAAA,EAAA,CAAA;CAMd,cAAA;EAAA,IAAAa,QAAAC,QAAA,GAAAC,QAAAF,MAAAG,YAAAC,QAAAF,MAAAC,YAAAE,QAAAH,MAAAI;EAAAC,KAIUC,YAAY;GAEhB,IAAI,OAAOtC,MAAMuC,QAAQ,YAAYvC,MAAMuC,IAAID,OAAO;QACjD,IAAItC,MAAMuC,KAAKvC,MAAMuC,MAAMD;GAMhCE,IAHqBC,qBAAqB;IACxClC,QAAQ+B,QAAQI,gBAAgB1C,MAAMZ,iBAAAA,IAAoC;GAC5E,CACAoD,CAAAA,CAASG,QAAQL,OAAO;EAC1B,GAACR,KAAA;EAAAc,OAAAd,OAAAe,WAAA,EAAA,KAAA,WAAA;GAAA,OAZMvE,GAAG,sEAAsE0B,MAAM8C,KAAK;EAAC,EAAA,GACxF7C,IAAI,GAAA,OAAA,IAAA;EAAAkB,OAAAe,OAc4D1B,KAAK;EAAAW,OAAAa,OAAAZ,gBACtEJ,SAAO,CAAA,CAAA,GAAA,IAAA;EAAAG,OAAAgB,OAAAf,gBAiBPrD,MAAI;GAAA,IACH6D,OAAI;IAAA,OAAE5B,MAAMrB;GAAS;GAAA,IACrBkD,WAAQ;IAAA,cAAA;KAAA,IAAAkB,QAAAC,QAAA,GAAAC,QAAAF,MAAAd;KAAAd,OAAA8B,OAAA7B,gBAGDrD,MAAI;MAAA,IAAC6D,OAAI;OAAA,OAAE5B,MAAMb;MAAe;MAAA,IAAE0C,WAAQ;OAAA,OAAE7B,MAAMvB,KAAKkC,QAAQ,OAAO,EAAE;MAAC;MAAA,IAAAgB,WAAA;OAAA,OAAAP,gBACvEtD,KAAG;QAAA,IAACoF,OAAI;SAAA,OAAExC,MAAM;QAAC;QAAAiB,WACdwB,MAAMC,iBAAK;SAAA,IAAAC,QAAAC,QAAA,GAAAC,QAAAF,MAAApB,YAAAuB,SAAAD,MAAAnB;SAAAjB,OAAAoC,aAMNH,MAAM,IAAI,CAAC;SAAAjC,OAAAqC,QAE+BL,QAAQ,GAAG;SAAAM,aAAAC,UAAAF,QAA3ClF,GAAG,EAAE,WAAW0B,MAAMV,KAAK,CAAC,CAAC,CAAA;SAAA,OAAA+D;QAAA,EAAA,CAAA;OAE7C,CAAA;MAAA;KAAA,CAAA,CAAA;KAAAI,QAAAE,QAAAC,iBAAAb,OAAA,eAd0C/C,MAAMd,cAAc,KAAK,CAAA;KAAA,OAAA6D;IAAA,EAAA,CAAA;GAAA;GAAApB,WAqB5EhD,qBAAS;IAAA,IAAAkF,SAAAC,QAAA;IAAA3C,OAAA0C,cAA6BlF,UAAU,CAAC,CAACqB,MAAMvB,IAAI,CAAC;IAAA,OAAAoF;GAAA,EAAA,CAAA;EAAO,CAAA,GAAA,IAAA;EAAA1C,OAAAgB,OAAAf,gBAOvErD,MAAI;GAAA,IAAC6D,OAAI;IAAA,OAAE,CAAC5B,MAAMnB;GAAQ;GAAA,IAAA8C,WAAA;IAAA,OAAAoC,QAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA5C,OAAAW,OAAAV,gBAI5BrD,MAAI;GAAA,IAAC6D,OAAI;IAAA,OAAEtB,KAAK;GAAC;GAAA,IAAAqB,WAAA;IAAA,IAAAqC,QAAAC,QAAA;IAAA9C,OAAA6C,OAAA5C,gBAKbJ,SAAO,CAAA,CAAA,CAAA;IAAA,OAAAgD;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAP,QAAAS,QAAA;GAAA,IAAAC,MArDCnE,MAAMV,OAAO,KAAKmC,KAAAA,GAAS2C,OAC/B9F,GAAG,+BAA+B;IAGvC,mBAAmB0B,MAAMX,cAAcoC,KAAAA;IACvC,gEAAgEzB,MAAMV;GACxE,CAAC,GAAC+E,OACKhF,UAAU,MAAMoC,KAAAA,IAAYA,KAAAA,IAAY,EAAE,cAAcpC,UAAU,EAAE,GAACiF,OAIhE7D,YAAY;GAAC0D,QAAAD,IAAAK,KAAAC,aAAArC,OAAA,aAAA+B,IAAAK,IAAAJ,GAAA;GAAAC,SAAAF,IAAAO,KAAAf,UAAAvB,OAAA+B,IAAAO,IAAAL,IAAA;GAAAF,IAAAQ,IAAAC,MAAAxC,OAAAkC,MAAAH,IAAAQ,CAAA;GAAAJ,SAAAJ,IAAAU,KAAAJ,aAAArC,OAAA,cAAA+B,IAAAU,IAAAN,IAAA;GAAA,OAAAJ;EAAA,GAAA;GAAAK,GAAA9C,KAAAA;GAAAgD,GAAAhD,KAAAA;GAAAiD,GAAAjD,KAAAA;GAAAmD,GAAAnD,KAAAA;EAAA,CAAA;EAAA,OAAAK;CAAA,EAAA,CAAA;AA+CjC;;;;;;;;;;;AAYA,SAAgB+C,WAAW9E,OAA+B;CACxD,MAAM,CAACC,OAAOC,QAAQjC,WAAW+B,OAAO,CAAC,OAAO,CAAC;CAEjD,cAAA;EAAA,IAAA+E,SAAAC,QAAA;EAAAnC,OAAAkC,QAAAjC,WAAA,EAAA,KAAA,WAAA;GAAA,OAEWvE,GACL,2GACA0B,MAAM8C,KACR;EAAC,EAAA,GACG7C,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA6E;CAAA,EAAA,CAAA;AAGd"}
@@ -1,2 +1,2 @@
1
- export { Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, itemVariants, type ItemProps, } from './item';
1
+ export { Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, itemVariants, type ItemDescriptionProps, type ItemProps, } from './item';
2
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,eAAe,EACf,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,YAAY,EACZ,KAAK,SAAS,GACf,MAAM,QAAQ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,eAAe,EACf,SAAS,EACT,SAAS,EACT,cAAc,EACd,SAAS,EACT,YAAY,EACZ,KAAK,oBAAoB,EACzB,KAAK,SAAS,GACf,MAAM,QAAQ,CAAA"}
@@ -28,10 +28,26 @@ export type ItemProps = ComponentProps<'div'> & ItemVariantProps & {
28
28
  media?: JSX.Element;
29
29
  /** A trailing slot: a badge, a timestamp, a menu trigger. */
30
30
  trailing?: JSX.Element;
31
+ /**
32
+ * Move the trailing slot onto its own line, end-aligned under the body, once
33
+ * the item is narrower than 28rem — the width `ListRow` stacks its trailing
34
+ * controls at. Off by default: a row of badges and an icon button fits beside
35
+ * the body, but a pair of labelled buttons squeezes a phone-width body to a
36
+ * few characters.
37
+ */
38
+ stackTrailing?: boolean;
31
39
  };
32
40
  export declare function Item(props: ItemProps): JSX.Element;
33
41
  export declare function ItemTitle(props: ComponentProps<'div'>): JSX.Element;
34
- export declare function ItemDescription(props: ComponentProps<'div'>): JSX.Element;
42
+ export type ItemDescriptionProps = ComponentProps<'div'> & {
43
+ /**
44
+ * Clamp to two lines. On by default, so a grid of items keeps one height; pass
45
+ * `false` where the description is the content — a setting's explanation, an
46
+ * error the reader has to read in full.
47
+ */
48
+ clamp?: boolean;
49
+ };
50
+ export declare function ItemDescription(props: ItemDescriptionProps): JSX.Element;
35
51
  /**
36
52
  * A group of items with an optional heading. It is a real `role="list"` with
37
53
  * `role="listitem"` children, so a screen reader reports the count — which is how a
@@ -1 +1 @@
1
- {"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/item.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAGtD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY;;;8EAgBvB,CAAA;AAEF,KAAK,gBAAgB,GAAG,YAAY,CAAC,OAAO,YAAY,CAAC,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAC3C,gBAAgB,GAAG;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,CAAA;AAEH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,eAkCpC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eAGrD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eAS3D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CACtD,eAgBF;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,eAE9C;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASrD"}
1
+ {"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/item/item.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAEnD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,eAAe,CAAA;AAGtD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY;;;8EAgBvB,CAAA;AAEF,KAAK,gBAAgB,GAAG,YAAY,CAAC,OAAO,YAAY,CAAC,CAAA;AAEzD,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAC3C,gBAAgB,GAAG;IACjB,qDAAqD;IACrD,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,CAAA;AAEH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,eAiDpC;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eAGrD;AAED,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IACzD;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,eAa1D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CACtD,eAgBF;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,eAE9C;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASrD"}