@kerfjs/ui 5.0.0-beta.18 → 5.0.0-beta.22

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 (170) hide show
  1. package/README.md +114 -3
  2. package/ai/application-ui-diagnostic-ids-v1.json +92 -0
  3. package/ai/application-ui-profile-sync.cjs +529 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +102 -0
  6. package/ai/application-ui-profile.defaults.json +41 -0
  7. package/ai/application-ui-profile.mjs +626 -0
  8. package/ai/application-ui-profile.schema.json +130 -0
  9. package/ai/catalog-authoring.json +25 -0
  10. package/ai/catalog-authoring.schema.json +61 -0
  11. package/ai/compile-time-contracts-v1.json +101 -0
  12. package/ai/compile-time-contracts-v1.schema.json +49 -0
  13. package/ai/component-catalog-extension-v2.d.ts +7 -0
  14. package/ai/component-catalog-extension-v2.schema.json +7 -0
  15. package/ai/component-catalog-v2-overrides.json +326 -0
  16. package/ai/component-catalog-v2.d.ts +67 -0
  17. package/ai/component-catalog-v2.json +4441 -0
  18. package/ai/component-catalog-v2.schema.json +246 -0
  19. package/ai/component-catalog.json +150 -6
  20. package/ai/public-api-signatures-v1.md +294 -64
  21. package/ai/skill.md +41 -5
  22. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  23. package/analyzer/cli.mjs +46 -0
  24. package/analyzer/index.d.mts +45 -0
  25. package/analyzer/index.mjs +743 -0
  26. package/analyzer/report.schema.json +66 -0
  27. package/dist/browser/pane.js +3 -0
  28. package/dist/browser/sunken-panel.js +3 -0
  29. package/dist/catalog-resources.d.ts +34 -0
  30. package/dist/catalog-resources.js +22 -0
  31. package/dist/catalog-resources.js.map +1 -0
  32. package/dist/catalog.d.ts +21 -3
  33. package/dist/catalog.js +239 -210
  34. package/dist/catalog.js.map +1 -1
  35. package/dist/{chunk-ZK2G3VR5.js → chunk-5VTB2BKP.js} +1 -1
  36. package/dist/chunk-5VTB2BKP.js.map +1 -0
  37. package/dist/{chunk-S3HKPI7I.js → chunk-7KEUJIIC.js} +1 -1
  38. package/dist/chunk-7KEUJIIC.js.map +1 -0
  39. package/dist/{chunk-NVWXKU37.js → chunk-AASLYRGE.js} +1 -1
  40. package/dist/chunk-AASLYRGE.js.map +1 -0
  41. package/dist/{chunk-QIRXYKCE.js → chunk-DSNIYOZ3.js} +3 -2
  42. package/dist/chunk-DSNIYOZ3.js.map +1 -0
  43. package/dist/{chunk-ENVTNLYC.js → chunk-GP5DP5DL.js} +55 -15
  44. package/dist/chunk-GP5DP5DL.js.map +1 -0
  45. package/dist/chunk-JRKSK2HX.js +79 -0
  46. package/dist/chunk-JRKSK2HX.js.map +1 -0
  47. package/dist/{chunk-U5IFGQCD.js → chunk-K57KRAOV.js} +1 -1
  48. package/dist/chunk-K57KRAOV.js.map +1 -0
  49. package/dist/{chunk-ZVI5YST2.js → chunk-M7JBER5U.js} +1 -1
  50. package/dist/chunk-M7JBER5U.js.map +1 -0
  51. package/dist/{chunk-43FUQH35.js → chunk-OHU6CI22.js} +2 -2
  52. package/dist/{chunk-43FUQH35.js.map → chunk-OHU6CI22.js.map} +1 -1
  53. package/dist/{chunk-4TRDV6RL.js → chunk-OZWU5ZPZ.js} +1 -1
  54. package/dist/chunk-OZWU5ZPZ.js.map +1 -0
  55. package/dist/{chunk-JTHUXDN2.js → chunk-RYRIFZCT.js} +3 -3
  56. package/dist/chunk-RYRIFZCT.js.map +1 -0
  57. package/dist/{chunk-6ETZKKAE.js → chunk-VLYV65LK.js} +1 -1
  58. package/dist/chunk-VLYV65LK.js.map +1 -0
  59. package/dist/chunk-W4CXQYSG.js +23 -0
  60. package/dist/chunk-W4CXQYSG.js.map +1 -0
  61. package/dist/{chunk-S3PCZ7NV.js → chunk-W7YD6E4N.js} +1 -1
  62. package/dist/chunk-W7YD6E4N.js.map +1 -0
  63. package/dist/index.d.ts +6 -4
  64. package/dist/index.js +14 -12
  65. package/dist/list-header.d.ts +29 -8
  66. package/dist/list-header.js +1 -1
  67. package/dist/nav-stack.js +1 -1
  68. package/dist/pane.d.ts +49 -0
  69. package/dist/pane.js +4 -0
  70. package/dist/pane.js.map +1 -0
  71. package/dist/panel-header.d.ts +9 -3
  72. package/dist/panel-header.js +1 -1
  73. package/dist/segmented-control.d.ts +6 -6
  74. package/dist/segmented-control.js +1 -1
  75. package/dist/select.d.ts +10 -4
  76. package/dist/select.js +1 -1
  77. package/dist/split-view.js +2 -2
  78. package/dist/state-banner.d.ts +3 -2
  79. package/dist/state-banner.js +1 -1
  80. package/dist/styles/catalog.css +6 -6
  81. package/dist/styles/layout.css +2 -20
  82. package/dist/styles/pane.css +62 -0
  83. package/dist/styles/segmented-control.css +3 -7
  84. package/dist/styles/sunken-panel.css +18 -0
  85. package/dist/sunken-panel.d.ts +16 -0
  86. package/dist/sunken-panel.js +3 -0
  87. package/dist/sunken-panel.js.map +1 -0
  88. package/dist/tab-bar.d.ts +3 -2
  89. package/dist/tab-bar.js +1 -1
  90. package/dist/tab-scaffold.d.ts +6 -6
  91. package/dist/tab-scaffold.js.map +1 -1
  92. package/dist/token-search-field.d.ts +25 -9
  93. package/dist/token-search-field.js +1 -1
  94. package/dist/toolbar-control-group.d.ts +9 -5
  95. package/dist/toolbar-control-group.js +1 -1
  96. package/dist/toolbar-text.d.ts +10 -11
  97. package/dist/toolbar-text.js +1 -1
  98. package/dist/wire-tab-bars.d.ts +5 -2
  99. package/dist/wire-tab-bars.js +1 -1
  100. package/dist/wire-token-search-fields.d.ts +11 -6
  101. package/dist/wire-token-search-fields.js +1 -1
  102. package/docs/catalog.md +186 -67
  103. package/docs/component-contract.md +102 -5
  104. package/docs/component-selection.md +9 -5
  105. package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -1
  106. package/docs/design/templates/panel-header/icon-actions.svg +1 -1
  107. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -1
  108. package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -1
  109. package/docs/design/templates/panel-header/no-icon-dark.svg +1 -1
  110. package/docs/design/templates/panel-header/no-icon.svg +1 -1
  111. package/docs/design/templates/panel-header/page-heading-dark.svg +1 -1
  112. package/docs/design/templates/panel-header/page-heading.svg +1 -1
  113. package/docs/design/templates/panel-header/title-only-dark.svg +1 -1
  114. package/docs/design/templates/panel-header/title-only.svg +1 -1
  115. package/docs/design/templates/panel-header-dark.svg +5 -5
  116. package/docs/design/templates/panel-header.svg +5 -5
  117. package/docs/design/templates/segmented-control/equal-dark.svg +1 -1
  118. package/docs/design/templates/segmented-control/equal.svg +1 -1
  119. package/docs/design/templates/segmented-control-dark.svg +1 -1
  120. package/docs/design/templates/segmented-control.svg +1 -1
  121. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  122. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  123. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  124. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  125. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  126. package/docs/design/templates/toolbar-control-group.svg +1 -1
  127. package/docs/design/templates/toolbar-dark.svg +1 -1
  128. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  129. package/docs/design/templates/toolbar-text/default.svg +1 -1
  130. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  131. package/docs/design/templates/toolbar-text/large.svg +1 -1
  132. package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
  133. package/docs/design/templates/toolbar-text/small.svg +1 -1
  134. package/docs/design/templates/toolbar-text-dark.svg +3 -3
  135. package/docs/design/templates/toolbar-text.svg +3 -3
  136. package/docs/design/templates/toolbar.svg +1 -1
  137. package/docs/design/templates.md +11 -6
  138. package/docs/design-philosophy.md +2 -4
  139. package/docs/examples/application-ui-profile.json +60 -0
  140. package/docs/examples/component-catalog-extension-v2.json +81 -0
  141. package/docs/layout.md +42 -23
  142. package/docs/sunken-panel.md +43 -0
  143. package/docs/type-contracts.md +99 -0
  144. package/docs/ui-analyzer.md +81 -0
  145. package/docs/ui-doctor.md +77 -0
  146. package/docs/ui-evaluator.md +122 -0
  147. package/doctor/cli.mjs +115 -0
  148. package/doctor/config.schema.json +68 -0
  149. package/doctor/index.d.mts +116 -0
  150. package/doctor/index.mjs +1256 -0
  151. package/doctor/report.schema.json +118 -0
  152. package/evaluator/cli.mjs +89 -0
  153. package/evaluator/index.d.mts +90 -0
  154. package/evaluator/index.mjs +1138 -0
  155. package/evaluator/report.schema.json +152 -0
  156. package/llms.txt +40 -3
  157. package/package.json +66 -8
  158. package/ux-demo/recipes/compact-toolbar.tsx +2 -2
  159. package/ux-demo/recipes/composer-form.tsx +1 -1
  160. package/dist/chunk-4TRDV6RL.js.map +0 -1
  161. package/dist/chunk-6ETZKKAE.js.map +0 -1
  162. package/dist/chunk-ENVTNLYC.js.map +0 -1
  163. package/dist/chunk-JTHUXDN2.js.map +0 -1
  164. package/dist/chunk-NVWXKU37.js.map +0 -1
  165. package/dist/chunk-QIRXYKCE.js.map +0 -1
  166. package/dist/chunk-S3HKPI7I.js.map +0 -1
  167. package/dist/chunk-S3PCZ7NV.js.map +0 -1
  168. package/dist/chunk-U5IFGQCD.js.map +0 -1
  169. package/dist/chunk-ZK2G3VR5.js.map +0 -1
  170. package/dist/chunk-ZVI5YST2.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.18` and `kerfjs@5.0.0-beta.18`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.22` and `kerfjs@5.0.0-beta.22`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/disclosure-arrow`
6
6
 
@@ -47,7 +47,7 @@ import * as kerfjs from 'kerfjs';
47
47
  type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';
48
48
  /** ARIA heading level for a title exposed as a heading landmark. */
49
49
  type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
50
- interface ToolbarTextProps {
50
+ interface ToolbarTextBaseProps {
51
51
  text: string;
52
52
  size?: ToolbarTextSize;
53
53
  className?: string;
@@ -61,23 +61,22 @@ interface ToolbarTextProps {
61
61
  headingLevel?: HeadingLevel;
62
62
  /** Render the text as an unanimated loading skeleton instead of its value. */
63
63
  placeholder?: boolean;
64
- /**
65
- * Wrap onto multiple lines when the text does not fit, instead of the default
66
- * single line. Combine with `maxLines` to cap the number of lines. Default false.
67
- */
68
- wrap?: boolean;
69
64
  /**
70
65
  * Show a trailing ellipsis (…) where the text is truncated — on the single line
71
66
  * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
72
67
  * instead. Default true.
73
68
  */
74
69
  ellipsis?: boolean;
75
- /**
76
- * Cap wrapped text to this many lines, truncating past it. Only takes effect with
77
- * `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.
78
- */
79
- maxLines?: number | null;
80
70
  }
