@kerfjs/ui 5.0.0-beta.23 → 5.0.0-beta.24

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 (193) hide show
  1. package/README.md +1 -0
  2. package/ai/compile-time-contracts-v1.json +2 -1
  3. package/ai/component-catalog-v2-overrides.json +5 -12
  4. package/ai/component-catalog-v2.json +43 -32
  5. package/ai/component-catalog.json +56 -37
  6. package/ai/public-api-signatures-v1.md +29 -16
  7. package/ai/skill.md +4 -4
  8. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  9. package/dist/app-tab.js +1 -1
  10. package/dist/browser/list.js +3 -0
  11. package/dist/catalog.d.ts +8 -4
  12. package/dist/catalog.js +19 -10
  13. package/dist/catalog.js.map +1 -1
  14. package/dist/{chunk-RYRIFZCT.js → chunk-7XRDUG4Y.js} +5 -5
  15. package/dist/{chunk-RYRIFZCT.js.map → chunk-7XRDUG4Y.js.map} +1 -1
  16. package/dist/{chunk-OHU6CI22.js → chunk-B3A3IVO5.js} +2 -2
  17. package/dist/{chunk-OHU6CI22.js.map → chunk-B3A3IVO5.js.map} +1 -1
  18. package/dist/{chunk-ELSJX4W7.js → chunk-FEAMR7S4.js} +2 -2
  19. package/dist/{chunk-ELSJX4W7.js.map → chunk-FEAMR7S4.js.map} +1 -1
  20. package/dist/{chunk-OZWU5ZPZ.js → chunk-IJBSZ4NX.js} +2 -2
  21. package/dist/{chunk-OZWU5ZPZ.js.map → chunk-IJBSZ4NX.js.map} +1 -1
  22. package/dist/chunk-J5BFYY7Q.js +35 -0
  23. package/dist/chunk-J5BFYY7Q.js.map +1 -0
  24. package/dist/{chunk-QQFJMRIB.js → chunk-KWWR5VMS.js} +2 -2
  25. package/dist/{chunk-QQFJMRIB.js.map → chunk-KWWR5VMS.js.map} +1 -1
  26. package/dist/{chunk-K57KRAOV.js → chunk-NWSEP5RT.js} +2 -2
  27. package/dist/{chunk-K57KRAOV.js.map → chunk-NWSEP5RT.js.map} +1 -1
  28. package/dist/{chunk-GP5DP5DL.js → chunk-OJWWHFH3.js} +39 -4
  29. package/dist/chunk-OJWWHFH3.js.map +1 -0
  30. package/dist/{chunk-DSNIYOZ3.js → chunk-PSKN344N.js} +2 -2
  31. package/dist/{chunk-DSNIYOZ3.js.map → chunk-PSKN344N.js.map} +1 -1
  32. package/dist/{chunk-W7YD6E4N.js → chunk-R7ZWUN64.js} +2 -2
  33. package/dist/{chunk-W7YD6E4N.js.map → chunk-R7ZWUN64.js.map} +1 -1
  34. package/dist/{chunk-PZQVSDLH.js → chunk-RKVQEH4J.js} +1 -1
  35. package/dist/chunk-RKVQEH4J.js.map +1 -0
  36. package/dist/{chunk-7L6EHUMV.js → chunk-SYDMYBPG.js} +3 -3
  37. package/dist/chunk-SYDMYBPG.js.map +1 -0
  38. package/dist/{chunk-W4CXQYSG.js → chunk-TB6DY7H5.js} +3 -1
  39. package/dist/chunk-TB6DY7H5.js.map +1 -0
  40. package/dist/{chunk-BBRXX6PF.js → chunk-UFZKLBCF.js} +2 -2
  41. package/dist/{chunk-BBRXX6PF.js.map → chunk-UFZKLBCF.js.map} +1 -1
  42. package/dist/{chunk-AASLYRGE.js → chunk-UKKSMZYT.js} +18 -7
  43. package/dist/chunk-UKKSMZYT.js.map +1 -0
  44. package/dist/{chunk-VLYV65LK.js → chunk-W5L2JSBY.js} +5 -3
  45. package/dist/chunk-W5L2JSBY.js.map +1 -0
  46. package/dist/{chunk-PIDTEQ3U.js → chunk-Y45PPBDV.js} +2 -2
  47. package/dist/{chunk-PIDTEQ3U.js.map → chunk-Y45PPBDV.js.map} +1 -1
  48. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  49. package/dist/index.d.ts +3 -1
  50. package/dist/index.js +16 -15
  51. package/dist/list-action-row.js +1 -1
  52. package/dist/list-header.js +1 -1
  53. package/dist/list-item.js +1 -1
  54. package/dist/list.d.ts +19 -0
  55. package/dist/list.js +3 -0
  56. package/dist/list.js.map +1 -0
  57. package/dist/nav-stack.js +1 -1
  58. package/dist/panel-header.js +1 -1
  59. package/dist/segmented-control.js +1 -1
  60. package/dist/select.js +1 -1
  61. package/dist/skeleton.d.ts +1 -1
  62. package/dist/skeleton.js +1 -1
  63. package/dist/split-view.js +1 -1
  64. package/dist/state-banner.d.ts +4 -2
  65. package/dist/state-banner.js +1 -1
  66. package/dist/styles/catalog.css +8 -2
  67. package/dist/styles/list-item.css +6 -6
  68. package/dist/styles/list.css +43 -0
  69. package/dist/styles/state-banner.css +32 -1
  70. package/dist/styles/styles.css +1 -0
  71. package/dist/styles/sunken-panel.css +4 -0
  72. package/dist/styles/toolbar.css +38 -2
  73. package/dist/sunken-panel.d.ts +5 -2
  74. package/dist/sunken-panel.js +1 -1
  75. package/dist/toolbar-text.js +1 -1
  76. package/dist/toolbar.d.ts +5 -3
  77. package/dist/toolbar.js +1 -1
  78. package/dist/value-table.js +1 -1
  79. package/dist/wire-catalog.d.ts +4 -2
  80. package/dist/wire-catalog.js +95 -36
  81. package/dist/wire-catalog.js.map +1 -1
  82. package/dist/wire-tab-bars.js +1 -1
  83. package/dist/wire-token-search-fields.d.ts +1 -1
  84. package/dist/wire-token-search-fields.js +1 -1
  85. package/docs/accessibility.md +12 -3
  86. package/docs/catalog.md +17 -12
  87. package/docs/component-contract.md +14 -1
  88. package/docs/component-selection.md +6 -2
  89. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  90. package/docs/design/templates/empty-state/filtered.svg +1 -1
  91. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  92. package/docs/design/templates/empty-state/with-action.svg +1 -1
  93. package/docs/design/templates/empty-state-dark.svg +1 -7
  94. package/docs/design/templates/empty-state.svg +1 -7
  95. package/docs/design/templates/list/compact-dark.svg +1 -0
  96. package/docs/design/templates/list/compact.svg +1 -0
  97. package/docs/design/templates/list/stack-dark.svg +1 -0
  98. package/docs/design/templates/list/stack.svg +1 -0
  99. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  100. package/docs/design/templates/list-action-row/default.svg +1 -1
  101. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  102. package/docs/design/templates/list-action-row/selected.svg +1 -1
  103. package/docs/design/templates/list-action-row-dark.svg +1 -7
  104. package/docs/design/templates/list-action-row.svg +1 -7
  105. package/docs/design/templates/list-dark.svg +1 -0
  106. package/docs/design/templates/list-header/action-dark.svg +1 -1
  107. package/docs/design/templates/list-header/action.svg +1 -1
  108. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -1
  109. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -1
  110. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  111. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  112. package/docs/design/templates/list-header-dark.svg +1 -11
  113. package/docs/design/templates/list-header.svg +1 -11
  114. package/docs/design/templates/list-item/default-dark.svg +1 -1
  115. package/docs/design/templates/list-item/default.svg +1 -1
  116. package/docs/design/templates/list-item/multiline-dark.svg +1 -1
  117. package/docs/design/templates/list-item/multiline.svg +1 -1
  118. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  119. package/docs/design/templates/list-item/selected.svg +1 -1
  120. package/docs/design/templates/list-item/trailing-dark.svg +1 -1
  121. package/docs/design/templates/list-item/trailing.svg +1 -1
  122. package/docs/design/templates/list-item-dark.svg +1 -11
  123. package/docs/design/templates/list-item.svg +1 -11
  124. package/docs/design/templates/list.svg +1 -0
  125. package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -1
  126. package/docs/design/templates/panel-header/icon-actions.svg +1 -1
  127. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -1
  128. package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -1
  129. package/docs/design/templates/panel-header-dark.svg +1 -13
  130. package/docs/design/templates/panel-header.svg +1 -13
  131. package/docs/design/templates/segmented-control-dark.svg +1 -7
  132. package/docs/design/templates/segmented-control.svg +1 -7
  133. package/docs/design/templates/skeleton-dark.svg +1 -9
  134. package/docs/design/templates/skeleton.svg +1 -9
  135. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  136. package/docs/design/templates/state-banner/danger.svg +1 -1
  137. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  138. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  139. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  140. package/docs/design/templates/state-banner/info.svg +1 -1
  141. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  142. package/docs/design/templates/state-banner/success.svg +1 -1
  143. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  144. package/docs/design/templates/state-banner/warning.svg +1 -1
  145. package/docs/design/templates/state-banner-dark.svg +1 -11
  146. package/docs/design/templates/state-banner.svg +1 -11
  147. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
  148. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
  149. package/docs/design/templates/tab-bar-dark.svg +1 -5
  150. package/docs/design/templates/tab-bar.svg +1 -5
  151. package/docs/design/templates/token-search-field/empty-dark.svg +1 -1
  152. package/docs/design/templates/token-search-field/empty.svg +1 -1
  153. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -1
  154. package/docs/design/templates/token-search-field/tokens.svg +1 -1
  155. package/docs/design/templates/token-search-field-dark.svg +1 -7
  156. package/docs/design/templates/token-search-field.svg +1 -7
  157. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  158. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  159. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -1
  160. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -1
  161. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -1
  162. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -1
  163. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -1
  164. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -1
  165. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  166. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  167. package/docs/design/templates/toolbar-control-group-dark.svg +1 -11
  168. package/docs/design/templates/toolbar-control-group.svg +1 -11
  169. package/docs/design/templates/toolbar-dark.svg +1 -5
  170. package/docs/design/templates/toolbar-text-dark.svg +1 -9
  171. package/docs/design/templates/toolbar-text.svg +1 -9
  172. package/docs/design/templates/toolbar.svg +1 -5
  173. package/docs/design/templates/value-table/details-dark.svg +1 -1
  174. package/docs/design/templates/value-table/details.svg +1 -1
  175. package/docs/design/templates/value-table-dark.svg +1 -5
  176. package/docs/design/templates/value-table.svg +1 -5
  177. package/docs/design/templates.md +25 -14
  178. package/docs/sunken-panel.md +11 -1
  179. package/docs/type-contracts.md +6 -3
  180. package/docs/ui-doctor.md +6 -4
  181. package/docs/ux-demo.md +1 -1
  182. package/doctor/index.mjs +5 -2
  183. package/llms.txt +15 -3
  184. package/package.json +8 -2
  185. package/traversal-exclusions.mjs +7 -1
  186. package/ux-demo/recipes/collapsible-sidebar.tsx +1 -1
  187. package/ux-demo/recipes/navigation-sidebar.tsx +1 -1
  188. package/dist/chunk-7L6EHUMV.js.map +0 -1
  189. package/dist/chunk-AASLYRGE.js.map +0 -1
  190. package/dist/chunk-GP5DP5DL.js.map +0 -1
  191. package/dist/chunk-PZQVSDLH.js.map +0 -1
  192. package/dist/chunk-VLYV65LK.js.map +0 -1
  193. package/dist/chunk-W4CXQYSG.js.map +0 -1
