@adia-ai/a2ui-corpus 0.6.34 → 0.6.36
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.
- package/CHANGELOG.md +16 -0
- package/catalog-a2ui_0_9.json +3699 -333
- package/catalog-a2ui_0_9_rules.txt +134 -4
- package/chunk-embeddings.json +1 -1
- package/package.json +1 -1
|
@@ -64,6 +64,9 @@
|
|
|
64
64
|
- Inline alert/banner for status messages within a content region. Severity via variant (info, success, warn, error).
|
|
65
65
|
- For ephemeral toast notifications use <toast-ui> (or post to <feed-ui>); alert-ui is persistent inline.
|
|
66
66
|
- For modal-style critical alerts use <modal-ui> with alert content.
|
|
67
|
+
- Billing dunning / payment-failed notices use pattern="dunning" + amount + currency + dueAt props, NOT inlined into title/description strings.
|
|
68
|
+
- pattern="dunning" SHOULD use variant="danger" (default) or variant="warning" (grace period); never variant="info" or "success".
|
|
69
|
+
- When pattern="dunning", slot at least one button-ui in slot="actions" with data-dunning-action ("update" or "retry").
|
|
67
70
|
|
|
68
71
|
## Aside
|
|
69
72
|
- Use <aside-ui> as a slot stub inside an IN-PAGE primitive container parent (<card-ui>, <drawer-ui>, <modal-ui>, <page-ui>) for two-column layouts with a semantic side region. It ships no behavior; the parent reads [collapsible] and [width] via @scope. Typical contents: <list-ui> / <tree-ui> / <nav-ui variant="section">.
|
|
@@ -296,12 +299,18 @@
|
|
|
296
299
|
- Set aspect-ratio attribute to lock dimensions and prevent layout shift during load.
|
|
297
300
|
- For decorative-only images, set alt='' and aria-hidden='true' so screen readers skip them.
|
|
298
301
|
|
|
302
|
+
## InlineMessage
|
|
303
|
+
- In-flow annotation under a form input (validation feedback, hint copy, inline confirmation). Severity via [variant] (info, success, warning, danger).
|
|
304
|
+
- For overlay / banner-style notices use <alert-ui> instead; for transient toasts use <toast-ui>.
|
|
305
|
+
- Place inside <field-ui>, <col-ui>, or <row-ui>; never as a page-level banner.
|
|
306
|
+
- Do not nest a focusable child (button, link with action) — InlineMessage is non-interactive annotation.
|
|
307
|
+
|
|
299
308
|
## Input
|
|
300
309
|
- <input-ui> is the canonical single-line text input. The host IS the contenteditable surface — NEVER wrap a native <input>. The sole exception is type="password", which internally uses a real <input type="password"> for masking (per ADR-0025).
|
|
301
310
|
- Wrap <input-ui> in <field-ui label="…" hint="…" error="…"> for the canonical stacked label / hint / error chrome. The inline [label] / [hint] / [error] props are also supported on the primitive for compact use.
|
|
302
311
|
- Form participation is implicit via UIFormElement. Set [name] for FormData submission; [required] / [disabled] / [readonly] reflect; listen for `change` (blur or Enter commit) and `input` (per keystroke). `submit` event fires when Enter commits the value (used by <chat-composer>'s `composer-submit` forwarding).
|
|
303
312
|
- For numeric input use [type="number"] with [min] [max] [step] [precision] [prefix] / [suffix] — this stamps a contenteditable surface + <button-ui> / <icon-ui> stepper column with ARIA spinbutton semantics. Read `el.valueAsNumber` for the parsed Number. Never substitute a native <input type="number">.
|
|
304
|
-
- Inside <chat-composer>, the canonical inner input is <chat-input-ui
|
|
313
|
+
- Inside <chat-composer>, the canonical inner input is <chat-input-ui> (chat variant subclass — adds the send button + model picker + paste-to-attach plumbing). The plain <input-ui> primitive ALSO fires a bubbling `submit` event on Enter (unconditional, no opt-in attribute); <chat-input-ui> simply builds on that semantic.
|
|
305
314
|
|
|
306
315
|
## Inspector
|
|
307
316
|
- Developer-tools pane for A2UI runtime state — composes <tabs-ui> + <code-ui> internally.
|
|
@@ -346,6 +355,15 @@
|
|
|
346
355
|
- For interactive selection lists use <nav-ui> (single-select navigation) or <menu-ui> (action menu); list-ui is content display.
|
|
347
356
|
- For data-grid / sortable / sticky-header needs use <table-ui> instead.
|
|
348
357
|
|
|
358
|
+
## LoadingOverlay
|
|
359
|
+
- <LoadingOverlay> MUST be placed inside a sized container with content (Card, Section, Table body, Chart). It absolutely positions against the nearest positioned ancestor. The parent's CSS must include `position: relative` (or any non-static positioning) — the component does not mutate parent layout styles.
|
|
360
|
+
- Toggle <LoadingOverlay active> from consumer code while async work is in flight. The overlay applies aria-busy="true" to its parent while active; on dismiss, both inert and aria-busy are released.
|
|
361
|
+
- The [delay] grace window (default 200ms) suppresses paint on fast-resolving loads. For server-rendered "definitely-slow" states (>1s known wait) consider [delay="0"] for immediate feedback.
|
|
362
|
+
- Do NOT use as a page-level / viewport loader — use a dedicated route-loader pattern. <LoadingOverlay> is container-scoped.
|
|
363
|
+
- Do NOT nest <LoadingOverlay> inside <Modal> or <Drawer> — those primitives own their own busy state. When they support it, pass [loading] to those directly.
|
|
364
|
+
- Do NOT use <LoadingOverlay> to disable a form during submit — use <Button type="submit" loading> for the submit affordance.
|
|
365
|
+
- The default slot accepts any busy indicator. When empty, a centered <Spinner size="lg"> is auto-stamped. Slot a <Skeleton> stack for placeholder-shaped loading; slot <Progress value=…> when the wait is determinate.
|
|
366
|
+
|
|
349
367
|
## MenuDivider
|
|
350
368
|
- <menu-divider-ui> MUST be a direct child of <menu-ui>; a raw <hr> will render OUTSIDE the popover because <menu-ui> only hoists <menu-item-ui> and <menu-divider-ui> children via its direct-child selector.
|
|
351
369
|
- Use to group items by semantic tier — primary actions → secondary → destructive (danger). Avoid leading / trailing dividers and consecutive dividers; they produce visual noise without grouping value.
|
|
@@ -506,6 +524,8 @@
|
|
|
506
524
|
- For dynamic option lists rendered inside <editor-shell>, set the JSON via the [data-options] attribute — <editor-shell>'s wireSelects() finds select-ui[data-options], JSON.parses the attribute, and assigns `.options` on connect. Useful for static-HTML toolbars where JS hydration would be awkward.
|
|
507
525
|
- <select-ui> owns its own label / hint / error chrome (via [label] / [hint] / [error] props). Only wrap in <field-ui> when you need to share the field-chrome stack with sibling inputs in the same form row group.
|
|
508
526
|
- Enable [searchable] for > 10 options; add [free-text] only when unmatched values are valid (tag entry, email-with-suggestion). Use [multiple searchable] for multi-select rather than authoring a separate multi-select primitive.
|
|
527
|
+
- For multi-select, set [multiple] — the trigger automatically renders <tag-ui> chips per selected option; the popover renders checkbox-style option rows where clicks toggle membership without closing. Form value is comma-separated under [name]. There is NO `<multi-select-ui>` tag — that name does not exist.
|
|
528
|
+
- Multi-select bulk controls: [select-all] renders a "Select all" / "Clear" control above the option list; [clearable] adds a clear-all `x` affordance to the trigger. [max-chips] caps the visible chip count and renders "+N more". [min] / [max] gate form validity.
|
|
509
529
|
|
|
510
530
|
## Skeleton
|
|
511
531
|
- Use to placeholder content during loading. Shape via CSS sizing (width/height/border-radius); shimmer is automatic.
|
|
@@ -604,6 +624,14 @@
|
|
|
604
624
|
- Use [size="sm"] for inline-with-text contexts (doc page headers, table cells, badges next to titles); [size="md"] (default) for filter-bar chips and standalone tag rows.
|
|
605
625
|
- Group multiple tags inside a <row-ui gap="2"> — never stack them vertically; vertical lists of dismissable items are an <action-list-ui> use case, not <tag-ui>.
|
|
606
626
|
|
|
627
|
+
## TagsInput
|
|
628
|
+
- Use <tags-input-ui> for OPEN-SET free-form token entry — labels, keywords, email recipients, comma-separated lists. For CLOSED option sets (pick N from a fixed list), use <select-ui multiple> (SPEC-040) — that primitive gates the value against `options[]`.
|
|
629
|
+
- `TagsInput.value` MUST be a string array. Pass `["a","b"]`, not the comma-joined string `"a,b"`. The host parses string-form `value` attributes as JSON; non-array shapes throw `invalid`.
|
|
630
|
+
- `delimiter: "enter"` disables in-line character commits; only the Enter key (or programmatic `addToken`) commits. Use this when the token grammar legitimately includes the default `,`.
|
|
631
|
+
- `unique: true` (default) silently coalesces accidental duplicate adds. Do NOT emit `invalid` for those — the contract specifies silent dedup for ergonomic typing.
|
|
632
|
+
- `validateFn` is a JS property, NOT serializable in A2UI JSON. Wire validators post-mount via DOM scripting; A2UI authors should not expect to declare a validator in the JSON payload.
|
|
633
|
+
- Chips are rendered automatically from `value`. Do NOT slot individual <tag-ui> children manually — that decouples the rendered DOM from the form value and breaks Backspace removal.
|
|
634
|
+
|
|
607
635
|
## Text
|
|
608
636
|
- Use for typographic content with semantic role (heading, body, label, caption). Variant attribute sets the role.
|
|
609
637
|
- For inline-flow rich content with multiple paragraphs, use <richtext-ui> instead.
|
|
@@ -614,7 +642,7 @@
|
|
|
614
642
|
- Wrap <textarea-ui> in <field-ui label="…" hint="…" error="…"> for the canonical labeled stack. The inline [label] / [hint] / [error] props are also supported on the primitive for compact use.
|
|
615
643
|
- Form participation is implicit via UIFormElement. Set [name] for FormData submission; [required] / [disabled] / [readonly] reflect; listen for `change` (on blur after value change) and `input` (per keystroke).
|
|
616
644
|
- Use [rows] to set initial height (default 3) and [resize] (vertical | horizontal | both | none; default vertical) to control user resize. Never substitute a native <textarea> just to get rows / resize.
|
|
617
|
-
- Enter inserts a newline
|
|
645
|
+
- Enter (without Shift) dispatches a bubbling `submit` event; Shift+Enter inserts a newline. This is unconditional — there is no opt-in/opt-out attribute. For chat composer surfaces wrap inside <chat-composer> + <chat-input-ui> (which adds the send button + model picker + paste-to-attach plumbing on top of the same Enter→submit semantics).
|
|
618
646
|
|
|
619
647
|
## TimePicker
|
|
620
648
|
- `<time-picker-ui>` is the canonical standalone time-of-day picker. Per ADR-0025 NEVER wrap a native `<input type="time">` — segments are contenteditable spans + ElementInternals provides form participation.
|
|
@@ -690,8 +718,57 @@
|
|
|
690
718
|
- Multiple attribute enables multi-file selection; accept= constrains file types.
|
|
691
719
|
- For agent chat attachments use <chat-composer-ui>'s built-in upload affordance instead.
|
|
692
720
|
|
|
721
|
+
## BillingOverview
|
|
722
|
+
- BillingOverview.account is REQUIRED. The composite is meaningless without an account snapshot; emitting BillingOverview with null account and no data-stream-src renders the empty state.
|
|
723
|
+
- account.status MUST be one of `active` | `trialing` | `past_due` | `canceled` | `paused`. Unknown values render neutral chrome.
|
|
724
|
+
- account.dunning is REQUIRED when account.status is `past_due` (otherwise the dunning banner cannot render). Shape: `{amount, currency, dueAt, cardLast4?, reason?}`. The composite stamps `<alert-ui pattern="dunning">` per SPEC-006.
|
|
725
|
+
- account.plans is REQUIRED when [variant] is `full` (otherwise the plan-picker section has nothing to render). Omit `plans` when [variant] is `compact` or `enterprise` to skip the plan-picker section.
|
|
726
|
+
- account.paymentMethods is forwarded verbatim to <payment-method-list-ui>; account.invoices is forwarded verbatim to <invoice-history-ui>. Their shapes are owned by SPEC-010 and SPEC-008 respectively.
|
|
727
|
+
- BillingOverview MUST NOT be nested inside Modal or Drawer. The dashboard is a route, not a modal — Modal traps focus + clips on long content; this surface is designed for full-page layout.
|
|
728
|
+
- Wire one listener to `account-action` instead of subscribing to every child primitive's event. The composite normalises plan-action / payment-action / invoice-action / dunning-action into the single bubbling event.
|
|
729
|
+
|
|
730
|
+
## InvoiceDetail
|
|
731
|
+
- InvoiceDetail MUST set `invoice` OR `data-stream-src`. Neither produces the empty state but cannot be the submission state for a billing surface.
|
|
732
|
+
- invoice.lines[] MUST contain at least one row with `description`, `qty`, `unitAmount`, `amount`. Empty lines arrays render the lines table empty-state row, not the host empty state.
|
|
733
|
+
- invoice.total MUST equal subtotal + tax − (discount or 0). The composite renders the host-supplied total verbatim; consumers are authoritative on rounding rules per locale (SPEC-007 OD-001).
|
|
734
|
+
- invoice.status MUST be one of draft / open / paid / past-due / void. Unknown statuses render the default badge variant + a one-shot console.warn.
|
|
735
|
+
- InvoiceDetail MUST NOT be nested inside Modal or Drawer. Invoices are routes, not modals — Modal traps focus + clips on long content; the composite is designed for full-page layout. Use `<a href="/invoices/{number}">` instead.
|
|
736
|
+
- Slot ALL header actions explicitly via `slot="header-actions"` — the composite NEVER stamps a default toolbar (SPEC-007 OD-002). Action sets vary across products; default is too opinionated.
|
|
737
|
+
|
|
738
|
+
## InvoiceHistory
|
|
739
|
+
- InvoiceHistory.invoices MUST be a non-empty array on commit. Empty arrays render the empty-state (no rows).
|
|
740
|
+
- invoices[].status MUST be one of "draft" | "open" | "paid" | "past-due" | "void". Unknown values fall back to a neutral badge variant.
|
|
741
|
+
- InvoiceHistory MUST NOT receive a `columns` prop — columns are owned by the composite. Consumers needing custom columns should compose <table-ui> directly with their own column array.
|
|
742
|
+
- `hrefPattern` is a string template with `{number}` interpolation. Consumers needing dynamic resolution (query-strings, function- based routes) should preventDefault on `invoice-row-click` and route themselves.
|
|
743
|
+
- Pair with `InvoiceDetail` (SPEC-007) at the route resolved by `hrefPattern` so the row-click destination exists.
|
|
744
|
+
|
|
745
|
+
## PaymentMethodForm
|
|
746
|
+
- PaymentMethodForm SHOULD be wrapped in a <Form> so its form-value (the resolved token) is delivered on submission. Standalone use requires the consumer wire `tokenize()` from a custom submit handler.
|
|
747
|
+
- PaymentMethodForm MUST NOT receive a `value` prop carrying raw card digits. The form value is the tokenized output; raw card digits live only inside the form's sub-fields and are never mirrored to `value`.
|
|
748
|
+
- PaymentMethodForm SHOULD only be used in test / demo / non-PCI surfaces. Production card-capture flows MUST tokenize via a real payment processor (Stripe Elements, Braintree, Adyen) and consume only the resulting token — this primitive is the form chrome + structured validation, not a tokenization provider.
|
|
749
|
+
- Wrapping PaymentMethodForm inside <Field> duplicates the label and corrupts the grid layout. PaymentMethodForm IS a field group; the host carries `aria-label` directly.
|
|
750
|
+
- `countries` SHOULD list every ISO 3166-1 alpha-2 code your product accepts. Empty arrays fall back to a default short list (US, CA, GB, DE, FR, AU) suitable for early-stage demos only — production deployments should set this explicitly.
|
|
751
|
+
|
|
752
|
+
## PaymentMethodList
|
|
753
|
+
- PaymentMethodList.methods MUST be an array. Empty array is valid and renders the empty-state.
|
|
754
|
+
- methods[].id MUST be unique within the list. Duplicate ids cause row collision at diff time.
|
|
755
|
+
- Exactly one record in `methods` MAY have `default: true`. If none does, the primitive marks the first row as default on connect.
|
|
756
|
+
- methods[].brand SHOULD be from the enumerated set (visa | mastercard | amex | discover | jcb | unionpay | diners | paypal | apple-pay | google-pay | ach | sepa). Unknown brands fall back to a generic credit-card mark.
|
|
757
|
+
- methods[].type MUST be `card`, `bank`, or `wallet`. Drives the brand-icon fallback when `brand` is unknown.
|
|
758
|
+
- `value` wins on conflict with methods[].default — if both are set and disagree, value is the source of truth and the primitive emits `change` to reconcile.
|
|
759
|
+
- Mutations are events, not props. Wire `change` / `add` / `remove` / `select` to your billing API; the primitive does NOT write to the underlying data source.
|
|
760
|
+
|
|
761
|
+
## PlanPicker
|
|
762
|
+
- PlanPicker.plans MUST be a non-empty array on commit. Empty arrays render the empty-state but cannot be the submission state.
|
|
763
|
+
- At most ONE plans[] record may carry `recommended: true`. Two recommended plans are visually ambiguous and defeat the treatment's purpose.
|
|
764
|
+
- `current` MUST match a plans[].id or be empty. Unknown ids fail silently — no anchor row renders, no current-state contextualization on siblings.
|
|
765
|
+
- plans[].prices.monthly is REQUIRED on every record. annual is optional; if absent and cycle="annual" the card renders the monthly price with the annual cycle suffix and no `note`.
|
|
766
|
+
- `cycle` MUST be `monthly` or `annual`. Other values are coerced to `monthly` and a one-shot console.warn fires.
|
|
767
|
+
- Use `layout="list"` for in-app settings panels where the picker sits inside a <card-ui> and width is constrained. Use the default `layout="grid"` for marketing pricing pages.
|
|
768
|
+
- Wire `select` (not `change`) to the actual commit handler — change fires on selection without commit (keyboard focus traversal, programmatic value-set); select fires when the user clicks the CTA inside a card.
|
|
769
|
+
|
|
693
770
|
## ChatComposer
|
|
694
|
-
- chat-composer is the bespoke replacement for legacy <chat-input-ui data-chat-input> inside <chat-shell>. Place an inner <chat-input-ui
|
|
771
|
+
- chat-composer is the bespoke replacement for legacy <chat-input-ui data-chat-input> inside <chat-shell>. Place an inner <chat-input-ui> as the primary input — it emits `submit` on Enter without Shift (unconditional; delegated from the inner textarea).
|
|
695
772
|
- The host listens for 'composer-submit' on the composer (not on the inner input). The event detail mirrors the inner submit event so existing handlers Just Work.
|
|
696
773
|
- Default slot holds a single <chat-input-ui> child; trailing/attach/leading slots host action buttons (send, attach, model picker).
|
|
697
774
|
- For non-chat input surfaces (forms, prompts, search) use <chat-input-ui> directly without the composer wrapper.
|
|
@@ -707,7 +784,7 @@
|
|
|
707
784
|
- For admin-shell-style chrome bars inside chat-shell, use <admin-topbar> instead — chat-header is for in-chat metadata only.
|
|
708
785
|
|
|
709
786
|
## ChatShell
|
|
710
|
-
- chat-shell takes bespoke chat-* children only. The canonical composition is <chat-thread> (with optional first-child <chat-empty>) followed by <chat-composer> wrapping a <chat-input-ui
|
|
787
|
+
- chat-shell takes bespoke chat-* children only. The canonical composition is <chat-thread> (with optional first-child <chat-empty>) followed by <chat-composer> wrapping a <chat-input-ui>. Add <chat-header> / <chat-sidebar> / <chat-status> as needed.
|
|
711
788
|
- Don't nest col-ui / row-ui or generic layout primitives directly inside chat-shell — the shell's CSS reads child tag selectors to lay them out. Generic layout goes inside the bespoke children.
|
|
712
789
|
- The shell listens for 'composer-submit' on <chat-composer> (not on the inner input). Streaming state is reflected on this host and propagates to <chat-thread>[streaming] + <chat-composer>[disabled] automatically — don't toggle child attributes manually.
|
|
713
790
|
- Legacy data-attribute shapes were retired in v0.4.0 per ADR-0024. Do not author <section data-chat-messages>, <chat-input-ui data-chat-input>, <empty-state-ui data-chat-empty>, or <header data-chat-name> inside chat-shell.
|
|
@@ -729,6 +806,29 @@
|
|
|
729
806
|
- Different from primitive <chat-thread-ui>: chat-thread (no -ui suffix) is the module-tier shell-aware version with scroll-on-new-message + load-more + empty-state coordination.
|
|
730
807
|
- Hosts message blocks (typically agent/user message rows) as default-slot children; <chat-empty> goes as first child for the empty state.
|
|
731
808
|
|
|
809
|
+
## DashboardLayout
|
|
810
|
+
- DashboardLayout MUST receive children via the four named slots
|
|
811
|
+
(toolbar / kpis / charts / table) plus the optional aside.
|
|
812
|
+
Children without a [slot] attribute will not render in any band.
|
|
813
|
+
|
|
814
|
+
- DashboardLayout[kpi-columns] MUST be 2-6. Out-of-range values
|
|
815
|
+
fall back to the default 4. Eight cards in a row is too narrow
|
|
816
|
+
to be legible at typical container widths; container queries
|
|
817
|
+
collapse the band to 2 columns ≤48em and 1 column ≤32em.
|
|
818
|
+
|
|
819
|
+
- DashboardLayout[chart-split] MUST be one of the documented
|
|
820
|
+
enum strings — "" (full-width) / 2 / 2:1 / 3:2 / 3:1.
|
|
821
|
+
|
|
822
|
+
- A DashboardLayout SHOULD sit inside AdminPageBody for the
|
|
823
|
+
canonical chrome stack. Top-level placement is allowed for
|
|
824
|
+
surface-only demos but not the production shape.
|
|
825
|
+
|
|
826
|
+
- A DashboardLayout MUST NOT contain another DashboardLayout as
|
|
827
|
+
a descendant. Two parametric-density containers fight over the
|
|
828
|
+
--a-density cascade. For nested dashboards use sibling
|
|
829
|
+
DashboardLayouts switched by Tabs.
|
|
830
|
+
|
|
831
|
+
|
|
732
832
|
## DateRangeSelector
|
|
733
833
|
- DateRangeSelector MUST sit inside a toolbar context — typically a
|
|
734
834
|
Row inside DashboardLayout.slot=toolbar, or directly inside
|
|
@@ -818,6 +918,36 @@ submission. With [name], FormData carries the value "{from}:{to}".
|
|
|
818
918
|
- Hosts <chat-thread-ui>, <canvas-ui>, and <inspector-ui> children via named slots — chat slot for the conversation lane, canvas slot for the artifact lane, inspector slot for the dev-tools lane.
|
|
819
919
|
- Mode attribute (chat-only|split|canvas-only) controls layout; transitions are CSS-animated.
|
|
820
920
|
|
|
921
|
+
## IntegrationsPage
|
|
922
|
+
- Use <integrations-page-ui> for the canonical Settings >
|
|
923
|
+
Integrations grid. Supply the `integrations` array — the
|
|
924
|
+
composite owns the search + grouping + grid breakpoints +
|
|
925
|
+
empty state.
|
|
926
|
+
|
|
927
|
+
- `integrations` MUST be a non-empty array OR the page renders
|
|
928
|
+
the empty-state. Each item MUST satisfy the SPEC-062 card
|
|
929
|
+
prop contract (provider + name required).
|
|
930
|
+
|
|
931
|
+
- Do NOT slot <integration-card-ui> children directly — the page
|
|
932
|
+
generates cards from the `integrations` prop. Slotted children
|
|
933
|
+
are ignored.
|
|
934
|
+
|
|
935
|
+
- Do NOT nest <integrations-page-ui> inside another
|
|
936
|
+
<integrations-page-ui>.
|
|
937
|
+
|
|
938
|
+
- For one-off integration tile groups (e.g., a single recommended
|
|
939
|
+
provider on a marketing page) use <integration-card-ui> directly
|
|
940
|
+
inside <grid-ui>; reach for the page composite only when search
|
|
941
|
+
+ grouping + empty-state semantics are needed.
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
## NotificationPreferences
|
|
945
|
+
- NotificationPreferences.channels MUST be non-empty. A matrix with zero columns renders nothing meaningful — author at least one channel record.
|
|
946
|
+
- NotificationPreferences.preferences[].channels keys MUST match the channels[].key set. Mismatched keys silently render the cell as `false` (off) — they do NOT raise an error, but the user can never toggle them into existence either.
|
|
947
|
+
- The composite is fully CONTROLLED — toggling a cell does NOT mutate `preferences`. The host MUST listen for `change` (and `bulk-toggle` for the column master), apply the change to its own state, then re-pass the new `preferences` array. Failing to do this leaves the toggle visually flipping back to its prior state on the next render.
|
|
948
|
+
- For binary on/off cell toggles, use <check-ui> semantics — NOT <switch-ui>. The column-header master IS a <switch-ui> (because it's a "setting" — flip the whole column), but each cell is a multi-select-style checkbox.
|
|
949
|
+
- Use `group-by="group"` only when `preferences[]` records carry a stable `group` field. Mixing grouped + ungrouped rows in the same matrix produces inconsistent section heights.
|
|
950
|
+
|
|
821
951
|
## AdminCommand
|
|
822
952
|
- admin-command wraps a native <dialog>; the inner <command-ui> is the actual palette. Keyboard shortcut defaults to both Cmd+K (mac) and Ctrl+K (other) — the AdiaUI convention.
|
|
823
953
|
- Place admin-command as a direct child of admin-shell, NOT inside a sidebar or main column. The host coordinates triggers ([data-command-trigger]) by reaching across siblings.
|