71
+ type ToolbarTextWrappingProps = {
72
+ /** Wrap onto multiple lines; combine with `maxLines` to cap them. */
73
+ wrap: true;
74
+ maxLines?: number | null;
75
+ } | {
76
+ wrap?: false;
77
+ maxLines?: never;
78
+ };
79
+ type ToolbarTextProps = ToolbarTextBaseProps & ToolbarTextWrappingProps;
81
80
  declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, }: ToolbarTextProps): kerfjs.SafeHtml;
82
81
 
83
82
  export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
@@ -88,21 +87,25 @@ export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarText
88
87
  ```ts
89
88
  import { SafeHtml } from 'kerfjs';
90
89
 
90
+ type ToolbarControlGroupAppearance = 'contained' | 'borderless';
91
+ type ToolbarControlGroupTone = 'default' | 'dark';
92
+ type ToolbarControlGroupButtonAppearance = 'plain' | 'push';
93
+ type ToolbarControlGroupShape = 'pill' | 'rounded';
91
94
  interface ToolbarControlGroupProps {
92
95
  children: SafeHtml | SafeHtml[];
93
96
  label?: string;
94
97
  className?: string;
95
98
  expanded?: boolean;
96
99
  single?: boolean;
97
- appearance?: 'contained' | 'borderless';
98
- tone?: 'default' | 'dark';
99
- buttonAppearance?: 'plain' | 'push';
100
+ appearance?: ToolbarControlGroupAppearance;
101
+ tone?: ToolbarControlGroupTone;
102
+ buttonAppearance?: ToolbarControlGroupButtonAppearance;
100
103
  /** Corner shape: fully round `pill` (default) or a softer `rounded` rectangle. */
101
- shape?: 'pill' | 'rounded';
104
+ shape?: ToolbarControlGroupShape;
102
105
  }
103
106
  declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance, shape, }: ToolbarControlGroupProps): SafeHtml;
104
107
 
105
- export { ToolbarControlGroup, type ToolbarControlGroupProps };
108
+ export { ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupProps, type ToolbarControlGroupShape, type ToolbarControlGroupTone };
106
109
  ```