@@ -29,22 +29,14 @@ function isTransparent(color) {
29
29
  const normalized = color.replace(/\s+/g, "");
30
30
  return normalized === "transparent" || /(?:,|\/)0(?:\.0+)?\)$/.test(normalized);
31
31
  }
32
- function isExampleLabel(element) {
33
- const parent = element.parentElement;
34
- if (!element.classList.contains("kui-list-header") || !parent?.classList.contains("kui-catalog-example") || element !== parent.firstElementChild)
35
- return false;
36
- for (let sibling = element.nextElementSibling; sibling; sibling = sibling.nextElementSibling)
37
- if (!sibling.classList.contains("kui-catalog-example__note")) return true;
38
- return false;
39
- }
40
32
  function geometrySpecimens(canvas) {
41
33
  const result = [];
42
34
  for (const example of canvas.querySelectorAll(
43
- ".kui-catalog-example"
35
+ "[data-catalog-example]"
44
36
  )) {
45
37
  if (example.closest("[data-catalog-geometry-overlay-skip]")) continue;
46
38
  for (const child of example.children) {
47
- if (child.matches("[data-catalog-geometry-overlay]") || child.classList.contains("kui-catalog-example__note") || isExampleLabel(child))
39
+ if (child.matches("[data-catalog-geometry-overlay]") || child.matches("[data-catalog-example-label]") || child.matches("[data-catalog-example-note]"))
48
40
  continue;
49
41
  result.push(child);
50
42
  }
@@ -68,6 +60,9 @@ function geometryBox(document, className, left, top, width, height) {
68
60
  element.style.height = `${Math.max(0, height)}px`;
69
61
  return element;
70
62
  }
63
+ function effectiveBorderWidth(width, style) {
64
+ return style === "none" || style === "hidden" ? 0 : pixels(width);
65
+ }
71
66
  function wireCatalogGeometryOverlay(root) {
72
67
  const catalog = root.matches('[data-component="catalog"]') ? root : root.querySelector('[data-component="catalog"]');
73
68
  const canvas = catalog?.querySelector(".kui-catalog__canvas");
@@ -81,9 +76,14 @@ function wireCatalogGeometryOverlay(root) {
81
76
  const render = () => {
82
77
  frame = 0;
83
78
  layer.replaceChildren();
84
- if (catalog.dataset.geometryOverlay !== "true") return;
79
+ if (catalog.dataset.geometryOverlay !== "true") {
80
+ syncObservedSpecimens([]);
81
+ return;
82
+ }
85
83
  const base = canvas.getBoundingClientRect();
86
- for (const element of geometrySpecimens(canvas)) {
84
+ const specimens = geometrySpecimens(canvas);
85
+ syncObservedSpecimens(specimens);
86
+ for (const [specimenIndex, element] of specimens.entries()) {
87
87
  const rect = element.getBoundingClientRect();
88
88
  const style = view.getComputedStyle(element);
89
89
  const align = pixels(
@@ -101,35 +101,66 @@ function wireCatalogGeometryOverlay(root) {
101
101
  );
102
102
  const x = rect.left - base.left + canvas.scrollLeft;
103
103
  const y = rect.top - base.top + canvas.scrollTop;
104
- if (isTransparent(style.backgroundColor))
105
- layer.append(
106
- geometryBox(
107
- root.ownerDocument,
108
- "kui-catalog__geometry-bound",
109
- x,
110
- y,
111
- rect.width,
112
- rect.height
113
- )
104
+ const borderWidths = [
105
+ effectiveBorderWidth(style.borderTopWidth, style.borderTopStyle),
106
+ effectiveBorderWidth(style.borderRightWidth, style.borderRightStyle),
107
+ effectiveBorderWidth(style.borderBottomWidth, style.borderBottomStyle),
108
+ effectiveBorderWidth(style.borderLeftWidth, style.borderLeftStyle)
109
+ ];
110
+ if (borderWidths.some((width) => width > 0)) {
111
+ const border = geometryBox(
112
+ root.ownerDocument,
113
+ "kui-catalog__geometry-border",
114
+ x,
115
+ y,
116
+ rect.width,
117
+ rect.height
118
+ );
119
+ border.dataset.catalogGeometrySpecimen = String(specimenIndex);
120
+ border.style.borderTopWidth = `${borderWidths[0]}px`;
121
+ border.style.borderRightWidth = `${borderWidths[1]}px`;
122
+ border.style.borderBottomWidth = `${borderWidths[2]}px`;
123
+ border.style.borderLeftWidth = `${borderWidths[3]}px`;
124
+ border.style.borderRadius = style.borderRadius;
125
+ layer.append(border);
126
+ } else if (isTransparent(style.backgroundColor)) {
127
+ const bound = geometryBox(
128
+ root.ownerDocument,
129
+ "kui-catalog__geometry-bound",
130
+ x,
131
+ y,
132
+ rect.width,
133
+ rect.height
114
134
  );
135
+ bound.dataset.catalogGeometrySpecimen = String(specimenIndex);
136
+ layer.append(bound);
137
+ }
115
138
  const margins = [
116
- [top, x - left, y - top, rect.width + left + right, top],
117
- [bottom, x - left, y + rect.height, rect.width + left + right, bottom],
118
- [left, x - left, y, left, rect.height],
119
- [right, x + rect.width, y, right, rect.height]
139
+ ["top", top, x - left, y - top, rect.width + left + right, top],
140
+ [
141
+ "bottom",
142
+ bottom,
143
+ x - left,
144
+ y + rect.height,
145
+ rect.width + left + right,
146
+ bottom
147
+ ],
148
+ ["left", left, x - left, y, left, rect.height],
149
+ ["right", right, x + rect.width, y, right, rect.height]
120
150
  ];
121
- for (const [size, marginX, marginY, width, height] of margins) {
151
+ for (const [side, size, marginX, marginY, width, height] of margins) {
122
152
  if (size <= 0) continue;
123
- layer.append(
124
- geometryBox(
125
- root.ownerDocument,
126
- "kui-catalog__geometry-margin",
127
- marginX,
128
- marginY,
129
- width,
130
- height
131
- )
153
+ const margin = geometryBox(
154
+ root.ownerDocument,
155
+ "kui-catalog__geometry-margin",
156
+ marginX,
157
+ marginY,
158
+ width,
159
+ height
132
160
  );
161
+ margin.dataset.catalogGeometrySpecimen = String(specimenIndex);
162
+ margin.dataset.catalogGeometrySide = side;
163
+ layer.append(margin);
133
164
  }
134
165
  }
135
166
  };
@@ -138,6 +169,20 @@ function wireCatalogGeometryOverlay(root) {
138
169
  };
139
170
  const resizeObserver = new view.ResizeObserver(schedule);
140
171
  resizeObserver.observe(canvas);
172
+ const observedSpecimens = /* @__PURE__ */ new Set();
173
+ const syncObservedSpecimens = (specimens) => {
174
+ const next = new Set(specimens);
175
+ for (const specimen of observedSpecimens) {
176
+ if (next.has(specimen)) continue;
177
+ resizeObserver.unobserve(specimen);
178
+ observedSpecimens.delete(specimen);
179
+ }
180
+ for (const specimen of next) {
181
+ if (observedSpecimens.has(specimen)) continue;
182
+ resizeObserver.observe(specimen);
183
+ observedSpecimens.add(specimen);
184
+ }
185
+ };
141
186
  const mutationObserver = new view.MutationObserver((records) => {
142
187
  if (records.some(
143
188
  ({ target }) => target !== layer && !layer.contains(target)
@@ -149,12 +194,26 @@ function wireCatalogGeometryOverlay(root) {
149
194
  childList: true,
150
195
  subtree: true
151
196
  });
197
+ mutationObserver.observe(root.ownerDocument.documentElement, {
198
+ attributes: true
199
+ });
200
+ const documentHead = root.ownerDocument.head;
201
+ if (documentHead)
202
+ mutationObserver.observe(documentHead, {
203
+ attributes: true,
204
+ characterData: true,
205
+ childList: true,
206
+ subtree: true
207
+ });
208
+ const stylesheetLoaded = () => schedule();
209
+ documentHead?.addEventListener("load", stylesheetLoaded, true);
152
210
  view.addEventListener("resize", schedule);
153
211
  render();
154
212
  return () => {
155
213
  if (frame) view.cancelAnimationFrame(frame);
156
214
  resizeObserver.disconnect();
157
215
  mutationObserver.disconnect();
216
+ documentHead?.removeEventListener("load", stylesheetLoaded, true);
158
217
  view.removeEventListener("resize", schedule);
159
218
  layer.replaceChildren();
160
219
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wire-catalog.ts"],"names":[],"mappings":"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","file":"wire-catalog.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleSecondaryAction?: string;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\nconst catalogDesktopMedia = '(min-width: 52.01rem)';\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\nfunction pixels(value: string): number {\n const number = Number.parseFloat(value);\n if (!Number.isFinite(number)) return 0;\n return value.trim().endsWith('rem') ? number * 16 : number;\n}\n\nfunction isTransparent(color: string): boolean {\n const normalized = color.replace(/\\s+/g, '');\n return (\n normalized === 'transparent' || /(?:,|\\/)0(?:\\.0+)?\\)$/.test(normalized)\n );\n}\n\nfunction isExampleLabel(element: Element): boolean {\n const parent = element.parentElement;\n if (\n !element.classList.contains('kui-list-header') ||\n !parent?.classList.contains('kui-catalog-example') ||\n element !== parent.firstElementChild\n )\n return false;\n for (\n let sibling = element.nextElementSibling;\n sibling;\n sibling = sibling.nextElementSibling\n )\n if (!sibling.classList.contains('kui-catalog-example__note')) return true;\n return false;\n}\n\nfunction geometrySpecimens(canvas: HTMLElement): Element[] {\n const result: Element[] = [];\n for (const example of canvas.querySelectorAll<HTMLElement>(\n '.kui-catalog-example',\n )) {\n if (example.closest('[data-catalog-geometry-overlay-skip]')) continue;\n for (const child of example.children) {\n if (\n child.matches('[data-catalog-geometry-overlay]') ||\n child.classList.contains('kui-catalog-example__note') ||\n isExampleLabel(child)\n )\n continue;\n result.push(child);\n }\n }\n for (const element of canvas.querySelectorAll<HTMLElement>(\n '[data-component]',\n )) {\n if (\n element.closest('[data-catalog-geometry-overlay]') ||\n element.closest('[data-catalog-geometry-overlay-skip]') ||\n element.closest('.kui-catalog-example')\n )\n continue;\n const parent =\n element.parentElement?.closest<HTMLElement>('[data-component]');\n if (parent && canvas.contains(parent)) continue;\n result.push(element);\n }\n return result;\n}\n\nfunction geometryBox(\n document: Document,\n className: string,\n left: number,\n top: number,\n width: number,\n height: number,\n): HTMLElement {\n const element = document.createElement('div');\n element.className = className;\n element.style.transform = `translate(${left}px, ${top}px)`;\n element.style.width = `${Math.max(0, width)}px`;\n element.style.height = `${Math.max(0, height)}px`;\n return element;\n}\n\n/**\n * Keep a Catalog's opt-in geometry overlay synchronized with its preview.\n * Transparent specimens receive a dashed outer bound and positive margins use\n * devtools-style orange bands. Returns a disposer.\n */\nexport function wireCatalogGeometryOverlay(root: HTMLElement): () => void {\n const catalog = root.matches('[data-component=\"catalog\"]')\n ? root\n : root.querySelector<HTMLElement>('[data-component=\"catalog\"]');\n const canvas = catalog?.querySelector<HTMLElement>('.kui-catalog__canvas');\n const layer = catalog?.querySelector<HTMLElement>(\n '[data-catalog-geometry-overlay]',\n );\n const view = root.ownerDocument.defaultView;\n if (!catalog || !canvas || !layer || !view) return () => {};\n\n let frame = 0;\n const render = (): void => {\n frame = 0;\n layer.replaceChildren();\n if (catalog.dataset.geometryOverlay !== 'true') return;\n const base = canvas.getBoundingClientRect();\n for (const element of geometrySpecimens(canvas)) {\n const rect = element.getBoundingClientRect();\n const style = view.getComputedStyle(element);\n const align = pixels(\n style.getPropertyValue('--kui-catalog-example-align'),\n );\n const top = pixels(style.marginTop);\n const right = Math.max(\n 0,\n pixels(style.marginRight) - (style.direction === 'rtl' ? align : 0),\n );\n const bottom = pixels(style.marginBottom);\n const left = Math.max(\n 0,\n pixels(style.marginLeft) - (style.direction === 'rtl' ? 0 : align),\n );\n const x = rect.left - base.left + canvas.scrollLeft;\n const y = rect.top - base.top + canvas.scrollTop;\n if (isTransparent(style.backgroundColor))\n layer.append(\n geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-bound',\n x,\n y,\n rect.width,\n rect.height,\n ),\n );\n const margins = [\n [top, x - left, y - top, rect.width + left + right, top],\n [bottom, x - left, y + rect.height, rect.width + left + right, bottom],\n [left, x - left, y, left, rect.height],\n [right, x + rect.width, y, right, rect.height],\n ];\n for (const [size, marginX, marginY, width, height] of margins) {\n if (size <= 0) continue;\n layer.append(\n geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-margin',\n marginX,\n marginY,\n width,\n height,\n ),\n );\n }\n }\n };\n const schedule = (): void => {\n if (!frame) frame = view.requestAnimationFrame(render);\n };\n const resizeObserver = new view.ResizeObserver(schedule);\n resizeObserver.observe(canvas);\n const mutationObserver = new view.MutationObserver((records) => {\n if (\n records.some(\n ({ target }) => target !== layer && !layer.contains(target as Node),\n )\n )\n schedule();\n });\n mutationObserver.observe(catalog, {\n attributes: true,\n childList: true,\n subtree: true,\n });\n view.addEventListener('resize', schedule);\n render();\n return () => {\n if (frame) view.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n view.removeEventListener('resize', schedule);\n layer.replaceChildren();\n };\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n }: WireCatalogOptions,\n): () => void {\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n return () => {\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/wire-catalog.ts"],"names":[],"mappings":"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","file":"wire-catalog.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n /**\n * Reveal the chosen sidebar row after selection. `true` uses desktop-safe\n * defaults; pass options to customize scroll alignment or the media guard.\n */\n revealSelection?: boolean | CatalogRevealOptions;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleSecondaryAction?: string;\n}\n\nexport interface CatalogRevealOptions {\n /** Scroll alignment within the sidebar. Default `'nearest'`. */\n block?: ScrollLogicalPosition;\n /** Cross-axis alignment. Default `'nearest'`. */\n inline?: ScrollLogicalPosition;\n /** Scroll behavior. Default `'auto'`. */\n behavior?: ScrollBehavior;\n /**\n * Only reveal when this media query matches. Defaults to the Catalog's\n * desktop layout; pass `false` to reveal at every viewport size.\n */\n media?: string | false;\n}\n\nconst catalogDesktopMedia = '(min-width: 52.01rem)';\n\n/**\n * Reveal one Catalog sidebar entry after the controlled render settles without\n * moving focus. Returns a cancellation function for rapid selection changes.\n */\nexport function revealCatalogEntry(\n root: HTMLElement,\n id: string,\n {\n block = 'nearest',\n inline = 'nearest',\n behavior = 'auto',\n media = catalogDesktopMedia,\n }: CatalogRevealOptions = {},\n): () => void {\n const view = root.ownerDocument.defaultView;\n if (!view || (media && !view.matchMedia(media).matches)) return () => {};\n const frame = view.requestAnimationFrame(() => {\n for (const item of root.querySelectorAll<HTMLElement>('[data-item-id]')) {\n if (item.dataset.itemId !== id) continue;\n item.scrollIntoView({ block, inline, behavior });\n break;\n }\n });\n return () => view.cancelAnimationFrame(frame);\n}\n\nfunction pixels(value: string): number {\n const number = Number.parseFloat(value);\n if (!Number.isFinite(number)) return 0;\n return value.trim().endsWith('rem') ? number * 16 : number;\n}\n\nfunction isTransparent(color: string): boolean {\n const normalized = color.replace(/\\s+/g, '');\n return (\n normalized === 'transparent' || /(?:,|\\/)0(?:\\.0+)?\\)$/.test(normalized)\n );\n}\n\nfunction geometrySpecimens(canvas: HTMLElement): Element[] {\n const result: Element[] = [];\n for (const example of canvas.querySelectorAll<HTMLElement>(\n '[data-catalog-example]',\n )) {\n if (example.closest('[data-catalog-geometry-overlay-skip]')) continue;\n for (const child of example.children) {\n if (\n child.matches('[data-catalog-geometry-overlay]') ||\n child.matches('[data-catalog-example-label]') ||\n child.matches('[data-catalog-example-note]')\n )\n continue;\n result.push(child);\n }\n }\n for (const element of canvas.querySelectorAll<HTMLElement>(\n '[data-component]',\n )) {\n if (\n element.closest('[data-catalog-geometry-overlay]') ||\n element.closest('[data-catalog-geometry-overlay-skip]') ||\n element.closest('.kui-catalog-example')\n )\n continue;\n const parent =\n element.parentElement?.closest<HTMLElement>('[data-component]');\n if (parent && canvas.contains(parent)) continue;\n result.push(element);\n }\n return result;\n}\n\nfunction geometryBox(\n document: Document,\n className: string,\n left: number,\n top: number,\n width: number,\n height: number,\n): HTMLElement {\n const element = document.createElement('div');\n element.className = className;\n element.style.transform = `translate(${left}px, ${top}px)`;\n element.style.width = `${Math.max(0, width)}px`;\n element.style.height = `${Math.max(0, height)}px`;\n return element;\n}\n\nfunction effectiveBorderWidth(width: string, style: string): number {\n return style === 'none' || style === 'hidden' ? 0 : pixels(width);\n}\n\n/**\n * Keep a Catalog's opt-in geometry overlay synchronized with its preview.\n * Specimens receive computed border highlights (or a dashed bound when they\n * have no border and are transparent), while positive computed margins use\n * devtools-style orange bands. CSS/stylesheet-only changes are observed too.\n * Returns a disposer.\n */\nexport function wireCatalogGeometryOverlay(root: HTMLElement): () => void {\n const catalog = root.matches('[data-component=\"catalog\"]')\n ? root\n : root.querySelector<HTMLElement>('[data-component=\"catalog\"]');\n const canvas = catalog?.querySelector<HTMLElement>('.kui-catalog__canvas');\n const layer = catalog?.querySelector<HTMLElement>(\n '[data-catalog-geometry-overlay]',\n );\n const view = root.ownerDocument.defaultView;\n if (!catalog || !canvas || !layer || !view) return () => {};\n\n let frame = 0;\n const render = (): void => {\n frame = 0;\n layer.replaceChildren();\n if (catalog.dataset.geometryOverlay !== 'true') {\n syncObservedSpecimens([]);\n return;\n }\n const base = canvas.getBoundingClientRect();\n const specimens = geometrySpecimens(canvas);\n syncObservedSpecimens(specimens);\n for (const [specimenIndex, element] of specimens.entries()) {\n const rect = element.getBoundingClientRect();\n const style = view.getComputedStyle(element);\n const align = pixels(\n style.getPropertyValue('--kui-catalog-example-align'),\n );\n const top = pixels(style.marginTop);\n const right = Math.max(\n 0,\n pixels(style.marginRight) - (style.direction === 'rtl' ? align : 0),\n );\n const bottom = pixels(style.marginBottom);\n const left = Math.max(\n 0,\n pixels(style.marginLeft) - (style.direction === 'rtl' ? 0 : align),\n );\n const x = rect.left - base.left + canvas.scrollLeft;\n const y = rect.top - base.top + canvas.scrollTop;\n const borderWidths = [\n effectiveBorderWidth(style.borderTopWidth, style.borderTopStyle),\n effectiveBorderWidth(style.borderRightWidth, style.borderRightStyle),\n effectiveBorderWidth(style.borderBottomWidth, style.borderBottomStyle),\n effectiveBorderWidth(style.borderLeftWidth, style.borderLeftStyle),\n ];\n if (borderWidths.some((width) => width > 0)) {\n const border = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-border',\n x,\n y,\n rect.width,\n rect.height,\n );\n border.dataset.catalogGeometrySpecimen = String(specimenIndex);\n border.style.borderTopWidth = `${borderWidths[0]}px`;\n border.style.borderRightWidth = `${borderWidths[1]}px`;\n border.style.borderBottomWidth = `${borderWidths[2]}px`;\n border.style.borderLeftWidth = `${borderWidths[3]}px`;\n border.style.borderRadius = style.borderRadius;\n layer.append(border);\n } else if (isTransparent(style.backgroundColor)) {\n const bound = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-bound',\n x,\n y,\n rect.width,\n rect.height,\n );\n bound.dataset.catalogGeometrySpecimen = String(specimenIndex);\n layer.append(bound);\n }\n const margins: Array<\n readonly [string, number, number, number, number, number]\n > = [\n ['top', top, x - left, y - top, rect.width + left + right, top],\n [\n 'bottom',\n bottom,\n x - left,\n y + rect.height,\n rect.width + left + right,\n bottom,\n ],\n ['left', left, x - left, y, left, rect.height],\n ['right', right, x + rect.width, y, right, rect.height],\n ];\n for (const [side, size, marginX, marginY, width, height] of margins) {\n if (size <= 0) continue;\n const margin = geometryBox(\n root.ownerDocument,\n 'kui-catalog__geometry-margin',\n marginX,\n marginY,\n width,\n height,\n );\n margin.dataset.catalogGeometrySpecimen = String(specimenIndex);\n margin.dataset.catalogGeometrySide = side;\n layer.append(margin);\n }\n }\n };\n const schedule = (): void => {\n if (!frame) frame = view.requestAnimationFrame(render);\n };\n const resizeObserver = new view.ResizeObserver(schedule);\n resizeObserver.observe(canvas);\n const observedSpecimens = new Set<Element>();\n const syncObservedSpecimens = (specimens: Element[]): void => {\n const next = new Set(specimens);\n for (const specimen of observedSpecimens) {\n if (next.has(specimen)) continue;\n resizeObserver.unobserve(specimen);\n observedSpecimens.delete(specimen);\n }\n for (const specimen of next) {\n if (observedSpecimens.has(specimen)) continue;\n resizeObserver.observe(specimen);\n observedSpecimens.add(specimen);\n }\n };\n const mutationObserver = new view.MutationObserver((records) => {\n if (\n records.some(\n ({ target }) => target !== layer && !layer.contains(target as Node),\n )\n )\n schedule();\n });\n mutationObserver.observe(catalog, {\n attributes: true,\n childList: true,\n subtree: true,\n });\n mutationObserver.observe(root.ownerDocument.documentElement, {\n attributes: true,\n });\n const documentHead = root.ownerDocument.head;\n if (documentHead)\n mutationObserver.observe(documentHead, {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n });\n const stylesheetLoaded = (): void => schedule();\n documentHead?.addEventListener('load', stylesheetLoaded, true);\n view.addEventListener('resize', schedule);\n render();\n return () => {\n if (frame) view.cancelAnimationFrame(frame);\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n documentHead?.removeEventListener('load', stylesheetLoaded, true);\n view.removeEventListener('resize', schedule);\n layer.replaceChildren();\n };\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry popup menu), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleSecondary,\n urlParam,\n revealSelection,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n }: WireCatalogOptions,\n): () => void {\n let cancelReveal: (() => void) | undefined;\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n if (revealSelection) {\n cancelReveal?.();\n cancelReveal = revealCatalogEntry(\n root,\n id,\n revealSelection === true ? undefined : revealSelection,\n );\n }\n };\n\n const disposers: Array<() => void> = [\n // Sidebar items AND the footer's related-entry popup-menu items both carry\n // `data-action={selectAction}` + `data-item-id`, so one delegated click covers both.\n delegate(\n root,\n 'click',\n `[data-action=\"${selectAction}\"]`,\n (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n },\n ),\n ];\n if (onToggleSidebar) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () =>\n onToggleSidebar(),\n ),\n );\n }\n if (onToggleTheme) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () =>\n onToggleTheme(),\n ),\n );\n }\n if (onToggleSecondary) {\n disposers.push(\n delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () =>\n onToggleSecondary(),\n ),\n );\n }\n return () => {\n cancelReveal?.();\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
@@ -1,3 +1,3 @@
1
- export { reorderTabs, wireTabBars } from './chunk-AASLYRGE.js';
1
+ export { reorderTabs, wireTabBars } from './chunk-UKKSMZYT.js';
2
2
 
3
3
  //# sourceMappingURL=wire-tab-bars.js.map
@@ -61,7 +61,7 @@ interface TokenSearchCollapsibleOptions {
61
61
  collapseOnEmptyBlur?: boolean;
62
62
  /** Collapse an empty field on Escape and restore focus to its trigger. Default: true. */
63
63
  collapseOnEscape?: boolean;
64
- /** Focus the editor on expand and the trigger on Escape-collapse. Default: true. */
64
+ /** Focus the editor on expand/controlled clear and the trigger on Escape-collapse. Default: true. */
65
65
  manageFocus?: boolean;
66
66
  /**
67
67
  * Keep an empty field expanded when focus moves to a caller-owned surface
@@ -1,4 +1,4 @@
1
- export { wireTokenSearchFields } from './chunk-GP5DP5DL.js';
1
+ export { wireTokenSearchFields } from './chunk-OJWWHFH3.js';
2
2
 
3
3
 
4
4
 
@@ -6,14 +6,16 @@ These are normative contracts for package components and consuming applications.
6
6
 
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
- - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
9
+ - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
10
  - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
+ - Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
11
12
  - In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; do not reduce the target to the visible icon.
12
13
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
13
- - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
14
+ - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
14
15
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
15
16
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
16
17
  - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
18
+ - `List` is layout-only and adds no `list` role. Children own their native or ARIA semantics. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
17
19
 
18
20
  ## ListActionRow
19
21
 
@@ -86,7 +88,8 @@ stops on drop/drag end/disposal, and does not change keyboard behavior. It
86
88
  returns a disposer.
87
89
 
88
90
  Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
89
- default: they move roving focus and select the focused tab. Pass
91
+ default: they move roving focus and select the focused tab. If selection synchronously
92
+ replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
90
93
  `activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
91
94
  `TabBar` `activation` prop, which overrides the option per bar) for
92
95
  **manual activation**: arrow keys move roving focus only and the user selects
@@ -147,6 +150,12 @@ originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on
147
150
  `input`, letting a caller drop its own `input` listener; the application still
148
151
  owns query parsing and result-count/loading announcements.
149
152
 
153
+ Managed clear captures the action before application handlers run, keeps the adopted
154
+ expanded signal open during editor replacement, and focuses the current editor after
155
+ rendering so typing can continue. The app still owns clearing query/tokens and emptying
156
+ DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
157
+ and focus deliberately moved to another control; no app-level reopen callback is needed.
158
+
150
159
  ## CollapsiblePanel / sidebar
151
160
 
152
161
  `CollapsiblePanel` is a labeled `aside` region that is `aria-hidden` while
package/docs/catalog.md CHANGED
@@ -109,9 +109,10 @@ const buttonPreview = (
109
109
  );
110
110
  ```
111
111
 
112
- The overlay selects every immediate child of a `CatalogExample` except the
113
- helper-generated label and note. It does not recursively promote a nested child
114
- to be the specimen. Outside an example row, it selects only top-level
112
+ The overlay selects every immediate child of a `CatalogExample`; the helper
113
+ marks its generated label and note so they are excluded automatically. It does
114
+ not recursively promote a nested child to be the specimen. Outside an example
115
+ row, it selects only top-level
115
116
  `[data-component]` roots in the canvas and ignores nested component descendants.
116
117
  These rules keep a row's label/group scaffolding out of the measurement and make
117
118
  the authored nesting determine exactly what is inspected.
@@ -138,15 +139,19 @@ Pass the conditional `geometryOverlay` boolean to `Catalog`, then call
138
139
  `wireCatalogGeometryOverlay(root)` once after the first render and retain its
139
140
  disposer alongside `wireCatalog`'s.
140
141
 
141
- | Overlay mark | Meaning | It is not |
142
- | ----------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
143
- | Orange translucent band | A positive computed margin on the selected specimen, after subtracting the helper's alignment inset. | Padding, gap, or empty content. Zero and negative margins are not drawn. |
144
- | Quiet dashed outline | The border-box outer bound of a selected specimen whose computed background is transparent. | A real CSS border, focus ring, padding edge, or proof that the specimen owns its size. |
145
-
146
- The overlay does not visualize padding, borders, gaps, negative/zero margins,
147
- scroll overflow, hit targets, nested descendants, or geometry ownership. Inspect
148
- computed styles and the machine-readable `geometry` metadata for those facts.
149
- Opaque specimens do not receive the transparent-bound outline.
142
+ | Overlay mark | Meaning | It is not |
143
+ | ----------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
144
+ | Orange translucent band | A positive computed margin on the selected specimen, after subtracting the helper's alignment inset. | Padding, gap, or empty content. Zero and negative margins are not drawn. |
145
+ | Accent solid edge | The selected specimen's computed border widths and radius, except `none`/`hidden` styles. | A focus ring, outline, child border, or metadata ownership claim. |
146
+ | Quiet dashed outline | The border-box bound of a transparent selected specimen with no visible border. | A real CSS border, padding edge, or proof that the specimen owns size. |
147
+
148
+ The overlay recomputes from live rendered styles when specimens resize, preview
149
+ markup changes, theme/root attributes change, or loaded/inline stylesheets
150
+ change. Component CSS is therefore the source of truth; do not duplicate margin
151
+ or border values in demo data. The overlay does not visualize padding, gaps,
152
+ negative/zero margins, scroll overflow, hit targets, or nested descendants.
153
+ Inspect computed styles and the machine-readable `geometry` metadata for those
154
+ facts. Opaque borderless specimens do not receive the dashed bound.
150
155
 
151
156
  ```tsx
152
157
  <Catalog
@@ -193,6 +193,10 @@ an unlisted implementation class. Selectors such as `.kui-state-banner span`,
193
193
  private structure. If no prop, token, or cataloged class expresses a recurring
194
194
  need, request a supported hook instead of inferring one from rendered markup.
195
195
 
196
+ `ListItem` renders its leading icon and nested SVG at a root-scaled 18px by
197
+ default while retaining the row's 44px minimum interactive target. Multiline
198
+ rows align that 18px visual with the first inherited text line.
199
+
196
200
  `DisclosureArrow` uses an 18px root-scaled visual by default. Override
197
201
  `--kui-disclosure-arrow-size` on the component or its containing scope when a
198
202
  consumer needs another size. Kerf `Select` uses a separate Web Awesome expand
@@ -210,7 +214,9 @@ glyph defaults to 18px through `--kui-list-header-action-icon-size`.
210
214
 
211
215
  `StateBanner` exposes instance-level `--kui-state-banner-background`,
212
216
  `--kui-state-banner-border`, `--kui-state-banner-foreground`,
213
- `--kui-state-banner-detail`, and action background variables. Its five built-in
217
+ `--kui-state-banner-detail`, `--kui-state-banner-badge-background`,
218
+ `--kui-state-banner-badge-foreground`, and action background variables. Its optional
219
+ badge is a compact pill beside the title and follows the banner tone by default. Its five built-in
214
220
  tones can be rethemed globally with
215
221
  `--kui-state-banner-{tone}-{background|border|foreground}`. Toolbar control,
216
222
  segmented-control, app-tab, and tab-bar colors likewise use their public
@@ -231,6 +237,13 @@ header, one scrolling vertical `.kui-pane__content`, and an optional footer.
231
237
  Logical-edge separator lines are independently opt-in and default off.
232
238
  `@kerfjs/ui/layout.css` retains the pane roles and supplies the related content
233
239
  geometry classes.
240
+ `List` is the corresponding layout-only vertical stack: its children stretch by
241
+ default, while `gap`, `flex`, and `scrollable` opt into standard/custom spacing,
242
+ flex growth, and vertical scroll ownership. `dividerSides` accepts canonical
243
+ physical top/right/bottom/left combinations such as `tr` and `trbl`; the stack
244
+ adds no list semantics, margin, or padding of its own. String props receive
245
+ browser CSS, so use `gap="var(--kui-space-2xs)"` for the root-scaled 4px token or
246
+ `gap="0.25rem"`; source-only `remify(4px)` is not valid at runtime.
234
247
  `.kui-content` gives major children 24px vertical separation.
235
248
  `.kui-content-item` gives one child 8px inline margin, a real 1px border,
236
249
  8px padding, and 12px corners; border and background may be transparent without
@@ -83,13 +83,14 @@ an upstream component or recipe request.
83
83
  | Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained, borderless, pressed, or single-control treatment. | Do not use it merely to align unrelated buttons; use toolbar slots or ordinary layout. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `SegmentedControl` for an exclusive choice. | Actions, pressed/expanded state, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
84
84
  | Floating controls over content — `FloatingToolbar` | A small cluster of controls (e.g. a drawer restore) must float over scrolling content — distinct and forced-dark — inside a positioned container. | Do not use it for a primary page or panel toolbar (use `Toolbar`), or for anything that must sit above dialogs/overlays — it is not top-layer. | Compose `ToolbarControlGroup`s and delegate their actions; the app owns visibility and position. | Controls, visibility, and position. | `@kerfjs/ui/floating-toolbar` | [Component subpaths](../README.md#component-subpaths) |
85
85
  | Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel` (or prefer `PanelHeader`, which forwards it). | Optional `headingLevel` for `role="heading"` + `aria-level`. Defaults to one line, ellipsized when it does not fit; `wrap` flows onto multiple lines, `ellipsis` toggles the trailing …, and `maxLines` caps a wrap. | Text, heading level, overflow behavior, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
86
+ | Vertical stack — `List` | Rows, sections, or arbitrary components need a consistent vertical stretch layout, optional standard/custom gap, flex growth, one vertical scroll owner, or edge dividers. | It is layout-only and does not provide `ul`/`ol` semantics. Do not nest scroll owners or use it when a horizontal layout is required. | Choose `gap`, `flex`, `scrollable`, and canonical `dividerSides` (`t`, `r`, `b`, `l` combinations in that order); bound the block size when scrolling. | Child semantics/content, containing height, and which optional layout behaviors apply. | `@kerfjs/ui/list` | [Pane geometry](../README.md#pane-and-content-geometry) |
86
87
  | Navigation row — `ListItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `ListActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
87
88
  | Navigation row with trailing action — `ListActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `ListItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/list-action-row` | [Accessibility](./accessibility.md#listactionrow) |
88
89
  | Navigation section heading — `ListHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Do not shrink its 44px action target to the 18px visual. Do not use it as a page, panel, or dialog title; use `PanelHeader`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/list-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
89
90
  | Inset self-bordered control — `ListInsetControl` | A control that owns its own border and padding but no outer margin (a search input, a `SegmentedControl`) must line up inside a `.kui-content` list with the standard 8px inline margins and stretch to fill the row. | Do not wrap a `.kui-content-item` or a `ListItem`/`ListHeader` that already owns its inline margin — that double-insets it. Do not add ad-hoc `margin`/`padding` around a bare control to align it; use this instead. | Place the self-bordered control(s) as children; they stretch to fill. It applies only the inline margin, flex stretch, and 8px gap — the child owns its own border and padding. | The control(s), their state, and action wiring. | `@kerfjs/ui/list-inset-control` | [Pane geometry](../README.md#component-subpaths) |
90
91
  | Inset bare text — `ListInsetText` | A plain string or inline content with no margin, border, or padding of its own must sit in a `.kui-content` list with its text edge aligned to the bordered items around it. | Do not use it to wrap a component that already owns content-item geometry (`ListItem`, `StateBanner`, a `.kui-content-item`) — that double-insets it. Do not hand-roll the 8px margin / 1px border / 8px padding. | Pass the text or inline `SafeHtml` as children; it supplies the 8px inline margin, 1px transparent border, and 8px padding so the text edge lands at the standard 17px inset. Pass `horizontalOnly` to keep that horizontal inset but drop the vertical margin, border, and padding for tight text layout. | The text, copy, and localization. | `@kerfjs/ui/list-inset-text` | [Pane geometry](../README.md#component-subpaths) |
91
92
  | Application column — `Pane` | A sidebar, main area, inspector, or dialog needs shared vertical header/content/footer organization, one scroll owner, and optional edge separators. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Import `Pane`, provide optional fixed header/footer slots around its primary vertical content, and opt into only the needed logical-edge separators; add `.kui-content-item` as needed. A visible pane owns collapse in its toolbar; move a hidden inline-start pane's restore control to the main toolbar leading edge and an inline-end pane's restore control to its trailing edge. | Layout hierarchy, reading width, responsive relocation, pane visibility state, slot semantics, labels, and separator placement. | `@kerfjs/ui/pane` | [Pane anatomy](./layout.md#anatomy) |
92
- | Lowered content surface — `SunkenPanel` | A main work area or nested content group needs one visually lowered surface with a compact inset and vertical stack. | Do not use it merely to add padding, as a replacement for `Pane` anatomy, or around children that already own the same outer surface. | Compose the application-owned content as children and pass `ariaLabel` only when the surface is a distinct named region. | Region semantics, accessible name, scroll ownership, child ordering, and content. | `@kerfjs/ui/sunken-panel` | [SunkenPanel](./sunken-panel.md) |
93
+ | Lowered content surface — `SunkenPanel` | A main work area or nested content group needs one visually lowered surface with a compact inset and vertical stack. | Do not use it merely to add padding, as a replacement for `Pane` anatomy, or around children that already own the same outer surface. | Compose the application-owned content as children, choose the default `rounded` shape or `square` for a flush edge-to-edge area, and pass `ariaLabel` only when the surface is a distinct named region. | Region semantics, accessible name, shape, scroll ownership, child ordering, and content. | `@kerfjs/ui/sunken-panel` | [SunkenPanel](./sunken-panel.md) |
93
94
  | Menu composition | Navigation sections need full-size rows and the same content-item geometry as every other pane. | Do not add sidebar-specific wrapper padding, shrink targets to icon size, nest an interactive trailing control in `ListItem`, or use a chevron on a row that does not disclose content. Use ordinary links for a different navigation contract. | Compose `ListHeader`, `ListItem`, and `ListActionRow` in `.kui-content`; use `ListHeader` toggle mode with real controlled content, and use `.kui-content-item` for other surfaces plus a pane footer for toolbar actions. | Information architecture, disclosure content and state, responsive drawer/shell behavior, and token overrides. | `@kerfjs/ui/layout.css` | [Pane geometry](../README.md#pane-and-content-geometry) |
94
95
  | Resizable application pane — `ResizableRegion`, `clampRegionSize`, `resizeRegionFromPointer` | A controlled split pane needs the Kerf separator, collapse state, pointer plus keyboard resizing, or a product-specific decorative grip. | Do not use it for a static two-column layout; use CSS grid. Prefer it over Web Awesome `wa-split-panel` unless that component's distinct API is required. Keep `handleIcon` noninteractive. | Call `wireResizableRegions` from `@kerfjs/ui/wire-resizable-regions` once and retain its disposer. | Size signal, min/max policy, collapse policy, persistence, and optional decorative handle icon. | `@kerfjs/ui/resizable-region` | [ResizableRegion contract](./accessibility.md#resizableregion) |
95
96
  | One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, safe domain metadata, or a product-specific close glyph. | Do not render it alone or use it for a small settings choice; compose in `TabBar`, or use `SegmentedControl`. Keep `closeIcon` noninteractive. | Compose in `TabBar`; let `wireTabBars` manage interaction. Put only domain `data-*` values in `rootAttributes`. | Tab identity, order, selection, close policy, content, and domain metadata values. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
@@ -101,7 +102,7 @@ an upstream component or recipe request.
101
102
  | Value selection — `Select` | A controlled form value comes from a moderate or long choice list, possibly grouped or icon-bearing. | Do not use it for commands; use a real action menu. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once; listen for standard input/change events. | Controlled value, validation, choices, and domain mapping. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
102
103
  | Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `Select`. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
103
104
  | Structured search editor — `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields` | Free text and ordered, editable, removable filter tokens share one searchbox; enable `collapsible` when an empty, unfocused field should reduce to one iconic action, standalone or in a toolbar group. | Do not use it for ordinary text entry; use a native input or Web Awesome `wa-input`. Do not use it when filters belong in separate form controls. | Read DOM-owned text on input, empty `textContent` on clear, and use `placeTokenSearchCaret` after explicit controlled focus changes. Call `wireTokenSearchFields` from `@kerfjs/ui/wire-token-search-fields` once so Enter submits without adding a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. In `collapsible` mode it also manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse); bind the field's `expanded` to the signal on the returned handle (`handle.expanded(id)`) or adopt your own via `collapsible.signals`, and opt out per behavior only when the app must own it. | Parsing, suggestions, tokens, query execution, results, announcements, and — only if overriding the default — the collapsible `expanded` signal. | `@kerfjs/ui/token-search-field` | [TokenSearchField contract](./accessibility.md#tokensearchfield) |
104
- | Persistent inline status — `StateBanner` | A neutral, info, success, warning, or danger message belongs next to the affected work. | Do not use it for a no-content screen; use `EmptyState`. Do not use it for transient confirmation; use a toast. Web Awesome `wa-callout` is the ecosystem alternative for Web Awesome-owned content. | Delegate an optional action; choose alert urgency only for attention-requiring failure. | State mapping, message lifetime, retry/action behavior, and copy. | `@kerfjs/ui/state-banner` | [Feedback accessibility](./accessibility.md#shared-rules) |
105
+ | Persistent inline status — `StateBanner` | A neutral, info, success, warning, or danger message belongs next to the affected work. | Do not use it for a no-content screen; use `EmptyState`. Do not use it for transient confirmation; use a toast. Web Awesome `wa-callout` is the ecosystem alternative for Web Awesome-owned content. | Delegate an optional action; choose alert urgency only for attention-requiring failure. Use the optional tone-tinted `badge` for a terse count or status beside the title, not as the only expression of meaning. | State mapping, message lifetime, retry/action behavior, badge content, and copy. | `@kerfjs/ui/state-banner` | [Feedback accessibility](./accessibility.md#shared-rules) |
105
106
  | Empty or busy content area — `EmptyState` | A content region has no items, cannot proceed, or is loading and needs explanation plus an optional action. | Do not use it for an inline status update; use `StateBanner`. Do not use it for transient success; use `wa-toast`/`wa-toast-item` or the application's toast system. | Delegate its optional action; it composes `LoadingSpinner` when busy. | Empty/busy policy, recovery action, illustration, and copy. | `@kerfjs/ui/empty-state` | [Feedback ownership](./component-contract.md#extracted-versus-application-specific) |
106
107
 
107
108
  ## Ambiguous choices
@@ -123,6 +124,9 @@ toolbar geometry, hover/pressed treatment, and grouping. `SegmentedControl`,
123
124
  `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live
124
125
  **inside** a `ToolbarControlGroup`, not loose in the zone. `PanelHeader` is the
125
126
  one wrapper that composes these for you as a panel/dialog/page heading.
127
+ Its bottom divider is enabled by default. Set `dividerSides=""` for none, or
128
+ use the canonical physical-edge combinations (`t`, `r`, `b`, `l`, in that
129
+ order—for example `tr` or `trbl`) when the toolbar owns other separator edges.
126
130
 
127
131
  Common toolbar patterns:
128
132
 
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 190" width="460" height="190"><defs><clipPath id="cp0"><rect x="214" y="66" width="32" height="32.3"/></clipPath><clipPath id="ct1"><rect x="174.4" y="114.3" width="111.2" height="23"/></clipPath><clipPath id="ct2"><rect x="115.7" y="145.3" width="228.6" height="20.3"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="191" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g transform="translate(214 66.3)" color="rgb(174, 174, 178)"><svg data-lucide="filter" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" font-family="system-ui, -apple-system, sans-serif" style="overflow:hidden" width="32" height="32"><path d="M10 20a1 1 0 0 0 .6.9l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .5-1.3l7.2-8A1 1 0 0 0 21 3H3a1 1 0 0 0-.7 1.7l7.2 8A2 2 0 0 1 10 14Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></svg></g></g><text x="174.42" y="133.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(245,245,247)">No matches</text><text x="115.72" y="160.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(174,174,178)">No tickets match the current filters.</text></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 190" width="460" height="190"><defs><clipPath id="cp0"><rect x="214" y="66" width="32" height="32.3"/></clipPath><clipPath id="ct2"><rect x="174.4" y="114.3" width="111.2" height="23"/></clipPath><clipPath id="ct3"><rect x="115.7" y="145.3" width="228.6" height="20.3"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="191" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g color="rgb(174, 174, 178)"><clipPath id="svgic1"><rect x="214" y="66.3" width="32" height="32"/></clipPath><g clip-path="url(#svgic1)"><g transform="translate(214 66.344)scale(1.33333)"><path d="M10 20a1 1 0 0 0 .6.9l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .5-1.3l7.2-8A1 1 0 0 0 21 3H3a1 1 0 0 0-.7 1.7l7.2 8A2 2 0 0 1 10 14Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><text x="174.42" y="133.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(245,245,247)">No matches</text><text x="115.72" y="160.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(174,174,178)">No tickets match the current filters.</text></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 190" width="460" height="190"><defs><clipPath id="cp0"><rect x="214" y="66" width="32" height="32.3"/></clipPath><clipPath id="ct1"><rect x="174.4" y="114.3" width="111.2" height="23"/></clipPath><clipPath id="ct2"><rect x="115.7" y="145.3" width="228.6" height="20.3"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="191" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g transform="translate(214 66.3)" color="rgb(110, 110, 115)"><svg data-lucide="filter" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" font-family="system-ui, -apple-system, sans-serif" style="overflow:hidden" width="32" height="32"><path d="M10 20a1 1 0 0 0 .6.9l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .5-1.3l7.2-8A1 1 0 0 0 21 3H3a1 1 0 0 0-.7 1.7l7.2 8A2 2 0 0 1 10 14Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></svg></g></g><text x="174.42" y="133.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(29,29,31)">No matches</text><text x="115.72" y="160.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(110,110,115)">No tickets match the current filters.</text></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 190" width="460" height="190"><defs><clipPath id="cp0"><rect x="214" y="66" width="32" height="32.3"/></clipPath><clipPath id="ct2"><rect x="174.4" y="114.3" width="111.2" height="23"/></clipPath><clipPath id="ct3"><rect x="115.7" y="145.3" width="228.6" height="20.3"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="191" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g color="rgb(110, 110, 115)"><clipPath id="svgic1"><rect x="214" y="66.3" width="32" height="32"/></clipPath><g clip-path="url(#svgic1)"><g transform="translate(214 66.344)scale(1.33333)"><path d="M10 20a1 1 0 0 0 .6.9l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .5-1.3l7.2-8A1 1 0 0 0 21 3H3a1 1 0 0 0-.7 1.7l7.2 8A2 2 0 0 1 10 14Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><text x="174.42" y="133.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(29,29,31)">No matches</text><text x="115.72" y="160.34" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(110,110,115)">No tickets match the current filters.</text></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 220" width="460" height="220"><defs><clipPath id="cp0"><rect x="214" y="53" width="32" height="32"/></clipPath><clipPath id="ct1"><rect x="157.7" y="101" width="144.7" height="23"/></clipPath><clipPath id="ct2"><rect x="73.7" y="132" width="312.7" height="20.3"/></clipPath><clipPath id="ct3"><rect x="178.6" y="168.3" width="102.8" height="32"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="212" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g transform="translate(214 53)" color="rgb(174, 174, 178)"><svg data-lucide="inbox" aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" font-family="system-ui, -apple-system, sans-serif" style="overflow:hidden" width="32" height="32"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></svg></g></g><text x="157.67" y="120" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(245,245,247)">No open tickets</text><text x="73.67" y="147" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(174,174,178)">When someone files a ticket it will show up here.</text><rect x="178.6" y="168.3" width="102.8" height="32" rx="16" fill="rgb(28,28,30)"/><rect x="179.5" y="168.5" width="101" height="31" rx="15.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="191.63" y="190.3" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(245,245,247)">New ticket</text></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 460 220" width="460" height="220"><defs><clipPath id="cp0"><rect x="214" y="53" width="32" height="32"/></clipPath><clipPath id="ct2"><rect x="157.7" y="101" width="144.7" height="23"/></clipPath><clipPath id="ct3"><rect x="73.7" y="132" width="312.7" height="20.3"/></clipPath><clipPath id="ct4"><rect x="178.6" y="168.3" width="102.8" height="32"/></clipPath></defs><rect x="28.5" y="20.5" width="403" height="212" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g color="rgb(174, 174, 178)"><clipPath id="svgic1"><rect x="214" y="53" width="32" height="32"/></clipPath><g clip-path="url(#svgic1)"><g transform="translate(214 53)scale(1.33333)"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="1.5px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><text x="157.67" y="120" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="20" font-weight="650" fill="rgb(245,245,247)">No open tickets</text><text x="73.67" y="147" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="14" fill="rgb(174,174,178)">When someone files a ticket it will show up here.</text><rect x="178.6" y="168.3" width="102.8" height="32" rx="16" fill="rgb(28,28,30)"/><rect x="179.5" y="168.5" width="101" height="31" rx="15.5" fill="none" stroke="rgb(72,72,74)" stroke-width="1"/><text x="191.63" y="190.3" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(245,245,247)">New ticket</text></svg>