@adia-ai/web-modules 0.8.36 → 0.8.37

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 (89) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +3 -3
  3. package/agent-admin/admin-roster/admin-roster.js +1 -1
  4. package/agent-admin/admin-settings/admin-settings.js +1 -1
  5. package/agent-admin/agent-admin/agent-admin.js +1 -1
  6. package/billing/billing-overview/billing-overview.a2ui.json +281 -0
  7. package/billing/billing-overview/billing-overview.class.js +838 -0
  8. package/billing/billing-overview/billing-overview.css +223 -0
  9. package/billing/billing-overview/billing-overview.js +16 -0
  10. package/billing/billing-overview/billing-overview.yaml +441 -0
  11. package/billing/index.js +21 -0
  12. package/billing/invoice-detail/invoice-detail.a2ui.json +276 -0
  13. package/billing/invoice-detail/invoice-detail.class.js +711 -0
  14. package/billing/invoice-detail/invoice-detail.css +305 -0
  15. package/billing/invoice-detail/invoice-detail.js +16 -0
  16. package/billing/invoice-detail/invoice-detail.yaml +411 -0
  17. package/billing/invoice-history/invoice-history.a2ui.json +315 -0
  18. package/billing/invoice-history/invoice-history.class.js +672 -0
  19. package/billing/invoice-history/invoice-history.css +130 -0
  20. package/billing/invoice-history/invoice-history.js +16 -0
  21. package/billing/invoice-history/invoice-history.yaml +398 -0
  22. package/billing/payment-method-form/payment-method-form.a2ui.json +310 -0
  23. package/billing/payment-method-form/payment-method-form.class.js +886 -0
  24. package/billing/payment-method-form/payment-method-form.css +149 -0
  25. package/billing/payment-method-form/payment-method-form.js +16 -0
  26. package/billing/payment-method-form/payment-method-form.yaml +430 -0
  27. package/billing/payment-method-list/payment-method-list.a2ui.json +283 -0
  28. package/billing/payment-method-list/payment-method-list.class.js +705 -0
  29. package/billing/payment-method-list/payment-method-list.css +128 -0
  30. package/billing/payment-method-list/payment-method-list.js +16 -0
  31. package/billing/payment-method-list/payment-method-list.yaml +355 -0
  32. package/billing/plan-picker/plan-picker.a2ui.json +349 -0
  33. package/billing/plan-picker/plan-picker.class.js +635 -0
  34. package/billing/plan-picker/plan-picker.css +227 -0
  35. package/billing/plan-picker/plan-picker.js +16 -0
  36. package/billing/plan-picker/plan-picker.yaml +453 -0
  37. package/chat/README.md +1 -1
  38. package/chat/chat-composer/chat-composer.js +2 -2
  39. package/chat/chat-empty/chat-empty.js +2 -2
  40. package/chat/chat-shell/chat-shell.js +4 -4
  41. package/chat/chat-sidebar/chat-sidebar.js +2 -2
  42. package/chat/chat-surfaces/chat-surfaces.js +2 -2
  43. package/chat/chat-thread/chat-thread.js +2 -2
  44. package/dashboard/dashboard-layout/dashboard-layout.a2ui.json +254 -0
  45. package/dashboard/dashboard-layout/dashboard-layout.css +190 -0
  46. package/dashboard/dashboard-layout/dashboard-layout.js +289 -0
  47. package/dashboard/dashboard-layout/dashboard-layout.yaml +374 -0
  48. package/dashboard/date-range-selector/date-range-selector.a2ui.json +236 -0
  49. package/dashboard/date-range-selector/date-range-selector.css +84 -0
  50. package/dashboard/date-range-selector/date-range-selector.js +686 -0
  51. package/dashboard/date-range-selector/date-range-selector.yaml +358 -0
  52. package/dashboard/index.js +14 -0
  53. package/dist/everything.min.js +1 -1
  54. package/editor/editor-canvas/editor-canvas.js +2 -2
  55. package/editor/editor-shell/editor-shell.js +2 -2
  56. package/editor/editor-sidebar/editor-sidebar.js +2 -2
  57. package/editor/editor-toolbar/editor-toolbar.js +2 -2
  58. package/feedback/confirm-dialog/confirm-dialog.a2ui.json +206 -0
  59. package/feedback/confirm-dialog/confirm-dialog.css +157 -0
  60. package/feedback/confirm-dialog/confirm-dialog.js +383 -0
  61. package/feedback/confirm-dialog/confirm-dialog.yaml +297 -0
  62. package/feedback/index.js +10 -0
  63. package/form/form-popover/form-popover.js +1 -1
  64. package/generative/index.d.ts +1 -1
  65. package/generative/index.js +2 -2
  66. package/onboarding/index.js +1 -0
  67. package/onboarding/onboarding-checklist/onboarding-checklist.a2ui.json +263 -0
  68. package/onboarding/onboarding-checklist/onboarding-checklist.css +157 -0
  69. package/onboarding/onboarding-checklist/onboarding-checklist.js +428 -0
  70. package/onboarding/onboarding-checklist/onboarding-checklist.yaml +236 -0
  71. package/package.json +99 -6
  72. package/runtime/a2ui-root/a2ui-root.js +3 -3
  73. package/runtime/gen-root/gen-root.js +5 -5
  74. package/settings/index.js +12 -0
  75. package/settings/integrations-page/integrations-page.a2ui.json +274 -0
  76. package/settings/integrations-page/integrations-page.css +177 -0
  77. package/settings/integrations-page/integrations-page.d.ts +77 -0
  78. package/settings/integrations-page/integrations-page.js +413 -0
  79. package/settings/integrations-page/integrations-page.yaml +317 -0
  80. package/settings/notification-preferences/notification-preferences.a2ui.json +245 -0
  81. package/settings/notification-preferences/notification-preferences.css +204 -0
  82. package/settings/notification-preferences/notification-preferences.js +622 -0
  83. package/settings/notification-preferences/notification-preferences.yaml +358 -0
  84. package/shell/admin-command/admin-command.js +2 -2
  85. package/shell/admin-shell/admin-shell.js +2 -2
  86. package/shell/admin-sidebar/admin-sidebar.js +2 -2
  87. package/shell/embed-shell/embed-shell.js +3 -3
  88. package/simple/simple-shell/simple-shell.js +1 -1
  89. package/theme/theme-panel/theme-panel.js +2 -2
