@kerfjs/ui 5.0.0-beta.70 → 5.0.0-beta.72

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 (46) hide show
  1. package/ai/component-catalog.json +21 -5
  2. package/ai/component-catalog.schema.json +2 -0
  3. package/ai/component-composition-rules.json +4 -4
  4. package/ai/component-composition.d.ts +2 -0
  5. package/ai/component-composition.json +23 -5
  6. package/ai/component-composition.schema.json +9 -0
  7. package/ai/components/toolbar-text.md +3 -1
  8. package/ai/components/toolbar.md +6 -6
  9. package/ai/public-api-signatures-v1.md +16 -11
  10. package/ai/skill.md +1 -1
  11. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  12. package/analyzer/component-ownership.mjs +131 -16
  13. package/analyzer/index.d.mts +2 -1
  14. package/analyzer/index.mjs +298 -29
  15. package/dist/catalog.js +2 -2
  16. package/dist/{chunk-KCCEH7RF.js → chunk-3UE24JSC.js} +2 -2
  17. package/dist/{chunk-KCCEH7RF.js.map → chunk-3UE24JSC.js.map} +1 -1
  18. package/dist/{chunk-V3DX3K65.js → chunk-R5F5XUJ7.js} +19 -2
  19. package/dist/chunk-R5F5XUJ7.js.map +1 -0
  20. package/dist/{chunk-T2E5WBCZ.js → chunk-SLWCCHNP.js} +11 -2
  21. package/dist/chunk-SLWCCHNP.js.map +1 -0
  22. package/dist/{chunk-DC6CN2DS.js → chunk-VLYIPBCQ.js} +2 -2
  23. package/dist/{chunk-DC6CN2DS.js.map → chunk-VLYIPBCQ.js.map} +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/nav-stack.js +1 -1
  26. package/dist/split-view.js +1 -1
  27. package/dist/styles/app-tab.css +3 -0
  28. package/dist/styles/chip.css +6 -0
  29. package/dist/styles/toolbar-text.css +20 -0
  30. package/dist/styles/toolbar.css +10 -1
  31. package/dist/toolbar-text.d.ts +15 -10
  32. package/dist/toolbar-text.js +1 -1
  33. package/dist/wire-tab-bars.js +1 -1
  34. package/dist/workbench.js +1 -1
  35. package/docs/accessibility.md +5 -0
  36. package/docs/component-contract.md +17 -2
  37. package/docs/component-selection.md +27 -16
  38. package/docs/layout.md +4 -0
  39. package/docs/ui-analyzer.md +42 -5
  40. package/docs/ui-doctor.md +28 -3
  41. package/doctor/config.schema.json +23 -1
  42. package/doctor/index.d.mts +2 -1
  43. package/doctor/index.mjs +42 -2
  44. package/package.json +1 -1
  45. package/dist/chunk-T2E5WBCZ.js.map +0 -1
  46. package/dist/chunk-V3DX3K65.js.map +0 -1
@@ -161,7 +161,8 @@
161
161
  compact group's own smaller group size never redefines it.
162
162
  `--_kui-toolbar-band` is context the toolbar provides: a ToolbarControlGroup
163
163
  and a ToolbarText each center themselves in it from their own stylesheets
164
- (toolbar-control-group.css, toolbar-text.css). */
164
+ (toolbar-control-group.css, toolbar-text.css). A direct primary wa-button in
165
+ trailing is centered here without altering its Web Awesome chrome. */
165
166
  .kui-toolbar {
166
167
  --_kui-toolbar-band: var(--kui-toolbar-group-size, calc(2px + 2.625rem));
167
168
  }
@@ -237,6 +238,14 @@
237
238
  flex-wrap: wrap;
238
239
  }
239
240
 
241
+ .kui-toolbar__trailing > wa-button {
242
+ margin-block-start: max(
243
+ 0px,
244
+ calc((var(--_kui-toolbar-band) - var(--wa-form-control-height)) / 2)
245
+ );
246
+ flex: 0 0 auto;
247
+ }
248
+
240
249
  /* An expanded trailing search gets a complete second row while the leading
241
250
  identity and optional center controls remain in the first. The query styles
242
251
  descendants because a toolbar cannot query its own container width. */
@@ -9,14 +9,6 @@ interface ToolbarTextBaseProps {
9
9
  className?: string;
10
10
  /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
11
11
  id?: string;
12
- /**
13
- * Expose heading semantics (`role="heading"` + `aria-level`) so the text acts as
14
- * a heading landmark — e.g. a page's primary title. Omit to keep the plain span
15
- * (the default), which suits a dialog title referenced via `aria-labelledby`.
16
- */
17
- headingLevel?: HeadingLevel;
18
- /** Render the text as an unanimated loading skeleton instead of its value. */
19
- placeholder?: boolean;
20
12
  /**
21
13
  * Show a trailing ellipsis (…) where the text is truncated — on the single line
22
14
  * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip
@@ -40,7 +32,20 @@ type ToolbarTextWrappingProps = {
40
32
  wrap?: false;
41
33
  maxLines?: never;
42
34
  };
43
- type ToolbarTextProps = ToolbarTextBaseProps & ToolbarTextWrappingProps;
44
- declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, fill, slot, }: ToolbarTextProps): kerfjs.SafeHtml;
35
+ type ToolbarTextProps = ToolbarTextBaseProps & (({
36
+ action?: never;
37
+ /** Expose `role="heading"` and `aria-level` on read-only text. */
38
+ headingLevel?: HeadingLevel;
39
+ /** Render an unanimated loading skeleton instead of the text. */
40
+ placeholder?: boolean;
41
+ } & ToolbarTextWrappingProps) | {
42
+ /** Render a native button with this delegated action; the app owns editing. */
43
+ action: string;
44
+ headingLevel?: never;
45
+ placeholder?: never;
46
+ wrap?: never;
47
+ maxLines?: never;
48
+ });
49
+ declare function ToolbarText({ text, size, className, id, headingLevel, placeholder, wrap, ellipsis, maxLines, fill, slot, action, }: ToolbarTextProps): kerfjs.SafeHtml;
45
50
 
46
51
  export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
@@ -1,4 +1,4 @@
1
- export { ToolbarText } from './chunk-V3DX3K65.js';
1
+ export { ToolbarText } from './chunk-R5F5XUJ7.js';
2
2
 
3
3
 
4
4
 
@@ -1,3 +1,3 @@
1
- export { reorderTabs, wireTabBars } from './chunk-T2E5WBCZ.js';
1
+ export { reorderTabs, wireTabBars } from './chunk-SLWCCHNP.js';
2
2
 
3
3
  //# sourceMappingURL=wire-tab-bars.js.map
package/dist/workbench.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Workbench } from './chunk-KCCEH7RF.js';
1
+ export { Workbench } from './chunk-3UE24JSC.js';
2
2
 
3
3
 
4
4
 
@@ -322,6 +322,11 @@ role/level pair used throughout Kerf UI. Keep the levels meaningful and
322
322
  non-skipping within a view. Pass trailing
323
323
  controls as a labeled `ToolbarControlGroup` when that group needs an accessible
324
324
  name. Keep supporting copy as app-owned content below the toolbar.
325
+ An editable title can use `ToolbarText action="edit-title"`; it renders a native
326
+ button whose full text remains its accessible name even when visually truncated.
327
+ Click, Enter, and Space activate the delegated action. The app owns the input
328
+ shown while editing and its focus lifecycle. Because the button cannot also be
329
+ a heading landmark, expose a heading elsewhere when the view needs one.
325
330
 
326
331
  ## FloatingToolbar
327
332
 
@@ -70,6 +70,12 @@ every consumer catalog a `.kerf-ui-profile.json` declares, whatever the class
70
70
  prefix. `create-kerf-component` packages declare the same fields in
71
71
  `kerf.components.json`.
72
72
 
73
+ `descendantRootAttribute` optionally names a `data-*` attribute on a
74
+ cataloged `rootElement`. It anchors ownership of arbitrary unclassed child
75
+ element selectors below that exact root, as `LucideIcon` does with
76
+ `svg[data-lucide]`. Without the attribute in a selector, the analyzer uses
77
+ only cataloged `descendantElements` and composed JSX evidence.
78
+
73
79
  An entry's optional `cssValueProps` makes CSS-adjacent prop grammar equally
74
80
  machine-evaluable. Each path records the property grammar, finite shorthands,
75
81
  canonical versus exceptional spacing choices, standalone and expression-only
@@ -593,8 +599,9 @@ The enclosing `Toolbar` owns zone alignment and responsive topology: use
593
599
  choose `responsive="stack"` with `responsiveAt="compact" | "narrow"`,
594
600
  `responsive="wrap"` for a heading whose title must stay whole, or
595
601
  `responsive="center-priority"` instead of selecting its zone classes from a
596
- parent stylesheet. No policy clips an action: the trailing zone, and a stacked
597
- center zone, wrap whole groups onto another row. The center zone never
602
+ parent stylesheet. No policy clips an action: the trailing zone wraps whole
603
+ groups and its standalone primary `wa-button` onto another row; a stacked
604
+ center zone wraps its groups. The center zone never
598
605
  compresses below its content width, so when the leading identity, center, and
599
606
  trailing zones share a row that is too narrow, the leading title is what gives
