@dowel-ui/registry 0.4.0 → 0.7.0

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/dist/build.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as registryIndexSchema, c as hashContent, o as registryItemSchema } from "./schema-ByqIpT47.js";
1
+ import { l as hashContent, o as registryIndexSchema, s as registryItemSchema } from "./schema-BbyNjH6Q.js";
2
2
  import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -9,7 +9,7 @@ function defineMeta(meta) {
9
9
  }
10
10
  //#endregion
11
11
  //#region ../ui/dist/components/accordion/meta.js
12
- const meta$67 = defineMeta({
12
+ const meta$91 = defineMeta({
13
13
  name: "accordion",
14
14
  title: "Accordion",
15
15
  description: "Vertically stacked sections that expand to reveal their content.",
@@ -22,7 +22,7 @@ const meta$67 = defineMeta({
22
22
  });
23
23
  //#endregion
24
24
  //#region ../ui/dist/components/activity-feed/meta.js
25
- const meta$66 = defineMeta({
25
+ const meta$90 = defineMeta({
26
26
  name: "activity-feed",
27
27
  title: "Activity Feed",
28
28
  description: "A chronological list of events, with a connecting timeline rail.",
@@ -35,7 +35,7 @@ const meta$66 = defineMeta({
35
35
  });
36
36
  //#endregion
37
37
  //#region ../ui/dist/components/ai-action-ledger/meta.js
38
- const meta$65 = defineMeta({
38
+ const meta$89 = defineMeta({
39
39
  name: "ai-action-ledger",
40
40
  title: "AI Action Ledger",
41
41
  description: "What an agent actually did, classified by what can be undone.",
@@ -48,7 +48,7 @@ const meta$65 = defineMeta({
48
48
  });
49
49
  //#endregion
50
50
  //#region ../ui/dist/components/ai-agent-plan/meta.js
51
- const meta$64 = defineMeta({
51
+ const meta$88 = defineMeta({
52
52
  name: "ai-agent-plan",
53
53
  title: "AI Agent Plan",
54
54
  description: "What an agent intends to do — including when it changes its mind mid-run.",
@@ -61,7 +61,7 @@ const meta$64 = defineMeta({
61
61
  });
62
62
  //#endregion
63
63
  //#region ../ui/dist/components/ai-agent-status/meta.js
64
- const meta$63 = defineMeta({
64
+ const meta$87 = defineMeta({
65
65
  name: "ai-agent-status",
66
66
  title: "AI Agent Status",
67
67
  description: "A pill showing what an agent is doing, in words as well as colour.",
@@ -74,7 +74,7 @@ const meta$63 = defineMeta({
74
74
  });
75
75
  //#endregion
76
76
  //#region ../ui/dist/components/ai-approval-request/meta.js
77
- const meta$62 = defineMeta({
77
+ const meta$86 = defineMeta({
78
78
  name: "ai-approval-request",
79
79
  title: "AI Approval Request",
80
80
  description: "Approve a tool call — after correcting the arguments the model got wrong.",
@@ -87,7 +87,7 @@ const meta$62 = defineMeta({
87
87
  });
88
88
  //#endregion
89
89
  //#region ../ui/dist/components/ai-conversation/meta.js
90
- const meta$61 = defineMeta({
90
+ const meta$85 = defineMeta({
91
91
  name: "ai-conversation",
92
92
  title: "AI Conversation",
93
93
  description: "The scrolling transcript container, with follow-on-new-content and a status region.",
@@ -100,7 +100,7 @@ const meta$61 = defineMeta({
100
100
  });
101
101
  //#endregion
102
102
  //#region ../ui/dist/components/ai-disclosure/meta.js
103
- const meta$60 = defineMeta({
103
+ const meta$84 = defineMeta({
104
104
  name: "ai-disclosure",
105
105
  title: "AI Disclosure",
106
106
  description: "Tells a reader they are looking at AI, and what is actually known about it.",
@@ -112,8 +112,21 @@ const meta$60 = defineMeta({
112
112
  a11y: "The text is the disclosure and the icon is decoration, marked aria-hidden — a sparkle alone is a mark a sighted reader may not decode and a screen reader passes over in silence, which is the exact failure this component exists to prevent. Each of the four kinds carries wording of its own, so the distinction between generated, manipulated and human-reviewed survives for anyone who cannot see the styling. The provenance panel states in words whether anyone checked the claims and who, rather than implying it with a tick, and its three states are told apart by their sentences rather than by colour. Scope: this renders the human-visible disclosures of EU AI Act Article 50(1) and 50(4). It cannot satisfy 50(2), which requires machine-readable marking inside the artifact by whoever generated it, and no React component can. Not legal advice."
113
113
  });
114
114
  //#endregion
115
+ //#region ../ui/dist/components/ai-extraction-review/meta.js
116
+ const meta$83 = defineMeta({
117
+ name: "ai-extraction-review",
118
+ title: "AI Extraction Review",
119
+ description: "Check extracted fields against the document they came from, and decide each one.",
120
+ category: "ai",
121
+ status: "stable",
122
+ dependencies: [],
123
+ registryDependencies: [],
124
+ files: ["extraction-model.ts", "ai-extraction-review.tsx"],
125
+ a11y: "Every field quotes its evidence in text under the value, so a reviewer who cannot see the highlight in the source still has what the model read. A value with no evidence is said in words — never left to look like a good one. The source is a focusable named region because it scrolls, and its highlights are mark elements with nothing spliced into the text, since a document read aloud with field names inserted is no longer the document. Focus anywhere in a field brings its evidence into view without moving focus, so a keyboard user is shown the source rather than sent into it. Each control's name carries the field it belongs to, so ten Accept buttons are ten different buttons. Status is a word, outside the label so editing never renames the control, and the running count is a polite live region present from the start. Enter accepts, except while an IME is composing."
126
+ });
127
+ //#endregion
115
128
  //#region ../ui/dist/components/ai-inline-completion/meta.js
116
- const meta$59 = defineMeta({
129
+ const meta$82 = defineMeta({
117
130
  name: "ai-inline-completion",
118
131
  title: "AI Inline Completion",
119
132
  description: "Ghost-text suggestion inside a real textarea, accepted with Tab.",
@@ -126,7 +139,7 @@ const meta$59 = defineMeta({
126
139
  });
127
140
  //#endregion
128
141
  //#region ../ui/dist/components/ai-message/meta.js
129
- const meta$58 = defineMeta({
142
+ const meta$81 = defineMeta({
130
143
  name: "ai-message",
131
144
  title: "AI Message",
132
145
  description: "One turn in a conversation, with role, avatar, actions and footer slots.",
@@ -139,7 +152,7 @@ const meta$58 = defineMeta({
139
152
  });
140
153
  //#endregion
141
154
  //#region ../ui/dist/components/ai-model-selector/meta.js
142
- const meta$57 = defineMeta({
155
+ const meta$80 = defineMeta({
143
156
  name: "ai-model-selector",
144
157
  title: "AI Model Selector",
145
158
  description: "Picks the model a conversation runs on, with descriptions and availability.",
@@ -152,7 +165,7 @@ const meta$57 = defineMeta({
152
165
  });
153
166
  //#endregion
154
167
  //#region ../ui/dist/components/ai-prompt-input/meta.js
155
- const meta$56 = defineMeta({
168
+ const meta$79 = defineMeta({
156
169
  name: "ai-prompt-input",
157
170
  title: "AI Prompt Input",
158
171
  description: "The composer: auto-growing textarea, send-on-Enter, and a send/stop control.",
@@ -165,7 +178,7 @@ const meta$56 = defineMeta({
165
178
  });
166
179
  //#endregion
167
180
  //#region ../ui/dist/components/ai-reasoning/meta.js
168
- const meta$55 = defineMeta({
181
+ const meta$78 = defineMeta({
169
182
  name: "ai-reasoning",
170
183
  title: "AI Reasoning",
171
184
  description: "A collapsible disclosure for the model's reasoning.",
@@ -178,7 +191,7 @@ const meta$55 = defineMeta({
178
191
  });
179
192
  //#endregion
180
193
  //#region ../ui/dist/components/ai-response/meta.js
181
- const meta$54 = defineMeta({
194
+ const meta$77 = defineMeta({
182
195
  name: "ai-response",
183
196
  title: "AI Response",
184
197
  description: "Assistant response text, with a streaming caret and a thinking indicator.",
@@ -191,7 +204,7 @@ const meta$54 = defineMeta({
191
204
  });
192
205
  //#endregion
193
206
  //#region ../ui/dist/components/ai-sources/meta.js
194
- const meta$53 = defineMeta({
207
+ const meta$76 = defineMeta({
195
208
  name: "ai-sources",
196
209
  title: "AI Sources",
197
210
  description: "Inline citation markers and the source list they refer to.",
@@ -204,7 +217,7 @@ const meta$53 = defineMeta({
204
217
  });
205
218
  //#endregion
206
219
  //#region ../ui/dist/components/ai-structured-output/meta.js
207
- const meta$52 = defineMeta({
220
+ const meta$75 = defineMeta({
208
221
  name: "ai-structured-output",
209
222
  title: "AI Structured Output",
210
223
  description: "An object arriving field by field from the model, without the layout jumping.",
@@ -216,8 +229,21 @@ const meta$52 = defineMeta({
216
229
  a11y: "A description list, so each value is associated with its label rather than floating beside it. The region is aria-live=\"polite\" with aria-busy while streaming, so fields are announced as they settle instead of re-reading the whole object on every token. Height is reserved from the declared field list, which keeps focus and reading position stable as values arrive. Confidence is stated as a number in words, never as a colour alone."
217
230
  });
218
231
  //#endregion
232
+ //#region ../ui/dist/components/ai-suggested-value/meta.js
233
+ const meta$74 = defineMeta({
234
+ name: "ai-suggested-value",
235
+ title: "AI Suggested Value",
236
+ description: "An AI-proposed value for any form control, pending until accepted and marked afterwards.",
237
+ category: "ai",
238
+ status: "stable",
239
+ dependencies: [],
240
+ registryDependencies: [],
241
+ files: ["ai-suggested-value.tsx"],
242
+ a11y: "The suggestion is the control's description, so a reader who lands on the field hears it without any announcement — and announcing is opt-in, because a form filling twenty fields at once would narrate all twenty. The row that holds it is rendered from the start and never display:none, so when announcing is on, a suggestion arriving after mount is actually heard. Each button's name carries the field it belongs to, so a page of Accepts is a page of different buttons. Provenance and confidence are words with a number, never a colour. Acceptance is reported, not performed: the control is the consumer's, so its value, its label and its own keyboard behaviour are untouched — there is no shortcut hijacked from a select or a date field. The id and ARIA a FormControl passes down are forwarded to the control, and an existing aria-describedby is merged rather than replaced."
243
+ });
244
+ //#endregion
219
245
  //#region ../ui/dist/components/ai-token-usage/meta.js
220
- const meta$51 = defineMeta({
246
+ const meta$73 = defineMeta({
221
247
  name: "ai-token-usage",
222
248
  title: "AI Token Usage",
223
249
  description: "A gauge showing how much of the context window a conversation has used.",
@@ -230,7 +256,7 @@ const meta$51 = defineMeta({
230
256
  });
231
257
  //#endregion
232
258
  //#region ../ui/dist/components/ai-tool/meta.js
233
- const meta$50 = defineMeta({
259
+ const meta$72 = defineMeta({
234
260
  name: "ai-tool",
235
261
  title: "AI Tool Call",
236
262
  description: "A collapsible record of a tool the model called, its arguments and its result.",
@@ -243,7 +269,7 @@ const meta$50 = defineMeta({
243
269
  });
244
270
  //#endregion
245
271
  //#region ../ui/dist/components/alert/meta.js
246
- const meta$49 = defineMeta({
272
+ const meta$71 = defineMeta({
247
273
  name: "alert",
248
274
  title: "Alert",
249
275
  description: "A callout that draws attention to an important message.",
@@ -256,7 +282,7 @@ const meta$49 = defineMeta({
256
282
  });
257
283
  //#endregion
258
284
  //#region ../ui/dist/components/avatar/meta.js
259
- const meta$48 = defineMeta({
285
+ const meta$70 = defineMeta({
260
286
  name: "avatar",
261
287
  title: "Avatar",
262
288
  description: "An image representation of a user or entity with a graceful text fallback.",
@@ -269,7 +295,7 @@ const meta$48 = defineMeta({
269
295
  });
270
296
  //#endregion
271
297
  //#region ../ui/dist/components/badge/meta.js
272
- const meta$47 = defineMeta({
298
+ const meta$69 = defineMeta({
273
299
  name: "badge",
274
300
  title: "Badge",
275
301
  description: "A compact marker for status, counts and categories.",
@@ -281,8 +307,21 @@ const meta$47 = defineMeta({
281
307
  a11y: "The badge label must convey the meaning on its own; variant colour is decoration. Use asChild to render an interactive badge as a link or button rather than adding handlers."
282
308
  });
283
309
  //#endregion
310
+ //#region ../ui/dist/components/breadcrumb/meta.js
311
+ const meta$68 = defineMeta({
312
+ name: "breadcrumb",
313
+ title: "Breadcrumb",
314
+ description: "Where the current page sits in the hierarchy, and how to get back up it.",
315
+ category: "navigation",
316
+ status: "stable",
317
+ dependencies: ["radix-ui"],
318
+ registryDependencies: [],
319
+ files: ["breadcrumb.tsx"],
320
+ a11y: "The current page is a span carrying aria-current=\"page\", not a link: a link to the page you are already on does nothing, and in a list of links it is indistinguishable from the ones that go somewhere. Separators are hidden from assistive technology, because the list already conveys the sequence and reading \"slash\" between every item is the design's punctuation leaking into the content. The ellipsis is the exception and is named, because it is content: it says levels have been left out."
321
+ });
322
+ //#endregion
284
323
  //#region ../ui/dist/components/button/meta.js
285
- const meta$46 = defineMeta({
324
+ const meta$67 = defineMeta({
286
325
  name: "button",
287
326
  title: "Button",
288
327
  description: "Triggers an action or event, with variants for every level of emphasis.",
@@ -295,7 +334,7 @@ const meta$46 = defineMeta({
295
334
  });
296
335
  //#endregion
297
336
  //#region ../ui/dist/components/calendar/meta.js
298
- const meta$45 = defineMeta({
337
+ const meta$66 = defineMeta({
299
338
  name: "calendar",
300
339
  title: "Calendar",
301
340
  description: "A date grid for selecting a day, several days or a range.",
@@ -308,7 +347,7 @@ const meta$45 = defineMeta({
308
347
  });
309
348
  //#endregion
310
349
  //#region ../ui/dist/components/card/meta.js
311
- const meta$44 = defineMeta({
350
+ const meta$65 = defineMeta({
312
351
  name: "card",
313
352
  title: "Card",
314
353
  description: "A surface that groups related content and actions into a single block.",
@@ -321,7 +360,7 @@ const meta$44 = defineMeta({
321
360
  });
322
361
  //#endregion
323
362
  //#region ../ui/dist/components/checkbox/meta.js
324
- const meta$43 = defineMeta({
363
+ const meta$64 = defineMeta({
325
364
  name: "checkbox",
326
365
  title: "Checkbox",
327
366
  description: "A control for an on/off choice, with support for an indeterminate state.",
@@ -334,7 +373,7 @@ const meta$43 = defineMeta({
334
373
  });
335
374
  //#endregion
336
375
  //#region ../ui/dist/components/code-block/meta.js
337
- const meta$42 = defineMeta({
376
+ const meta$63 = defineMeta({
338
377
  name: "code-block",
339
378
  title: "Code Block",
340
379
  description: "A block of code with a copy control, ready for any highlighter.",
@@ -346,8 +385,21 @@ const meta$42 = defineMeta({
346
385
  a11y: "The pre is a focusable named region, since code overflows horizontally and an unfocusable scroll box is unreachable by keyboard. Copying is announced through a polite live region as well as shown, and a refused clipboard write reports failure rather than looking like success."
347
386
  });
348
387
  //#endregion
388
+ //#region ../ui/dist/components/collapsible/meta.js
389
+ const meta$62 = defineMeta({
390
+ name: "collapsible",
391
+ title: "Collapsible",
392
+ description: "A single section that opens and closes, without an accordion's set semantics.",
393
+ category: "layout",
394
+ status: "stable",
395
+ dependencies: ["radix-ui"],
396
+ registryDependencies: [],
397
+ files: ["collapsible.tsx"],
398
+ a11y: "The trigger carries aria-expanded and points at the region it controls, both from the primitive. Reach for this rather than an Accordion of one item: a single-item Accordion gives its trigger a heading role and a position in a list of one, neither of which is true."
399
+ });
400
+ //#endregion
349
401
  //#region ../ui/dist/components/combobox/meta.js
350
- const meta$41 = defineMeta({
402
+ const meta$61 = defineMeta({
351
403
  name: "combobox",
352
404
  title: "Combobox",
353
405
  description: "A searchable single-select built on the ARIA combobox pattern.",
@@ -360,7 +412,7 @@ const meta$41 = defineMeta({
360
412
  });
361
413
  //#endregion
362
414
  //#region ../ui/dist/components/command/meta.js
363
- const meta$40 = defineMeta({
415
+ const meta$60 = defineMeta({
364
416
  name: "command",
365
417
  title: "Command",
366
418
  description: "A searchable command palette, with groups and a ⌘K dialog variant.",
@@ -372,8 +424,34 @@ const meta$40 = defineMeta({
372
424
  a11y: "The input owns role=\"combobox\" and the list role=\"listbox\"; focus stays in the input while arrow keys move aria-activedescendant, so typing is never interrupted. Enter runs the active item through the same path as a click. A group whose items are all filtered out hides its heading rather than labelling nothing. CommandDialog always renders a title and description, visually hidden, so the dialog is never unnamed."
373
425
  });
374
426
  //#endregion
427
+ //#region ../ui/dist/components/confirm-typed/meta.js
428
+ const meta$59 = defineMeta({
429
+ name: "confirm-typed",
430
+ title: "Confirm Typed",
431
+ description: "Type the name to confirm — and be told, not ignored, when it does not match.",
432
+ category: "form",
433
+ status: "stable",
434
+ dependencies: [],
435
+ registryDependencies: ["button", "input"],
436
+ files: ["confirm-typed.tsx"],
437
+ a11y: "The button stays reachable while the text does not match, dimmed rather than disabled, and pressing it or Enter announces what was expected, marks the field invalid and returns focus to it — the common version disables the button and says nothing, so a keyboard user cannot tell whether anything happened. The match is announced once, on the transition, naming the action that became available, so nobody has to watch the button change colour. Typing itself stays silent, because a verdict on every keystroke is noise. The hint is the field's description and the button's, and the status region exists from the first render so the first announcement is heard. Pasting is allowed: blocking it punishes people who cannot type a long name easily and stops nobody who can select-all and copy."
438
+ });
439
+ //#endregion
440
+ //#region ../ui/dist/components/cron-editor/meta.js
441
+ const meta$58 = defineMeta({
442
+ name: "cron-editor",
443
+ title: "Cron Editor",
444
+ description: "A schedule as a cron expression, with a plain-language reading and the next runs.",
445
+ category: "form",
446
+ status: "stable",
447
+ dependencies: [],
448
+ registryDependencies: ["input", "select"],
449
+ files: ["cron-expression.ts", "cron-editor.tsx"],
450
+ a11y: "The expression field is described by one element that holds either the plain-language reading or the reason the expression is invalid, so a reader hears the error replace the reading rather than both at once; an invalid expression is never applied. The builder's day buttons are a named group of aria-pressed toggles with the full day name as the accessible name, and the last selected day cannot be removed silently — the group says why. Days 29 to 31 say in text that shorter months skip them. Next runs are a list of time elements with machine-readable datetimes, headed by the zone they are in, because a time with no zone is the classic scheduling mistake. Nothing clock-dependent renders until the clock is known, so server and client cannot disagree."
451
+ });
452
+ //#endregion
375
453
  //#region ../ui/dist/components/data-table/meta.js
376
- const meta$39 = defineMeta({
454
+ const meta$57 = defineMeta({
377
455
  name: "data-table",
378
456
  title: "Data Table",
379
457
  description: "Presentation for a TanStack Table instance: sorting, column visibility and pagination.",
@@ -391,7 +469,7 @@ const meta$39 = defineMeta({
391
469
  });
392
470
  //#endregion
393
471
  //#region ../ui/dist/components/date-picker/meta.js
394
- const meta$38 = defineMeta({
472
+ const meta$56 = defineMeta({
395
473
  name: "date-picker",
396
474
  title: "Date Picker",
397
475
  description: "A button that opens a calendar to pick a date or a date range.",
@@ -407,8 +485,21 @@ const meta$38 = defineMeta({
407
485
  a11y: "The trigger states the selected date, formatted for the locale, so its accessible name changes with the value. The popover is named because it carries role=\"dialog\". Selecting a single date closes the popover and returns focus to the trigger; a range stays open, since one click is only half an answer. Offer a typed input alongside it for dates the user already knows."
408
486
  });
409
487
  //#endregion
488
+ //#region ../ui/dist/components/direction/meta.js
489
+ const meta$55 = defineMeta({
490
+ name: "direction",
491
+ title: "Direction Provider",
492
+ description: "Tells the component primitives which way the writing runs, so menus and sliders mirror with the rest of the page.",
493
+ category: "foundation",
494
+ status: "stable",
495
+ dependencies: ["radix-ui"],
496
+ registryDependencies: [],
497
+ files: ["direction.tsx"],
498
+ a11y: "Needed only for right-to-left languages, and needed in addition to `dir` on the document rather than instead of it. The styling here is written with logical properties and follows `dir` by itself; the primitives underneath read direction from React context and assume left-to-right without a provider, which mirrors a page correctly except for its menus, selects and sliders — worse than not mirroring at all, because it looks deliberate."
499
+ });
500
+ //#endregion
410
501
  //#region ../ui/dist/components/dialog/meta.js
411
- const meta$37 = defineMeta({
502
+ const meta$54 = defineMeta({
412
503
  name: "dialog",
413
504
  title: "Dialog",
414
505
  description: "A modal window that interrupts the user to gather a response.",
@@ -421,7 +512,7 @@ const meta$37 = defineMeta({
421
512
  });
422
513
  //#endregion
423
514
  //#region ../ui/dist/components/diff-viewer/meta.js
424
- const meta$36 = defineMeta({
515
+ const meta$53 = defineMeta({
425
516
  name: "diff-viewer",
426
517
  title: "Diff Viewer",
427
518
  description: "A diff you can style, and accept or reject hunk by hunk.",
@@ -433,8 +524,21 @@ const meta$36 = defineMeta({
433
524
  a11y: "A semantic table, not a grid. role=\"grid\" would promise cell-by-cell arrow navigation that does not exist here and makes no sense for reading code. Every row states added, removed or unchanged in text, because a plus sign and a green tint are not information — a diff read aloud without it is just the same file twice. Line numbers are aria-hidden: they orient a sighted reader, and announcing two numbers before every line makes the diff unlistenable. Changed words are marked with mark elements so they survive as structure, and the empty half of a split pair is hidden rather than read as a blank line of code."
434
525
  });
435
526
  //#endregion
527
+ //#region ../ui/dist/components/dns-record/meta.js
528
+ const meta$52 = defineMeta({
529
+ name: "dns-record",
530
+ title: "DNS Record",
531
+ description: "Add this record at your provider: parts copied separately, checked with what was found.",
532
+ category: "data",
533
+ status: "stable",
534
+ dependencies: [],
535
+ registryDependencies: ["button", "spinner"],
536
+ files: ["dns-record.tsx"],
537
+ a11y: "The card is a region named by the record's type and purpose. Each part is a definition with its own copy button, named by the part it copies, so five Copy buttons are five different buttons. Verification status is a word in a polite live region, so the answer to a check arrives where it was asked for, and the card is aria-busy while checking. Copy results are announced in a separate region, present from the start, so a copy never replaces a check result mid-sentence; failure is announced with what to do instead. What the check found is a list, not a colour."
538
+ });
539
+ //#endregion
436
540
  //#region ../ui/dist/components/drawer/meta.js
437
- const meta$35 = defineMeta({
541
+ const meta$51 = defineMeta({
438
542
  name: "drawer",
439
543
  title: "Drawer",
440
544
  description: "A bottom sheet that can be dismissed by dragging it down.",
@@ -447,7 +551,7 @@ const meta$35 = defineMeta({
447
551
  });
448
552
  //#endregion
449
553
  //#region ../ui/dist/components/dropdown-menu/meta.js
450
- const meta$34 = defineMeta({
554
+ const meta$50 = defineMeta({
451
555
  name: "dropdown-menu",
452
556
  title: "Dropdown Menu",
453
557
  description: "A menu of actions revealed from a trigger, with submenus and selectable items.",
@@ -460,7 +564,7 @@ const meta$34 = defineMeta({
460
564
  });
461
565
  //#endregion
462
566
  //#region ../ui/dist/components/empty-state/meta.js
463
- const meta$33 = defineMeta({
567
+ const meta$49 = defineMeta({
464
568
  name: "empty-state",
465
569
  title: "Empty State",
466
570
  description: "Shown where content would be, explaining why it is empty and what to do next.",
@@ -473,7 +577,7 @@ const meta$33 = defineMeta({
473
577
  });
474
578
  //#endregion
475
579
  //#region ../ui/dist/components/file-upload/meta.js
476
- const meta$32 = defineMeta({
580
+ const meta$48 = defineMeta({
477
581
  name: "file-upload",
478
582
  title: "File Upload",
479
583
  description: "A dropzone over a real file input, plus the upload queue nobody ships.",
@@ -486,7 +590,7 @@ const meta$32 = defineMeta({
486
590
  });
487
591
  //#endregion
488
592
  //#region ../ui/dist/components/form/meta.js
489
- const meta$31 = defineMeta({
593
+ const meta$47 = defineMeta({
490
594
  name: "form",
491
595
  title: "Form",
492
596
  description: "Accessible field wiring that works with any form library, or none.",
@@ -499,7 +603,7 @@ const meta$31 = defineMeta({
499
603
  });
500
604
  //#endregion
501
605
  //#region ../ui/dist/components/input/meta.js
502
- const meta$30 = defineMeta({
606
+ const meta$46 = defineMeta({
503
607
  name: "input",
504
608
  title: "Input",
505
609
  description: "A single-line text field with size variants and native validation styling.",
@@ -512,7 +616,7 @@ const meta$30 = defineMeta({
512
616
  });
513
617
  //#endregion
514
618
  //#region ../ui/dist/components/label/meta.js
515
- const meta$29 = defineMeta({
619
+ const meta$45 = defineMeta({
516
620
  name: "label",
517
621
  title: "Label",
518
622
  description: "An accessible caption that associates text with a form control.",
@@ -525,7 +629,7 @@ const meta$29 = defineMeta({
525
629
  });
526
630
  //#endregion
527
631
  //#region ../ui/dist/components/log-viewer/meta.js
528
- const meta$28 = defineMeta({
632
+ const meta$44 = defineMeta({
529
633
  name: "log-viewer",
530
634
  title: "Log Viewer",
531
635
  description: "A streaming console with filtering, level facets and follow mode.",
@@ -538,7 +642,7 @@ const meta$28 = defineMeta({
538
642
  });
539
643
  //#endregion
540
644
  //#region ../ui/dist/components/meter/meta.js
541
- const meta$27 = defineMeta({
645
+ const meta$43 = defineMeta({
542
646
  name: "meter",
543
647
  title: "Meter",
544
648
  description: "A measurement against a capacity — quota, seats, spend — segmented by category.",
@@ -551,7 +655,7 @@ const meta$27 = defineMeta({
551
655
  });
552
656
  //#endregion
553
657
  //#region ../ui/dist/components/metric-delta/meta.js
554
- const meta$26 = defineMeta({
658
+ const meta$42 = defineMeta({
555
659
  name: "metric-delta",
556
660
  title: "Metric Delta",
557
661
  description: "A headline number with a change indicator that knows which direction is good.",
@@ -564,7 +668,7 @@ const meta$26 = defineMeta({
564
668
  });
565
669
  //#endregion
566
670
  //#region ../ui/dist/components/pagination/meta.js
567
- const meta$25 = defineMeta({
671
+ const meta$41 = defineMeta({
568
672
  name: "pagination",
569
673
  title: "Pagination",
570
674
  description: "Navigation between pages of a list.",
@@ -576,8 +680,21 @@ const meta$25 = defineMeta({
576
680
  a11y: "A named nav landmark. The current page carries aria-current=\"page\" — styling it differently conveys nothing on its own. Links by default, since a page of results is a place worth sharing; use asChild for your router's link, or render buttons when paging only changes client state."
577
681
  });
578
682
  //#endregion
683
+ //#region ../ui/dist/components/permission-matrix/meta.js
684
+ const meta$40 = defineMeta({
685
+ name: "permission-matrix",
686
+ title: "Permission Matrix",
687
+ description: "Roles across, permissions down, with inheritance, locked roles and one tab stop.",
688
+ category: "data",
689
+ status: "stable",
690
+ dependencies: [],
691
+ registryDependencies: ["checkbox"],
692
+ files: ["permission-model.ts", "permission-matrix.tsx"],
693
+ a11y: "A grid in the WAI-ARIA sense: role=\"grid\" with one tab stop, arrow keys between cells, Home and End along a row and Ctrl with them across the whole matrix — the right call for a matrix that is operated, where it was the wrong one for a diff that is read. Every checkbox is named by both coordinates, \"Delete projects for Editor\", so a reader arriving by arrow key knows where they are without re-reading the headers. An inherited grant is a checked box that cannot be changed here, marked aria-disabled rather than disabled so it stays in the keyboard path, with the role it came from as its description. A locked role says in each cell's description that nothing can be changed. Group toggles are tri-state, and the column header counts each role's grants in text."
694
+ });
695
+ //#endregion
579
696
  //#region ../ui/dist/components/popover/meta.js
580
- const meta$24 = defineMeta({
697
+ const meta$39 = defineMeta({
581
698
  name: "popover",
582
699
  title: "Popover",
583
700
  description: "Rich floating content anchored to a trigger.",
@@ -590,7 +707,7 @@ const meta$24 = defineMeta({
590
707
  });
591
708
  //#endregion
592
709
  //#region ../ui/dist/components/progress/meta.js
593
- const meta$23 = defineMeta({
710
+ const meta$38 = defineMeta({
594
711
  name: "progress",
595
712
  title: "Progress",
596
713
  description: "Shows how far along a task is, or that one is running at all.",
@@ -603,7 +720,7 @@ const meta$23 = defineMeta({
603
720
  });
604
721
  //#endregion
605
722
  //#region ../ui/dist/components/radio-group/meta.js
606
- const meta$22 = defineMeta({
723
+ const meta$37 = defineMeta({
607
724
  name: "radio-group",
608
725
  title: "Radio Group",
609
726
  description: "A set of mutually exclusive options.",
@@ -616,7 +733,7 @@ const meta$22 = defineMeta({
616
733
  });
617
734
  //#endregion
618
735
  //#region ../ui/dist/components/record-diff/meta.js
619
- const meta$21 = defineMeta({
736
+ const meta$36 = defineMeta({
620
737
  name: "record-diff",
621
738
  title: "Record Diff",
622
739
  description: "Field-level before and after, for audit entries, settings history and revisions.",
@@ -628,8 +745,21 @@ const meta$21 = defineMeta({
628
745
  a11y: "A real table, not a grid of divs. Each field name is a th with scope=\"row\", so a value is announced with the field it belongs to rather than as a loose cell. The kind of change is never carried by row colour alone — every row states added, removed or changed in text for screen readers, satisfying WCAG 1.4.1. The unchanged-field toggle is a real button with aria-expanded. Redacted values never reach the DOM at all."
629
746
  });
630
747
  //#endregion
748
+ //#region ../ui/dist/components/secret-field/meta.js
749
+ const meta$35 = defineMeta({
750
+ name: "secret-field",
751
+ title: "Secret Field",
752
+ description: "An API key in its three states: shown once, hidden but revealable, and gone.",
753
+ category: "form",
754
+ status: "stable",
755
+ dependencies: [],
756
+ registryDependencies: ["button", "input"],
757
+ files: ["secret-field.tsx"],
758
+ a11y: "The value sits in a read-only input rather than a disabled one, so it can be focused, selected and read, and focusing a shown value selects all of it for copying by hand. The field's state — shown once, hidden, revealed, gone — is a sentence attached as its description, never a colour or an icon. Copying is announced through a status region that exists before the first copy, and failure is announced too, with what to do instead, because the clipboard can be refused. Every button carries the field's label in its name, so a settings page with several keys has several distinct Copy buttons. Regenerating is confirmed inline in a named group that states the consequence, since it revokes the current key."
759
+ });
760
+ //#endregion
631
761
  //#region ../ui/dist/components/select/meta.js
632
- const meta$20 = defineMeta({
762
+ const meta$34 = defineMeta({
633
763
  name: "select",
634
764
  title: "Select",
635
765
  description: "Picks one value from a short list of options.",
@@ -642,7 +772,7 @@ const meta$20 = defineMeta({
642
772
  });
643
773
  //#endregion
644
774
  //#region ../ui/dist/components/separator/meta.js
645
- const meta$19 = defineMeta({
775
+ const meta$33 = defineMeta({
646
776
  name: "separator",
647
777
  title: "Separator",
648
778
  description: "A horizontal or vertical rule that divides content.",
@@ -654,8 +784,34 @@ const meta$19 = defineMeta({
654
784
  a11y: "Decorative by default and hidden from assistive technology. Set decorative={false} to expose it as a semantic separator when it divides meaningfully distinct groups."
655
785
  });
656
786
  //#endregion
787
+ //#region ../ui/dist/components/session-expiry/meta.js
788
+ const meta$32 = defineMeta({
789
+ name: "session-expiry",
790
+ title: "Session Expiry",
791
+ description: "The idle-timeout warning WCAG 2.2.1 asks for: warned, extendable, and not dismissable by accident.",
792
+ category: "feedback",
793
+ status: "stable",
794
+ dependencies: [],
795
+ registryDependencies: ["button", "dialog"],
796
+ files: ["session-expiry.tsx"],
797
+ a11y: "An alert dialog, named by its heading and described by the sentence that says how long is left. Escape and the backdrop do nothing, because dismissing a session warning without choosing is choosing nothing; the two buttons are the way out, and focus opens on the safe one. The visible countdown is not live — a number every second for two minutes drowns the question — and a separate status region speaks four times: when the warning opens, at one minute, thirty seconds and ten, still saying a threshold a slow tick skipped over. The clock is read in an effect, so the server renders nothing rather than a countdown from the wrong instant. This satisfies the warn-and-extend half of WCAG 2.2.1 Timing Adjustable; the twenty-second minimum is the default window's business and the application's."
798
+ });
799
+ //#endregion
800
+ //#region ../ui/dist/components/sidebar/meta.js
801
+ const meta$31 = defineMeta({
802
+ name: "sidebar",
803
+ title: "Sidebar",
804
+ description: "The application's own navigation: a collapsible rail on a wide screen, an overlay on a narrow one.",
805
+ category: "navigation",
806
+ status: "stable",
807
+ dependencies: ["radix-ui"],
808
+ registryDependencies: ["sheet"],
809
+ files: ["sidebar.tsx"],
810
+ a11y: "The navigation landmark must be named — a page has several, and three regions all called \"navigation\" is a list nobody can choose from. The active entry carries aria-current=\"page\" rather than only a background colour. On a narrow screen it is a Sheet, not a styled div: an overlay needs a focus trap and an Escape key, and without them the page behind stays reachable by Tab while the menu covers it. When the rail collapses, labels are visually hidden rather than removed — removing them leaves controls whose only content is an icon, which is how a collapsed sidebar becomes a column of links all announced as \"link\". The trigger says what pressing it will do, not what the state currently is. The overlay is mounted only on a narrow screen and has its own open state: a modal dialog that CSS hides is still modal, and mounting it always made every wide screen aria-hidden and unclickable whenever the rail was open."
811
+ });
812
+ //#endregion
657
813
  //#region ../ui/dist/components/sheet/meta.js
658
- const meta$18 = defineMeta({
814
+ const meta$30 = defineMeta({
659
815
  name: "sheet",
660
816
  title: "Sheet",
661
817
  description: "A panel that enters from an edge of the viewport for secondary content.",
@@ -667,8 +823,21 @@ const meta$18 = defineMeta({
667
823
  a11y: "Modal, with the same focus trapping and restoration as Dialog. Always render a SheetTitle. For a side navigation sheet, put a nav landmark inside rather than relying on placement to convey the role."
668
824
  });
669
825
  //#endregion
826
+ //#region ../ui/dist/components/shortcut-recorder/meta.js
827
+ const meta$29 = defineMeta({
828
+ name: "shortcut-recorder",
829
+ title: "Shortcut Recorder",
830
+ description: "Press the keys you want: records a chord, platform-aware, and says when it clashes.",
831
+ category: "form",
832
+ status: "stable",
833
+ dependencies: [],
834
+ registryDependencies: [],
835
+ files: ["shortcut-model.ts", "shortcut-recorder.tsx"],
836
+ a11y: "The recorder is a button named by what the shortcut does and described by its value in words — Command Shift K — while the key caps on it are hidden from assistive technology, since ⌘⇧K read aloud is noise. While recording it is aria-pressed and its description says what to do; Tab always leaves and Escape always cancels, so it is never a keyboard trap. Every outcome is announced through a status region present from the start: recorded, refused with the reason, clashing with which command, cancelled, cleared. Symbols are shown on a Mac and words elsewhere, and the description uses the platform's own names for its keys."
837
+ });
838
+ //#endregion
670
839
  //#region ../ui/dist/components/skeleton/meta.js
671
- const meta$17 = defineMeta({
840
+ const meta$28 = defineMeta({
672
841
  name: "skeleton",
673
842
  title: "Skeleton",
674
843
  description: "A shaped placeholder that holds layout while content loads.",
@@ -681,7 +850,7 @@ const meta$17 = defineMeta({
681
850
  });
682
851
  //#endregion
683
852
  //#region ../ui/dist/components/slider/meta.js
684
- const meta$16 = defineMeta({
853
+ const meta$27 = defineMeta({
685
854
  name: "slider",
686
855
  title: "Slider",
687
856
  description: "Selects a value, or a range, from a continuous span.",
@@ -694,7 +863,7 @@ const meta$16 = defineMeta({
694
863
  });
695
864
  //#endregion
696
865
  //#region ../ui/dist/components/spinner/meta.js
697
- const meta$15 = defineMeta({
866
+ const meta$26 = defineMeta({
698
867
  name: "spinner",
699
868
  title: "Spinner",
700
869
  description: "An indeterminate loading indicator for buttons, panels and inline content.",
@@ -707,7 +876,7 @@ const meta$15 = defineMeta({
707
876
  });
708
877
  //#endregion
709
878
  //#region ../ui/dist/components/switch/meta.js
710
- const meta$14 = defineMeta({
879
+ const meta$25 = defineMeta({
711
880
  name: "switch",
712
881
  title: "Switch",
713
882
  description: "An immediate on/off toggle for settings that apply straight away.",
@@ -719,8 +888,21 @@ const meta$14 = defineMeta({
719
888
  a11y: "Exposed as role=\"switch\" and toggled with Space or Enter. Needs a visible label tied with htmlFor/id. Do not rely on colour alone to show state — position and the label carry it too."
720
889
  });
721
890
  //#endregion
891
+ //#region ../ui/dist/components/sync-status/meta.js
892
+ const meta$24 = defineMeta({
893
+ name: "sync-status",
894
+ title: "Sync Status",
895
+ description: "Offline, saving, saved or failed — with the count of changes being held.",
896
+ category: "feedback",
897
+ status: "stable",
898
+ dependencies: [],
899
+ registryDependencies: ["button"],
900
+ files: ["sync-status.tsx"],
901
+ a11y: "The word is the status and the coloured dot is decoration, hidden from assistive technology. The visible text is not a live region: every save flips it and a region on it would narrate the whole session. A separate region, present from the first render, announces transitions only — went offline with what will happen to the changes, came back with what is being saved, could not save, saved after a failure — and never the churn of saving itself. Announcing is on by default because going offline is the one thing a reader who is typing has to be told, and it can be turned off. The server render assumes online, since a page with no interface to report has no business saying offline."
902
+ });
903
+ //#endregion
722
904
  //#region ../ui/dist/components/table/meta.js
723
- const meta$13 = defineMeta({
905
+ const meta$23 = defineMeta({
724
906
  name: "table",
725
907
  title: "Table",
726
908
  description: "Semantic table primitives for tabular data.",
@@ -733,7 +915,7 @@ const meta$13 = defineMeta({
733
915
  });
734
916
  //#endregion
735
917
  //#region ../ui/dist/components/tabs/meta.js
736
- const meta$12 = defineMeta({
918
+ const meta$22 = defineMeta({
737
919
  name: "tabs",
738
920
  title: "Tabs",
739
921
  description: "Switches between panels of related content.",
@@ -746,7 +928,7 @@ const meta$12 = defineMeta({
746
928
  });
747
929
  //#endregion
748
930
  //#region ../ui/dist/components/tags-input/meta.js
749
- const meta$11 = defineMeta({
931
+ const meta$21 = defineMeta({
750
932
  name: "tags-input",
751
933
  title: "Tags Input",
752
934
  description: "A list of short values, where invalid entries stay visible instead of vanishing.",
@@ -759,7 +941,7 @@ const meta$11 = defineMeta({
759
941
  });
760
942
  //#endregion
761
943
  //#region ../ui/dist/components/time-range-picker/meta.js
762
- const meta$10 = defineMeta({
944
+ const meta$20 = defineMeta({
763
945
  name: "time-range-picker",
764
946
  title: "Time Range Picker",
765
947
  description: "A time range whose value is an expression, so it stays relative across a reload.",
@@ -776,8 +958,21 @@ const meta$10 = defineMeta({
776
958
  a11y: "The trigger is named by the range it holds — \"Last 6 hours\" — and the window that resolves to is read after it, so the relative choice is not replaced by two timestamps. The popover carries role=\"dialog\" and is named. Presets are buttons rather than radios because choosing one both selects and dismisses, with aria-pressed carrying which matches the current expression. The expression field reports why an unparseable entry is invalid through one aria-describedby element that the preview and the error share, so a reader hears the error replace the preview rather than both at once; an invalid expression is never applied, because a chart silently re-scoping itself is worse than one that refuses."
777
959
  });
778
960
  //#endregion
961
+ //#region ../ui/dist/components/textarea/meta.js
962
+ const meta$19 = defineMeta({
963
+ name: "textarea",
964
+ title: "Textarea",
965
+ description: "A multi-line text field, with optional auto-resize and a character count that knows when to stay quiet.",
966
+ category: "form",
967
+ status: "stable",
968
+ dependencies: ["class-variance-authority"],
969
+ registryDependencies: [],
970
+ files: ["textarea.tsx"],
971
+ a11y: "Pair with a Label via htmlFor/id. The error state is driven by aria-invalid, so assistive technology and styling stay in sync. The character count is described by the field rather than announced on every keystroke — a permanently live counter reads the remaining number between every letter typed, which is unusable — so it goes live only once the limit is close, and states the remainder in words rather than as a bare ratio that is read aloud as two unlabelled numbers. Horizontal resizing is off by default, because a field dragged wider than its container is a broken layout."
972
+ });
973
+ //#endregion
779
974
  //#region ../ui/dist/components/toast/meta.js
780
- const meta$9 = defineMeta({
975
+ const meta$18 = defineMeta({
781
976
  name: "toast",
782
977
  title: "Toast",
783
978
  description: "Brief, non-blocking messages with an imperative API callable from anywhere.",
@@ -790,7 +985,7 @@ const meta$9 = defineMeta({
790
985
  });
791
986
  //#endregion
792
987
  //#region ../ui/dist/components/tooltip/meta.js
793
- const meta$8 = defineMeta({
988
+ const meta$17 = defineMeta({
794
989
  name: "tooltip",
795
990
  title: "Tooltip",
796
991
  description: "A short label revealed on hover or keyboard focus.",
@@ -813,6 +1008,31 @@ const meta$8 = defineMeta({
813
1008
  * `meta.test.ts` fails if a block directory exists that is missing from here.
814
1009
  */
815
1010
  const blockMetas = [
1011
+ defineMeta({
1012
+ name: "admin-dashboard",
1013
+ kind: "block",
1014
+ title: "Admin dashboard",
1015
+ description: "An administration console: the shell every admin page shares — navigation, breadcrumb, account menu — and the overview that is its front page, with what needs attention first.",
1016
+ category: "layout",
1017
+ status: "stable",
1018
+ access: "pro",
1019
+ dependencies: [],
1020
+ registryDependencies: [
1021
+ "alert",
1022
+ "avatar",
1023
+ "badge",
1024
+ "breadcrumb",
1025
+ "button",
1026
+ "card",
1027
+ "dropdown-menu",
1028
+ "empty-state",
1029
+ "metric-delta",
1030
+ "sidebar",
1031
+ "table"
1032
+ ],
1033
+ files: ["admin-dashboard.tsx"],
1034
+ a11y: "What needs attention comes first and is the only loud part of the page; it is not a live region, because the list is there on load and re-announcing it on every refresh would make the console unusable. The navigation is a landmark called Admin, the breadcrumb another, so neither is one more thing called 'navigation'. The current page carries aria-current, a count beside a nav item says what it counts, the account menu is named after its owner, and each Open button is named after its account. Every status is a word, with colour agreeing rather than replacing."
1035
+ }),
816
1036
  defineMeta({
817
1037
  name: "admin-users",
818
1038
  kind: "block",
@@ -834,6 +1054,27 @@ const blockMetas = [
834
1054
  files: ["admin-users.tsx"],
835
1055
  a11y: "Filtering changes the results without the user asking, so the matching count is announced politely. The filter has a real label rather than only a placeholder, which disappears the moment anyone types. Each row's action button is named after its row — four identical \"Actions\" buttons say nothing about which row you are on."
836
1056
  }),
1057
+ defineMeta({
1058
+ name: "agent-console",
1059
+ kind: "block",
1060
+ title: "Agent console",
1061
+ description: "One agent run, watched: the plan it is working from, the approval it is blocked on, and the ledger of what it has already done.",
1062
+ category: "ai",
1063
+ status: "stable",
1064
+ dependencies: [],
1065
+ registryDependencies: [
1066
+ "ai-action-ledger",
1067
+ "ai-agent-plan",
1068
+ "ai-agent-status",
1069
+ "ai-approval-request",
1070
+ "ai-token-usage",
1071
+ "button",
1072
+ "card",
1073
+ "empty-state"
1074
+ ],
1075
+ files: ["agent-console.tsx"],
1076
+ a11y: "Whatever the run is blocked on is rendered first rather than below a scrolling plan, because it is the only part of the page waiting on a person. This is the one agent on the page, so its status announces its own transitions — a list of runs each doing that would be noise, which is why the underlying component makes it opt in. Every completed action states whether it can be undone, offset or not taken back at all, in words, since presenting all three behind one Undo is a lie discovered after the click."
1077
+ }),
837
1078
  defineMeta({
838
1079
  name: "ai-chat",
839
1080
  kind: "block",
@@ -858,6 +1099,137 @@ const blockMetas = [
858
1099
  files: ["ai-chat.tsx"],
859
1100
  a11y: "Assembled from the AI components rather than reimplementing them, so their decisions still hold: the transcript is a list and not a live region, state is announced separately through ConversationStatus, and the composer will not send while an IME composition is active. Tool payloads are named per tool, so two open tool calls do not present two regions called \"Result\"."
860
1101
  }),
1102
+ defineMeta({
1103
+ name: "ai-dashboard",
1104
+ kind: "block",
1105
+ title: "AI dashboard",
1106
+ description: "What the AI features cost and whether they worked: tokens, spend, failure rate, a breakdown by model, and the runs still going.",
1107
+ category: "ai",
1108
+ status: "stable",
1109
+ dependencies: [],
1110
+ registryDependencies: [
1111
+ "ai-agent-status",
1112
+ "ai-token-usage",
1113
+ "card",
1114
+ "empty-state",
1115
+ "metric-delta",
1116
+ "table"
1117
+ ],
1118
+ files: ["ai-dashboard.tsx"],
1119
+ a11y: "Spend and failure rate are declared lower-is-better, so a rising bill is not painted green — which is the mistake most usage dashboards make. The totals row is a real table footer rather than a last body row that looks like one, because inside the body it is announced as another model and there is no model called Total. Statuses in the run list do not announce their own changes: several runs each doing that turns a list into a stream of interruptions, and watching one run is the agent console's job."
1120
+ }),
1121
+ defineMeta({
1122
+ name: "ai-workspace",
1123
+ kind: "block",
1124
+ title: "AI workspace",
1125
+ description: "A whole AI application surface: conversations down one side, the active transcript with reasoning, tool calls and sources in the middle, and the context — window usage, attachments and a structured result — on the other.",
1126
+ category: "ai",
1127
+ status: "stable",
1128
+ access: "pro",
1129
+ dependencies: [],
1130
+ registryDependencies: [
1131
+ "ai-conversation",
1132
+ "ai-message",
1133
+ "ai-model-selector",
1134
+ "ai-prompt-input",
1135
+ "ai-reasoning",
1136
+ "ai-response",
1137
+ "ai-sources",
1138
+ "ai-structured-output",
1139
+ "ai-token-usage",
1140
+ "ai-tool",
1141
+ "button",
1142
+ "empty-state",
1143
+ "sidebar"
1144
+ ],
1145
+ files: ["ai-workspace.tsx"],
1146
+ a11y: "Three landmarks, each named: the conversation list is a navigation called Conversations, the transcript is the main content, and the context is a complementary region called Context, so a screen reader user can jump between them and skip the one they do not want. The transcript is a list and never a live region; the response state is announced from a separate status line, so a streaming answer does not read itself aloud token by token. Each attachment's remove button is named after its file. On a narrow screen the conversation list becomes a sheet with a focus trap and an Escape key, not a div slid over content that stays reachable by Tab."
1147
+ }),
1148
+ defineMeta({
1149
+ name: "analytics",
1150
+ kind: "block",
1151
+ title: "Analytics",
1152
+ description: "An analytics page: headline metrics against the previous period, a series over time, and the breakdown that made it up.",
1153
+ category: "data",
1154
+ status: "stable",
1155
+ dependencies: [],
1156
+ registryDependencies: [
1157
+ "button",
1158
+ "card",
1159
+ "metric-delta",
1160
+ "select",
1161
+ "table"
1162
+ ],
1163
+ files: ["analytics.tsx"],
1164
+ a11y: "The bars are declared as a single image with a one-sentence summary — where the series started, where it ended, and where its extremes were — rather than as forty separately labelled elements, which reads as noise instead of a shape. The exact numbers live in a real table that is collapsed for everyone and revealed for everyone from a control on the page, rather than in a visually-hidden copy only screen readers can reach, because the hidden copy is the one that goes stale. The range selector is a select rather than a tab set: tabs promise panels, and a range selector has none — it changes the data behind the whole page, so styled-as-tabs it points every tab at a panel that does not exist."
1165
+ }),
1166
+ defineMeta({
1167
+ name: "billing",
1168
+ kind: "block",
1169
+ title: "Billing",
1170
+ description: "A billing page: the current plan, metered usage against its limits, the payment method and the invoice history.",
1171
+ category: "data",
1172
+ status: "stable",
1173
+ dependencies: [],
1174
+ registryDependencies: [
1175
+ "badge",
1176
+ "button",
1177
+ "card",
1178
+ "empty-state",
1179
+ "progress",
1180
+ "table"
1181
+ ],
1182
+ files: ["billing.tsx"],
1183
+ a11y: "Every usage meter states where it stands in words — \"8 of 10 seats used, 2 left\", or how far over the limit it is — because a bar that turns red says nothing to a screen reader and nothing to anyone who cannot tell the colours apart; the bar itself is hidden from the accessibility tree so the same fact is not announced twice. Each invoice download is named after its invoice rather than being one of ten identical \"Download\" links, which is all a links list would show. A card's last four digits are spoken as digits instead of as a four-figure number. Dates carry a machine-readable time element."
1184
+ }),
1185
+ defineMeta({
1186
+ name: "command-center",
1187
+ kind: "block",
1188
+ title: "Command center",
1189
+ description: "Operations at a glance: service health worst-first, open incidents by severity, capacity meters, a filterable log stream, and a command palette for every action an operator reaches for.",
1190
+ category: "data",
1191
+ status: "stable",
1192
+ access: "pro",
1193
+ dependencies: [],
1194
+ registryDependencies: [
1195
+ "alert",
1196
+ "badge",
1197
+ "button",
1198
+ "card",
1199
+ "command",
1200
+ "empty-state",
1201
+ "log-viewer",
1202
+ "meter"
1203
+ ],
1204
+ files: ["command-center.tsx"],
1205
+ a11y: "The overall status is one sentence computed from the worst service, and it comes first — 'one service is down' rather than a wall of tiles to scan. It is not a live region: the page is refreshed by the application, and a status that re-announces on every poll is one nobody can work beside. Health and severity are words on every row, sorted on, with colour agreeing rather than replacing. ⌘K opens the palette but the Actions button is always visible, so the shortcut is an accelerator and not the only way in. New log lines are not read aloud unless asked."
1206
+ }),
1207
+ defineMeta({
1208
+ name: "crm",
1209
+ kind: "block",
1210
+ title: "CRM",
1211
+ description: "A sales pipeline: open value by stage, the deals in it with filtering and sorting, the win rate and cycle length, and the last few touches.",
1212
+ category: "data",
1213
+ status: "stable",
1214
+ access: "pro",
1215
+ dependencies: ["@tanstack/react-table"],
1216
+ registryDependencies: [
1217
+ "activity-feed",
1218
+ "avatar",
1219
+ "badge",
1220
+ "button",
1221
+ "card",
1222
+ "data-table",
1223
+ "empty-state",
1224
+ "input",
1225
+ "label",
1226
+ "meter",
1227
+ "metric-delta",
1228
+ "select"
1229
+ ],
1230
+ files: ["crm.tsx"],
1231
+ a11y: "The pipeline is one bar split by stage, with the same figures listed beside it as numbers, so nobody reads a proportion off a coloured segment; the meter announces the total and its legend names every stage. Sales cycle is declared lower-is-better, so a slowing pipeline is not painted green. The deal filter has a real label and announces the matching count politely, and each Open button is named after its deal rather than being one of ten identical buttons."
1232
+ }),
861
1233
  defineMeta({
862
1234
  name: "dashboard",
863
1235
  kind: "block",
@@ -913,6 +1285,23 @@ const blockMetas = [
913
1285
  files: ["login.tsx"],
914
1286
  a11y: "Email and password fields carry the right type and autocomplete, so password managers and mobile keyboards behave. Field errors are tied to their inputs through the Form wiring; a server error is an assertive alert, because it arrived after the user acted and explains why nothing happened. The submit button reports its own busy state rather than going quiet."
915
1287
  }),
1288
+ defineMeta({
1289
+ name: "onboarding",
1290
+ kind: "block",
1291
+ title: "Onboarding",
1292
+ description: "A setup checklist: what is done, which step is current, and what is blocked and why.",
1293
+ category: "feedback",
1294
+ status: "stable",
1295
+ dependencies: [],
1296
+ registryDependencies: [
1297
+ "badge",
1298
+ "button",
1299
+ "card",
1300
+ "progress"
1301
+ ],
1302
+ files: ["onboarding.tsx"],
1303
+ a11y: "Every step states its state in a word — Done, In progress, Not started, Blocked — because a green tick announces nothing and means nothing to a reader who cannot tell the colours apart; the tick and the step number are decoration on top of that. Progress is given as a count rather than only as a bar, and the bar is hidden so the same fact is not announced twice. The current step carries aria-current, so it can be found rather than merely seen. Blocked is a distinct state from not-started and says why. Each step's action is named after its step, so six buttons are not six identical \"Start\" entries in a controls list."
1304
+ }),
916
1305
  defineMeta({
917
1306
  name: "pricing",
918
1307
  kind: "block",
@@ -988,6 +1377,30 @@ const blockMetas = [
988
1377
  * words — `switch` cannot be a binding name.
989
1378
  */
990
1379
  const componentMetas = [
1380
+ meta$91,
1381
+ meta$90,
1382
+ meta$89,
1383
+ meta$88,
1384
+ meta$87,
1385
+ meta$86,
1386
+ meta$85,
1387
+ meta$84,
1388
+ meta$83,
1389
+ meta$82,
1390
+ meta$81,
1391
+ meta$80,
1392
+ meta$79,
1393
+ meta$78,
1394
+ meta$77,
1395
+ meta$76,
1396
+ meta$75,
1397
+ meta$74,
1398
+ meta$73,
1399
+ meta$72,
1400
+ meta$71,
1401
+ meta$70,
1402
+ meta$69,
1403
+ meta$68,
991
1404
  meta$67,
992
1405
  meta$66,
993
1406
  meta$65,
@@ -1000,8 +1413,8 @@ const componentMetas = [
1000
1413
  meta$58,
1001
1414
  meta$57,
1002
1415
  meta$56,
1003
- meta$55,
1004
1416
  meta$54,
1417
+ meta$55,
1005
1418
  meta$53,
1006
1419
  meta$52,
1007
1420
  meta$51,
@@ -1024,8 +1437,8 @@ const componentMetas = [
1024
1437
  meta$34,
1025
1438
  meta$33,
1026
1439
  meta$32,
1027
- meta$31,
1028
1440
  meta$30,
1441
+ meta$31,
1029
1442
  meta$29,
1030
1443
  meta$28,
1031
1444
  meta$27,
@@ -1038,16 +1451,7 @@ const componentMetas = [
1038
1451
  meta$20,
1039
1452
  meta$19,
1040
1453
  meta$18,
1041
- meta$17,
1042
- meta$16,
1043
- meta$15,
1044
- meta$14,
1045
- meta$13,
1046
- meta$12,
1047
- meta$11,
1048
- meta$10,
1049
- meta$9,
1050
- meta$8
1454
+ meta$17
1051
1455
  ];
1052
1456
  //#endregion
1053
1457
  //#region src/build.ts
@@ -1097,7 +1501,8 @@ function buildSourceItem(meta) {
1097
1501
  dependencies: meta.dependencies,
1098
1502
  registryDependencies: meta.registryDependencies,
1099
1503
  files,
1100
- a11y: meta.a11y
1504
+ a11y: meta.a11y,
1505
+ access: meta.access ?? "free"
1101
1506
  });
1102
1507
  }
1103
1508
  /**
@@ -1167,6 +1572,14 @@ function buildRegistry() {
1167
1572
  ...blockMetas.map(buildSourceItem)
1168
1573
  ];
1169
1574
  }
1575
+ /** Items whose source anyone may fetch. */
1576
+ function freeItems(items) {
1577
+ return items.filter((item) => item.access !== "pro");
1578
+ }
1579
+ /** Items whose source requires a licence. */
1580
+ function proItems(items) {
1581
+ return items.filter((item) => item.access === "pro");
1582
+ }
1170
1583
  function buildIndex(items) {
1171
1584
  const entries = items.map((item) => ({
1172
1585
  name: item.name,
@@ -1177,6 +1590,7 @@ function buildIndex(items) {
1177
1590
  status: item.status,
1178
1591
  dependencies: item.dependencies,
1179
1592
  registryDependencies: item.registryDependencies,
1593
+ access: item.access,
1180
1594
  fileCount: item.files.length
1181
1595
  }));
1182
1596
  return registryIndexSchema.parse({
@@ -1186,28 +1600,64 @@ function buildIndex(items) {
1186
1600
  items: entries
1187
1601
  });
1188
1602
  }
1603
+ /**
1604
+ * Writes the public registry.
1605
+ *
1606
+ * The index lists everything, including licensed items — that is the catalogue,
1607
+ * and an item nobody can see is an item nobody buys. What it does *not* write is
1608
+ * a licensed item's body: those files never touch the directory a CDN serves,
1609
+ * because a paywall that can be stepped around by fetching the JSON directly is
1610
+ * not a paywall. Their bodies go to `writeLicensedModule` instead, behind a
1611
+ * request the server can refuse.
1612
+ */
1189
1613
  function writeRegistry(outDir) {
1190
1614
  const items = buildRegistry();
1191
1615
  const index = buildIndex(items);
1616
+ const free = freeItems(items);
1192
1617
  rmSync(outDir, {
1193
1618
  recursive: true,
1194
1619
  force: true
1195
1620
  });
1196
1621
  mkdirSync(outDir, { recursive: true });
1197
1622
  writeFileSync(join(outDir, "index.json"), `${JSON.stringify(index, null, 2)}\n`);
1198
- for (const item of items) writeFileSync(join(outDir, `${item.name}.json`), `${JSON.stringify(item, null, 2)}\n`);
1623
+ for (const item of free) writeFileSync(join(outDir, `${item.name}.json`), `${JSON.stringify(item, null, 2)}\n`);
1199
1624
  return {
1200
1625
  items: items.length,
1201
- files: items.reduce((total, item) => total + item.files.length, 0)
1626
+ files: free.reduce((total, item) => total + item.files.length, 0),
1627
+ licensed: items.length - free.length
1202
1628
  };
1203
1629
  }
1630
+ /**
1631
+ * Writes the licensed item bodies as a TypeScript module.
1632
+ *
1633
+ * A module rather than a directory of JSON, because the server that serves
1634
+ * these runs on a platform that traces imports to decide what to deploy. A file
1635
+ * read at runtime from a path the bundler never saw is a file that is not there
1636
+ * in production — and the failure appears only once someone with a valid
1637
+ * licence tries to install something.
1638
+ */
1639
+ function writeLicensedModule(outFile) {
1640
+ const licensed = proItems(buildRegistry());
1641
+ const byName = Object.fromEntries(licensed.map((item) => [item.name, item]));
1642
+ mkdirSync(dirname(outFile), { recursive: true });
1643
+ writeFileSync(outFile, `// Generated by @dowel-ui/registry. Do not edit.
1644
+ //
1645
+ // The bodies of licensed registry items. Imported by the route that serves them
1646
+ // so the platform's dependency tracing includes them in the deployment; they are
1647
+ // deliberately absent from the public registry directory.
1648
+ import type { RegistryItem } from "@dowel-ui/registry";
1649
+
1650
+ export const licensedItems: Record<string, RegistryItem> = ${JSON.stringify(byName, null, 2)};
1651
+ `);
1652
+ return licensed.length;
1653
+ }
1204
1654
  if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL(process.argv[1]).href) {
1205
1655
  const outIndex = process.argv.indexOf("--out");
1206
1656
  const outDir = outIndex === -1 ? join(here, "..", "r") : process.argv[outIndex + 1] ?? join(here, "..", "r");
1207
1657
  const result = writeRegistry(outDir);
1208
- console.log(`Registry written to ${outDir}: ${String(result.items)} items, ${String(result.files)} files.`);
1658
+ console.log(`Registry written to ${outDir}: ${String(result.items)} items, ${String(result.files)} files` + (result.licensed > 0 ? `, ${String(result.licensed)} licensed item(s) withheld.` : "."));
1209
1659
  }
1210
1660
  //#endregion
1211
- export { buildIndex, buildRegistry, writeRegistry };
1661
+ export { buildIndex, buildRegistry, freeItems, proItems, writeLicensedModule, writeRegistry };
1212
1662
 
1213
1663
  //# sourceMappingURL=build.js.map