107
110
 
108
111
  ## `@kerfjs/ui/floating-toolbar`
@@ -159,18 +162,39 @@ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | un
159
162
  }>;
160
163
  interface ListHeaderBaseProps {
161
164
  label: string;
162
- action?: string;
163
- actionLabel?: string;
164
- actionIcon?: SafeHtml;
165
- actionDisabled?: boolean;
166
- disabledReason?: string;
167
- expanded?: boolean;
168
- toggle?: boolean;
169
165
  /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
170
166
  placeholder?: boolean;
171
167
  rootAttributes?: ListHeaderRootAttributes;
172
168
  triggerAttributes?: ListHeaderTriggerAttributes;
173
169
  }
170
+ type ListHeaderModeProps = {
171
+ /** Render the title as a controlled disclosure trigger. */
172
+ toggle: true;
173
+ action: string;
174
+ expanded: boolean;
175
+ actionIcon?: SafeHtml;
176
+ actionLabel?: never;
177
+ actionDisabled?: boolean;
178
+ disabledReason?: string;
179
+ } | {
180
+ /** Render a separately named trailing action. */
181
+ toggle?: false;
182
+ action: string;
183
+ actionLabel: string;
184
+ actionIcon: SafeHtml;
185
+ expanded?: never;
186
+ actionDisabled?: boolean;
187
+ disabledReason?: string;
188
+ } | {
189
+ /** Render a passive section heading. */
190
+ toggle?: false;
191
+ action?: never;
192
+ actionLabel?: never;
193
+ actionIcon?: never;
194
+ expanded?: never;
195
+ actionDisabled?: never;
196
+ disabledReason?: never;
197
+ };
174
198
  type ListHeaderIndicatorProps = {
175
199
  count: number;
176
200
  countLabel: string;
@@ -180,7 +204,7 @@ type ListHeaderIndicatorProps = {
180
204
  countLabel?: never;
181
205
  badge?: SafeHtml;
182
206
  };
183
- type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps;
207
+ type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps & ListHeaderModeProps;
184
208
  declare function ListHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, }: ListHeaderProps): SafeHtml;