600
607
  way: it truncates with an ellipsis (as a platform toolbar's title does) instead
@@ -615,6 +622,12 @@ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
615
622
  `ToolbarText`; app actions belong in a trailing `ToolbarControlGroup`. Omit the
616
623
  icon group when it has no content. Supporting copy is app-owned content below
617
624
  the toolbar so it cannot pull the icon or actions out of alignment.
625
+ For an editable title, `ToolbarText action="edit-title"` renders a native button
626
+ directly in the leading zone. It keeps content width when there is room, shrinks
627
+ and ellipsizes beside a status chip when space is tight, and emits the delegated
628
+ action on click or keyboard activation. The application owns the editing control
629
+ and the swap between display and edit states. An actionable title is a button,
630
+ so place any required heading landmark on another element in the view.
618
631
 
619
632
  `ValueTable` composes typed `ValueTableRow` entries. A row owns its `dt`/`dd`
620
633
  semantics and may receive a leading `SafeHtml` icon. Every row keeps 8px of
@@ -704,6 +717,8 @@ tab's trailing edge while peer tabs scroll beneath it, including in RTL.
704
717
  With `snapTabs`, `wireTabBars` measures the pinned tab and settles peers at
705
718
  whole-tab starts after scroll and selection changes, reserving end room so the
706
719
  last peer can align there. The application still owns selection and tab order.
720
+ ResizeObserver notifications coalesce snap measurements into the next animation
721
+ frame, so inset writes cannot resize the observed strip during observer delivery.
707
722
 
708
723
  CSS, the generated wrappers that make it
709
724
  reachable, and the registration module are the package's only declared side
@@ -284,8 +284,10 @@ without styling its strip or tab internals.
284
284
  Set the public
285
285
  `--kui-app-tab-pinned-background` token if the surrounding surface differs from
286
286
  the default; the pinned tab must cover peers as they scroll beneath it. Its
287
- background also covers the strip's inline padding and border so scrolled labels cannot
288
- paint beside the pinned edge, including in right-to-left strips. Call
287
+ background also covers the strip's inline padding and border so scrolled labels
288
+ cannot paint beside the pinned edge, including in right-to-left strips. The
289
+ backing follows the tab's pill radius, keeping a selected pinned tab's corners
290
+ and shadow whole beside the overflow divider. Call
289
291
  `wireTabBars` for keyboard selection and reorder behavior and its WebKit RTL
290
292
  scroll correction.
291
293
 
@@ -383,14 +385,22 @@ A `FloatingToolbar` accepts `ToolbarControlGroup` directly as its children in
383
385
  both floating and inline placement. Name the toolbar with `label`; group its
384
386
  controls as you would in a `Toolbar` zone.
385
387
 
386
- A `Toolbar` has three zones — `leading`, `center`, and `trailing`. In almost
387
- every case the only things that go **directly** in a zone are `ToolbarText`
388
- (identity/title text) and `ToolbarControlGroup` (any control or cluster of
389
- controls). Do not drop bare buttons, inputs, links, or arbitrary markup straight
388
+ A `Toolbar` has three zones — `leading`, `center`, and `trailing`. The direct
389
+ zone children are `ToolbarText` (identity/title text) and `ToolbarControlGroup`
390
+ (a control or cluster of controls). The `trailing` zone also accepts one
391
+ standalone `wa-button` for the application's primary action. It keeps its own
392
+ Web Awesome brand, danger, size, and hover chrome, while Toolbar aligns it in
393
+ the control band and wraps it with other trailing items when space is tight.
394
+ For a title that opens an editor, set `action` on a direct `ToolbarText`: it
395
+ renders a native button that keeps its content width when possible and
396
+ ellipsizes before an adjacent status chip when the leading zone narrows. The
397
+ application handles the delegated action and replaces it with its own input
398
+ while editing. Use a separate heading landmark if the view needs one.
399
+ Do not drop other bare buttons, inputs, links, or arbitrary markup straight
390
400
  into a zone; wrap controls in a `ToolbarControlGroup` so they get the shared
391
401
  toolbar geometry, hover/pressed treatment, and grouping. `SegmentedControl`,
392
- `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live
393
- **inside** a `ToolbarControlGroup`, not loose in the zone. For a page, panel, or
402
+ `Select`, a collapsible `TokenSearchField`, and other Web Awesome controls live
403
+ **inside** a `ToolbarControlGroup`. For a page, panel, or
394
404
  dialog heading, put an extra-large `ToolbarText` directly in the leading zone,
395
405
  optionally preceded by a grouped icon, and group trailing actions. Keep supporting
396
406
  copy below as app-owned content. Size the title to its track: a narrow
@@ -425,14 +435,15 @@ For a long-running icon action, set `busy` on its single-control
425
435
  the control's dimensions, shows `LoadingSpinner` in its icon slot, sets
426
436
  `aria-busy`, and makes the control inert until the app clears `busy`.
427
437
 
428
- | Want | Put in the zone | Notes |
429
- | ----------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
430
- | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
431
- | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
432
- | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
433
- | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
434
- | A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
435
- | A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
438
+ | Want | Put in the zone | Notes |
439
+ | ------------------------------ | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
440
+ | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
441
+ | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
442
+ | One primary application action | `trailing={<wa-button variant="brand">New ticket…</wa-button>}` | The standalone button keeps its Web Awesome chrome; use `variant="danger"` when the action is destructive |
443
+ | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
444
+ | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
445
+ | A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
446
+ | A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
436
447
 
437
448
  ### Popup menu migration
438
449
 
package/docs/layout.md CHANGED
@@ -309,6 +309,10 @@ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
309
309
  `ToolbarText`; actions belong in a trailing group. Omit empty groups. Supporting
310
310
  copy is app-owned content below the toolbar and aligns with the intended content
311
311
  edge.
312
+ When the title itself opens editing, set `ToolbarText action="edit-title"` and
313
+ render it directly in the leading zone beside any status chip. The button
314
+ shrinks and ellipsizes at the space left by its siblings without filling unused
315
+ space; the app swaps its own input into the edit state.
312
316
 
313
317
  ## Spacing scale
314
318
 
@@ -134,7 +134,21 @@ which element a selector styles.
134
134
  are also reported below a hook class placed on an imported component, below
135
135
  another entry's root in the same package, or at a direct child root whose
136
136
  JSX parent composes only a component with that cataloged `rootElement` type.
137
- The finding names the composing class and component. A `::part()` subject is left to
137
+ Descendant selectors also see a composed child's cataloged root and
138
+ `descendantElements` below an own wrapper. `LucideIcon` publishes `svg` and
139
+ its Lucide SVG child tags, so `.wrapper > svg`, `.wrapper svg`, and
140
+ `.wrapper path` can name the composed icon. Ambiguous evidence from multiple
141
+ different child components is discarded. For arbitrary custom `IconNode`
142
+ child tags outside the cataloged SVG tag list, a selector rooted at
143
+ `svg[data-lucide]` identifies the descendant as
144
+ LucideIcon-owned (for example `svg[data-lucide] filter`). Without that root
145
+ marker, an unknown tag beneath a wrapper remains ambiguous and is not
146
+ attributed to LucideIcon or to unrelated HTML.
147
+ The scan follows returned JSX from
148
+ same-module function and arrow-function components at their call sites,
149
+ recursively with a cycle guard; their intrinsic elements remain app-owned.
150
+ The finding names the composing
151
+ class and component. A `::part()` subject is left to
138
152
  `KUI-L011`; a rule that only sets custom properties is configuration, judged
139
153
  by the token rules. Forcing a component's dimensions is one such restyle
140
154
  and reports here once: `.kui-pane { width: 300px }`.
@@ -159,7 +173,12 @@ which element a selector styles.
159
173
  component's public class. The package boundary applies by default;
160
174
  component mode also protects sibling modules through `componentSource`.
161
175
  A component's own source and its `placeableClasses` remain allowed. Findings
162
- name the class, owner, write site, and configuration route.
176
+ name the class, owner, write site, and configuration route. This diagnostic
177
+ scans raw HTML in plain strings and the static segments of interpolated
178
+ template literals. When a substitution interrupts a class value, only
179
+ complete literal tokens are reported; split tokens are not guessed. It
180
+ skips test modules (`*.test.*`, `*.spec.*`, and files under `__tests__/`),
181
+ where HTML literals commonly assert on output rather than render markup.
163
182
 
164
183
  Every message names the configuration to use (the component's typed props,
165
184
  public tokens, and variants from its catalog entry) and ends with the same
@@ -210,16 +229,34 @@ even without a composition entry. Each entry owns its declared `styleSources`
210
229
  for CSS and its selection `source` module for JSX; when `styleSources` is absent,
211
230
  direct relative CSS imports from that source are used. The analyzer also derives
212
231
  literal JSX `class` and `className` values whose BEM block is styled in those
213
- stylesheets, including element and modifier classes. Sibling files are foreign.
232
+ stylesheets. A root or styled literal establishes ownership of the whole BEM
233
+ block: `block`, `block__*`, and `block--*`, including dynamically constructed
234
+ element and modifier names. The boundary applies to selectors and markup class
235
+ writes, including a class prop on an imported component. Modules that share a
236
+ stylesheet co-own its classes, whether the stylesheet was declared or directly
237
+ imported; a source that imports none of an owner's stylesheets remains foreign.
238
+ When several entries infer the same class from a shared stylesheet, the
239
+ analyzer does not arbitrarily assign that class to one of them for `KUI-L019`.
240
+ Other sibling files are foreign.
214
241
  Paths are relative to the entry's package root. Set
215
242
  `implicitComponentOwnership: true` to apply the same source and stylesheet
216
243
  inference to modules with direct CSS imports that have no catalog entry.
244
+ For a shell stylesheet imported once but used by style-less modules, pass
245
+ `ownershipGroups: [{ styleSources: ['src/style.css'], sources: ['src/main.tsx', 'src/app/'] }]`.
246
+ The paths are relative to the analyzed package root. A source ending in `/`
247
+ matches every module below that directory; other source paths match exactly.
248
+ Each group owns the subject classes in its stylesheets for `KUI-L019` and
249
+ `KUI-L023`. Its stylesheet and listed source modules share one owner, while
250
+ sibling stylesheets and modules remain foreign. This option applies only with
251
+ `ownership: 'component'`.
217
252
  Set `ownershipContext: 'any'` alongside component ownership when one sibling
218
253
  component must not mention another sibling's class anywhere in a CSS selector.
219
254
  The default, `'subject'`, checks the styled subject. Strict findings use
220
255
  `KUI-L019` with `evidence.via: 'context'` and a `position` of `ancestor`,
221
- `sibling`, `has`, `is`, `where`, or `not`. Context from another package remains
222
- allowed.
256
+ `sibling`, `has`, `is`, `where`, or `not`. Set
257
+ `ownershipContext: 'any-package'` to apply the same context rule to components
258
+ from other packages, including their public classes and `[data-component]`
259
+ roots. The `'any'` mode continues to allow cross-package context.
223
260
 
224
261
  ### Retired: `KUI-L005` (forced component dimension)
225
262
 
package/docs/ui-doctor.md CHANGED
@@ -37,11 +37,23 @@ application or third-party CSS that restyles another package's component
37
37
  token a typed prop sets (`KUI-L021`), or styles it through a hook class on its
38
38
  root (`KUI-L022`). It also reports source modules that render another
39
39
  component's public classes as their own markup (`KUI-L023`), including raw HTML
40
- and DOM class writes. The ESLint stage's `ui-component-ownership` rule reports
40
+ and DOM class writes. Raw HTML checks include complete static class tokens in
41
+ interpolated template literals. `KUI-L023` skips `*.test.*`, `*.spec.*`, and `__tests__/`
42
+ modules so output assertions are not mistaken for rendered markup. The ESLint
43
+ stage's `ui-component-ownership` rule reports
41
44
  `KUI-L020` / `KUI-L021` in script. Each finding's `action` points at the
42
45
  component's configuration, and its message routes a need with no prop to a
43
46
  component-gap report instead of an override (see
44
47
  [the analyzer rule](./ui-analyzer.md#component-ownership-kui-l019kui-l023)).
48
+ In component mode, `KUI-L019` also follows an owned wrapper to a composed
49
+ child's cataloged root and descendant element types. For example, a wrapper
50
+ around `LucideIcon` cannot size its `svg` or `path` with an element selector;
51
+ use the icon's props or style an app-owned element. The same check follows
52
+ local function components returned from the owner module and treats their
53
+ intrinsic markup as app-owned.
54
+ Unknown custom `IconNode` tags are identified when a selector explicitly
55
+ passes through the icon's `svg[data-lucide]` root; unrelated HTML descendants
56
+ without that marker remain app-owned.
45
57
  The analyzer resolves a cataloged application component imported through an
46
58
  exact or wildcard TypeScript `paths` alias from the nearest consumer tsconfig,
47
59
  so the same root hook-class check applies in those files.
@@ -51,13 +63,26 @@ boundary between a consumer package's own cataloged components. The default is
51
63
  `styleSources` (relative to its package root), or its direct relative CSS
52
64
  imports when that list is absent, and its selection catalog `source` module.
53
65
  Literal rendered BEM classes are inferred when those stylesheets style their
54
- block. Set `"implicitComponentOwnership": true` to include uncataloged modules
66
+ block. An owned root or styled literal owns its whole `block__*` and `block--*`
67
+ family, including dynamic names and classes placed on imported components.
68
+ Modules sharing one of those stylesheets co-own its classes, so their
69
+ own markup does not borrow from one another. A class inferred by several
70
+ co-owners has no arbitrary single owner for CSS subject diagnostics.
71
+ Set `"implicitComponentOwnership": true` to include uncataloged modules
55
72
  that directly import CSS. Component mode reports sibling CSS and hook-class
56
73
  restyles while allowing a component's own stylesheet and module.
74
+ For an entry stylesheet shared by modules without direct CSS imports, configure
75
+ `"ownershipGroups": [{ "styleSources": ["src/style.css"], "sources": ["src/main.tsx", "src/app/"] }]`.
76
+ These package-relative paths define one owner: its stylesheet and every listed
77
+ source file or directory prefix. Doctor reports sibling CSS restyles and markup
78
+ class use as `KUI-L019` and `KUI-L023`. Source paths ending in `/` match a
79
+ directory subtree; other paths match exactly.
57
80
  Set `"ownershipContext": "any"` to also report a same-package sibling's class
58
81
  used as ancestor or sibling context or within `:has()`, `:is()`, `:where()`, or
59
82
  `:not()`. The default `"subject"` keeps subject-only context policy; classes
60
- from another package may still provide context.
83
+ from another package may still provide context. Set
84
+ `"ownershipContext": "any-package"` to include cross-package component
85
+ classes and `[data-component]` roots in the same selector-context checks.
61
86
 
62
87
  The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
63
88
 
@@ -10,8 +10,30 @@
10
10
  "mode": { "enum": ["full", "changed"] },
11
11
  "package": { "type": "string", "minLength": 1 },
12
12
  "ownership": { "enum": ["package", "component"] },
13
- "ownershipContext": { "enum": ["subject", "any"] },
13
+ "ownershipContext": { "enum": ["subject", "any", "any-package"] },
14
14
  "implicitComponentOwnership": { "type": "boolean" },
15
+ "ownershipGroups": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "object",
19
+ "required": ["styleSources", "sources"],
20
+ "properties": {
21
+ "styleSources": {
22
+ "type": "array",
23
+ "minItems": 1,
24
+ "uniqueItems": true,
25
+ "items": { "$ref": "#/$defs/path" }
26
+ },
27
+ "sources": {
28
+ "type": "array",
29
+ "minItems": 1,
30
+ "uniqueItems": true,
31
+ "items": { "$ref": "#/$defs/path" }
32
+ }
33
+ },
34
+ "additionalProperties": false
35
+ }
36
+ },
15
37
  "stages": {
16
38
  "type": "object",
17
39
  "properties": {
@@ -23,8 +23,9 @@ export interface UiDoctorConfig {
23
23
  mode?: 'full' | 'changed';
24
24
  package?: string;
25
25
  ownership?: 'package' | 'component';
26
- ownershipContext?: 'subject' | 'any';
26
+ ownershipContext?: 'subject' | 'any' | 'any-package';
27
27
  implicitComponentOwnership?: boolean;
28
+ ownershipGroups?: Array<{ styleSources: string[]; sources: string[] }>;
28
29
  stages?: Partial<
29
30
  Record<
30
31
  'catalog' | 'typescript' | 'eslint' | 'analyzer' | 'browser',
package/doctor/index.mjs CHANGED
@@ -134,6 +134,7 @@ export function validateUiDoctorConfig(
134
134
  'ownership',
135
135
  'ownershipContext',
136
136
  'implicitComponentOwnership',
137
+ 'ownershipGroups',
137
138
  'stages',
138
139
  'browser',
139
140
  'cache',
@@ -163,9 +164,12 @@ export function validateUiDoctorConfig(
163
164
  add('$.ownership', 'ownership must be package or component.');
164
165
  if (
165
166
  config.ownershipContext !== undefined &&
166
- !['subject', 'any'].includes(config.ownershipContext)
167
+ !['subject', 'any', 'any-package'].includes(config.ownershipContext)
167
168
  )
168
- add('$.ownershipContext', 'ownershipContext must be subject or any.');
169
+ add(
170
+ '$.ownershipContext',
171
+ 'ownershipContext must be subject, any, or any-package.',
172
+ );
169
173
  if (
170
174
  config.implicitComponentOwnership !== undefined &&
171
175
  typeof config.implicitComponentOwnership !== 'boolean'
@@ -174,6 +178,40 @@ export function validateUiDoctorConfig(
174
178
  '$.implicitComponentOwnership',
175
179
  'implicitComponentOwnership must be a boolean.',
176
180
  );
181
+ if (config.ownershipGroups !== undefined) {
182
+ if (!Array.isArray(config.ownershipGroups))
183
+ add('$.ownershipGroups', 'ownershipGroups must be an array.');
184
+ else
185
+ config.ownershipGroups.forEach((group, index) => {
186
+ const path = `$.ownershipGroups[${index}]`;
187
+ if (!group || typeof group !== 'object' || Array.isArray(group)) {
188
+ add(path, 'Ownership group must be an object.');
189
+ return;
190
+ }
191
+ for (const key of Object.keys(group))
192
+ if (!['styleSources', 'sources'].includes(key))
193
+ add(`${path}.${key}`, 'Unknown ownership group property.');
194
+ for (const key of ['styleSources', 'sources']) {
195
+ const paths = group[key];
196
+ if (
197
+ !Array.isArray(paths) ||
198
+ !paths.length ||
199
+ paths.some(
200
+ (item) =>
201
+ typeof item !== 'string' ||
202
+ !item ||
203
+ item.startsWith('/') ||
204
+ /(^|\/)\.\.(\/|$)|[?*]/.test(item),
205
+ ) ||
206
+ new Set(paths).size !== paths.length
207
+ )
208
+ add(
209
+ `${path}.${key}`,
210
+ `${key} must be a non-empty array of unique, relative paths without traversal or globs.`,
211
+ );
212
+ }
213
+ });
214
+ }
177
215
  if (config.stages !== undefined) {
178
216
  if (
179
217
  !config.stages ||
@@ -1164,6 +1202,7 @@ export async function runUiDoctor({
1164
1202
  ownershipContext: config.ownershipContext ?? 'subject',
1165
1203
  implicitComponentOwnership:
1166
1204
  config.implicitComponentOwnership ?? false,
1205
+ ownershipGroups: config.ownershipGroups ?? [],
1167
1206
  signal,
1168
1207
  })
1169
1208
  : (async () => ({
@@ -1175,6 +1214,7 @@ export async function runUiDoctor({
1175
1214
  ownershipContext: config.ownershipContext ?? 'subject',
1176
1215
  implicitComponentOwnership:
1177
1216
  config.implicitComponentOwnership ?? false,
1217
+ ownershipGroups: config.ownershipGroups ?? [],
1178
1218
  knownRules: [...registeredRuleIds, ...eslintRuleIds],
1179
1219
  }),
1180
1220
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.70",
3
+ "version": "5.0.0-beta.72",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/wire-tab-bars.ts"],"names":["target"],"mappings":";AAkCA,IAAM,mBAAA,GAAsB,EAAA;AAC5B,IAAM,6BAAA,GAAgC,GAAA;AACtC,IAAM,6BAAA,GAAgC,GAAA;AAE/B,SAAS,WAAA,CACd,KAAA,EACA,KAAA,EACA,QAAA,EACA,UACA,QAAA,EACK;AACL,EAAA,IAAI,QAAA,KAAa,QAAA,EAAU,OAAO,CAAC,GAAG,KAAK,CAAA;AAC3C,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,CAAC,SAAS,KAAA,CAAM,IAAI,MAAM,QAAQ,CAAA;AAC5D,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,KAAA,CAAM,IAAA,CAAK,CAAC,IAAA,KAAS,KAAA,CAAM,IAAI,CAAA,KAAM,QAAQ,CAAA;AAC3D,IAAA,OAAO,CAAC,GAAG,KAAK,CAAA;AAClB,EAAA,MAAM,SAAA,GAAY,MAAM,MAAA,CAAO,CAAC,SAAS,KAAA,CAAM,IAAI,MAAM,QAAQ,CAAA;AACjE,EAAA,MAAM,WAAA,GAAc,UAAU,SAAA,CAAU,CAAC,SAAS,KAAA,CAAM,IAAI,MAAM,QAAQ,CAAA;AAC1E,EAAA,SAAA,CAAU,OAAO,WAAA,IAAe,QAAA,KAAa,UAAU,CAAA,GAAI,CAAA,CAAA,EAAI,GAAG,MAAM,CAAA;AACxE,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,QAAQ,MAAA,EAAiD;AAChE,EAAA,OAAO,kBAAkB,OAAA,GACpB,MAAA,CAAO,OAAA,CAAiB,4BAA4B,KAAK,MAAA,GAC1D,MAAA;AACN;AAEA,SAAS,OAAO,GAAA,EAAsC;AACpD,EAAA,OAAO,GAAA,CAAI,OAAA,CAAoB,4BAA4B,CAAA,IAAK,MAAA;AAClE;AAEA,SAAS,MAAM,GAAA,EAAkC;AAC/C,EAAA,OAAO,GAAA,CAAI,QAAQ,KAAA,IAAS,MAAA;AAC9B;AAEA,SAAS,MAAM,GAAA,EAAqC;AAClD,EAAA,OAAO,GAAA,CAAI,QAAQ,QAAA,IAAY,MAAA;AACjC;AAEA,SAAS,WAAA,CAAY,OAAkB,GAAA,EAA+B;AACpE,EAAA,MAAM,MAAA,GAAS,IAAI,qBAAA,EAAsB;AACzC,EAAA,OAAO,MAAM,OAAA,GAAU,MAAA,CAAO,OAAO,MAAA,CAAO,KAAA,GAAQ,IAAI,QAAA,GAAW,OAAA;AACrE;AAEA,SAAS,OAAO,GAAA,EAAsC;AACpD,EAAA,OAAO;AAAA,IACL,GAAG,GAAA,CAAI,gBAAA;AAAA,MACL;AAAA;AACF,GACF;AACF;AAEA,SAAS,OAAO,GAAA,EAA2C;AACzD,EAAA,GAAA,EAAK,eAAe,EAAE,KAAA,EAAO,SAAA,EAAW,MAAA,EAAQ,WAAW,CAAA;AAC3D,EAAA,IACE,GAAA,EACI,OAAA,CAAQ,qBAAqB,CAAA,EAC7B,cAAc,2CAA2C,CAAA;AAE7D,IAAA,aAAA,CAAc,GAAG,CAAA;AACrB;AAQA,SAAS,cAAc,GAAA,EAAwB;AAC7C,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,OAAA,CAAqB,qBAAqB,CAAA;AAC5D,EAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,WAAA,IAAe,MAAM,WAAA,EAAa;AACtD,EAAA,MAAM,MAAA,GAAS,MAAM,qBAAA,EAAsB;AAC3C,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,OAAA,CAAQ,QAAA,KAAa,MAAA;AACxC,EAAA,MAAM,GAAA,GAAM,IAAA,GACP,GAAA,CAAI,OAAA,CAAqB,cAAc,CAAA,EAAG,qBAAA,EAAsB,IACjE,GAAA,CAAI,qBAAA,EAAsB,GAC1B,GAAA,CAAI,qBAAA,EAAsB;AAC9B,EAAA,MAAM,SAAS,KAAA,CAAM,aAAA;AAAA,IACnB;AAAA,GACF;AACA,EAAA,IAAI,MAAA,EAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC3B,EAAA,MAAM,SAAA,GAAY,QAAQ,qBAAA,EAAsB;AAChD,EAAA,IACE,MAAM,aAAA,CAAc,WAAA,EAAa,iBAAiB,KAAK,CAAA,CAAE,cAAc,KAAA,EACvE;AACA,IAAA,MAAM,KAAA,GAAQ,YACV,IAAA,CAAK,GAAA,CAAI,OAAO,KAAA,EAAO,SAAA,CAAU,IAAI,CAAA,GACrC,MAAA,CAAO,KAAA;AACX,IAAA,IAAI,IAAA,EAAM,KAAA,CAAM,UAAA,IAAc,GAAA,CAAI,KAAA,GAAQ,KAAA;AAAA,SAAA,IACjC,IAAI,KAAA,GAAQ,KAAA,EAAO,KAAA,CAAM,UAAA,IAAc,IAAI,KAAA,GAAQ,KAAA;AAAA,SAAA,IACnD,GAAA,CAAI,OAAO,MAAA,CAAO,IAAA;AACzB,MAAA,KAAA,CAAM,UAAA,IAAc,KAAK,GAAA,CAAI,MAAA,CAAO,OAAO,GAAA,CAAI,IAAA,EAAM,KAAA,GAAQ,GAAA,CAAI,KAAK,CAAA;AACxE,IAAA;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAO,YAAY,IAAA,CAAK,GAAA,CAAI,OAAO,IAAA,EAAM,SAAA,CAAU,KAAK,CAAA,GAAI,MAAA,CAAO,IAAA;AACzE,EAAA,IAAI,IAAA,EAAM,KAAA,CAAM,UAAA,IAAc,GAAA,CAAI,IAAA,GAAO,IAAA;AAAA,OAAA,IAChC,IAAI,IAAA,GAAO,IAAA,EAAM,KAAA,CAAM,UAAA,IAAc,OAAO,GAAA,CAAI,IAAA;AAAA,OAAA,IAChD,GAAA,CAAI,QAAQ,MAAA,CAAO,KAAA;AAC1B,IAAA,KAAA,CAAM,UAAA,IAAc,KAAK,GAAA,CAAI,GAAA,CAAI,QAAQ,MAAA,CAAO,KAAA,EAAO,GAAA,CAAI,IAAA,GAAO,IAAI,CAAA;AAC1E;AAGA,SAAS,aAAa,IAAA,EAA8B;AAClD,EAAA,MAAM,QAAA,uBAAe,GAAA,EAA6B;AAClD,EAAA,IAAA,CACG,gBAAA;AAAA,IACC;AAAA,GACF,CACC,OAAA,CAAQ,CAAC,MAAA,KAAW;AACnB,IAAA,MAAM,GAAA,GAAM,QAAQ,MAAM,CAAA;AAC1B,IAAA,MAAM,GAAA,GAAM,GAAA,IAAO,MAAA,CAAO,GAAG,CAAA;AAC7B,IAAA,IAAI,GAAA,IAAO,CAAC,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA,EAAG,QAAA,CAAS,GAAA,CAAI,GAAA,EAAK,MAAM,CAAA;AAAA,EACzD,CAAC,CAAA;AACH,EAAA,OAAO,QAAA;AACT;AAGO,SAAS,YACd,IAAA,EACA,EAAE,SAAA,EAAW,UAAA,GAAa,aAAY,EAC1B;AACZ,EAAA,MAAM,aAAA,GACJ,IAAA,CAAK,QAAA,KAAa,CAAA,GAAK,OAAoB,IAAA,CAAK,aAAA;AAElD,EAAA,MAAM,OAAO,aAAA,CAAc,WAAA;AAI3B,EAAA,MAAM,aAAA,uBAAoB,OAAA,EAA6B;AACvD,EAAA,MAAM,cAAA,uBAAqB,GAAA,EAAiB;AAC5C,EAAA,MAAM,UAAA,uBAAiB,OAAA,EAA6B;AACpD,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAAiB;AACxC,EAAA,MAAM,YAAA,uBAAmB,GAAA,EAAiB;AAC1C,EAAA,MAAM,kBAAA,GAAqB,KAAK,cAAA,GAC5B,IAAI,KAAK,cAAA,CAAe,MAAM,cAAA,EAAgB,CAAA,GAC9C,MAAA;AACJ,EAAA,MAAM,WAAA,GAAc,CAAC,OAAA,KAAyB;AAC5C,IAAA,IAAI,YAAA,CAAa,GAAA,CAAI,OAAO,CAAA,EAAG;AAC/B,IAAA,YAAA,CAAa,IAAI,OAAO,CAAA;AACxB,IAAA,kBAAA,EAAoB,QAAQ,OAAO,CAAA;AAAA,EACrC,CAAA;AACA,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAuB;AACvC,IAAA,MAAM,aAAA,GAAgB,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,IAAK,CAAA;AAC/C,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,QAAA,KAAa,MAAA,EAAQ;AACrC,MAAA,KAAA,CAAM,KAAA,CAAM,eAAe,0BAA0B,CAAA;AACrD,MAAA,KAAA,CAAM,KAAA,CAAM,eAAe,8BAA8B,CAAA;AACzD,MAAA,UAAA,CAAW,OAAO,KAAK,CAAA;AACvB,MAAA,UAAA,CAAW,OAAO,KAAK,CAAA;AACvB,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,IAAI,KAAK,CAAA;AACpB,IAAA,WAAA,CAAY,KAAK,CAAA;AACjB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,GAAG,KAAA,CAAM,gBAAA,CAA8B,uBAAuB;AAAA,KAChE;AACA,IAAA,IAAA,CAAK,QAAQ,WAAW,CAAA;AACxB,IAAA,MAAM,SAAS,IAAA,CAAK,IAAA;AAAA,MAClB,CAAC,SAAA,KAAc,SAAA,CAAU,OAAA,CAAQ,MAAA,KAAW;AAAA,KAC9C;AACA,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,EAAA,CAAG,EAAE,CAAA;AACvB,IAAA,MAAM,MAAA,GAAS,MAAM,qBAAA,EAAsB;AAC3C,IAAA,MAAM,SAAA,GAAY,QAAQ,qBAAA,EAAsB;AAChD,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,gBAAA,CAAiB,KAAK,EAAE,SAAA,KAAc,KAAA;AACvD,IAAA,MAAM,KAAA,GAAQ,SAAA,GACV,GAAA,GACE,MAAA,CAAO,KAAA,GAAQ,UAAU,IAAA,GACzB,SAAA,CAAU,KAAA,GAAQ,MAAA,CAAO,IAAA,GAC3B,CAAA;AACJ,IAAA,KAAA,CAAM,KAAA,CAAM,WAAA;AAAA,MACV,0BAAA;AAAA,MACA,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,KAAK,CAAC,CAAA,EAAA;AAAA,KACvB;AACA,IAAA,MAAM,cAAc,IAAA,CAAK,GAAA;AAAA,MACvB,CAAA;AAAA,MACA,KAAA,CAAM,WAAA,GAAc,KAAA,CAAM,WAAA,GAAc;AAAA,KAC1C;AACA,IAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,IAAA,IAAI,WAAA,GAAc,CAAA,IAAK,IAAA,IAAQ,IAAA,KAAS,MAAA,EAAQ;AAC9C,MAAA,MAAM,OAAA,GAAU,KAAK,qBAAA,EAAsB;AAC3C,MAAA,MAAM,UAAU,GAAA,GACZ,EACE,KAAA,CAAM,UAAA,GACN,QAAQ,KAAA,IACP,SAAA,EAAW,IAAA,IAAQ,MAAA,CAAO,UAE7B,KAAA,CAAM,UAAA,GAAa,QAAQ,IAAA,IAAQ,SAAA,EAAW,SAAS,MAAA,CAAO,IAAA,CAAA;AAClE,MAAA,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,GAAU,WAAW,CAAA;AAAA,IAC3C;AACA,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,aAAa,KAAK,GAAA,EAAK;AAC1C,MAAA,KAAA,CAAM,KAAA,CAAM,WAAA,CAAY,8BAAA,EAAgC,CAAA,EAAG,KAAK,CAAA,EAAA,CAAI,CAAA;AACpE,MAAA,UAAA,CAAW,GAAA,CAAI,OAAO,KAAK,CAAA;AAAA,IAC7B;AAAA,EACF,CAAA;AACA,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,KAAA,MAAW,OAAA,IAAW,YAAA;AACpB,MAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,OAAO,CAAA,EAAG;AAC3B,QAAA,kBAAA,EAAoB,UAAU,OAAO,CAAA;AACrC,QAAA,YAAA,CAAa,OAAO,OAAO,CAAA;AAAA,MAC7B;AACF,IAAA,KAAA,MAAW,KAAA,IAAS,UAAA;AAClB,MAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG;AACzB,QAAA,KAAA,CAAM,KAAA,CAAM,eAAe,0BAA0B,CAAA;AACrD,QAAA,KAAA,CAAM,KAAA,CAAM,eAAe,8BAA8B,CAAA;AACzD,QAAA,UAAA,CAAW,OAAO,KAAK,CAAA;AAAA,MACzB;AACF,IAAA,IAAA,CAAK,gBAAA,CAA8B,qBAAqB,CAAA,CAAE,OAAA,CAAQ,QAAQ,CAAA;AAAA,EAC5E,CAAA;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,KAAA,KAAuB;AACzC,IAAA,MAAM,SAAS,KAAA,CAAM,aAAA;AAAA,MACnB;AAAA,KACF;AACA,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAM,QAAA,GAAW,OAAO,KAAA,CAAM,SAAA,GACzB,cAAc,GAAA,CAAI,MAAM,KAAK,CAAA,GAC9B,CAAA;AACJ,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,gBAAA,CAAiB,KAAK,CAAA;AACzC,IAAA,IAAI,MAAM,SAAA,KAAc,KAAA,IAAS,KAAA,CAAM,WAAA,IAAe,MAAM,WAAA,EAAa;AACvE,MAAA,IAAI,QAAA,KAAa,CAAA,EAAG,MAAA,CAAO,KAAA,CAAM,SAAA,GAAY,EAAA;AAC7C,MAAA,aAAA,CAAc,GAAA,CAAI,QAAQ,CAAC,CAAA;AAC3B,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAA,GACJ,KAAA,CAAM,qBAAA,EAAsB,CAAE,SAC7B,MAAA,CAAO,UAAA,CAAW,KAAA,CAAM,gBAAgB,KAAK,CAAA,CAAA,IAC7C,MAAA,CAAO,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA,IAAK,CAAA,CAAA;AAC5C,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,qBAAA,EAAsB,CAAE,KAAA,GAAQ,QAAA;AACxD,IAAA,MAAM,QAAQ,WAAA,GAAc,QAAA;AAC5B,IAAA,MAAM,OAAO,IAAA,CAAK,GAAA,CAAI,KAAK,CAAA,GAAI,MAAM,CAAA,GAAI,KAAA;AACzC,IAAA,IAAI,IAAA,KAAS,UAAU,MAAA,CAAO,KAAA,CAAM,YAAY,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,IAAA,CAAA,GAAS,EAAA;AACvE,IAAA,aAAA,CAAc,GAAA,CAAI,QAAQ,IAAI,CAAA;AAC9B,IAAA,cAAA,CAAe,IAAI,MAAM,CAAA;AAAA,EAC3B,CAAA;AACA,EAAA,MAAM,mBAAmB,MACvB,IAAA,CACG,iBAA8B,qBAAqB,CAAA,CACnD,QAAQ,UAAU,CAAA;AACvB,EAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAiB;AACtC,IAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,IAAA,IAAI,MAAA,YAAkB,WAAA,IAAe,MAAA,CAAO,OAAA,CAAQ,qBAAqB,CAAA;AACvE,MAAA,UAAA,CAAW,MAAM,CAAA;AAAA,EACrB,CAAA;AAIA,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAiB;AACzC,IAAA,MAAM,QAAQ,KAAA,CAAM,MAAA;AACpB,IAAA,IACE,EAAE,KAAA,YAAiB,WAAA,CAAA,IACnB,CAAC,KAAA,CAAM,OAAA,CAAQ,4CAA4C,CAAA,IAC3D,IAAA,CAAK,GAAA,CAAI,KAAA,CAAM,UAAU,CAAA,GAAI,GAAA;AAE7B,MAAA;AACF,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,GAAG,KAAA,CAAM,gBAAA;AAAA,QACP;AAAA;AACF,KACF;AACA,IAAA,IAAI,CAAC,MAAM,MAAA,EAAQ;AACnB,IAAA,MAAM,MAAA,GAAS,MAAM,qBAAA,EAAsB;AAC3C,IAAA,MAAM,SAAS,KAAA,CAAM,aAAA;AAAA,MACnB;AAAA,KACF;AACA,IAAA,MAAM,SAAA,GAAY,QAAQ,qBAAA,EAAsB;AAChD,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,gBAAA,CAAiB,KAAK,EAAE,SAAA,KAAc,KAAA;AACvD,IAAA,MAAM,IAAA,GAAO,MACR,SAAA,EAAW,IAAA,IAAQ,OAAO,KAAA,GAC1B,SAAA,EAAW,SAAS,MAAA,CAAO,IAAA;AAChC,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACpC,MAAA,MAAM,GAAA,GAAM,KAAK,qBAAA,EAAsB;AACvC,MAAA,OAAO,GAAA,GAAM,GAAA,CAAI,KAAA,GAAQ,IAAA,GAAO,IAAI,IAAA,GAAO,IAAA;AAAA,IAC7C,CAAC,CAAA;AACD,IAAA,MAAM,UAAU,SAAA,CAAU,MAAA;AAAA,MAAO,CAAC,IAAA,EAAM,QAAA,KACtC,IAAA,CAAK,GAAA,CAAI,QAAQ,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,GAAI,QAAA,GAAW;AAAA,KACnD;AACA,IAAA,IAAI,KAAK,GAAA,CAAI,OAAO,CAAA,GAAI,GAAA,QAAW,UAAA,IAAc,OAAA;AAAA,EACnD,CAAA;AACA,EAAA,IAAI,UAAA;AAQJ,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,IAAI,CAAC,UAAA,EAAY;AACjB,IAAA,IAAI,WAAW,KAAA,KAAU,MAAA;AACvB,MAAA,IAAA,CAAK,oBAAA,CAAqB,WAAW,KAAK,CAAA;AAC5C,IAAA,OAAO,UAAA,CAAW,MAAM,OAAA,CAAQ,aAAA;AAChC,IAAA,UAAA,GAAa,MAAA;AAAA,EACf,CAAA;AACA,EAAA,MAAM,aAAA,GAAgB,CAAC,IAAA,KAAiB;AACtC,IAAA,MAAM,OAAA,GAAU,UAAA;AAChB,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,OAAA,CAAQ,KAAA,GAAQ,MAAA;AAChB,IAAA,MAAM,OAAA,GACJ,OAAA,CAAQ,YAAA,KAAiB,MAAA,GACrB,MAAO,EAAA,GACP,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,KAAK,GAAA,CAAI,CAAA,EAAG,IAAA,GAAO,OAAA,CAAQ,YAAY,CAAC,CAAA;AAC3D,IAAA,OAAA,CAAQ,YAAA,GAAe,IAAA;AACvB,IAAA,MAAM,UAAU,IAAA,CAAK,GAAA;AAAA,MACnB,CAAA;AAAA,MACA,OAAA,CAAQ,KAAA,CAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM;AAAA,KAC5C;AACA,IAAA,MAAM,QAAA,GAAW,QAAQ,KAAA,CAAM,UAAA;AAC/B,IAAA,OAAA,CAAQ,KAAA,CAAM,aAAa,IAAA,CAAK,GAAA;AAAA,MAC9B,CAAA;AAAA,MACA,KAAK,GAAA,CAAI,OAAA,EAAS,WAAY,OAAA,CAAQ,QAAA,GAAW,UAAW,GAAI;AAAA,KAClE;AACA,IAAA,IAAI,OAAA,CAAQ,KAAA,CAAM,UAAA,KAAe,QAAA,EAAU;AACzC,MAAA,cAAA,EAAe;AACf,MAAA;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,KAAA,GAAQ,IAAA,CAAK,qBAAA,CAAsB,aAAa,CAAA;AAAA,EAC1D,CAAA;AACA,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,EAAkB,KAAA,KAAuB;AACjE,IAAA,IAAI,KAAA,CAAM,WAAA,IAAe,KAAA,CAAM,WAAA,EAAa;AAC1C,MAAA,cAAA,EAAe;AACf,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,MAAM,qBAAA,EAAsB;AAC3C,IAAA,MAAM,OAAO,IAAA,CAAK,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,QAAQ,CAAC,CAAA;AAC3D,IAAA,MAAM,aAAA,GAAgB,KAAA,CAAM,OAAA,GAAU,MAAA,CAAO,IAAA;AAC7C,IAAA,MAAM,WAAA,GAAc,MAAA,CAAO,KAAA,GAAQ,KAAA,CAAM,OAAA;AACzC,IAAA,IAAI,SAAA,GAAY,CAAA;AAChB,IAAA,IAAI,QAAA,GAAW,CAAA;AACf,IAAA,IAAI,aAAA,IAAiB,CAAA,IAAK,aAAA,GAAgB,IAAA,EAAM;AAC9C,MAAA,SAAA,GAAY,EAAA;AACZ,MAAA,QAAA,GAAW,IAAI,aAAA,GAAgB,IAAA;AAAA,IACjC,CAAA,MAAA,IAAW,WAAA,IAAe,CAAA,IAAK,WAAA,GAAc,IAAA,EAAM;AACjD,MAAA,SAAA,GAAY,CAAA;AACZ,MAAA,QAAA,GAAW,IAAI,WAAA,GAAc,IAAA;AAAA,IAC/B;AACA,IAAA,IAAI,cAAc,CAAA,EAAG;AACnB,MAAA,cAAA,EAAe;AACf,MAAA;AAAA,IACF;AACA,IAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,GAAG,KAAA,CAAM,WAAA,GAAc,MAAM,WAAW,CAAA;AACjE,IAAA,IACG,SAAA,GAAY,KAAK,KAAA,CAAM,UAAA,IAAc,KACrC,SAAA,GAAY,CAAA,IAAK,KAAA,CAAM,UAAA,IAAc,OAAA,EACtC;AACA,MAAA,cAAA,EAAe;AACf,MAAA;AAAA,IACF;AACA,IAAA,MAAM,KAAA,GACJ,6BAAA,GAAA,CACC,6BAAA,GAAgC,6BAAA,IAC/B,QAAA,GACA,QAAA;AACJ,IAAA,IAAI,UAAA,EAAY,KAAA,KAAU,KAAA,EAAO,cAAA,EAAe;AAChD,IAAA,UAAA,KAAe;AAAA,MACb,KAAA;AAAA,MACA,QAAA,EAAU,CAAA;AAAA,MACV,KAAA,EAAO,MAAA;AAAA,MACP,YAAA,EAAc;AAAA,KAChB;AACA,IAAA,UAAA,CAAW,WAAW,SAAA,GAAY,KAAA;AAClC,IAAA,KAAA,CAAM,OAAA,CAAQ,aAAA,GAAgB,SAAA,GAAY,CAAA,GAAI,OAAA,GAAU,KAAA;AACxD,IAAA,UAAA,CAAW,KAAA,KAAU,IAAA,CAAK,qBAAA,CAAsB,aAAa,CAAA;AAAA,EAC/D,CAAA;AACA,EAAA,IAAI,OAAA;AACJ,EAAA,MAAM,kBAAA,GAAqB,MACzB,IAAA,CACG,gBAAA,CAA8B,0BAA0B,CAAA,CACxD,OAAA,CAAQ,CAAC,GAAA,KAAQ,OAAO,GAAA,CAAI,OAAA,CAAQ,eAAe,CAAA;AACxD,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,cAAA,EAAe;AACf,IAAA,OAAA,GAAU,MAAA;AACV,IAAA,IAAA,CACG,gBAAA;AAAA,MACC;AAAA,KACF,CACC,OAAA,CAAQ,CAAC,GAAA,KAAQ;AAChB,MAAA,OAAO,IAAI,OAAA,CAAQ,WAAA;AACnB,MAAA,OAAO,IAAI,OAAA,CAAQ,eAAA;AAAA,IACrB,CAAC,CAAA;AAAA,EACL,CAAA;AACA,EAAA,IAAI,QAAA,GAAW,KAAA;AACf,EAAA,MAAM,wBAAwB,CAAC,WAAA,EAAqB,WAAA,KAClD,UAAA,CAAW,eAAe,MAAM;AAC9B,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,MAAM,GAAA,GAAM;AAAA,MACV,GAAG,IAAA,CAAK,gBAAA,CAA0B,4BAA4B;AAAA,KAChE,CAAE,IAAA,CAAK,CAAC,SAAA,KAAc;AACpB,MAAA,MAAM,YAAA,GAAe,OAAO,SAAS,CAAA;AACrC,MAAA,OACE,KAAA,CAAM,SAAS,CAAA,KAAM,WAAA,IACrB,iBAAiB,MAAA,IACjB,KAAA,CAAM,YAAY,CAAA,KAAM,WAAA;AAAA,IAE5B,CAAC,CAAA;AACD,IAAA,MAAM,MAAA,GAAS,GAAA,EAAK,aAAA,CAAiC,cAAc,CAAA;AACnE,IAAA,MAAA,EAAQ,KAAA,EAAM;AACd,IAAA,MAAA,CAAO,MAAM,CAAA;AAAA,EACf,CAAC,CAAA;AAEH,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAiB;AACpC,IAAA,MAAM,SAAA,GAAY,KAAA;AAClB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAChC,IAAA,MAAM,GAAA,GAAM,GAAA,IAAO,MAAA,CAAO,GAAG,CAAA;AAC7B,IAAA,MAAM,QAAA,GAAW,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACjC,IAAA,MAAM,WAAA,GAAc,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACpC,IAAA,IACE,CAAC,OACD,GAAA,CAAI,YAAA,CAAa,WAAW,CAAA,KAAM,MAAA,IAClC,CAAC,QAAA,IACD,CAAC,WAAA;AAED,MAAA;AACF,IAAA,OAAA,GAAU,EAAE,KAAA,EAAO,WAAA,EAAa,KAAA,EAAO,QAAA,EAAS;AAChD,IAAA,GAAA,CAAI,QAAQ,WAAA,GAAc,MAAA;AAC1B,IAAA,IAAI,UAAU,YAAA,EAAc;AAC1B,MAAA,SAAA,CAAU,aAAa,aAAA,GAAgB,MAAA;AACvC,MAAA,SAAA,CAAU,YAAA,CAAa,OAAA;AAAA,QACrB,wBAAA;AAAA,QACA,CAAA,EAAG,WAAW,CAAA,CAAA,EAAI,QAAQ,CAAA;AAAA,OAC5B;AAAA,IACF;AAAA,EACF,CAAA;AACA,EAAA,MAAM,UAAA,GAAa,CAAC,KAAA,KAAiB;AACnC,IAAA,MAAM,SAAA,GAAY,KAAA;AAClB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAChC,IAAA,MAAM,GAAA,GACJ,MAAM,MAAA,YAAkB,OAAA,GACnB,MAAM,MAAA,CAAO,OAAA,CAAoB,4BAA4B,CAAA,IAC9D,MAAA,GACA,MAAA;AACN,IAAA,MAAM,QAAA,GAAW,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACjC,IAAA,MAAM,WAAA,GAAc,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACpC,IAAA,IAAI,CAAC,OAAA,IAAW,CAAC,GAAA,IAAO,WAAA,KAAgB,QAAQ,KAAA,EAAO;AACrD,MAAA,cAAA,EAAe;AACf,MAAA,kBAAA,EAAmB;AACnB,MAAA;AAAA,IACF;AACA,IAAA,SAAA,CAAU,cAAA,EAAe;AACzB,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,aAAA,CAA2B,qBAAqB,CAAA;AAClE,IAAA,IAAI,KAAA,EAAO,gBAAA,CAAiB,SAAA,EAAW,KAAK,CAAA;AAAA,SACvC,cAAA,EAAe;AACpB,IAAA,kBAAA,EAAmB;AACnB,IAAA,IAAI,CAAC,GAAA,IAAO,CAAC,QAAA,IAAY,QAAA,KAAa,QAAQ,KAAA,EAAO;AACrD,IAAA,GAAA,CAAI,OAAA,CAAQ,eAAA,GAAkB,WAAA,CAAY,SAAA,EAAW,GAAG,CAAA;AACxD,IAAA,IAAI,SAAA,CAAU,YAAA,EAAc,SAAA,CAAU,YAAA,CAAa,UAAA,GAAa,MAAA;AAAA,EAClE,CAAA;AACA,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KAAiB;AAC/B,IAAA,MAAM,SAAA,GAAY,KAAA;AAClB,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAA;AAChC,IAAA,MAAM,GAAA,GAAM,GAAA,IAAO,MAAA,CAAO,GAAG,CAAA;AAC7B,IAAA,MAAM,QAAA,GAAW,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACjC,IAAA,MAAM,WAAA,GAAc,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACpC,IAAA,IACE,CAAC,OAAA,IACD,CAAC,GAAA,IACD,CAAC,QAAA,IACD,QAAA,KAAa,OAAA,CAAQ,KAAA,IACrB,WAAA,KAAgB,OAAA,CAAQ,KAAA,EACxB;AACA,MAAA,KAAA,EAAM;AACN,MAAA;AAAA,IACF;AACA,IAAA,SAAA,CAAU,cAAA,EAAe;AACzB,IAAA,MAAM,MAAA,GAAqB;AAAA,MACzB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,UAAU,OAAA,CAAQ,KAAA;AAAA,MAClB,QAAA;AAAA,MACA,QAAA,EAAU,WAAA,CAAY,SAAA,EAAW,GAAG,CAAA;AAAA,MACpC,MAAA,EAAQ;AAAA,KACV;AACA,IAAA,KAAA,EAAM;AACN,IAAA,SAAA,CAAU,MAAM,CAAA;AAChB,IAAA,qBAAA,CAAsB,MAAA,CAAO,KAAA,EAAO,MAAA,CAAO,QAAQ,CAAA;AAAA,EACrD,CAAA;AACA,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAiB;AAClC,IAAA,MAAM,aAAA,GAAgB,KAAA;AACtB,IAAA,MAAM,MAAA,GACJ,MAAM,MAAA,YAAkB,OAAA,GACpB,MAAM,MAAA,CAAO,OAAA,CAA2B,cAAc,CAAA,GACtD,IAAA;AACN,IAAA,MAAM,GAAA,GAAM,MAAA,IAAU,OAAA,CAAQ,MAAM,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,GAAA,IAAO,MAAA,CAAO,GAAG,CAAA;AAC7B,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,GAAA,IAAO,CAAC,GAAA,EAAK;AAC7B,IAAA,IAAI,aAAA,CAAc,GAAA,KAAQ,QAAA,IAAY,aAAA,CAAc,QAAQ,WAAA,EAAa;AACvE,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,aAAA,CAAiC,qBAAqB,CAAA;AACxE,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,aAAA,CAAc,cAAA,EAAe;AAC7B,MAAA,KAAA,CAAM,KAAA,EAAM;AACZ,MAAA;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,OAAO,GAAG,CAAA;AACvB,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA;AACnC,IAAA,IAAI,UAAU,CAAA,EAAG;AACjB,IAAA,IACE,aAAA,CAAc,UACd,aAAA,CAAc,QAAA,KACb,cAAc,GAAA,KAAQ,WAAA,IAAe,aAAA,CAAc,GAAA,KAAQ,YAAA,CAAA,EAC5D;AACA,MAAA,MAAMA,UACJ,IAAA,CAAK,OAAA,IAAW,cAAc,GAAA,KAAQ,WAAA,GAAc,KAAK,CAAA,CAAE,CAAA;AAC7D,MAAA,MAAM,QAAA,GAAW,MAAM,GAAG,CAAA;AAC1B,MAAA,MAAM,UAAA,GAAaA,OAAAA,IAAU,OAAA,CAAQA,OAAM,CAAA;AAC3C,MAAA,MAAM,QAAA,GAAW,UAAA,IAAc,KAAA,CAAM,UAAU,CAAA;AAC/C,MAAA,MAAM,WAAA,GAAc,MAAM,GAAG,CAAA;AAC7B,MAAA,IACE,CAACA,OAAAA,IACD,CAAC,QAAA,IACD,CAAC,QAAA,IACD,CAAC,WAAA,IACD,GAAA,CAAI,YAAA,CAAa,WAAW,CAAA,KAAM,MAAA;AAElC,QAAA;AACF,MAAA,aAAA,CAAc,cAAA,EAAe;AAC7B,MAAA,SAAA,CAAU;AAAA,QACR,KAAA,EAAO,WAAA;AAAA,QACP,QAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA,EAAU,aAAA,CAAc,GAAA,KAAQ,WAAA,GAAc,QAAA,GAAW,OAAA;AAAA,QACzD,MAAA,EAAQ;AAAA,OACT,CAAA;AACD,MAAA,qBAAA,CAAsB,aAAa,QAAQ,CAAA;AAC3C,MAAA;AAAA,IACF;AACA,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,cAAc,GAAA,KAAQ,YAAA,EAAc,IAAA,GAAA,CAAQ,OAAA,GAAU,KAAK,IAAA,CAAK,MAAA;AAAA,SAAA,IAC3D,cAAc,GAAA,KAAQ,WAAA;AAC7B,MAAA,IAAA,GAAA,CAAQ,OAAA,GAAU,CAAA,GAAI,IAAA,CAAK,MAAA,IAAU,IAAA,CAAK,MAAA;AAAA,SAAA,IACnC,aAAA,CAAc,GAAA,KAAQ,MAAA,EAAQ,IAAA,GAAO,CAAA;AAAA,SAAA,IACrC,aAAA,CAAc,GAAA,KAAQ,KAAA,EAAO,IAAA,GAAO,KAAK,MAAA,GAAS,CAAA;AAC3D,IAAA,IAAI,SAAS,MAAA,EAAW;AACxB,IAAA,aAAA,CAAc,cAAA,EAAe;AAE7B,IAAA,MAAM,MAAA,GAAS,IAAI,OAAA,CAAQ,aAAA;AAC3B,IAAA,MAAM,IAAA,GACJ,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,cAAc,MAAA,GAAS,UAAA;AAC3D,IAAA,MAAM,MAAA,GAAS,KAAK,IAAI,CAAA;AACxB,IAAA,MAAA,CAAO,KAAA,EAAM;AAGb,IAAA,IAAI,SAAS,WAAA,EAAa;AACxB,MAAA,MAAA,CAAO,KAAA,EAAM;AAGb,MAAA,IAAI,CAAC,MAAA,CAAO,WAAA;AACV,QAAA,qBAAA;AAAA,UACE,IAAI,OAAA,CAAQ,QAAA;AAAA,UACZ,OAAA,CAAQ,MAAM,CAAA,CAAG,OAAA,CAAQ;AAAA,SAC3B;AAAA,IACJ;AACA,IAAA,MAAA,CAAO,MAAM,CAAA;AAAA,EACf,CAAA;AACA,EAAA,MAAM,SAAA,GAAY,CAAC,KAAA,KAAiB;AAClC,IAAA,MAAM,GAAA,GACJ,KAAA,CAAM,MAAA,YAAkB,WAAA,IACxB,KAAA,CAAM,OAAO,OAAA,CAAQ,cAAc,CAAA,GAC/B,KAAA,CAAM,MAAA,GACN,MAAA;AACN,IAAA,MAAA,CAAO,GAAG,CAAA;AAAA,EACZ,CAAA;AAEA,EAAA,IAAA,CAAK,gBAAA,CAAiB,aAAa,WAAW,CAAA;AAC9C,EAAA,IAAA,CAAK,gBAAA,CAAiB,YAAY,UAAU,CAAA;AAC5C,EAAA,IAAA,CAAK,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACpC,EAAA,IAAA,CAAK,gBAAA,CAAiB,WAAW,KAAK,CAAA;AACtC,EAAA,IAAA,CAAK,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC1C,EAAA,IAAA,CAAK,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC1C,EAAA,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU,aAAA,EAAe,IAAI,CAAA;AACnD,EAAA,IAAA,CAAK,gBAAA,CAAiB,WAAA,EAAa,gBAAA,EAAkB,IAAI,CAAA;AACzD,EAAA,cAAA,EAAe;AACf,EAAA,IAAA,CACG,gBAAA;AAAA,IACC;AAAA,GACF,CACC,QAAQ,MAAM,CAAA;AAMjB,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,MAAM,kBAAkB,MAAM;AAC5B,IAAA,MAAM,UAAyB,EAAC;AAChC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,MAAM,CAAA,IAAK,YAAA,CAAa,IAAI,CAAA,EAAG;AAC9C,MAAA,MAAM,EAAA,GAAK,MAAM,GAAG,CAAA;AACpB,MAAA,MAAM,GAAA,GAAM,QAAQ,MAAM,CAAA;AAC1B,MAAA,MAAM,UAAA,GAAa,GAAA,IAAO,KAAA,CAAM,GAAG,CAAA;AACnC,MAAA,IAAI,CAAC,EAAA,IAAM,CAAC,UAAA,EAAY;AACxB,MAAA,IAAI,YAAY,GAAA,CAAI,EAAE,KAAK,WAAA,CAAY,GAAA,CAAI,EAAE,CAAA,KAAM,UAAA;AACjD,QAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AACrB,MAAA,WAAA,CAAY,GAAA,CAAI,IAAI,UAAU,CAAA;AAAA,IAChC;AACA,IAAA,OAAO,OAAA;AAAA,EACT,CAAA;AACA,EAAA,eAAA,EAAgB;AAChB,EAAA,gBAAA,EAAiB;AACjB,EAAA,MAAM,iBAAA,GAAoB,IAAI,IAAA,CAAK,gBAAA,CAAiB,MAAM;AACxD,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,cAAA,EAAe;AACf,IAAA,KAAA,MAAW,UAAU,eAAA,EAAgB;AACnC,MAAA,IAAI,aAAA,CAAc,aAAA,KAAkB,MAAA,EAAQ,aAAA,CAAc,MAAM,CAAA;AAClE,IAAA,gBAAA,EAAiB;AAAA,EACnB,CAAC,CAAA;AACD,EAAA,iBAAA,CAAkB,QAAQ,IAAA,EAAM;AAAA,IAC9B,OAAA,EAAS,IAAA;AAAA,IACT,SAAA,EAAW,IAAA;AAAA,IACX,UAAA,EAAY,IAAA;AAAA,IACZ,eAAA,EAAiB,CAAC,eAAA,EAAiB,KAAA,EAAO,kBAAkB,aAAa;AAAA,GAC1E,CAAA;AAED,EAAA,OAAO,MAAM;AACX,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,iBAAA,CAAkB,UAAA,EAAW;AAC7B,IAAA,kBAAA,EAAoB,UAAA,EAAW;AAC/B,IAAA,KAAA,MAAW,SAAS,UAAA,EAAY;AAC9B,MAAA,KAAA,CAAM,KAAA,CAAM,eAAe,0BAA0B,CAAA;AACrD,MAAA,KAAA,CAAM,KAAA,CAAM,eAAe,8BAA8B,CAAA;AAAA,IAC3D;AACA,IAAA,UAAA,CAAW,KAAA,EAAM;AACjB,IAAA,YAAA,CAAa,KAAA,EAAM;AACnB,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,mBAAA,CAAoB,aAAa,WAAW,CAAA;AACjD,IAAA,IAAA,CAAK,mBAAA,CAAoB,YAAY,UAAU,CAAA;AAC/C,IAAA,IAAA,CAAK,mBAAA,CAAoB,QAAQ,MAAM,CAAA;AACvC,IAAA,IAAA,CAAK,mBAAA,CAAoB,WAAW,KAAK,CAAA;AACzC,IAAA,IAAA,CAAK,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAC7C,IAAA,IAAA,CAAK,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAC7C,IAAA,IAAA,CAAK,mBAAA,CAAoB,QAAA,EAAU,aAAA,EAAe,IAAI,CAAA;AACtD,IAAA,IAAA,CAAK,mBAAA,CAAoB,WAAA,EAAa,gBAAA,EAAkB,IAAI,CAAA;AAC5D,IAAA,KAAA,MAAW,MAAA,IAAU,cAAA,EAAgB,MAAA,CAAO,KAAA,CAAM,SAAA,GAAY,EAAA;AAC9D,IAAA,cAAA,CAAe,KAAA,EAAM;AAAA,EACvB,CAAA;AACF","file":"chunk-T2E5WBCZ.js","sourcesContent":["import type { TabActivation } from './tab-bar.js';\n\nexport type TabReorderSource = 'pointer' | 'keyboard';\nexport type TabDropPosition = 'before' | 'after';\n\nexport interface TabReorder {\n barId: string;\n sourceId: string;\n targetId: string;\n position: TabDropPosition;\n source: TabReorderSource;\n}\n\nexport type { TabActivation } from './tab-bar.js';\n\nexport interface WireTabBarsOptions {\n onReorder: (change: TabReorder) => void;\n /**\n * How arrow / Home / End keys activate tabs (default `'automatic'`):\n * - `'automatic'` moves roving focus **and** selects the focused tab (clicks it).\n * - `'manual'` moves roving focus only; the user selects with Enter / Space / click\n * (the ARIA Tabs manual-activation pattern). Use this when activation is a heavy or\n * side-effecting action (e.g. a tab that loads a project) so arrowing through the\n * strip doesn't trigger it on every tab.\n *\n * A per-bar `data-tab-activation=\"manual\" | \"automatic\"` attribute (see the `TabBar`\n * `activation` prop) overrides this option for that strip.\n */\n activation?: TabActivation;\n}\n\ntype TabRoot = HTMLElement;\ntype TabBarRoot = HTMLElement;\n\nconst AUTO_SCROLL_EDGE_PX = 56;\nconst AUTO_SCROLL_MIN_PX_PER_SECOND = 180;\nconst AUTO_SCROLL_MAX_PX_PER_SECOND = 900;\n\nexport function reorderTabs<T>(\n items: readonly T[],\n getId: (item: T) => string,\n sourceId: string,\n targetId: string,\n position: TabDropPosition,\n): T[] {\n if (sourceId === targetId) return [...items];\n const source = items.find((item) => getId(item) === sourceId);\n if (!source || !items.some((item) => getId(item) === targetId))\n return [...items];\n const remaining = items.filter((item) => getId(item) !== sourceId);\n const targetIndex = remaining.findIndex((item) => getId(item) === targetId);\n remaining.splice(targetIndex + (position === 'after' ? 1 : 0), 0, source);\n return remaining;\n}\n\nfunction tabRoot(target: EventTarget | null): TabRoot | undefined {\n return target instanceof Element\n ? (target.closest<TabRoot>('[data-component=\"app-tab\"]') ?? undefined)\n : undefined;\n}\n\nfunction tabBar(tab: TabRoot): TabBarRoot | undefined {\n return tab.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ?? undefined;\n}\n\nfunction tabId(tab: TabRoot): string | undefined {\n return tab.dataset.tabId || undefined;\n}\n\nfunction barId(bar: TabBarRoot): string | undefined {\n return bar.dataset.tabBarId || undefined;\n}\n\nfunction positionFor(event: DragEvent, tab: TabRoot): TabDropPosition {\n const bounds = tab.getBoundingClientRect();\n return event.clientX < bounds.left + bounds.width / 2 ? 'before' : 'after';\n}\n\nfunction tabsIn(bar: TabBarRoot): HTMLButtonElement[] {\n return [\n ...bar.querySelectorAll<HTMLButtonElement>(\n '[data-kui-tab-list] [role=\"tab\"]',\n ),\n ];\n}\n\nfunction reveal(tab: HTMLElement | null | undefined): void {\n tab?.scrollIntoView({ block: 'nearest', inline: 'nearest' });\n if (\n tab\n ?.closest('[data-kui-tab-list]')\n ?.querySelector(':scope > .kui-app-tab[data-pinned=\"true\"]')\n )\n revealInStrip(tab);\n}\n\n/**\n * Scroll a tab's own strip, and nothing else, so the tab is fully visible.\n * Used for selection changes the application makes without focus (adding and\n * selecting a tab): unlike `scrollIntoView`, it never scrolls the page to a\n * bar that is out of view.\n */\nfunction revealInStrip(tab: HTMLElement): void {\n const strip = tab.closest<HTMLElement>('[data-kui-tab-list]');\n if (!strip || strip.scrollWidth <= strip.clientWidth) return;\n const bounds = strip.getBoundingClientRect();\n const snap = strip.dataset.snapTabs === 'true';\n const box = snap\n ? (tab.closest<HTMLElement>('.kui-app-tab')?.getBoundingClientRect() ??\n tab.getBoundingClientRect())\n : tab.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (pinned?.contains(tab)) return;\n const pinnedBox = pinned?.getBoundingClientRect();\n if (\n strip.ownerDocument.defaultView?.getComputedStyle(strip).direction === 'rtl'\n ) {\n const right = pinnedBox\n ? Math.min(bounds.right, pinnedBox.left)\n : bounds.right;\n if (snap) strip.scrollLeft += box.right - right;\n else if (box.right > right) strip.scrollLeft += box.right - right;\n else if (box.left < bounds.left)\n strip.scrollLeft -= Math.min(bounds.left - box.left, right - box.right);\n return;\n }\n const left = pinnedBox ? Math.max(bounds.left, pinnedBox.right) : bounds.left;\n if (snap) strip.scrollLeft += box.left - left;\n else if (box.left < left) strip.scrollLeft -= left - box.left;\n else if (box.right > bounds.right)\n strip.scrollLeft += Math.min(box.right - bounds.right, box.left - left);\n}\n\n/** The selected tab button of every bar below root, keyed by bar. */\nfunction selectedTabs(root: HTMLElement | Document) {\n const selected = new Map<TabBarRoot, HTMLElement>();\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach((button) => {\n const tab = tabRoot(button);\n const bar = tab && tabBar(tab);\n if (bar && !selected.has(bar)) selected.set(bar, button);\n });\n return selected;\n}\n\n/** Wire reordering and keyboard navigation while leaving controlled state in the application. */\nexport function wireTabBars(\n root: HTMLElement | Document,\n { onReorder, activation = 'automatic' }: WireTabBarsOptions,\n): () => void {\n const ownerDocument = (\n root.nodeType === 9 ? (root as Document) : root.ownerDocument\n )!;\n const view = ownerDocument.defaultView!;\n // WebKit positions an RTL sticky flex child as though the negative scroll\n // offset were added to its inline-start inset. Measure the actual pinned\n // edge and correct only the displacement; Chromium and Firefox stay at 0.\n const pinnedOffsets = new WeakMap<HTMLElement, number>();\n const adjustedPinned = new Set<HTMLElement>();\n const snapExtras = new WeakMap<HTMLElement, number>();\n const snapStrips = new Set<HTMLElement>();\n const snapObserved = new Set<HTMLElement>();\n const snapResizeObserver = view.ResizeObserver\n ? new view.ResizeObserver(() => syncSnapInRoot())\n : undefined;\n const observeSnap = (element: HTMLElement) => {\n if (snapObserved.has(element)) return;\n snapObserved.add(element);\n snapResizeObserver?.observe(element);\n };\n const syncSnap = (strip: HTMLElement) => {\n const previousExtra = snapExtras.get(strip) ?? 0;\n if (strip.dataset.snapTabs !== 'true') {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapExtras.delete(strip);\n snapStrips.delete(strip);\n return;\n }\n snapStrips.add(strip);\n observeSnap(strip);\n const tabs = [\n ...strip.querySelectorAll<HTMLElement>(':scope > .kui-app-tab'),\n ];\n tabs.forEach(observeSnap);\n const pinned = tabs.find(\n (candidate) => candidate.dataset.pinned === 'true',\n );\n const last = tabs.at(-1);\n const bounds = strip.getBoundingClientRect();\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const inset = pinnedBox\n ? rtl\n ? bounds.right - pinnedBox.left\n : pinnedBox.right - bounds.left\n : 0;\n strip.style.setProperty(\n '--kui-tab-bar-snap-inset',\n `${Math.max(0, inset)}px`,\n );\n const baseMaximum = Math.max(\n 0,\n strip.scrollWidth - strip.clientWidth - previousExtra,\n );\n let extra = 0;\n if (baseMaximum > 0 && last && last !== pinned) {\n const lastBox = last.getBoundingClientRect();\n const desired = rtl\n ? -(\n strip.scrollLeft +\n lastBox.right -\n (pinnedBox?.left ?? bounds.right)\n )\n : strip.scrollLeft + lastBox.left - (pinnedBox?.right ?? bounds.left);\n extra = Math.max(0, desired - baseMaximum);\n }\n if (Math.abs(extra - previousExtra) >= 0.5) {\n strip.style.setProperty('--kui-tab-bar-snap-end-extra', `${extra}px`);\n snapExtras.set(strip, extra);\n }\n };\n const syncSnapInRoot = () => {\n for (const element of snapObserved)\n if (!root.contains(element)) {\n snapResizeObserver?.unobserve(element);\n snapObserved.delete(element);\n }\n for (const strip of snapStrips)\n if (!root.contains(strip)) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n snapStrips.delete(strip);\n }\n root.querySelectorAll<HTMLElement>('[data-kui-tab-list]').forEach(syncSnap);\n };\n const syncPinned = (strip: HTMLElement) => {\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n if (!pinned) return;\n const previous = pinned.style.translate\n ? (pinnedOffsets.get(pinned) ?? 0)\n : 0;\n const style = view.getComputedStyle(strip);\n if (style.direction !== 'rtl' || strip.scrollWidth <= strip.clientWidth) {\n if (previous !== 0) pinned.style.translate = '';\n pinnedOffsets.set(pinned, 0);\n return;\n }\n const targetRight =\n strip.getBoundingClientRect().right -\n (Number.parseFloat(style.borderRightWidth) || 0) -\n (Number.parseFloat(style.paddingRight) || 0);\n const rawRight = pinned.getBoundingClientRect().right - previous;\n const delta = targetRight - rawRight;\n const next = Math.abs(delta) < 0.5 ? 0 : delta;\n if (next !== previous) pinned.style.translate = next ? `${next}px 0` : '';\n pinnedOffsets.set(pinned, next);\n adjustedPinned.add(pinned);\n };\n const syncPinnedInRoot = () =>\n root\n .querySelectorAll<HTMLElement>('[data-kui-tab-list]')\n .forEach(syncPinned);\n const onStripScroll = (event: Event) => {\n const target = event.target;\n if (target instanceof HTMLElement && target.matches('[data-kui-tab-list]'))\n syncPinned(target);\n };\n // CSS snap settles direct gestures in most engines. WebKit can leave a\n // programmatic smooth scroll between snap points, so normalize its final\n // position to the nearest whole peer at the pinned inset.\n const onStripScrollEnd = (event: Event) => {\n const strip = event.target;\n if (\n !(strip instanceof HTMLElement) ||\n !strip.matches('[data-kui-tab-list][data-snap-tabs=\"true\"]') ||\n Math.abs(strip.scrollLeft) < 0.5\n )\n return;\n const peers = [\n ...strip.querySelectorAll<HTMLElement>(\n ':scope > .kui-app-tab:not([data-pinned=\"true\"])',\n ),\n ];\n if (!peers.length) return;\n const bounds = strip.getBoundingClientRect();\n const pinned = strip.querySelector<HTMLElement>(\n ':scope > .kui-app-tab[data-pinned=\"true\"]',\n );\n const pinnedBox = pinned?.getBoundingClientRect();\n const rtl = view.getComputedStyle(strip).direction === 'rtl';\n const edge = rtl\n ? (pinnedBox?.left ?? bounds.right)\n : (pinnedBox?.right ?? bounds.left);\n const distances = peers.map((peer) => {\n const box = peer.getBoundingClientRect();\n return rtl ? box.right - edge : box.left - edge;\n });\n const closest = distances.reduce((best, distance) =>\n Math.abs(distance) < Math.abs(best) ? distance : best,\n );\n if (Math.abs(closest) > 0.5) strip.scrollLeft += closest;\n };\n let autoScroll:\n | {\n strip: HTMLElement;\n velocity: number;\n frame: number | undefined;\n previousTime: number | undefined;\n }\n | undefined;\n const stopAutoScroll = () => {\n if (!autoScroll) return;\n if (autoScroll.frame !== undefined)\n view.cancelAnimationFrame(autoScroll.frame);\n delete autoScroll.strip.dataset.tabAutoscroll;\n autoScroll = undefined;\n };\n const runAutoScroll = (time: number) => {\n const current = autoScroll;\n if (!current) return;\n current.frame = undefined;\n const elapsed =\n current.previousTime === undefined\n ? 1000 / 60\n : Math.min(40, Math.max(1, time - current.previousTime));\n current.previousTime = time;\n const maximum = Math.max(\n 0,\n current.strip.scrollWidth - current.strip.clientWidth,\n );\n const previous = current.strip.scrollLeft;\n current.strip.scrollLeft = Math.max(\n 0,\n Math.min(maximum, previous + (current.velocity * elapsed) / 1000),\n );\n if (current.strip.scrollLeft === previous) {\n stopAutoScroll();\n return;\n }\n current.frame = view.requestAnimationFrame(runAutoScroll);\n };\n const updateAutoScroll = (event: DragEvent, strip: HTMLElement) => {\n if (strip.scrollWidth <= strip.clientWidth) {\n stopAutoScroll();\n return;\n }\n const bounds = strip.getBoundingClientRect();\n const edge = Math.min(AUTO_SCROLL_EDGE_PX, bounds.width / 3);\n const startDistance = event.clientX - bounds.left;\n const endDistance = bounds.right - event.clientX;\n let direction = 0;\n let strength = 0;\n if (startDistance >= 0 && startDistance < edge) {\n direction = -1;\n strength = 1 - startDistance / edge;\n } else if (endDistance >= 0 && endDistance < edge) {\n direction = 1;\n strength = 1 - endDistance / edge;\n }\n if (direction === 0) {\n stopAutoScroll();\n return;\n }\n const maximum = Math.max(0, strip.scrollWidth - strip.clientWidth);\n if (\n (direction < 0 && strip.scrollLeft <= 0) ||\n (direction > 0 && strip.scrollLeft >= maximum)\n ) {\n stopAutoScroll();\n return;\n }\n const speed =\n AUTO_SCROLL_MIN_PX_PER_SECOND +\n (AUTO_SCROLL_MAX_PX_PER_SECOND - AUTO_SCROLL_MIN_PX_PER_SECOND) *\n strength *\n strength;\n if (autoScroll?.strip !== strip) stopAutoScroll();\n autoScroll ??= {\n strip,\n velocity: 0,\n frame: undefined,\n previousTime: undefined,\n };\n autoScroll.velocity = direction * speed;\n strip.dataset.tabAutoscroll = direction < 0 ? 'start' : 'end';\n autoScroll.frame ??= view.requestAnimationFrame(runAutoScroll);\n };\n let dragged: { barId: string; tabId: string } | undefined;\n const clearDropPositions = () =>\n root\n .querySelectorAll<HTMLElement>('[data-tab-drop-position]')\n .forEach((tab) => delete tab.dataset.tabDropPosition);\n const clear = () => {\n stopAutoScroll();\n dragged = undefined;\n root\n .querySelectorAll<HTMLElement>(\n '[data-tab-dragging], [data-tab-drop-position]',\n )\n .forEach((tab) => {\n delete tab.dataset.tabDragging;\n delete tab.dataset.tabDropPosition;\n });\n };\n let disposed = false;\n const afterControlledRender = (sourceBarId: string, sourceTabId: string) =>\n globalThis.queueMicrotask(() => {\n if (disposed) return;\n const tab = [\n ...root.querySelectorAll<TabRoot>('[data-component=\"app-tab\"]'),\n ].find((candidate) => {\n const candidateBar = tabBar(candidate);\n return (\n tabId(candidate) === sourceTabId &&\n candidateBar !== undefined &&\n barId(candidateBar) === sourceBarId\n );\n });\n const button = tab?.querySelector<HTMLButtonElement>('[role=\"tab\"]');\n button?.focus();\n reveal(button);\n });\n\n const onDragStart = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const sourceId = tab && tabId(tab);\n const sourceBarId = bar && barId(bar);\n if (\n !tab ||\n tab.getAttribute('draggable') !== 'true' ||\n !sourceId ||\n !sourceBarId\n )\n return;\n dragged = { barId: sourceBarId, tabId: sourceId };\n tab.dataset.tabDragging = 'true';\n if (dragEvent.dataTransfer) {\n dragEvent.dataTransfer.effectAllowed = 'move';\n dragEvent.dataTransfer.setData(\n 'application/x-kerf-tab',\n `${sourceBarId}:${sourceId}`,\n );\n }\n };\n const onDragOver = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar =\n event.target instanceof Element\n ? (event.target.closest<TabBarRoot>('[data-component=\"tab-bar\"]') ??\n undefined)\n : undefined;\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (!dragged || !bar || targetBarId !== dragged.barId) {\n stopAutoScroll();\n clearDropPositions();\n return;\n }\n dragEvent.preventDefault();\n const strip = bar.querySelector<HTMLElement>('[data-kui-tab-list]');\n if (strip) updateAutoScroll(dragEvent, strip);\n else stopAutoScroll();\n clearDropPositions();\n if (!tab || !targetId || targetId === dragged.tabId) return;\n tab.dataset.tabDropPosition = positionFor(dragEvent, tab);\n if (dragEvent.dataTransfer) dragEvent.dataTransfer.dropEffect = 'move';\n };\n const onDrop = (event: Event) => {\n const dragEvent = event as DragEvent;\n const tab = tabRoot(event.target);\n const bar = tab && tabBar(tab);\n const targetId = tab && tabId(tab);\n const targetBarId = bar && barId(bar);\n if (\n !dragged ||\n !tab ||\n !targetId ||\n targetId === dragged.tabId ||\n targetBarId !== dragged.barId\n ) {\n clear();\n return;\n }\n dragEvent.preventDefault();\n const change: TabReorder = {\n barId: dragged.barId,\n sourceId: dragged.tabId,\n targetId,\n position: positionFor(dragEvent, tab),\n source: 'pointer',\n };\n clear();\n onReorder(change);\n afterControlledRender(change.barId, change.sourceId);\n };\n const onKeyDown = (event: Event) => {\n const keyboardEvent = event as KeyboardEvent;\n const button =\n event.target instanceof Element\n ? event.target.closest<HTMLButtonElement>('[role=\"tab\"]')\n : null;\n const tab = button && tabRoot(button);\n const bar = tab && tabBar(tab);\n if (!button || !tab || !bar) return;\n if (keyboardEvent.key === 'Delete' || keyboardEvent.key === 'Backspace') {\n const close = tab.querySelector<HTMLButtonElement>('.kui-app-tab__close');\n if (!close) return;\n keyboardEvent.preventDefault();\n close.click();\n return;\n }\n const tabs = tabsIn(bar);\n const current = tabs.indexOf(button);\n if (current < 0) return;\n if (\n keyboardEvent.altKey &&\n keyboardEvent.shiftKey &&\n (keyboardEvent.key === 'ArrowLeft' || keyboardEvent.key === 'ArrowRight')\n ) {\n const target =\n tabs[current + (keyboardEvent.key === 'ArrowLeft' ? -1 : 1)];\n const sourceId = tabId(tab);\n const targetRoot = target && tabRoot(target);\n const targetId = targetRoot && tabId(targetRoot);\n const sourceBarId = barId(bar);\n if (\n !target ||\n !sourceId ||\n !targetId ||\n !sourceBarId ||\n tab.getAttribute('draggable') !== 'true'\n )\n return;\n keyboardEvent.preventDefault();\n onReorder({\n barId: sourceBarId,\n sourceId,\n targetId,\n position: keyboardEvent.key === 'ArrowLeft' ? 'before' : 'after',\n source: 'keyboard',\n });\n afterControlledRender(sourceBarId, sourceId);\n return;\n }\n let next: number | undefined;\n if (keyboardEvent.key === 'ArrowRight') next = (current + 1) % tabs.length;\n else if (keyboardEvent.key === 'ArrowLeft')\n next = (current - 1 + tabs.length) % tabs.length;\n else if (keyboardEvent.key === 'Home') next = 0;\n else if (keyboardEvent.key === 'End') next = tabs.length - 1;\n if (next === undefined) return;\n keyboardEvent.preventDefault();\n // A per-bar data-tab-activation attribute overrides the wireTabBars option.\n const perBar = bar.dataset.tabActivation;\n const mode: TabActivation =\n perBar === 'manual' || perBar === 'automatic' ? perBar : activation;\n const target = tabs[next]!;\n target.focus();\n // Manual activation moves roving focus only; the user selects with Enter / Space\n // (native on the tab <button>) or click. Automatic also selects the focused tab.\n if (mode === 'automatic') {\n target.click();\n // Activation can synchronously replace the controlled strip. Resolve the\n // logical tab again after rendering instead of leaving focus on a dead node.\n if (!target.isConnected)\n afterControlledRender(\n bar.dataset.tabBarId!,\n tabRoot(target)!.dataset.tabId!,\n );\n }\n reveal(target);\n };\n const onFocusIn = (event: Event) => {\n const tab =\n event.target instanceof HTMLElement &&\n event.target.matches('[role=\"tab\"]')\n ? event.target\n : undefined;\n reveal(tab);\n };\n\n root.addEventListener('dragstart', onDragStart);\n root.addEventListener('dragover', onDragOver);\n root.addEventListener('drop', onDrop);\n root.addEventListener('dragend', clear);\n root.addEventListener('keydown', onKeyDown);\n root.addEventListener('focusin', onFocusIn);\n root.addEventListener('scroll', onStripScroll, true);\n root.addEventListener('scrollend', onStripScrollEnd, true);\n syncSnapInRoot();\n root\n .querySelectorAll<HTMLElement>(\n '[data-kui-tab-list] [role=\"tab\"][aria-selected=\"true\"]',\n )\n .forEach(reveal);\n\n // Reveal a tab the application selects without focus (for example a tab it\n // adds and selects) once its controlled render lands. Only a change of a\n // bar's selected tab reveals, so a re-render that keeps the selection never\n // pulls a strip the user scrolled back to it.\n const selectedIds = new Map<string, string>();\n const recordSelection = () => {\n const changed: HTMLElement[] = [];\n for (const [bar, button] of selectedTabs(root)) {\n const id = barId(bar);\n const tab = tabRoot(button);\n const selectedId = tab && tabId(tab);\n if (!id || !selectedId) continue;\n if (selectedIds.has(id) && selectedIds.get(id) !== selectedId)\n changed.push(button);\n selectedIds.set(id, selectedId);\n }\n return changed;\n };\n recordSelection();\n syncPinnedInRoot();\n const selectionObserver = new view.MutationObserver(() => {\n if (disposed) return;\n syncSnapInRoot();\n for (const button of recordSelection())\n if (ownerDocument.activeElement !== button) revealInStrip(button);\n syncPinnedInRoot();\n });\n selectionObserver.observe(root, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: ['aria-selected', 'dir', 'data-snap-tabs', 'data-pinned'],\n });\n\n return () => {\n disposed = true;\n selectionObserver.disconnect();\n snapResizeObserver?.disconnect();\n for (const strip of snapStrips) {\n strip.style.removeProperty('--kui-tab-bar-snap-inset');\n strip.style.removeProperty('--kui-tab-bar-snap-end-extra');\n }\n snapStrips.clear();\n snapObserved.clear();\n clear();\n root.removeEventListener('dragstart', onDragStart);\n root.removeEventListener('dragover', onDragOver);\n root.removeEventListener('drop', onDrop);\n root.removeEventListener('dragend', clear);\n root.removeEventListener('keydown', onKeyDown);\n root.removeEventListener('focusin', onFocusIn);\n root.removeEventListener('scroll', onStripScroll, true);\n root.removeEventListener('scrollend', onStripScrollEnd, true);\n for (const pinned of adjustedPinned) pinned.style.translate = '';\n adjustedPinned.clear();\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/toolbar-text.tsx"],"names":[],"mappings":";;;;AAqDO,SAAS,WAAA,CAAY;AAAA,EAC1B,IAAA;AAAA,EACA,IAAA,GAAO,SAAA;AAAA,EACP,SAAA,GAAY,EAAA;AAAA,EACZ,EAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA,GAAc,KAAA;AAAA,EACd,IAAA,GAAO,KAAA;AAAA,EACP,QAAA,GAAW,IAAA;AAAA,EACX,QAAA,GAAW,IAAA;AAAA,EACX,IAAA,GAAO,KAAA;AAAA,EACP;AACF,CAAA,EAAqB;AACnB,EAAA,MAAM,MAAA,GAAS,IAAA,IAAQ,QAAA,IAAY,IAAA,IAAQ,QAAA,GAAW,CAAA;AACtD,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MAC5C,gBAAA,EAAe,cAAA;AAAA,MACf,WAAA,EAAW,IAAA;AAAA,MACX,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,eAAA,EAAe,WAAW,MAAA,GAAY,OAAA;AAAA,MACtC,gBAAA,EAAgB,MAAA,GAAS,MAAA,CAAO,QAAQ,CAAA,GAAI,MAAA;AAAA,MAC5C,KAAA,EAAO,MAAA,GAAS,CAAA,6BAAA,EAAgC,QAAQ,CAAA,CAAA,GAAK,MAAA;AAAA,MAC7D,kBAAA,EAAkB,cAAc,MAAA,GAAS,MAAA;AAAA,MACzC,EAAA;AAAA,MACA,IAAA,EAAM,eAAe,SAAA,GAAY,MAAA;AAAA,MACjC,YAAA,EAAY,YAAA,GAAe,MAAA,CAAO,YAAY,CAAA,GAAI,MAAA;AAAA,MAClD,WAAA,EAAW,cAAc,MAAA,GAAS,MAAA;AAAA,MAClC,IAAA;AAAA,MAEC,QAAA,EAAA,WAAA,mBACC,GAAA,CAAC,QAAA,EAAA,EAAS,KAAA,EAAO,EAAA,CAAG,CAAC,CAAA,EAAG,CAAA,mBAExB,GAAA,CAAC,MAAA,EAAA,EAAK,KAAA,EAAM,wBAAA,EAA0B,QAAA,EAAA,IAAA,EAAK;AAAA;AAAA,GAE/C;AAEJ","file":"chunk-V3DX3K65.js","sourcesContent":["import { em } from './css-values.js';\nimport { Skeleton } from './skeleton.js';\n\nexport type ToolbarTextSize =\n 'xlarge' | 'xlarge-fixed' | 'large' | 'default' | 'small' | 'xsmall';\n\n/** ARIA heading level for a title exposed as a heading landmark. */\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\ninterface ToolbarTextBaseProps {\n text: string;\n size?: ToolbarTextSize;\n className?: string;\n /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */\n id?: string;\n /**\n * Expose heading semantics (`role=\"heading\"` + `aria-level`) so the text acts as\n * a heading landmark — e.g. a page's primary title. Omit to keep the plain span\n * (the default), which suits a dialog title referenced via `aria-labelledby`.\n */\n headingLevel?: HeadingLevel;\n /** Render the text as an unanimated loading skeleton instead of its value. */\n placeholder?: boolean;\n /**\n * Show a trailing ellipsis (…) where the text is truncated — on the single line\n * (default), or at the `maxLines` boundary when wrapping. Set false to hard-clip\n * instead. Default true.\n */\n ellipsis?: boolean;\n /**\n * Grow to fill the free space of the flex row it sits in (a Toolbar zone),\n * truncating at the space left by its siblings instead of taking only its\n * text's width. Default false.\n */\n fill?: boolean;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n /**\n * Cap wrapped text to this many lines, truncating past it. Only takes effect with\n * `wrap`; ignored on a single line. `null`/omitted wraps without a line cap. Default null.\n */\n}\n\ntype ToolbarTextWrappingProps =\n | {\n /** Wrap onto multiple lines; combine with `maxLines` to cap them. */\n wrap: true;\n maxLines?: number | null;\n }\n | { wrap?: false; maxLines?: never };\n\nexport type ToolbarTextProps = ToolbarTextBaseProps & ToolbarTextWrappingProps;\n\nexport function ToolbarText({\n text,\n size = 'default',\n className = '',\n id,\n headingLevel,\n placeholder = false,\n wrap = false,\n ellipsis = true,\n maxLines = null,\n fill = false,\n slot,\n}: ToolbarTextProps) {\n const capped = wrap && maxLines != null && maxLines > 0;\n return (\n <span\n class={`kui-toolbar-text ${className}`.trim()}\n data-component=\"toolbar-text\"\n data-size={size}\n data-fill={fill ? 'true' : undefined}\n data-wrap={wrap ? 'true' : undefined}\n data-ellipsis={ellipsis ? undefined : 'false'}\n data-max-lines={capped ? String(maxLines) : undefined}\n style={capped ? `--kui-toolbar-text-max-lines:${maxLines}` : undefined}\n data-placeholder={placeholder ? 'true' : undefined}\n id={id}\n role={headingLevel ? 'heading' : undefined}\n aria-level={headingLevel ? String(headingLevel) : undefined}\n aria-busy={placeholder ? 'true' : undefined}\n slot={slot}\n >\n {placeholder ? (\n <Skeleton width={em(8)} />\n ) : (\n <span class=\"kui-toolbar-text__text\">{text}</span>\n )}\n </span>\n );\n}\n"]}