@@ -0,0 +1,317 @@
1
+ # Edit this file; run `npm run build:components` to regenerate a2ui.json.
2
+ $schema: ../../../../scripts/schemas/component.yaml.schema.json
3
+ name: UIIntegrationsPage
4
+ tag: integrations-page-ui
5
+ status: stable
6
+ component: IntegrationsPage
7
+ category: container
8
+ version: 1
9
+ description: |
10
+ Module-tier Settings page composite — a searchable, category-grouped
11
+ grid of <integration-card-ui> tiles (SPEC-062). Owns the
12
+ `integrations` array, the search-filter state, the category /
13
+ status grouping, the responsive grid breakpoints, and the empty-state
14
+ copy. Card-level events (`connect` / `configure` / `disconnect` /
15
+ `retry`) bubble up unchanged with `provider` in detail; the page also
16
+ emits `search` (debounced) and a top-bar `filter-change` when the
17
+ user picks a status chip.
18
+
19
+ Lives in `packages/web-modules/settings/integrations-page/` per the
20
+ three-tier architecture — composes multiple primitives, holds layout
21
+ responsibility, consumed by the settings shell. Replaces the
22
+ hand-rolled `settings/integrations` HTML template per consumer.
23
+
24
+ Spec: .claude/docs/specs/implementation-ready/SPEC-063-integrations-page.md
25
+
26
+ # Per ADR-0027 — composites that programmatically create primitives in
27
+ # their render() do NOT auto-import those primitives. Consumer (or demo
28
+ # shell) must explicitly import each composed primitive.
29
+ composes:
30
+ - integration-card-ui
31
+ - grid-ui
32
+ - search-ui
33
+ - segmented-ui
34
+ - segment-ui
35
+ - text-ui
36
+
37
+ props:
38
+ integrations:
39
+ description: |
40
+ Array of integration descriptors — each item conforms to the
41
+ SPEC-062 <integration-card-ui> prop shape:
42
+ `{ provider, name, logo?, description?, status, category?,
43
+ error-message? }`. Empty array renders the empty-state slot.
44
+ type: array
45
+ default: []
46
+ required: true
47
+ dynamic: true
48
+ loading:
49
+ description: |
50
+ Renders a skeleton grid (6 tiles) instead of cards. Driven by the
51
+ consumer while the integrations payload is in flight.
52
+ type: boolean
53
+ default: false
54
+ reflect: true
55
+ dynamic: true
56
+ search-placeholder:
57
+ description: |
58
+ Placeholder copy for the built-in search input. Defaults to
59
+ "Search integrations".
60
+ type: string
61
+ default: "Search integrations"
62
+ reflect: true
63
+ group-by:
64
+ description: |
65
+ How to bucket cards into sections. `category` groups by the per-item
66
+ `category` key; `status` groups by connection state; `none` renders
67
+ a single ungrouped grid.
68
+ type: string
69
+ default: category
70
+ enum:
71
+ - category
72
+ - status
73
+ - none
74
+ reflect: true
75
+ status-filter:
76
+ description: |
77
+ Optional status filter — restricts the visible cards to one status.
78
+ `all` (default) shows everything; the rest restrict to that status
79
+ bucket. Reflects so consumers can deep-link.
80
+ type: string
81
+ default: all
82
+ enum:
83
+ - all
84
+ - available
85
+ - connected
86
+ - error
87
+ - coming-soon
88
+ reflect: true
89
+ dynamic: true
90
+
91
+ events:
92
+ connect:
93
+ description: Bubbled from a child <integration-card-ui> when its primary button is pressed while `status="available"`.
94
+ detail:
95
+ provider:
96
+ type: string
97
+ description: The provider key of the card that fired.
98
+ configure:
99
+ description: Bubbled from a child <integration-card-ui> while `status="connected"`.
100
+ detail:
101
+ provider:
102
+ type: string
103
+ description: The provider key of the card that fired.
104
+ disconnect:
105
+ description: Bubbled from a child card's overflow menu disconnect item.
106
+ detail:
107
+ provider:
108
+ type: string
109
+ description: The provider key of the card that fired.
110
+ retry:
111
+ description: Bubbled from a child <integration-card-ui> while `status="error"`.
112
+ detail:
113
+ provider:
114
+ type: string
115
+ description: The provider key of the card that fired.
116
+ search:
117
+ description: Debounced search input change.
118
+ detail:
119
+ query:
120
+ type: string
121
+ description: The current search query (lower-cased, trimmed).
122
+ filter-change:
123
+ description: User clicked a status filter chip in the top bar.
124
+ detail:
125
+ status:
126
+ type: string
127
+ description: The newly-selected status filter (`all` resets).
128
+
129
+ slots:
130
+ header:
131
+ description: |
132
+ Optional page header (kicker / title / subtitle). Overrides the
133
+ default rendering — when supplied the composite stamps no
134
+ built-in heading.
135
+ empty:
136
+ description: |
137
+ Optional override for the empty-state copy. When supplied, replaces
138
+ the built-in "No integrations match" / "No integrations available"
139
+ messages.
140
+
141
+ states:
142
+ - name: idle
143
+ description: Default; renders all groups matching the current filter.
144
+ - name: loading
145
+ description: Skeleton grid; cards hidden.
146
+ attribute: loading
147
+ - name: empty-search
148
+ description: Search query yields zero cards.
149
+ attribute: data-empty-search
150
+ - name: empty-data
151
+ description: "`integrations` array is empty."
152
+ attribute: data-empty-data
153
+
154
+ traits: []
155
+
156
+ tokens:
157
+ --integrations-page-bg:
158
+ description: Page background. Defaults to `var(--a-bg)`.
159
+ default: var(--a-bg)
160
+ --integrations-page-fg:
161
+ description: Primary foreground. Defaults to `var(--a-fg)`.
162
+ default: var(--a-fg)
163
+ --integrations-page-fg-muted:
164
+ description: Secondary foreground for empty-state copy + section meta.
165
+ default: var(--a-fg-muted)
166
+ --integrations-page-px:
167
+ description: Horizontal padding around the page.
168
+ default: var(--a-space-5)
169
+ --integrations-page-py:
170
+ description: Vertical padding around the page.
171
+ default: var(--a-space-5)
172
+ --integrations-page-gap:
173
+ description: Vertical gap between toolbar / sections / empty-state.
174
+ default: var(--a-space-5)
175
+ --integrations-page-section-gap:
176
+ description: Vertical gap between sibling category sections.
177
+ default: var(--a-space-6)
178
+ --integrations-page-card-gap:
179
+ description: Gap inside the grid between cards.
180
+ default: var(--a-space-4)
181
+ --integrations-page-grid-min:
182
+ description: Minimum card width for the auto-fill grid track.
183
+ default: var(--a-space-12)
184
+ --integrations-page-section-heading-fg:
185
+ description: Section heading color.
186
+ default: var(--a-fg-strong)
187
+ --integrations-page-section-heading-size:
188
+ description: Section heading font size.
189
+ default: var(--a-ui-sm)
190
+ --integrations-page-section-heading-weight:
191
+ description: Section heading font weight.
192
+ default: var(--a-weight-medium)
193
+
194
+ a2ui:
195
+ rules:
196
+ - rule: |
197
+ Use <integrations-page-ui> for the canonical Settings >
198
+ Integrations grid. Supply the `integrations` array — the
199
+ composite owns the search + grouping + grid breakpoints +
200
+ empty state.
201
+ reason: One composite replaces the per-app hand-rolled grid.
202
+ - rule: |
203
+ `integrations` MUST be a non-empty array OR the page renders
204
+ the empty-state. Each item MUST satisfy the SPEC-062 card
205
+ prop contract (provider + name required).
206
+ reason: Cards generated from data; missing required keys break a11y wiring.
207
+ - rule: |
208
+ Do NOT slot <integration-card-ui> children directly — the page
209
+ generates cards from the `integrations` prop. Slotted children
210
+ are ignored.
211
+ reason: Single source of truth (prop), not mixed slot + data.
212
+ - rule: |
213
+ Do NOT nest <integrations-page-ui> inside another
214
+ <integrations-page-ui>.
215
+ reason: Composite carries page-level layout; nesting double-pads + double-toolbars.
216
+ - rule: |
217
+ For one-off integration tile groups (e.g., a single recommended
218
+ provider on a marketing page) use <integration-card-ui> directly
219
+ inside <grid-ui>; reach for the page composite only when search
220
+ + grouping + empty-state semantics are needed.
221
+ reason: Capacity guidance — page is a Settings construct.
222
+
223
+ anti_patterns:
224
+ - wrong: |
225
+ {"component": "IntegrationsPage", "children": [
226
+ {"component": "IntegrationCard", "provider": "slack", "name": "Slack"}
227
+ ]}
228
+ why: |
229
+ Slotted cards are ignored — the page generates cards from the
230
+ `integrations` prop. The page renders empty.
231
+ fix: |
232
+ {"component": "IntegrationsPage", "integrations": [
233
+ {"provider": "slack", "name": "Slack", "status": "available"}
234
+ ]}
235
+ - wrong: |
236
+ {"component": "Page", "children": [
237
+ {"component": "Grid", "children": [
238
+ {"component": "IntegrationCard", "provider": "slack", "name": "Slack"}
239
+ ]}
240
+ ]}
241
+ why: |
242
+ Re-implements the page composite with primitives. Loses search,
243
+ grouping, and empty-state semantics.
244
+ fix: |
245
+ {"component": "IntegrationsPage", "integrations": [
246
+ {"provider": "slack", "name": "Slack", "status": "available"}
247
+ ]}
248
+
249
+ examples:
250
+ - name: category-grouped
251
+ description: Full integrations page grouped by category (default).
252
+ a2ui: |
253
+ [
254
+ {
255
+ "id": "page",
256
+ "component": "IntegrationsPage",
257
+ "group-by": "category",
258
+ "integrations": [
259
+ {"provider": "slack", "name": "Slack", "logo": "/integrations/slack.svg",
260
+ "description": "Notifications and replies in channels.",
261
+ "status": "connected", "category": "Communication"},
262
+ {"provider": "github", "name": "GitHub", "logo": "/integrations/github.svg",
263
+ "description": "Sync issues and pull requests.",
264
+ "status": "available", "category": "Developer Tools"},
265
+ {"provider": "stripe", "name": "Stripe", "logo": "/integrations/stripe.svg",
266
+ "description": "Charge customers and reconcile invoices.",
267
+ "status": "error", "category": "Payments",
268
+ "error-message": "Token expired — re-authenticate."}
269
+ ]
270
+ }
271
+ ]
272
+ - name: loading-skeleton
273
+ description: Loading state — 6 skeleton tiles, no cards.
274
+ a2ui: |
275
+ [{"id": "page", "component": "IntegrationsPage", "loading": true}]
276
+ - name: empty-data
277
+ description: Empty state — no integrations available.
278
+ a2ui: |
279
+ [{"id": "page", "component": "IntegrationsPage", "integrations": []}]
280
+
281
+ keywords:
282
+ - integrations
283
+ - integration
284
+ - settings
285
+ - settings-page
286
+ - connections
287
+ - third-party
288
+ - oauth
289
+ - providers
290
+ - app-directory
291
+ - marketplace
292
+
293
+ synonyms:
294
+ integrations:
295
+ - integration
296
+ - connections
297
+ - apps
298
+ - providers
299
+ settings:
300
+ - settings
301
+ - account
302
+ - admin
303
+ marketplace:
304
+ - integrations
305
+ - app-directory
306
+ - apps
307
+ oauth:
308
+ - integration
309
+ - connection
310
+ - auth
311
+
312
+ related:
313
+ - IntegrationCard
314
+ - Grid
315
+ - Search
316
+ - Segmented
317
+ - Page
@@ -0,0 +1,245 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/NotificationPreferences.json",
4
+ "title": "NotificationPreferences",
5
+ "description": "Page-level Settings composite: a matrix of notification types (rows)\n× delivery channels (columns: in-app, email, SMS) with a binary\n<check-ui> in each cell and an optional <switch-ui> column-header\nmaster toggle for bulk-flipping a channel across every row.\n\nForm-bearing — the composite extends UIFormElement and serializes its\ncurrent preferences shape as JSON on `value`. Per first-principle #2\n(\"save is the consumer's job\"), the composite does NOT mutate the\npassed-in `preferences` array; it emits a typed `change` event per\ncell toggle and a `bulk-toggle` event per column-header switch, and\nexposes the staged JSON via the form value so a host <form> submit\ncarries it through ElementInternals.\n\nChannels are columns; types are rows (the smaller-set / larger-set\ninvariant — channels are 3-5 in the wild, types are 10-50). Layout\nrenders as a semantic ARIA grid (`role=\"table\"`/`row`/`cell`/\n`rowheader`/`columnheader`) — NOT native `<table>` (per the HTML\nparser strip rule, table-content tags would foster-parent out of a\ncustom-element subtree).\n\nSpec: .claude/docs/specs/implementation-ready/SPEC-064-notification-preferences.md.\n",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "channelEnabled": {
17
+ "description": "Map of channel-key → master-toggle boolean for the column-header\n<switch-ui>. When omitted, master toggles default to `true` for\nevery channel. Independent of cell values — toggling the master\noff does NOT auto-update the cells; the host receives a\n`bulk-toggle` event and updates `preferences` itself.\n",
18
+ "type": "object",
19
+ "default": {}
20
+ },
21
+ "channels": {
22
+ "description": "Column definitions — one record per delivery channel. Each\ncarries a stable `key`, a `label` (the column header), and an\noptional `icon` name (Phosphor — defaults to no icon when\nabsent). Example: [{key:\"in-app\", label:\"In-app\", icon:\"chat-circle\"},\n{key:\"email\", label:\"Email\", icon:\"envelope\"}]. Empty array\nrenders nothing (the matrix needs at least one column).\n",
23
+ "$ref": "common_types.json#/$defs/DynamicStringList"
24
+ },
25
+ "component": {
26
+ "const": "NotificationPreferences"
27
+ },
28
+ "disabled": {
29
+ "description": "All cells + master toggles become non-interactive.",
30
+ "type": "boolean",
31
+ "default": false
32
+ },
33
+ "group-by": {
34
+ "description": "Row-grouping mode. `none` (default) renders all rows in one\nblock. `group` renders one section per unique `preferences[].group`\nvalue with a header row above each section.\n",
35
+ "type": "string",
36
+ "enum": [
37
+ "none",
38
+ "group"
39
+ ],
40
+ "default": "none"
41
+ },
42
+ "loading": {
43
+ "description": "Renders a skeleton matrix overlay. Cells become non-interactive\nand dim while loading.\n",
44
+ "type": "boolean",
45
+ "default": false
46
+ },
47
+ "preferences": {
48
+ "description": "Matrix data — one row per notification type. Each record carries\na stable `key`, a `label`, optional `description` (rendered under\nthe label), optional `group` (when `group-by=\"group\"`), and a\n`channels` map keyed by channel key with boolean values.\nExample: {key:\"mentions\", label:\"Mentions\", channels:{\"in-app\":true,\nemail:false, sms:false}}. Empty array renders the empty state.\n",
49
+ "$ref": "common_types.json#/$defs/DynamicStringList"
50
+ }
51
+ },
52
+ "required": [
53
+ "component",
54
+ "preferences",
55
+ "channels"
56
+ ],
57
+ "unevaluatedProperties": false,
58
+ "x-adiaui": {
59
+ "anti_patterns": [
60
+ {
61
+ "fix": "<notification-preferences-ui id=\"prefs\"></notification-preferences-ui>\n<script>\n document.querySelector('#prefs').channels = [\n {key:'email', label:'Email'}\n ];\n document.querySelector('#prefs').preferences = [\n {key:'mentions', label:'Mentions', channels:{email:true}},\n {key:'replies', label:'Replies', channels:{email:false}}\n ];\n</script>\n",
62
+ "why": "Cells are generated from the `preferences` matrix. Slotted\nswitches are ignored and the host renders empty.\n",
63
+ "wrong": "<notification-preferences-ui>\n <switch-ui label=\"Email mentions\"></switch-ui>\n <switch-ui label=\"Email replies\"></switch-ui>\n</notification-preferences-ui>\n"
64
+ },
65
+ {
66
+ "fix": "<notification-preferences-ui id=\"prefs\"></notification-preferences-ui>\n<script>/* set channels + preferences here */</script>\n",
67
+ "why": "Re-implements the matrix in raw HTML — loses the typed\n`change` / `bulk-toggle` event contract, the column-master\nswitch, the group-by support, and the consistent token-driven\nvisual treatment. Worse, table-content tags inside a custom\nelement subtree are foster-parented out of the DOM by the HTML\nparser — they silently vanish.\n",
68
+ "wrong": "<table>\n <thead><tr><th></th><th>Email</th></tr></thead>\n <tbody>\n <tr><th>Mentions</th><td><check-ui></check-ui></td></tr>\n </tbody>\n</table>\n"
69
+ }
70
+ ],
71
+ "category": "container",
72
+ "composes": [
73
+ "check-ui",
74
+ "switch-ui",
75
+ "icon-ui",
76
+ "text-ui",
77
+ "empty-state-ui"
78
+ ],
79
+ "events": {
80
+ "bulk-toggle": {
81
+ "description": "Fired when the user toggles a column-header <switch-ui>.\nBubbles. Mirrors `change` shape but for the column master —\nhost applies the bulk change to its own `preferences` data.\n",
82
+ "detail": {
83
+ "channel": {
84
+ "description": "The `key` of the channel column whose master flipped.",
85
+ "type": "string"
86
+ },
87
+ "enabled": {
88
+ "description": "The new desired master state.",
89
+ "type": "boolean"
90
+ }
91
+ }
92
+ },
93
+ "change": {
94
+ "description": "Fired when the user toggles a cell <check-ui>. Bubbles. The\ncomposite emits this with the previous state — the host must\napply the new state to its own data and re-pass `preferences`\nfor the cell to visually update.\n",
95
+ "detail": {
96
+ "channel": {
97
+ "description": "The `key` of the channel column whose cell flipped.",
98
+ "type": "string"
99
+ },
100
+ "enabled": {
101
+ "description": "The new desired state (true = opted in).",
102
+ "type": "boolean"
103
+ },
104
+ "row": {
105
+ "description": "The `key` of the notification type whose cell flipped.",
106
+ "type": "string"
107
+ }
108
+ }
109
+ }
110
+ },
111
+ "examples": [
112
+ {
113
+ "description": "Three channels (in-app / email / SMS) × four notification types.",
114
+ "a2ui": "[\n {\n \"id\": \"np-default\",\n \"component\": \"NotificationPreferences\",\n \"name\": \"notifications\",\n \"channels\": [\n {\"key\": \"in-app\", \"label\": \"In-app\", \"icon\": \"chat-circle\"},\n {\"key\": \"email\", \"label\": \"Email\", \"icon\": \"envelope\"},\n {\"key\": \"sms\", \"label\": \"SMS\", \"icon\": \"device-mobile\"}\n ],\n \"preferences\": [\n {\"key\": \"mentions\", \"label\": \"Mentions\",\n \"channels\": {\"in-app\": true, \"email\": true, \"sms\": false}},\n {\"key\": \"comments\", \"label\": \"Comments\",\n \"channels\": {\"in-app\": true, \"email\": false, \"sms\": false}},\n {\"key\": \"releases\", \"label\": \"Releases\",\n \"channels\": {\"in-app\": false,\"email\": true, \"sms\": false}},\n {\"key\": \"security\", \"label\": \"Security alerts\",\n \"channels\": {\"in-app\": true, \"email\": true, \"sms\": true}}\n ]\n }\n]\n",
115
+ "name": "default"
116
+ },
117
+ {
118
+ "description": "Same matrix grouped by category via group-by=\"group\".",
119
+ "a2ui": "[\n {\n \"id\": \"np-grouped\",\n \"component\": \"NotificationPreferences\",\n \"name\": \"notifications\",\n \"groupBy\": \"group\",\n \"channels\": [\n {\"key\": \"in-app\", \"label\": \"In-app\"},\n {\"key\": \"email\", \"label\": \"Email\"}\n ],\n \"preferences\": [\n {\"key\": \"mentions\", \"label\": \"Mentions\", \"group\": \"Activity\",\n \"channels\": {\"in-app\": true, \"email\": true}},\n {\"key\": \"comments\", \"label\": \"Comments\", \"group\": \"Activity\",\n \"channels\": {\"in-app\": true, \"email\": false}},\n {\"key\": \"digest\", \"label\": \"Weekly digest\", \"group\": \"Summaries\",\n \"channels\": {\"in-app\": false, \"email\": true}}\n ]\n }\n]\n",
120
+ "name": "grouped"
121
+ }
122
+ ],
123
+ "keywords": [
124
+ "notification-preferences",
125
+ "notifications",
126
+ "preferences",
127
+ "opt-in",
128
+ "opt-out",
129
+ "alerts",
130
+ "channels",
131
+ "in-app",
132
+ "email",
133
+ "sms",
134
+ "subscriptions",
135
+ "notification-settings"
136
+ ],
137
+ "name": "UINotificationPreferences",
138
+ "related": [
139
+ "Check",
140
+ "Switch",
141
+ "Table",
142
+ "EmptyState",
143
+ "Icon",
144
+ "Text"
145
+ ],
146
+ "slots": {
147
+ "empty": {
148
+ "description": "Custom empty-state copy shown when `preferences` is empty. Falls\nback to a default <empty-state-ui> when not provided.\n"
149
+ },
150
+ "footer": {
151
+ "description": "Optional content below the matrix — e.g. a global Save Changes\nbutton row or fine-print disclaimer. Falls back to nothing.\n"
152
+ },
153
+ "header": {
154
+ "description": "Optional content above the matrix — e.g. a page title, intro\ncopy, or per-page save row. Falls back to nothing when empty.\n"
155
+ }
156
+ },
157
+ "states": [
158
+ {
159
+ "description": "Default — matrix rendered, ready for interaction.",
160
+ "name": "idle"
161
+ },
162
+ {
163
+ "description": "Skeleton overlay while data loads.",
164
+ "attribute": "loading",
165
+ "name": "loading"
166
+ },
167
+ {
168
+ "description": "All toggles non-interactive.",
169
+ "attribute": "disabled",
170
+ "name": "disabled"
171
+ },
172
+ {
173
+ "description": "No notification types configured.",
174
+ "attribute": "empty",
175
+ "name": "empty"
176
+ }
177
+ ],
178
+ "status": "stable",
179
+ "synonyms": {
180
+ "channels": [
181
+ "delivery",
182
+ "destinations"
183
+ ],
184
+ "notifications": [
185
+ "alerts",
186
+ "messages",
187
+ "updates"
188
+ ],
189
+ "preferences": [
190
+ "settings",
191
+ "opt-ins",
192
+ "subscriptions"
193
+ ]
194
+ },
195
+ "tag": "notification-preferences-ui",
196
+ "tokens": {
197
+ "--notification-preferences-bg": {
198
+ "description": "Host background.",
199
+ "default": "var(--a-bg)"
200
+ },
201
+ "--notification-preferences-border": {
202
+ "description": "Row separator color.",
203
+ "default": "var(--a-border-subtle)"
204
+ },
205
+ "--notification-preferences-channel-width": {
206
+ "description": "Minimum column width for each channel cell.",
207
+ "default": "var(--a-space-9)"
208
+ },
209
+ "--notification-preferences-group-fg": {
210
+ "description": "Group-heading label color.",
211
+ "default": "var(--a-fg-muted)"
212
+ },
213
+ "--notification-preferences-px": {
214
+ "description": "Host horizontal padding.",
215
+ "default": "var(--a-space-5)"
216
+ },
217
+ "--notification-preferences-py": {
218
+ "description": "Host vertical padding.",
219
+ "default": "var(--a-space-5)"
220
+ },
221
+ "--notification-preferences-radius": {
222
+ "description": "Outer matrix container radius.",
223
+ "default": "var(--a-radius-md)"
224
+ },
225
+ "--notification-preferences-row-bg": {
226
+ "description": "Even-row zebra background.",
227
+ "default": "var(--a-bg-muted)"
228
+ },
229
+ "--notification-preferences-row-label-min": {
230
+ "description": "Minimum row-label column width.",
231
+ "default": "12rem"
232
+ },
233
+ "--notification-preferences-row-px": {
234
+ "description": "Per-cell horizontal padding.",
235
+ "default": "var(--a-space-3)"
236
+ },
237
+ "--notification-preferences-row-py": {
238
+ "description": "Per-row vertical padding.",
239
+ "default": "var(--a-space-3)"
240
+ }
241
+ },
242
+ "traits": [],
243
+ "version": 1
244
+ }
245
+ }