185
209
 
186
210
  export { ListHeader, type ListHeaderProps };
@@ -338,11 +362,9 @@ export { ListInsetText, type ListInsetTextProps };
338
362
  import { SafeHtml } from 'kerfjs';
339
363
  import { HeadingLevel } from './toolbar-text.js';
340
364
 
341
- interface PanelHeaderProps {
365
+ interface PanelHeaderBaseProps {
342
366
  title: string;
343
367
  titleId: string;
344
- summary?: string;
345
- summaryId?: string;
346
368
  icon?: SafeHtml;
347
369
  iconClassName?: string;
348
370
  actions?: SafeHtml;
@@ -356,6 +378,14 @@ interface PanelHeaderProps {
356
378
  /** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */
357
379
  placeholder?: boolean;
358
380
  }
381
+ type PanelHeaderSummaryProps = {
382
+ summary: string;
383
+ summaryId?: string;
384
+ } | {
385
+ summary?: never;
386
+ summaryId?: never;
387
+ };
388
+ type PanelHeaderProps = PanelHeaderBaseProps & PanelHeaderSummaryProps;
359
389
  /**
360
390
  * The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone
361
391
  * holds an optional icon control group and the title as extra-large `ToolbarText`,
@@ -438,6 +468,7 @@ export { AppTab, type AppTabProps };
438
468
  ```ts
439
469
  import { SafeHtml } from 'kerfjs';
440
470
 
471
+ type TabActivation = 'automatic' | 'manual';
441
472
  interface TabBarProps {
442
473
  id: string;
443
474
  label: string;
@@ -451,17 +482,20 @@ interface TabBarProps {
451
482
  * selects on arrow / Home / End; `'manual'` moves roving focus only and the user
452
483
  * selects with Enter / Space / click — use it when selecting a tab is a heavy action.
453
484
  */
454
- activation?: 'automatic' | 'manual';
485
+ activation?: TabActivation;
455
486
  }
456
487
  /** Render a controlled tab strip. The application owns selection, order, and persistence. */
457
488
  declare function TabBar({ id, label, children, leading, trailing, className, activation, }: TabBarProps): SafeHtml;
458
489
 
459
- export { TabBar, type TabBarProps };
490
+ export { type TabActivation, TabBar, type TabBarProps };
460
491
  ```
461
492
 
462
493
  ## `@kerfjs/ui/wire-tab-bars`
463
494
 
464
495
  ```ts
496
+ import { TabActivation } from './tab-bar.js';
497
+ import 'kerfjs';
498
+
465
499
  type TabReorderSource = 'pointer' | 'keyboard';
466
500
  type TabDropPosition = 'before' | 'after';
467
501
  interface TabReorder {
@@ -471,7 +505,7 @@ interface TabReorder {
471
505
  position: TabDropPosition;
472
506
  source: TabReorderSource;
473
507
  }
474
- type TabActivation = 'automatic' | 'manual';
508
+
475
509
  interface WireTabBarsOptions {
476
510
  onReorder: (change: TabReorder) => void;
477
511
  /**
@@ -491,7 +525,7 @@ declare function reorderTabs<T>(items: readonly T[], getId: (item: T) => string,
491
525
  /** Wire reordering and keyboard navigation while leaving controlled state in the application. */
492
526
  declare function wireTabBars(root: HTMLElement | Document, { onReorder, activation }: WireTabBarsOptions): () => void;
493
527
 
494
- export { type TabActivation, type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
528
+ export { TabActivation, type TabDropPosition, type TabReorder, type TabReorderSource, type WireTabBarsOptions, reorderTabs, wireTabBars };
495
529
  ```
496
530
 
497
531
  ## `@kerfjs/ui/nav-stack`
@@ -604,6 +638,60 @@ declare function SplitView({ id, label, list, detail, compact, detailActive, lis
604
638
  export { SplitView, type SplitViewProps, type SplitViewResizable };
605
639
  ```
606
640
 
641
+ ## `@kerfjs/ui/pane`
642
+
643
+ ```ts
644
+ import { SafeHtml } from 'kerfjs';
645
+
646
+ /** Logical sides that can show a {@link Pane} separator. */
647
+ type PaneSeparatorSide = 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
648
+ /** Semantic root elements supported by {@link Pane}. */
649
+ type PaneElement = 'article' | 'aside' | 'div' | 'main' | 'section';
650
+ /** Semantic elements supported by the scrolling content slot. */
651
+ type PaneContentElement = 'div' | 'main' | 'nav' | 'section';
652
+ type PaneRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
653
+ 'data-component'?: never;
654
+ 'data-separator-block-start'?: never;
655
+ 'data-separator-block-end'?: never;
656
+ 'data-separator-inline-start'?: never;
657
+ 'data-separator-inline-end'?: never;
658
+ }>;
659
+ interface PaneProps {
660
+ /** Optional fixed chrome above the scrolling content, arranged vertically. */
661
+ header?: SafeHtml | readonly SafeHtml[];
662
+ /** The pane's primary vertical, scrolling content stack. */
663
+ children?: SafeHtml | readonly SafeHtml[];
664
+ /** Optional fixed chrome below the scrolling content. */
665
+ footer?: SafeHtml | readonly SafeHtml[];
666
+ /** Root semantics. Defaults to `div`. */
667
+ element?: PaneElement;
668
+ /** Scrolling content semantics. Defaults to `div`. */
669
+ contentElement?: PaneContentElement;
670
+ /** Independent logical-edge separator lines. Defaults to none. */
671
+ separators?: readonly PaneSeparatorSide[];
672
+ id?: string;
673
+ /** Accessible name for a landmark root such as `aside` or `main`. */
674
+ label?: string;
675
+ /** Accessible name for a landmark scrolling slot such as `nav`. */
676
+ contentLabel?: string;
677
+ className?: string;
678
+ headerClassName?: string;
679
+ contentClassName?: string;
680
+ footerClassName?: string;
681
+ /** Safe `data-*` metadata; Pane-owned structural attributes remain protected. */
682
+ rootAttributes?: PaneRootAttributes;
683
+ }
684
+ /**
685
+ * An unpadded application column with optional fixed header/footer slots and one
686
+ * scrolling vertical content owner. Separator lines are independently opt-in on
687
+ * each logical edge, so the same component works as a sidebar, main area,
688
+ * inspector, or dialog column.
689
+ */
690
+ declare function Pane({ header, children, footer, element, contentElement, separators, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, }: PaneProps): SafeHtml;
691
+
692
+ export { Pane, type PaneContentElement, type PaneElement, type PaneProps, type PaneSeparatorSide };
693
+ ```
694
+
607
695
  ## `@kerfjs/ui/workbench`
608
696
 
609
697
  ```ts
@@ -766,21 +854,21 @@ export { type SidebarStorage, type WireSidebarOptions, type WireSidebarPanel, wi
766
854
  ```ts
767
855
  import { SafeHtml } from 'kerfjs';
768
856
 
769
- interface TabScaffoldTab {
770
- id: string;
857
+ interface TabScaffoldTab<Id extends string = string> {
858
+ id: Id;
771
859
  label: string;
772
860
  /** Decorative icon shown above the label in the bottom bar. */
773
861
  icon?: SafeHtml;
774
862
  /** The tab's content — typically a `NavStack` so each tab keeps its own stack. */
775
863
  content: SafeHtml;
776
864
  }
777
- interface TabScaffoldProps {
865
+ interface TabScaffoldProps<Id extends string = string> {
778
866
  id: string;
779
867
  /** Accessible name for the tab bar. */
780
868
  label: string;
781
- tabs: TabScaffoldTab[];
869
+ tabs: readonly TabScaffoldTab<Id>[];
782
870
  /** The controlled active tab id (the app owns selection). */
783
- active: string;
871
+ active: NoInfer<Id>;
784
872
  className?: string;
785
873
  }
786
874
  /**
@@ -791,7 +879,7 @@ interface TabScaffoldProps {
791
879
  * On larger classes, promote the tabs to a `Workbench` rail or sidebar instead of
792
880
  * a bottom bar. See `docs/23-app-layouts.md` §3.4.
793
881
  */
794
- declare function TabScaffold({ id, label, tabs, active, className, }: TabScaffoldProps): SafeHtml;
882
+ declare function TabScaffold<Id extends string>({ id, label, tabs, active, className, }: TabScaffoldProps<Id>): SafeHtml;
795
883
 
796
884
  export { TabScaffold, type TabScaffoldProps, type TabScaffoldTab };
797
885
  ```
@@ -1029,6 +1117,16 @@ declare function Catalog({ brand, sections, active, content, collapsed, theme, h
1029
1117
  * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).
1030
1118
  */
1031
1119
  type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
1120
+ type CatalogExampleRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1121
+ 'data-catalog-example'?: never;
1122
+ 'data-catalog-example-stack'?: never;
1123
+ 'data-align'?: never;
1124
+ }>;
1125
+ type CatalogExampleStackRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1126
+ 'data-catalog-example'?: never;
1127
+ 'data-catalog-example-stack'?: never;
1128
+ 'data-align'?: never;
1129
+ }>;
1032
1130
  interface CatalogExampleProps {
1033
1131
  /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */
1034
1132
  label?: string;
@@ -1036,6 +1134,8 @@ interface CatalogExampleProps {
1036
1134
  note?: SafeHtml | string;
1037
1135
  /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */
1038
1136
  align?: CatalogExampleAlign;
1137
+ /** Safe authoring `data-*` metadata for the rendered section. Helper-owned structural attributes remain protected. */
1138
+ rootAttributes?: CatalogExampleRootAttributes;
1039
1139
  className?: string;
1040
1140
  children?: SafeHtml | readonly SafeHtml[];
1041
1141
  }
@@ -1047,19 +1147,64 @@ interface CatalogExampleProps {
1047
1147
  * `--kui-catalog-example-align` custom property so a debug overlay can exclude it
1048
1148
  * from a specimen's measured margin.
1049
1149
  */
1050
- declare function CatalogExample({ label, note, align, className, children, }: CatalogExampleProps): SafeHtml;
1150
+ declare function CatalogExample({ label, note, align, rootAttributes, className, children, }: CatalogExampleProps): SafeHtml;
1051
1151
  interface CatalogExampleStackProps {
1052
1152
  /** Accessible label for the stack region. */
1053
1153
  label?: string;
1154
+ /** Safe authoring `data-*` metadata for the rendered stack. Helper-owned structural attributes remain protected. */
1155
+ rootAttributes?: CatalogExampleStackRootAttributes;
1054
1156
  className?: string;
1055
1157
  children?: SafeHtml | readonly SafeHtml[];
1056
1158
  }
1057
- /** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */
1058
- declare function CatalogExampleStack({ label, className, children, }: CatalogExampleStackProps): SafeHtml;
1159
+ /**
1160
+ * A vertically-stacked group of {@link CatalogExample}s with the catalog's
1161
+ * example rhythm. The semantic `section` becomes a named `region` when `label`
1162
+ * is supplied; an unlabeled stack remains an ordinary grouping.
1163
+ */
1164
+ declare function CatalogExampleStack({ label, rootAttributes, className, children, }: CatalogExampleStackProps): SafeHtml;
1059
1165
 
1060
1166
  export { Catalog, type CatalogBrand, type CatalogEntry, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogProps, type CatalogRelated, type CatalogResource, type CatalogSecondaryGroup, type CatalogSection };
1061
1167
  ```
1062
1168
 
1169
+ ## `@kerfjs/ui/catalog-resources`
1170
+
1171
+ ```ts
1172
+ import { CatalogResource } from './catalog.js';
1173
+ import 'kerfjs';
1174
+
1175
+ /**
1176
+ * Standard resource labels for a Kerf catalog detail footer. Keep these labels
1177
+ * stable across catalogs so people and AI-generated integrations see the same
1178
+ * choices in the same vocabulary.
1179
+ */
1180
+ type CatalogResourceKind = 'demoSource' | 'componentSource' | 'designTemplate' | 'guidance' | 'integrationGuidance';
1181
+ type CatalogGuidanceKind = 'guidance' | 'integrationGuidance';
1182
+ interface CatalogResourceTarget {
1183
+ href: string;
1184
+ /** Optional monospace detail, normally the repository-relative source path. */
1185
+ detail?: string;
1186
+ }
1187
+ interface CatalogResourcesInput {
1188
+ /** Required source for the runnable demonstration. */
1189
+ demoSource: CatalogResourceTarget;
1190
+ /** Source for the production component; omit for recipes and integrations. */
1191
+ componentSource?: CatalogResourceTarget;
1192
+ /** Optional design-tool template associated with the component. */
1193
+ designTemplate?: CatalogResourceTarget;
1194
+ /** Required UI or integration guidance. */
1195
+ guidance: CatalogResourceTarget;
1196
+ /** Use `integrationGuidance` when the component implementation is upstream. */
1197
+ guidanceKind?: CatalogGuidanceKind;
1198
+ }
1199
+ /**
1200
+ * Build the standard catalog resource group in its canonical order: demo,
1201
+ * component, optional design template, then guidance.
1202
+ */
1203
+ declare function catalogResources(input: CatalogResourcesInput): CatalogResource[];
1204
+
1205
+ export { type CatalogGuidanceKind, type CatalogResourceKind, type CatalogResourceTarget, type CatalogResourcesInput, catalogResources };
1206
+ ```
1207
+
1063
1208
  ## `@kerfjs/ui/wire-catalog`
1064
1209
 
1065
1210
  ```ts
@@ -1129,18 +1274,18 @@ type SegmentedControlAppearance = 'filled' | 'outlined' | 'toolbar';
1129
1274
  type SegmentedControlShape = 'rounded' | 'pill';
1130
1275
  type SegmentedControlSize = 'small' | 'default';
1131
1276
  type SegmentedControlLayout = 'content' | 'equal';
1132
- interface SegmentedControlChoice {
1133
- value: string;
1277
+ interface SegmentedControlChoice<Value extends string = string> {
1278
+ value: Value;
1134
1279
  label: string;
1135
1280
  content?: SafeHtml;
1136
1281
  title?: string;
1137
1282
  disabled?: boolean;
1138
1283
  }
1139
- interface SegmentedControlProps {
1284
+ interface SegmentedControlProps<Value extends string = string> {
1140
1285
  id: string;
1141
1286
  label: string;
1142
- value: string;
1143
- choices: readonly SegmentedControlChoice[];
1287
+ value: NoInfer<Value>;
1288
+ choices: readonly SegmentedControlChoice<Value>[];
1144
1289
  action?: string;
1145
1290
  appearance?: SegmentedControlAppearance;
1146
1291
  shape?: SegmentedControlShape;
@@ -1150,31 +1295,74 @@ interface SegmentedControlProps {
1150
1295
  /** Render as an unanimated loading skeleton, disabling every segment. */
1151
1296
  placeholder?: boolean;
1152
1297
  }
1153
- declare function SegmentedControl({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, }: SegmentedControlProps): SafeHtml;
1298
+ declare function SegmentedControl<Value extends string>({ id, label, value, choices, action, appearance, shape, size, layout, className, placeholder, }: SegmentedControlProps<Value>): SafeHtml;
1154
1299
 
1155
1300
  export { SegmentedControl, type SegmentedControlAppearance, type SegmentedControlChoice, type SegmentedControlLayout, type SegmentedControlProps, type SegmentedControlShape, type SegmentedControlSize };
1156
1301
  ```
1157
1302
 
1303
+ ## `@kerfjs/ui/select`
1304
+
1305
+ ```ts
1306
+ import { SafeHtml } from 'kerfjs';
1307
+ import { LucideNode } from './lucide-icon.js';
1308
+ import 'lucide';
1309
+
1310
+ interface SelectChoice<Value extends string = string> {
1311
+ value: Value;
1312
+ label: string;
1313
+ icon?: LucideNode;
1314
+ iconName?: string;
1315
+ color?: string;
1316
+ group?: string;
1317
+ separatorBefore?: boolean;
1318
+ }
1319
+ type SelectAccessibleName = {
1320
+ label: string;
1321
+ ariaLabel?: string;
1322
+ } | {
1323
+ label?: never;
1324
+ ariaLabel: string;
1325
+ };
1326
+ interface SelectBaseProps<Value extends string = string> {
1327
+ name: string;
1328
+ value: NoInfer<Value>;
1329
+ choices: readonly SelectChoice<Value>[];
1330
+ className?: string;
1331
+ /** Empty-value hint text shown in the closed control (the native select placeholder). */
1332
+ placeholderText?: string;
1333
+ disabled?: boolean;
1334
+ fitMenu?: boolean;
1335
+ renderSelected?: (choice: SelectChoice<Value>) => SafeHtml;
1336
+ /** Render as an unanimated loading skeleton: the label above a static, empty control box. */
1337
+ placeholder?: boolean;
1338
+ }
1339
+ type SelectProps<Value extends string = string> = SelectBaseProps<Value> & SelectAccessibleName;
1340
+ declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className, placeholderText, disabled, fitMenu, renderSelected, placeholder, }: SelectProps<Value>): SafeHtml;
1341
+
1342
+ export { Select, type SelectChoice, type SelectProps };
1343
+ ```
1344
+
1158
1345
  ## `@kerfjs/ui/state-banner`
1159
1346
 
1160
1347
  ```ts
1161
1348
  import { SafeHtml } from 'kerfjs';
1162
1349
 
1163
1350
  type StateBannerTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
1351
+ type StateBannerUrgency = 'status' | 'alert';
1164
1352
  interface StateBannerProps {
1165
1353
  title: string;
1166
1354
  detail?: string;
1167
1355
  icon?: SafeHtml;
1168
1356
  action?: SafeHtml;
1169
1357
  tone?: StateBannerTone;
1170
- urgency?: 'status' | 'alert';
1358
+ urgency?: StateBannerUrgency;
1171
1359
  className?: string;
1172
1360
  /** Render the title and detail as unanimated loading skeletons, keeping the icon and tone. */
1173
1361
  placeholder?: boolean;
1174
1362
  }
1175
1363
  declare function StateBanner({ title, detail, icon, action, tone, urgency, className, placeholder, }: StateBannerProps): SafeHtml;
1176
1364
 
1177
- export { StateBanner, type StateBannerProps, type StateBannerTone };
1365
+ export { StateBanner, type StateBannerProps, type StateBannerTone, type StateBannerUrgency };
1178
1366
  ```
1179
1367
 
1180
1368
  ## `@kerfjs/ui/empty-state`
@@ -1239,6 +1427,27 @@ declare function Skeleton({ width, height, radius, lines, label, className, }: S
1239
1427
  export { Skeleton, type SkeletonProps };
1240
1428
  ```
1241
1429
 
1430
+ ## `@kerfjs/ui/sunken-panel`
1431
+
1432
+ ```ts
1433
+ import { SafeHtml } from 'kerfjs';
1434
+
1435
+ interface SunkenPanelProps {
1436
+ children?: SafeHtml | readonly SafeHtml[];
1437
+ /** Optional accessible landmark name for a distinct application region. */
1438
+ ariaLabel?: string;
1439
+ className?: string;
1440
+ }
1441
+ /**
1442
+ * A lowered application surface with one compact inset and a vertical content
1443
+ * stack. The panel owns its background and padding; children own their own
1444
+ * borders and internal geometry.
1445
+ */
1446
+ declare function SunkenPanel({ children, ariaLabel, className, }: SunkenPanelProps): SafeHtml;
1447
+
1448
+ export { SunkenPanel, type SunkenPanelProps };
1449
+ ```
1450
+
1242
1451
  ## `@kerfjs/ui/token-search-field`
1243
1452
 
1244
1453
  ```ts
@@ -1250,7 +1459,15 @@ interface TokenSearchToken {
1250
1459
  offset?: number;
1251
1460
  accessibleLabel?: string;
1252
1461
  }
1253
- interface TokenSearchFieldProps {
1462
+ type TokenSearchEditorAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
1463
+ 'data-component'?: never;
1464
+ 'data-key'?: never;
1465
+ 'data-morph-skip'?: never;
1466
+ 'data-token-search-editor'?: never;
1467
+ 'data-token-count'?: never;
1468
+ 'data-placeholder'?: never;
1469
+ }>;
1470
+ interface TokenSearchFieldBaseProps {
1254
1471
  id: string;
1255
1472
  label: string;
1256
1473
  query?: string;
@@ -1259,12 +1476,6 @@ interface TokenSearchFieldProps {
1259
1476
  tokenPlaceholder?: string;
1260
1477
  disabled?: boolean;
1261
1478
  autofocus?: boolean;
1262
- /** Allow an empty field to render as one iconic action. */
1263
- collapsible?: boolean;
1264
- /** Keep an empty collapsible field open while the application owns focus. */
1265
- expanded?: boolean;
1266
- expandAction?: string;
1267
- expandLabel?: string;
1268
1479
  leading?: SafeHtml;
1269
1480
  trailing?: SafeHtml;
1270
1481
  editAction?: string;
@@ -1272,8 +1483,22 @@ interface TokenSearchFieldProps {
1272
1483
  clearAction?: string;
1273
1484
  clearLabel?: string;
1274
1485
  className?: string;
1275
- editorAttributes?: Readonly<Record<`data-${string}`, string>>;
1486
+ editorAttributes?: TokenSearchEditorAttributes;
1276
1487
  }
1488
+ type TokenSearchCollapsibleProps = {
1489
+ /** Allow an empty field to render as one iconic action. */
1490
+ collapsible: true;
1491
+ /** Keep an empty collapsible field open while the application owns focus. */
1492
+ expanded?: boolean;
1493
+ expandAction?: string;
1494
+ expandLabel?: string;
1495
+ } | {
1496
+ collapsible?: false;
1497
+ expanded?: never;
1498
+ expandAction?: never;
1499
+ expandLabel?: never;
1500
+ };
1501
+ type TokenSearchFieldProps = TokenSearchFieldBaseProps & TokenSearchCollapsibleProps;
1277
1502
  interface TokenSearchFieldValue {
1278
1503
  query: string;
1279
1504
  tokens: TokenSearchToken[];
@@ -1284,7 +1509,7 @@ declare function readTokenSearchField(editor: HTMLElement, knownTokens?: readonl
1284
1509
  /** Focus an editor and place its caret at a text offset, skipping atomic token chips. */
1285
1510
  declare function placeTokenSearchCaret(editor: HTMLElement, offset?: number): void;
1286
1511
 
1287
- export { TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValue, type TokenSearchToken, placeTokenSearchCaret, readTokenSearchField };
1512
+ export { type TokenSearchEditorAttributes, TokenSearchField, type TokenSearchFieldProps, type TokenSearchFieldValue, type TokenSearchToken, placeTokenSearchCaret, readTokenSearchField };
1288
1513
  ```
1289
1514
 
1290
1515
  ## `@kerfjs/ui/wire-token-search-fields`
@@ -1321,21 +1546,26 @@ interface TokenSearchTokenRemoval {
1321
1546
  * for the caller to apply, while caret movement past a chip is a pure ephemeral
1322
1547
  * mechanic the helper performs itself.
1323
1548
  */
1324
- interface TokenSearchKeyboardOptions {
1549
+ interface TokenSearchKeyboardBaseOptions {
1325
1550
  /**
1326
1551
  * From a collapsed caret with no selection, Backspace removes the token
1327
1552
  * immediately before it and Delete the token immediately after — reported via
1328
1553
  * `onRemoveToken` — instead of deleting a character. Default: true.
1329
1554
  */
1330
- removeAdjacentToken?: boolean;
1331
1555
  /**
1332
1556
  * ArrowRight moves the caret past a trailing atomic token so text typed next
1333
1557
  * lands after the chip. Default: true.
1334
1558
  */
1335
1559
  moveCaretPastToken?: boolean;
1336
- /** Apply the reported removal to your controlled state, then re-render. */
1337
- onRemoveToken?: (removal: TokenSearchTokenRemoval) => void;
1338
1560
  }
1561
+ type TokenSearchKeyboardOptions = TokenSearchKeyboardBaseOptions & ({
1562
+ removeAdjacentToken?: true;
1563
+ /** Apply the reported removal to your controlled state, then re-render. */
1564
+ onRemoveToken: (removal: TokenSearchTokenRemoval) => void;
1565
+ } | {
1566
+ removeAdjacentToken: false;
1567
+ onRemoveToken?: never;
1568
+ });
1339
1569
  /**
1340
1570
  * Managed collapsible behavior for the iconic TokenSearchField. Every piece is on
1341
1571
  * by default; disable a specific one to own it in the app. Provide `signals` to
@@ -1368,8 +1598,8 @@ interface WireTokenSearchFieldsOptions {
1368
1598
  onEdit?: (edit: TokenSearchEdit) => void;
1369
1599
  /** Managed collapsible transient behavior. `true`/omitted = on with defaults; `false` = fully off. */
1370
1600
  collapsible?: boolean | TokenSearchCollapsibleOptions;
1371
- /** Opt-in atomic-chip keyboard behavior (off by default). `true` = on with defaults. */
1372
- keyboard?: boolean | TokenSearchKeyboardOptions;
1601
+ /** Opt-in atomic-chip keyboard behavior (off by default). */
1602
+ keyboard?: false | TokenSearchKeyboardOptions;
1373
1603
  }
1374
1604
  /**
1375
1605
  * The value returned from {@link wireTokenSearchFields}: call it (or `dispose()`) to