@westonkd/sprint 0.7.0 → 0.8.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/index.d.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import { ComponentPropsWithRef } from 'react';
2
+ import { InputHTMLAttributes } from 'react';
2
3
  import { JSX } from 'react';
3
4
  import { MouseEvent as MouseEvent_2 } from 'react';
4
5
  import { MouseEventHandler } from 'react';
5
6
  import { Provider } from 'react';
7
+ import { ReactElement } from 'react';
6
8
  import { ReactNode } from 'react';
7
9
  import { Ref } from 'react';
10
+ import { RefObject } from 'react';
11
+ import { TextareaHTMLAttributes } from 'react';
8
12
 
9
13
  export declare function accessibleText(element: Element): string | undefined;
10
14
 
@@ -180,6 +184,8 @@ export declare interface AgentToolSpec {
180
184
 
181
185
  export declare function Alert(props: AlertProps): JSX.Element;
182
186
 
187
+ declare type AlertAnnouncement = "assertive" | "polite";
188
+
183
189
  export declare const alertMeta: {
184
190
  readonly name: "Alert";
185
191
  readonly category: "feedback";
@@ -203,6 +209,11 @@ export declare const alertMeta: {
203
209
  readonly values: readonly ["neutral", "info", "warning", "danger"];
204
210
  readonly default: "info";
205
211
  };
212
+ readonly announce: {
213
+ readonly kind: "enum";
214
+ readonly description: "Override how assistive technology announces the alert. assertive renders role=alert and interrupts; polite renders role=status and waits. Defaults to assertive for danger and warning and polite otherwise. Pass polite for a standing warning, such as an offline notice, that should not interrupt.";
215
+ readonly values: readonly ["assertive", "polite"];
216
+ };
206
217
  readonly onDismiss: {
207
218
  readonly kind: "handler";
208
219
  readonly description: "Called when the dismiss control is pressed. Providing it renders the control and registers the dismiss tool; the page owns removing the alert.";
@@ -246,15 +257,20 @@ export declare const alertMeta: {
246
257
  readonly title: "A failure";
247
258
  readonly description: "Danger announces assertively via role=alert.";
248
259
  readonly code: "<Alert tone=\"danger\" label=\"Sign-in failed\">Wrong callsign or access code.</Alert>";
260
+ }, {
261
+ readonly title: "A standing warning";
262
+ readonly description: "A warning that stays on screen while a condition holds announces politely, so it does not interrupt whatever the person is reading.";
263
+ readonly code: "<Alert tone=\"warning\" announce=\"polite\" label=\"Offline\">Changes are saved on this device until the connection returns.</Alert>";
249
264
  }];
250
265
  readonly a11y: {
251
266
  readonly role: "status";
252
- readonly notes: "Danger and warning render role=alert and announce assertively; info and neutral render role=status. The dismiss control is a labelled button. Render the alert when the condition occurs rather than toggling its visibility, or the announcement is lost.";
267
+ readonly notes: "Danger and warning render role=alert and announce assertively; info and neutral render role=status, unless announce overrides it. The dismiss control is a labelled button. Render the alert when the condition occurs rather than toggling its visibility, or the announcement is lost.";
253
268
  };
254
269
  };
255
270
 
256
271
  export declare interface AlertProps extends ComponentPropsWithRef<"div"> {
257
272
  tone?: AlertTone;
273
+ announce?: AlertAnnouncement;
258
274
  label?: string;
259
275
  onDismiss?: () => void;
260
276
  agentName?: string;
@@ -263,6 +279,78 @@ export declare interface AlertProps extends ComponentPropsWithRef<"div"> {
263
279
 
264
280
  export declare type AlertTone = "neutral" | "info" | "warning" | "danger";
265
281
 
282
+ export declare function Avatar(props: AvatarProps): JSX.Element | null;
283
+
284
+ export declare const avatarMeta: {
285
+ readonly name: "Avatar";
286
+ readonly category: "display";
287
+ readonly summary: "A person's photo in a circle, falling back to their initials when there is no photo or it fails to load.";
288
+ readonly whenToUse: "Use beside a person's name in a row, a card, or an account menu trigger. Pass the full name; it is the accessible name and the source of the initials. Set decorative when the name is already printed right beside it, so it is not read twice.";
289
+ readonly whenNotToUse: "Do not use for a logo or an illustration; that is an Image. Do not use as a button on its own; put it inside a Button or a Menu trigger that carries the label.";
290
+ readonly status: "experimental";
291
+ readonly props: {
292
+ readonly name: {
293
+ readonly kind: "string";
294
+ readonly description: "The person's name. Names the avatar and supplies the initials.";
295
+ readonly required: true;
296
+ };
297
+ readonly src: {
298
+ readonly kind: "string";
299
+ readonly description: "The photo's URL. Without it, or if it fails to load, the initials show instead.";
300
+ };
301
+ readonly size: {
302
+ readonly kind: "enum";
303
+ readonly description: "small for dense rows, medium beside body text, large in a profile header.";
304
+ readonly values: readonly ["small", "medium", "large"];
305
+ readonly default: "medium";
306
+ };
307
+ readonly decorative: {
308
+ readonly kind: "boolean";
309
+ readonly description: "Hide the avatar from assistive technology and the agent view, for when the name is printed beside it.";
310
+ readonly default: false;
311
+ };
312
+ };
313
+ readonly state: {
314
+ readonly photo: {
315
+ readonly description: "Present while a photo is showing rather than initials.";
316
+ readonly attribute: "data-sprint-photo";
317
+ };
318
+ readonly size: {
319
+ readonly description: "The size, when it is not medium.";
320
+ readonly attribute: "data-sprint-size";
321
+ readonly values: readonly ["small", "large"];
322
+ };
323
+ };
324
+ readonly agentView: {
325
+ readonly example: "- **Avatar** \"Ada Okafor\" [photo]";
326
+ };
327
+ readonly examples: readonly [{
328
+ readonly title: "A photo";
329
+ readonly code: "<Avatar name=\"Ada Okafor\" src=\"media/portrait.svg\" />";
330
+ }, {
331
+ readonly title: "Initials when there is no photo";
332
+ readonly description: "The first and last initials, on the inset surface.";
333
+ readonly code: "<Avatar name=\"Brother Lind\" size=\"large\" />";
334
+ }, {
335
+ readonly title: "Beside a printed name";
336
+ readonly description: "decorative keeps a screen reader and an agent from hearing the name twice.";
337
+ readonly code: "<Stack direction=\"row\" gap=\"snug\" align=\"center\">\n <Avatar name=\"Sister Amaral\" size=\"small\" decorative />\n <Text as=\"span\">Sister Amaral</Text>\n</Stack>";
338
+ }];
339
+ readonly a11y: {
340
+ readonly role: "img";
341
+ readonly notes: "A role=img element named by the person's name, with the photo's own alt left empty so the name is read once. decorative switches it to aria-hidden.";
342
+ };
343
+ };
344
+
345
+ export declare interface AvatarProps extends Omit<ComponentPropsWithRef<"span">, "children"> {
346
+ name: string;
347
+ src?: string;
348
+ size?: AvatarSize;
349
+ decorative?: boolean;
350
+ }
351
+
352
+ export declare type AvatarSize = "small" | "medium" | "large";
353
+
266
354
  export declare function Breadcrumb(props: BreadcrumbProps): JSX.Element;
267
355
 
268
356
  export declare interface BreadcrumbAction {
@@ -475,6 +563,25 @@ export declare const buttonMeta: {
475
563
  readonly values: readonly ["neutral", "action", "danger"];
476
564
  readonly default: "neutral";
477
565
  };
566
+ readonly size: {
567
+ readonly kind: "enum";
568
+ readonly description: "medium for ordinary forms and toolbars; small for dense surfaces such as table rows, cards and toasts.";
569
+ readonly values: readonly ["medium", "small"];
570
+ readonly default: "medium";
571
+ };
572
+ readonly icon: {
573
+ readonly kind: "node";
574
+ readonly description: "An icon drawn before the label, sized to the text. It is decorative and hidden from assistive technology; the label still names the button.";
575
+ };
576
+ readonly iconEnd: {
577
+ readonly kind: "node";
578
+ readonly description: "An icon drawn after the label, such as a chevron on a button that opens something. Ignored when hideLabel is set.";
579
+ };
580
+ readonly hideLabel: {
581
+ readonly kind: "boolean";
582
+ readonly description: "Show only the icon, as a square button. The label stays in the page for screen readers and agents, names the press tool, and appears as a tooltip on hover and focus. Requires icon.";
583
+ readonly default: false;
584
+ };
478
585
  readonly block: {
479
586
  readonly kind: "boolean";
480
587
  readonly description: "Render as a full-width bar. Combine with tone=\"action\" for the primary action of a region.";
@@ -510,6 +617,11 @@ export declare const buttonMeta: {
510
617
  readonly attribute: "data-sprint-tone";
511
618
  readonly values: readonly ["neutral", "action", "danger"];
512
619
  };
620
+ readonly size: {
621
+ readonly description: "Present as small on a compact button.";
622
+ readonly attribute: "data-sprint-size";
623
+ readonly values: readonly ["small"];
624
+ };
513
625
  readonly block: {
514
626
  readonly description: "Present when the button renders as a full-width bar.";
515
627
  readonly attribute: "data-sprint-block";
@@ -530,6 +642,14 @@ export declare const buttonMeta: {
530
642
  readonly example: "- **Button** \"Prepare launch\" [tone=action] → tool `press-prepare-launch`";
531
643
  };
532
644
  readonly examples: readonly [{
645
+ readonly title: "An icon-only button";
646
+ readonly description: "hideLabel draws only the icon. The label still names the button and its tool, and shows as a tooltip, so the control is never a mystery.";
647
+ readonly code: "<Button icon={<SidebarIcon />} hideLabel onClick={collapse}>\n Hide sidebar\n</Button>";
648
+ }, {
649
+ readonly title: "A compact button with an icon";
650
+ readonly description: "size=\"small\" for a dense row; the icon sits before the label.";
651
+ readonly code: "<Button size=\"small\" icon={<PlusIcon />} onClick={addSpeaker}>\n Add speaker\n</Button>";
652
+ }, {
533
653
  readonly title: "Primary action";
534
654
  readonly description: "The one rationed acid action bar for a view.";
535
655
  readonly code: "<Button tone=\"action\" block onClick={prepare}>Prepare launch</Button>";
@@ -558,12 +678,18 @@ export declare const buttonMeta: {
558
678
 
559
679
  export declare interface ButtonProps extends ComponentPropsWithRef<"button"> {
560
680
  tone?: ButtonTone;
681
+ size?: ButtonSize;
682
+ icon?: ReactNode;
683
+ iconEnd?: ReactNode;
684
+ hideLabel?: boolean;
561
685
  block?: boolean;
562
686
  loading?: boolean;
563
687
  agentName?: string;
564
688
  agentTool?: boolean;
565
689
  }
566
690
 
691
+ export declare type ButtonSize = "medium" | "small";
692
+
567
693
  export declare type ButtonTone = "neutral" | "action" | "danger";
568
694
 
569
695
  export declare function Card(props: CardProps): JSX.Element;
@@ -768,6 +894,11 @@ export declare const checkboxMeta: {
768
894
  readonly description: "Whether the box is checked. The box is fully controlled.";
769
895
  readonly required: true;
770
896
  };
897
+ readonly indeterminate: {
898
+ readonly kind: "boolean";
899
+ readonly description: "Show the box as partly checked, for a parent box whose children are some checked and some not. It overrides checked visually and reads as checked=mixed to agents and assistive technology. Pressing a mixed box calls onChange(true); the page then clears indeterminate.";
900
+ readonly default: false;
901
+ };
771
902
  readonly onChange: {
772
903
  readonly kind: "handler";
773
904
  readonly description: "Called with the new checked state. The set tool drives a real click, so this runs for agent changes too.";
@@ -807,7 +938,7 @@ export declare const checkboxMeta: {
807
938
  };
808
939
  readonly state: {
809
940
  readonly checked: {
810
- readonly description: "Present while the box is checked.";
941
+ readonly description: "Present while the box is checked, and \"mixed\" while it is indeterminate.";
811
942
  readonly attribute: "data-sprint-checked";
812
943
  };
813
944
  readonly disabled: {
@@ -841,6 +972,10 @@ export declare const checkboxMeta: {
841
972
  readonly title: "A disabled box";
842
973
  readonly description: "Disabled unregisters the tool, so an agent cannot change what a person could not.";
843
974
  readonly code: "<Checkbox\n label=\"Telemetry\"\n checked\n disabled\n onChange={setTelemetry}\n/>";
975
+ }, {
976
+ readonly title: "A partly picked group";
977
+ readonly description: "A parent box over a group shows mixed while only some of the group is picked. Pressing it picks everything.";
978
+ readonly code: "<Checkbox\n label=\"Whole crew\"\n checked={picked.length === crew.length}\n indeterminate={picked.length > 0 && picked.length < crew.length}\n onChange={(next) => setPicked(next ? crew : [])}\n/>";
844
979
  }];
845
980
  readonly a11y: {
846
981
  readonly role: "checkbox";
@@ -852,6 +987,7 @@ export declare const checkboxMeta: {
852
987
  export declare interface CheckboxProps extends Omit<ComponentPropsWithRef<"div">, "onChange"> {
853
988
  label: string;
854
989
  checked: boolean;
990
+ indeterminate?: boolean;
855
991
  onChange: (checked: boolean) => void;
856
992
  hint?: string;
857
993
  error?: string;
@@ -982,6 +1118,10 @@ export declare interface ChoiceOption {
982
1118
  glyph?: string;
983
1119
  }
984
1120
 
1121
+ export declare const CHOOSE_COMBOBOX_TOOL: AgentToolSpec;
1122
+
1123
+ export declare const CHOOSE_MENU_TOOL: AgentToolSpec;
1124
+
985
1125
  export declare const CHOOSE_TOOL: AgentToolSpec;
986
1126
 
987
1127
  export declare function clamp(text: string, max: number): string;
@@ -1064,6 +1204,214 @@ export declare interface CodeBlockProps extends ComponentPropsWithRef<"figure">
1064
1204
 
1065
1205
  export declare type CodeLanguage = "tsx" | "json" | "bash" | "text";
1066
1206
 
1207
+ export declare function Combobox(props: ComboboxProps): JSX.Element;
1208
+
1209
+ export declare type ComboboxFilter = (option: ComboboxOption, query: string) => boolean;
1210
+
1211
+ export declare const comboboxMeta: {
1212
+ readonly name: "Combobox";
1213
+ readonly category: "input";
1214
+ readonly summary: "A searchable single-choice field for long lists: type to filter, arrow to an option, Enter to pick. Options can be grouped, described, limited per group and rendered your own way. Registers one choose tool that takes a label or a search.";
1215
+ readonly whenToUse: "Use when there are too many options to scan: a member picker over hundreds of people, a calling picker grouped by organization, a board filter. Options hold data: { value, label, group?, description?, keywords?, disabled? }. The default filter matches every word of the query against the label, description and keywords, ignoring case and accents. For a server-side search, pass filter={false}, update options from onQueryChange, and set loading while results arrive.";
1216
+ readonly whenNotToUse: "Do not use for a short list a person can scan at once; that is a Select, or a SegmentedControl for two to four options. Do not use for free text that only suggests; this field only accepts its options. Do not use for choosing several values.";
1217
+ readonly status: "experimental";
1218
+ readonly props: {
1219
+ readonly label: {
1220
+ readonly kind: "string";
1221
+ readonly description: "What is being chosen, such as \"Member\". Names the field and derives the tool name.";
1222
+ readonly required: true;
1223
+ };
1224
+ readonly options: {
1225
+ readonly kind: "array";
1226
+ readonly description: "The options: { value, label, group?, description?, keywords?, disabled? }. Consecutive and non-consecutive options sharing a group are gathered under one heading, in the order groups first appear. keywords are extra search terms that are never shown.";
1227
+ readonly required: true;
1228
+ };
1229
+ readonly value: {
1230
+ readonly kind: "string";
1231
+ readonly description: "The chosen option's value, or an empty string for none. Fully controlled.";
1232
+ readonly required: true;
1233
+ };
1234
+ readonly onChange: {
1235
+ readonly kind: "handler";
1236
+ readonly description: "Called with the chosen value, or an empty string when cleared.";
1237
+ readonly required: true;
1238
+ };
1239
+ readonly placeholder: {
1240
+ readonly kind: "string";
1241
+ readonly description: "Ghost text while nothing is chosen or while searching.";
1242
+ };
1243
+ readonly hint: {
1244
+ readonly kind: "string";
1245
+ readonly description: "Guidance under the field. Replaced by error while one is set.";
1246
+ };
1247
+ readonly error: {
1248
+ readonly kind: "string";
1249
+ readonly description: "A validation message that marks the field invalid.";
1250
+ };
1251
+ readonly name: {
1252
+ readonly kind: "string";
1253
+ readonly description: "The native form name the chosen value submits under.";
1254
+ };
1255
+ readonly disabled: {
1256
+ readonly kind: "boolean";
1257
+ readonly description: "Disable the field and unregister the choose tool.";
1258
+ readonly default: false;
1259
+ };
1260
+ readonly required: {
1261
+ readonly kind: "boolean";
1262
+ readonly description: "Mark the field required. A required field is not clearable by default.";
1263
+ readonly default: false;
1264
+ };
1265
+ readonly hideLabel: {
1266
+ readonly kind: "boolean";
1267
+ readonly description: "Hide the label visually while keeping it for screen readers and agents. Pair it with a placeholder.";
1268
+ readonly default: false;
1269
+ };
1270
+ readonly clearable: {
1271
+ readonly kind: "boolean";
1272
+ readonly description: "Show a clear control while something is chosen. Defaults to not required.";
1273
+ };
1274
+ readonly clearLabel: {
1275
+ readonly kind: "string";
1276
+ readonly description: "Accessible name of the clear control.";
1277
+ readonly default: "Clear";
1278
+ };
1279
+ readonly loading: {
1280
+ readonly kind: "boolean";
1281
+ readonly description: "Show a small spinner in the field while options are being fetched.";
1282
+ readonly default: false;
1283
+ };
1284
+ readonly filter: {
1285
+ readonly kind: "handler";
1286
+ readonly description: "(option, query) => boolean, replacing the built-in word match. Pass false when the options are already the results of a search the page ran.";
1287
+ };
1288
+ readonly onQueryChange: {
1289
+ readonly kind: "handler";
1290
+ readonly description: "Called with the search text as it changes, and with an empty string on close.";
1291
+ };
1292
+ readonly groupLimit: {
1293
+ readonly kind: "number";
1294
+ readonly description: "Show at most this many options per group, with a \"more; keep typing\" note for the rest, so one large group cannot bury the others.";
1295
+ };
1296
+ readonly limit: {
1297
+ readonly kind: "number";
1298
+ readonly description: "Show at most this many options in total.";
1299
+ readonly default: 200;
1300
+ };
1301
+ readonly emptyLabel: {
1302
+ readonly kind: "string";
1303
+ readonly description: "What the list says when nothing matches.";
1304
+ readonly default: "No matches";
1305
+ };
1306
+ readonly renderOption: {
1307
+ readonly kind: "handler";
1308
+ readonly description: "(option) => ReactNode, drawing an option your own way, such as with an Avatar. It is the human rendering only: the label is still what is searched, announced and offered to agents.";
1309
+ };
1310
+ readonly inputRef: {
1311
+ readonly kind: "object";
1312
+ readonly description: "A ref to the underlying input, for focusing it.";
1313
+ };
1314
+ readonly agentName: {
1315
+ readonly kind: "string";
1316
+ readonly description: "Override the label used to derive the tool name.";
1317
+ };
1318
+ readonly agentTool: {
1319
+ readonly kind: "boolean";
1320
+ readonly description: "Set false to render the field without registering the choose tool.";
1321
+ readonly default: true;
1322
+ };
1323
+ };
1324
+ readonly state: {
1325
+ readonly value: {
1326
+ readonly description: "The chosen option's label.";
1327
+ readonly attribute: "data-sprint-value";
1328
+ };
1329
+ readonly empty: {
1330
+ readonly description: "Present while nothing is chosen.";
1331
+ readonly attribute: "data-sprint-empty";
1332
+ };
1333
+ readonly options: {
1334
+ readonly description: "How many options the field holds.";
1335
+ readonly attribute: "data-sprint-options";
1336
+ };
1337
+ readonly loading: {
1338
+ readonly description: "Present while options are being fetched.";
1339
+ readonly attribute: "data-sprint-loading";
1340
+ };
1341
+ readonly disabled: {
1342
+ readonly description: "Present when the field cannot be changed.";
1343
+ readonly attribute: "data-sprint-disabled";
1344
+ };
1345
+ readonly required: {
1346
+ readonly description: "Present when a choice is needed.";
1347
+ readonly attribute: "data-sprint-required";
1348
+ };
1349
+ readonly invalid: {
1350
+ readonly description: "Present while an error is set.";
1351
+ readonly attribute: "data-sprint-invalid";
1352
+ };
1353
+ };
1354
+ readonly tools: {
1355
+ readonly choose: AgentToolSpec;
1356
+ };
1357
+ readonly agentView: {
1358
+ readonly example: "- **Combobox** \"Member\" [empty, options=312, listed=50] → tool `choose-member`\n - part `option` \"Ada Okafor\" [group=Elders quorum]";
1359
+ };
1360
+ readonly examples: readonly [{
1361
+ readonly title: "A grouped member picker";
1362
+ readonly description: "Hundreds of members grouped by organization, at most five per group until the person types. An agent passes a name, or part of one, to the choose tool.";
1363
+ readonly code: "<Combobox\n label=\"Member\"\n placeholder=\"Search members\"\n value={memberId}\n onChange={setMemberId}\n groupLimit={5}\n options={members.map((member) => ({\n value: member.id,\n label: member.name,\n group: member.organization,\n keywords: [member.preferredName],\n }))}\n/>";
1364
+ }, {
1365
+ readonly title: "Options drawn your own way";
1366
+ readonly description: "renderOption draws each option with an avatar; the label still drives search, announcement and the agent view.";
1367
+ readonly code: "<Combobox\n label=\"Speaker\"\n value={speaker}\n onChange={setSpeaker}\n options={members}\n renderOption={(option) => (\n <Stack direction=\"row\" gap=\"snug\" align=\"center\">\n <Avatar name={option.label} size=\"small\" decorative />\n <Text as=\"span\">{option.label}</Text>\n </Stack>\n )}\n/>";
1368
+ }, {
1369
+ readonly title: "A server-side search";
1370
+ readonly description: "filter={false} trusts the options as given; the page searches on each onQueryChange and shows the spinner meanwhile.";
1371
+ readonly code: "<Combobox\n label=\"Calling\"\n value={calling}\n onChange={setCalling}\n filter={false}\n onQueryChange={search}\n loading={searching}\n options={results}\n error={calling === \"\" ? \"Choose a calling.\" : undefined}\n required\n/>";
1372
+ }];
1373
+ readonly a11y: {
1374
+ readonly role: "combobox";
1375
+ readonly keyboard: readonly ["Typing filters and opens the list", "Down and Up Arrow open the list and move between enabled options", "Ctrl+Home and Ctrl+End jump to the first and last option", "Enter chooses the highlighted option", "Escape closes the list and restores the chosen label"];
1376
+ readonly notes: "An editable combobox with list autocomplete. Focus stays in the input and aria-activedescendant follows the highlighted option. Groups are labelled role=group sections. The list is a popover in the top layer, rendered in place, so it opens above a modal Dialog and flips above the field when there is no room below.";
1377
+ };
1378
+ };
1379
+
1380
+ export declare interface ComboboxOption {
1381
+ value: string;
1382
+ label: string;
1383
+ group?: string | undefined;
1384
+ description?: string | undefined;
1385
+ keywords?: readonly string[] | undefined;
1386
+ disabled?: boolean | undefined;
1387
+ }
1388
+
1389
+ export declare interface ComboboxProps extends Omit<ComponentPropsWithRef<"div">, "onChange"> {
1390
+ label: string;
1391
+ options: readonly ComboboxOption[];
1392
+ value: string;
1393
+ onChange: (value: string) => void;
1394
+ placeholder?: string;
1395
+ hint?: string | undefined;
1396
+ error?: string | undefined;
1397
+ name?: string;
1398
+ disabled?: boolean;
1399
+ required?: boolean;
1400
+ hideLabel?: boolean;
1401
+ clearable?: boolean;
1402
+ clearLabel?: string;
1403
+ loading?: boolean;
1404
+ filter?: ComboboxFilter | false;
1405
+ onQueryChange?: (query: string) => void;
1406
+ groupLimit?: number;
1407
+ limit?: number;
1408
+ emptyLabel?: string;
1409
+ renderOption?: (option: ComboboxOption) => ReactNode;
1410
+ inputRef?: Ref<HTMLInputElement>;
1411
+ agentName?: string;
1412
+ agentTool?: boolean;
1413
+ }
1414
+
1067
1415
  export declare const COMPONENT_ATTRIBUTE = "data-sprint";
1068
1416
 
1069
1417
  export declare function CopyField(props: CopyFieldProps): JSX.Element;
@@ -1260,6 +1608,16 @@ export declare const dialogMeta: {
1260
1608
  readonly description: "Render the title as a real heading at this level, joining the page outline.";
1261
1609
  readonly values: readonly ["2", "3", "4"];
1262
1610
  };
1611
+ readonly size: {
1612
+ readonly kind: "enum";
1613
+ readonly description: "The dialog's width: small (26rem) for a confirmation, medium (36rem) for a short form, large (52rem) for a table or a two-column form. Every size shrinks to fit a phone.";
1614
+ readonly values: readonly ["small", "medium", "large"];
1615
+ readonly default: "small";
1616
+ };
1617
+ readonly initialFocus: {
1618
+ readonly kind: "object";
1619
+ readonly description: "A ref to the element that takes focus when the dialog opens, such as the first field of a form or the safe choice in a destructive confirmation. Without it the browser focuses the first focusable element, which is the close control.";
1620
+ };
1263
1621
  readonly owner: {
1264
1622
  readonly kind: "string";
1265
1623
  readonly description: "The tool name of the control that opened this dialog. Published as data-sprint-owner so a reading agent can attach the dialog to its opener.";
@@ -1279,6 +1637,11 @@ export declare const dialogMeta: {
1279
1637
  readonly description: "Present while the dialog is shown. A closed dialog is absent from the DOM entirely.";
1280
1638
  readonly attribute: "data-sprint-open";
1281
1639
  };
1640
+ readonly size: {
1641
+ readonly description: "The width the dialog was given, when it is not the default small.";
1642
+ readonly attribute: "data-sprint-size";
1643
+ readonly values: readonly ["medium", "large"];
1644
+ };
1282
1645
  };
1283
1646
  readonly tools: {
1284
1647
  readonly close: AgentToolSpec;
@@ -1294,6 +1657,10 @@ export declare const dialogMeta: {
1294
1657
  readonly title: "Owned by its opener";
1295
1658
  readonly description: "Passing the opener's tool name lets a reading agent attach the dialog to the control that produced it.";
1296
1659
  readonly code: "<Dialog\n label=\"Rotate secret\"\n open={rotating}\n owner=\"press-rotate-secret\"\n onClose={() => setRotating(false)}\n>\n <Text>The current secret keeps working for one hour.</Text>\n</Dialog>";
1660
+ }, {
1661
+ readonly title: "A form that focuses its first field";
1662
+ readonly description: "A medium dialog for a short form. initialFocus puts the cursor in the field instead of on the close control, so the person can type straight away.";
1663
+ readonly code: "<Dialog\n label=\"Rename station\"\n size=\"medium\"\n open={renaming}\n initialFocus={nameField}\n onClose={() => setRenaming(false)}\n>\n <Stack gap=\"tight\">\n <TextInput label=\"Station name\" value={name} onChange={setName} inputRef={nameField} />\n <Button tone=\"action\" onClick={save}>Save name</Button>\n </Stack>\n</Dialog>";
1297
1664
  }];
1298
1665
  readonly a11y: {
1299
1666
  readonly role: "dialog";
@@ -1307,11 +1674,15 @@ export declare interface DialogProps extends Omit<ComponentPropsWithRef<"dialog"
1307
1674
  open: boolean;
1308
1675
  onClose: () => void;
1309
1676
  headingLevel?: DialogHeadingLevel;
1677
+ size?: DialogSize;
1678
+ initialFocus?: RefObject<HTMLElement | null>;
1310
1679
  owner?: string;
1311
1680
  agentName?: string;
1312
1681
  agentTool?: boolean;
1313
1682
  }
1314
1683
 
1684
+ declare type DialogSize = "small" | "medium" | "large";
1685
+
1315
1686
  export declare function Disclosure(props: DisclosureProps): JSX.Element;
1316
1687
 
1317
1688
  export declare const disclosureMeta: {
@@ -1663,13 +2034,17 @@ export declare const FILL_TEXTAREA_TOOL: AgentToolSpec;
1663
2034
 
1664
2035
  export declare const FILL_TOOL: AgentToolSpec;
1665
2036
 
2037
+ export declare type FloatingAlign = "start" | "center" | "end";
2038
+
2039
+ export declare type FloatingSide = "below" | "above";
2040
+
1666
2041
  export declare function getAgentMeta(name: string): AgentComponentMeta | undefined;
1667
2042
 
1668
2043
  export declare function getModelContext(): ModelContext | null;
1669
2044
 
1670
2045
  export declare function Heading(props: HeadingProps): JSX.Element;
1671
2046
 
1672
- export declare type HeadingLevel = 1 | 2 | 3 | 4;
2047
+ export declare type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
1673
2048
 
1674
2049
  export declare const headingMeta: {
1675
2050
  readonly name: "Heading";
@@ -1686,15 +2061,25 @@ export declare const headingMeta: {
1686
2061
  };
1687
2062
  readonly level: {
1688
2063
  readonly kind: "enum";
1689
- readonly description: "Outline depth, rendered as the matching h element. 1 is the page title and there should be one per page.";
1690
- readonly values: readonly ["1", "2", "3", "4"];
2064
+ readonly description: "Outline depth, rendered as the matching h element. 1 is the page title and there should be one per page. 5 and 6 take the level-4 voice unless size says otherwise.";
2065
+ readonly values: readonly ["1", "2", "3", "4", "5", "6"];
1691
2066
  readonly default: "2";
1692
2067
  };
2068
+ readonly size: {
2069
+ readonly kind: "enum";
2070
+ readonly description: "The type voice, from 1 (display) to 4 (smallest), when it should differ from the level. Use it when a heading's place in the outline and its visual weight disagree, such as a level-2 title inside a dense card that should read small.";
2071
+ readonly values: readonly ["1", "2", "3", "4"];
2072
+ };
1693
2073
  };
1694
2074
  readonly state: {
1695
2075
  readonly level: {
1696
2076
  readonly description: "The outline depth, and so the type voice in use.";
1697
2077
  readonly attribute: "data-sprint-level";
2078
+ readonly values: readonly ["1", "2", "3", "4", "5", "6"];
2079
+ };
2080
+ readonly size: {
2081
+ readonly description: "The type voice, present only when it differs from the level.";
2082
+ readonly attribute: "data-sprint-size";
1698
2083
  readonly values: readonly ["1", "2", "3", "4"];
1699
2084
  };
1700
2085
  };
@@ -1708,13 +2093,22 @@ export declare const headingMeta: {
1708
2093
  readonly title: "A section title";
1709
2094
  readonly description: "The default level, for a region inside a page.";
1710
2095
  readonly code: "<Heading>Every variant</Heading>";
2096
+ }, {
2097
+ readonly title: "A deep heading in a small voice";
2098
+ readonly description: "The outline needs a level-3 heading, but inside a compact card it should read at the smallest size.";
2099
+ readonly code: "<Heading level={3} size={4}>Sunday speakers</Heading>";
1711
2100
  }];
1712
2101
  };
1713
2102
 
1714
2103
  export declare interface HeadingProps extends ComponentPropsWithRef<"h2"> {
1715
2104
  level?: HeadingLevel;
2105
+ size?: HeadingSize;
1716
2106
  }
1717
2107
 
2108
+ declare type HeadingSize = 1 | 2 | 3 | 4;
2109
+
2110
+ export declare function hideFloating(element: HTMLElement): void;
2111
+
1718
2112
  declare function Image_2(props: ImageProps): JSX.Element | null;
1719
2113
  export { Image_2 as Image }
1720
2114
 
@@ -1814,6 +2208,8 @@ export declare type ImageRatio = "1:1" | "4:3" | "3:2" | "16:9" | "auto";
1814
2208
 
1815
2209
  export declare type ImageStatus = "loading" | "ready" | "error";
1816
2210
 
2211
+ export declare function initialsOf(name: string): string;
2212
+
1817
2213
  export declare function isModelContextAvailable(): boolean;
1818
2214
 
1819
2215
  export declare interface JsonSchemaObject {
@@ -1833,6 +2229,42 @@ export declare interface JsonSchemaProperty {
1833
2229
 
1834
2230
  export declare type JsonSchemaType = "string" | "number" | "integer" | "boolean";
1835
2231
 
2232
+ export declare function Kbd(props: KbdProps): JSX.Element;
2233
+
2234
+ export declare const kbdMeta: {
2235
+ readonly name: "Kbd";
2236
+ readonly category: "typography";
2237
+ readonly summary: "A keyboard key or shortcut, drawn as key caps. Pass the combination as text joined with plus signs.";
2238
+ readonly whenToUse: "Use to tell a person which keys do something: an undo hint in a toast, a shortcut beside a menu item's description, a search field's focus key. Write the combination as one string, such as \"Ctrl+Z\" or \"Shift+Enter\"; each part becomes its own cap.";
2239
+ readonly whenNotToUse: "Do not use for code or for a value someone types into a field; that is code or a CodeBlock. Do not use it as a control; it does not press anything.";
2240
+ readonly status: "experimental";
2241
+ readonly props: {
2242
+ readonly children: {
2243
+ readonly kind: "string";
2244
+ readonly description: "The keys, joined with \"+\", such as \"Ctrl+Z\". A lone \"+\" is a key in its own right.";
2245
+ readonly required: true;
2246
+ };
2247
+ };
2248
+ readonly agentView: {
2249
+ readonly example: "- **Kbd** \"Ctrl+Z\"";
2250
+ };
2251
+ readonly examples: readonly [{
2252
+ readonly title: "An undo shortcut";
2253
+ readonly code: "<Kbd>Ctrl+Z</Kbd>";
2254
+ }, {
2255
+ readonly title: "A single key";
2256
+ readonly description: "One key is one cap.";
2257
+ readonly code: "<Kbd>/</Kbd>";
2258
+ }];
2259
+ readonly a11y: {
2260
+ readonly notes: "A kbd element nesting one kbd per key, which is the HTML pattern for a key combination. The plus signs between caps are hidden from assistive technology, which reads the keys in order.";
2261
+ };
2262
+ };
2263
+
2264
+ export declare interface KbdProps extends Omit<ComponentPropsWithRef<"kbd">, "children"> {
2265
+ children: string;
2266
+ }
2267
+
1836
2268
  export declare function Link(props: LinkProps): JSX.Element;
1837
2269
 
1838
2270
  export declare const linkMeta: {
@@ -2035,17 +2467,149 @@ export declare interface MarkdownOptions {
2035
2467
  includeSummary?: boolean;
2036
2468
  }
2037
2469
 
2038
- export declare function MetaLine(props: MetaLineProps): JSX.Element | null;
2470
+ export declare const matchesQuery: ComboboxFilter;
2039
2471
 
2040
- export declare interface MetaLineEntry {
2041
- term: string;
2042
- detail: string;
2472
+ export declare function Menu(props: MenuProps): JSX.Element;
2473
+
2474
+ export declare interface MenuItem {
2475
+ label: string;
2476
+ onSelect?: () => void;
2477
+ href?: string;
2478
+ external?: boolean;
2479
+ tone?: MenuItemTone;
2480
+ disabled?: boolean;
2481
+ checked?: boolean;
2482
+ group?: string;
2483
+ icon?: ReactNode;
2043
2484
  }
2044
2485
 
2045
- export declare const metaLineMeta: {
2046
- readonly name: "MetaLine";
2047
- readonly category: "display";
2048
- readonly summary: "A slash-separated manifest line of term–detail pairs: serials, build strings, issue dates. It is chrome, not content, and in the agent view it reads as the same single line of text a person sees.";
2486
+ export declare type MenuItemTone = "neutral" | "danger";
2487
+
2488
+ export declare const menuMeta: {
2489
+ readonly name: "Menu";
2490
+ readonly category: "action";
2491
+ readonly summary: "A button that opens a short list of actions, links, or one-of-several choices. Registers one choose tool enumerating the items an agent can run.";
2492
+ readonly whenToUse: "Use to gather secondary actions behind one control: a card's edit, duplicate and delete; an account menu; a share menu; a switcher between a few named views. Items hold data, not elements: each is { label, onSelect?, href?, checked?, tone?, disabled?, group?, icon? }. An item with checked becomes a radio choice with a visible mark, so a menu can also pick one value from a short list.";
2493
+ readonly whenNotToUse: "Do not use for the primary action of a view; that is a Button. Do not use to pick a form value that is submitted with other fields; that is a Select or a SegmentedControl. Do not use for navigation that should stay visible; that is a Nav or a Breadcrumb. Items take plain text labels and an optional icon, never components.";
2494
+ readonly status: "experimental";
2495
+ readonly props: {
2496
+ readonly label: {
2497
+ readonly kind: "string";
2498
+ readonly description: "What the menu holds, such as \"Card actions\" or \"Share\". It is the trigger's text, names the menu for assistive technology, and derives the choose tool name.";
2499
+ readonly required: true;
2500
+ };
2501
+ readonly items: {
2502
+ readonly kind: "array";
2503
+ readonly description: "The items in order: { label, onSelect?, href?, external?, checked?, tone?, disabled?, group?, icon? }. onSelect runs the item; href makes it a link, which agents reach by URL rather than the tool. checked, true or false, makes the item a radio choice. tone=\"danger\" marks a destructive item. Consecutive items sharing a group string are gathered under that heading.";
2504
+ readonly required: true;
2505
+ };
2506
+ readonly icon: {
2507
+ readonly kind: "node";
2508
+ readonly description: "An icon drawn on the trigger before its label.";
2509
+ };
2510
+ readonly hideLabel: {
2511
+ readonly kind: "boolean";
2512
+ readonly description: "Show only the icon on a square trigger, such as a three-dot card menu. The label stays the accessible name and the tool name, and shows as a tooltip. Requires icon.";
2513
+ readonly default: false;
2514
+ };
2515
+ readonly size: {
2516
+ readonly kind: "enum";
2517
+ readonly description: "The trigger's size, matching Button.";
2518
+ readonly values: readonly ["medium", "small"];
2519
+ readonly default: "medium";
2520
+ };
2521
+ readonly align: {
2522
+ readonly kind: "enum";
2523
+ readonly description: "Which edge of the trigger the list lines up with. Use end for a menu at the right of a card or a toolbar.";
2524
+ readonly values: readonly ["start", "center", "end"];
2525
+ readonly default: "start";
2526
+ };
2527
+ readonly side: {
2528
+ readonly kind: "enum";
2529
+ readonly description: "Where the list prefers to open. It flips to the other side when there is no room, so a menu low on the screen still opens fully in view.";
2530
+ readonly values: readonly ["below", "above"];
2531
+ readonly default: "below";
2532
+ };
2533
+ readonly disabled: {
2534
+ readonly kind: "boolean";
2535
+ readonly description: "Disable the trigger and unregister the choose tool.";
2536
+ readonly default: false;
2537
+ };
2538
+ readonly onOpenChange: {
2539
+ readonly kind: "handler";
2540
+ readonly description: "Called with true when the list opens and false when it closes.";
2541
+ };
2542
+ readonly agentName: {
2543
+ readonly kind: "string";
2544
+ readonly description: "Override the label used to derive the tool name, such as when every card on a page has a menu called Actions.";
2545
+ };
2546
+ readonly agentTool: {
2547
+ readonly kind: "boolean";
2548
+ readonly description: "Set false to render the menu without registering the choose tool.";
2549
+ readonly default: true;
2550
+ };
2551
+ };
2552
+ readonly state: {
2553
+ readonly open: {
2554
+ readonly description: "Present while the list is open on screen.";
2555
+ readonly attribute: "data-sprint-open";
2556
+ };
2557
+ readonly disabled: {
2558
+ readonly description: "Present when the menu cannot be opened.";
2559
+ readonly attribute: "data-sprint-disabled";
2560
+ };
2561
+ };
2562
+ readonly tools: {
2563
+ readonly choose: AgentToolSpec;
2564
+ };
2565
+ readonly agentView: {
2566
+ readonly example: "- **Menu** \"Card actions\" → tool `choose-card-actions`\n - part `item` \"Edit\"\n - part `item` \"Delete\" [tone=danger]";
2567
+ };
2568
+ readonly examples: readonly [{
2569
+ readonly title: "Card actions";
2570
+ readonly description: "A three-dot trigger at the end of a card. The destructive item is marked, and an agent runs either item through the choose tool without opening anything.";
2571
+ readonly code: "<Menu\n label=\"Card actions\"\n icon={<MoreIcon />}\n hideLabel\n size=\"small\"\n align=\"end\"\n items={[\n { label: \"Edit\", onSelect: edit },\n { label: \"Duplicate\", onSelect: duplicate },\n { label: \"Delete\", tone: \"danger\", onSelect: remove },\n ]}\n/>";
2572
+ }, {
2573
+ readonly title: "Links and actions together";
2574
+ readonly description: "An item with href is a real link and is left out of the tool, because a URL already reaches it. Groups gather related items under a heading.";
2575
+ readonly code: "<Menu\n label=\"Account\"\n items={[\n { label: \"Profile\", href: \"#/profile\", group: \"Signed in as Nomad\" },\n { label: \"Settings\", href: \"#/settings\", group: \"Signed in as Nomad\" },\n { label: \"Sign out\", onSelect: signOut },\n ]}\n/>";
2576
+ }, {
2577
+ readonly title: "Choosing one of several";
2578
+ readonly description: "checked turns items into radio choices with a visible mark, so the menu doubles as a compact picker. It opens with focus on the checked item.";
2579
+ readonly code: "<Menu\n label={plannerLabel}\n agentName=\"Planner\"\n items={planners.map((name) => ({\n label: name,\n checked: name === planner,\n onSelect: () => setPlanner(name),\n }))}\n/>";
2580
+ }];
2581
+ readonly a11y: {
2582
+ readonly role: "menu";
2583
+ readonly keyboard: readonly ["Enter, Space or Down Arrow on the trigger opens the list on the first or checked item", "Up Arrow on the trigger opens it on the last item", "Arrow keys move between items and wrap", "Home and End move to the first and last item", "A letter moves to the next item starting with it", "Escape closes the list and returns focus to the trigger", "Tab closes the list and moves on"];
2584
+ readonly notes: "The trigger has aria-haspopup=menu and aria-expanded. Items are menuitem buttons or links, or menuitemradio with aria-checked. The list is a popover in the top layer rendered inside the menu's own DOM, so it opens above a modal Dialog and is never clipped by an ancestor's overflow. Disabled items are skipped by the keyboard.";
2585
+ };
2586
+ };
2587
+
2588
+ export declare interface MenuProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
2589
+ label: string;
2590
+ items: readonly MenuItem[];
2591
+ icon?: ReactNode;
2592
+ hideLabel?: boolean;
2593
+ size?: ButtonSize;
2594
+ align?: FloatingAlign;
2595
+ side?: FloatingSide;
2596
+ disabled?: boolean;
2597
+ onOpenChange?: (open: boolean) => void;
2598
+ agentName?: string;
2599
+ agentTool?: boolean;
2600
+ }
2601
+
2602
+ export declare function MetaLine(props: MetaLineProps): JSX.Element | null;
2603
+
2604
+ export declare interface MetaLineEntry {
2605
+ term: string;
2606
+ detail: string;
2607
+ }
2608
+
2609
+ export declare const metaLineMeta: {
2610
+ readonly name: "MetaLine";
2611
+ readonly category: "display";
2612
+ readonly summary: "A slash-separated manifest line of term–detail pairs: serials, build strings, issue dates. It is chrome, not content, and in the agent view it reads as the same single line of text a person sees.";
2049
2613
  readonly whenToUse: "Use it for the compact strip of identifying metadata that belongs to a page, panel, or footer: version and build identifiers, timestamps, serial numbers, owners. Values are short. By default the line truncates with an ellipsis rather than wrapping; set wrap where it sits in a narrow container and every entry has to stay visible.";
2050
2614
  readonly whenNotToUse: "Do not use it for the details of a record a person is meant to study; that is a DescriptionList. Do not put anything interactive in it, and do not use it for prose.";
2051
2615
  readonly status: "experimental";
@@ -2556,6 +3120,8 @@ export declare interface PendingProps extends ComponentPropsWithRef<"div"> {
2556
3120
  label: string;
2557
3121
  }
2558
3122
 
3123
+ export declare function placeFloating(floating: HTMLElement, anchor: HTMLElement, side?: FloatingSide, align?: FloatingAlign, matchWidth?: boolean): FloatingSide;
3124
+
2559
3125
  export declare const PRESS_TOOL: AgentToolSpec;
2560
3126
 
2561
3127
  export declare function Progress(props: ProgressProps): JSX.Element;
@@ -2582,6 +3148,17 @@ export declare const progressMeta: {
2582
3148
  readonly description: "The value at which the work is complete.";
2583
3149
  readonly default: 100;
2584
3150
  };
3151
+ readonly tone: {
3152
+ readonly kind: "enum";
3153
+ readonly values: readonly ["info", "action", "warning", "danger"];
3154
+ readonly description: "The fill colour. info is the default for ordinary loading; action suits a goal being worked towards; warning and danger mark a bar running out or over a limit.";
3155
+ readonly default: "info";
3156
+ };
3157
+ readonly hideLabel: {
3158
+ readonly kind: "boolean";
3159
+ readonly description: "Hide the label and percentage visually while keeping the label as the bar's accessible name and agent label. Use when a nearby heading already says what the bar measures.";
3160
+ readonly default: false;
3161
+ };
2585
3162
  };
2586
3163
  readonly state: {
2587
3164
  readonly loading: {
@@ -2592,6 +3169,10 @@ export declare const progressMeta: {
2592
3169
  readonly description: "The fraction complete as a whole percentage, such as 40%. Absent while indeterminate.";
2593
3170
  readonly attribute: "data-sprint-value";
2594
3171
  };
3172
+ readonly tone: {
3173
+ readonly description: "The fill tone, when it is not the default info.";
3174
+ readonly attribute: "data-sprint-tone";
3175
+ };
2595
3176
  };
2596
3177
  readonly agentView: {
2597
3178
  readonly example: "- **Progress** \"Importing manifest\" [loading, value=40%]";
@@ -2608,6 +3189,10 @@ export declare const progressMeta: {
2608
3189
  readonly title: "Complete";
2609
3190
  readonly description: "At max the loading state clears, so the line reads as finished rather than stalled.";
2610
3191
  readonly code: "<Progress label=\"Importing manifest\" value={240} max={240} />";
3192
+ }, {
3193
+ readonly title: "A bare goal bar";
3194
+ readonly description: "A heading beside the bar already names it, so the label is hidden and only the bar shows, in the action tone.";
3195
+ readonly code: "<Progress label=\"Notes this week\" value={3} max={5} tone=\"action\" hideLabel />";
2611
3196
  }];
2612
3197
  readonly a11y: {
2613
3198
  readonly role: "progressbar";
@@ -2619,6 +3204,198 @@ export declare interface ProgressProps extends Omit<ComponentPropsWithRef<"div">
2619
3204
  label: string;
2620
3205
  value?: number;
2621
3206
  max?: number;
3207
+ tone?: ProgressTone;
3208
+ hideLabel?: boolean;
3209
+ }
3210
+
3211
+ declare type ProgressTone = "info" | "action" | "warning" | "danger";
3212
+
3213
+ export declare function Prose(props: ProseProps): JSX.Element;
3214
+
3215
+ export declare const proseMeta: {
3216
+ readonly name: "Prose";
3217
+ readonly category: "typography";
3218
+ readonly summary: "Typography for long-form rendered content such as Markdown: headings, paragraphs, lists, links, code, tables and quotes, in the theme's voice.";
3219
+ readonly whenToUse: "Wrap HTML you did not lay out yourself: member notes rendered from Markdown, an agenda's program details, a help article. Pass the original Markdown as source and the agent view carries it verbatim, which is better for an agent than flattened text. Sprint does not parse Markdown; render it with the library of your choice and pass the result as children.";
3220
+ readonly whenNotToUse: "Do not use for interface copy you write yourself; that is Text and Heading. Do not put interactive Sprint components inside it; Prose styles plain elements.";
3221
+ readonly status: "experimental";
3222
+ readonly props: {
3223
+ readonly children: {
3224
+ readonly kind: "node";
3225
+ readonly description: "The rendered content: plain HTML elements such as p, ul, a, code and table.";
3226
+ readonly required: true;
3227
+ };
3228
+ readonly source: {
3229
+ readonly kind: "string";
3230
+ readonly description: "The Markdown the children were rendered from. The agent view shows this instead of the flattened text, keeping lists, links and emphasis intact.";
3231
+ };
3232
+ readonly label: {
3233
+ readonly kind: "string";
3234
+ readonly description: "What the content is, such as \"Member notes\". Makes the block a labelled region and names it in the agent view.";
3235
+ };
3236
+ readonly size: {
3237
+ readonly kind: "enum";
3238
+ readonly description: "small for notes inside a card or a side panel.";
3239
+ readonly values: readonly ["small", "normal"];
3240
+ readonly default: "normal";
3241
+ };
3242
+ };
3243
+ readonly state: {
3244
+ readonly size: {
3245
+ readonly description: "Present as small for the compact size.";
3246
+ readonly attribute: "data-sprint-size";
3247
+ readonly values: readonly ["small"];
3248
+ };
3249
+ };
3250
+ readonly agentView: {
3251
+ readonly example: "- **Prose** \"Member notes\"\n - part `content` \"Moved in **March**. Plays the organ.\"";
3252
+ };
3253
+ readonly examples: readonly [{
3254
+ readonly title: "Rendered Markdown";
3255
+ readonly description: "The page renders Markdown however it likes and passes the original as source, which the agent view carries unchanged.";
3256
+ readonly code: "<Prose label=\"Member notes\" source={notes}>\n <Markdown>{notes}</Markdown>\n</Prose>";
3257
+ }, {
3258
+ readonly title: "Compact notes";
3259
+ readonly code: "<Prose size=\"small\">\n <p>Prefers <strong>text</strong> after six.</p>\n <ul>\n <li>Organ</li>\n <li>Youth program</li>\n </ul>\n</Prose>";
3260
+ }];
3261
+ readonly a11y: {
3262
+ readonly role: "region";
3263
+ readonly notes: "With a label the block is a named region; without one it adds no semantics of its own. The content keeps its own elements, so headings join the page outline.";
3264
+ };
3265
+ };
3266
+
3267
+ export declare interface ProseProps extends ComponentPropsWithRef<"div"> {
3268
+ label?: string;
3269
+ source?: string;
3270
+ size?: ProseSize;
3271
+ }
3272
+
3273
+ export declare type ProseSize = "small" | "normal";
3274
+
3275
+ export declare function RadioGroup(props: RadioGroupProps): JSX.Element;
3276
+
3277
+ export declare const radioGroupMeta: {
3278
+ readonly name: "RadioGroup";
3279
+ readonly category: "input";
3280
+ readonly summary: "One choice from a short list of options, each a native radio button with a label and an optional line of description. Registers one select tool enumerating the options.";
3281
+ readonly whenToUse: "Use when the options need explaining: a role with what it can do, a plan with what it includes, a delivery speed with its cost. Options hold data: { value, label, description?, disabled? }. The group is a fieldset whose legend is the label, so it submits under one name inside a form.";
3282
+ readonly whenNotToUse: "Do not use for two to four short labels that need no description; that is a SegmentedControl. Do not use for a long list; that is a Select. Do not use for choosing several; that is a set of Checkboxes. Descriptions are plain strings, not components.";
3283
+ readonly status: "experimental";
3284
+ readonly props: {
3285
+ readonly label: {
3286
+ readonly kind: "string";
3287
+ readonly description: "The question the options answer, such as \"Role\". Rendered as the legend and used to derive the tool name.";
3288
+ readonly required: true;
3289
+ };
3290
+ readonly options: {
3291
+ readonly kind: "array";
3292
+ readonly description: "The options in order: { value, label, description?, disabled? }. label is what a person reads and what the select tool accepts. description is a sentence under the label, linked to its radio with aria-describedby and carried into the agent view as part state.";
3293
+ readonly required: true;
3294
+ };
3295
+ readonly value: {
3296
+ readonly kind: "string";
3297
+ readonly description: "The selected option's value, or an empty string for none. Fully controlled.";
3298
+ readonly required: true;
3299
+ };
3300
+ readonly onChange: {
3301
+ readonly kind: "handler";
3302
+ readonly description: "Called with the value a person or an agent selects.";
3303
+ readonly required: true;
3304
+ };
3305
+ readonly hint: {
3306
+ readonly kind: "string";
3307
+ readonly description: "Guidance under the group. Replaced by error while one is set.";
3308
+ };
3309
+ readonly error: {
3310
+ readonly kind: "string";
3311
+ readonly description: "A validation message that marks the group invalid.";
3312
+ };
3313
+ readonly name: {
3314
+ readonly kind: "string";
3315
+ readonly description: "The native form name the selected value submits under.";
3316
+ };
3317
+ readonly disabled: {
3318
+ readonly kind: "boolean";
3319
+ readonly description: "Disable every option and unregister the select tool.";
3320
+ readonly default: false;
3321
+ };
3322
+ readonly required: {
3323
+ readonly kind: "boolean";
3324
+ readonly description: "Mark the group as needing an answer.";
3325
+ readonly default: false;
3326
+ };
3327
+ readonly agentName: {
3328
+ readonly kind: "string";
3329
+ readonly description: "Override the label used to derive the tool name.";
3330
+ };
3331
+ readonly agentTool: {
3332
+ readonly kind: "boolean";
3333
+ readonly description: "Set false to render the group without registering the select tool.";
3334
+ readonly default: true;
3335
+ };
3336
+ };
3337
+ readonly state: {
3338
+ readonly value: {
3339
+ readonly description: "The selected option's label.";
3340
+ readonly attribute: "data-sprint-value";
3341
+ };
3342
+ readonly empty: {
3343
+ readonly description: "Present while nothing is selected.";
3344
+ readonly attribute: "data-sprint-empty";
3345
+ };
3346
+ readonly disabled: {
3347
+ readonly description: "Present when the group cannot be changed.";
3348
+ readonly attribute: "data-sprint-disabled";
3349
+ };
3350
+ readonly required: {
3351
+ readonly description: "Present when an answer is needed.";
3352
+ readonly attribute: "data-sprint-required";
3353
+ };
3354
+ readonly invalid: {
3355
+ readonly description: "Present while an error is set.";
3356
+ readonly attribute: "data-sprint-invalid";
3357
+ };
3358
+ };
3359
+ readonly tools: {
3360
+ readonly select: AgentToolSpec;
3361
+ };
3362
+ readonly agentView: {
3363
+ readonly example: "- **RadioGroup** \"Role\" [value=Viewer] → tool `select-role`\n - part `option` \"Viewer\" [checked, description=Sees the board]\n - part `option` \"Editor\" [description=Changes callings]";
3364
+ };
3365
+ readonly examples: readonly [{
3366
+ readonly title: "Options that need explaining";
3367
+ readonly description: "Each role says what it allows. The descriptions reach screen readers through aria-describedby and agents through part state.";
3368
+ readonly code: "<RadioGroup\n label=\"Role\"\n value={role}\n onChange={setRole}\n options={[\n { value: \"viewer\", label: \"Viewer\", description: \"Sees the board and the agenda.\" },\n { value: \"editor\", label: \"Editor\", description: \"Moves people between callings.\" },\n { value: \"admin\", label: \"Admin\", description: \"Also invites and removes people.\" },\n ]}\n/>";
3369
+ }, {
3370
+ readonly title: "A required choice with an unavailable option";
3371
+ readonly code: "<RadioGroup\n label=\"Delivery\"\n required\n value={delivery}\n onChange={setDelivery}\n error={delivery === \"\" ? \"Choose how to send the invite.\" : undefined}\n options={[\n { value: \"email\", label: \"Email\" },\n { value: \"text\", label: \"Text message\", disabled: true, description: \"No phone number on file.\" },\n ]}\n/>";
3372
+ }];
3373
+ readonly a11y: {
3374
+ readonly role: "radiogroup";
3375
+ readonly keyboard: readonly ["Arrow keys move between options and select", "Tab enters and leaves the group"];
3376
+ readonly notes: "A fieldset of native radio inputs sharing one name, so the browser supplies arrow-key movement and form submission. The legend names the group; a description is linked to its own radio.";
3377
+ };
3378
+ };
3379
+
3380
+ export declare interface RadioGroupProps extends Omit<ComponentPropsWithRef<"fieldset">, "onChange"> {
3381
+ label: string;
3382
+ options: readonly RadioOption[];
3383
+ value: string;
3384
+ onChange: (value: string) => void;
3385
+ hint?: string | undefined;
3386
+ error?: string | undefined;
3387
+ name?: string;
3388
+ disabled?: boolean;
3389
+ required?: boolean;
3390
+ agentName?: string;
3391
+ agentTool?: boolean;
3392
+ }
3393
+
3394
+ export declare interface RadioOption {
3395
+ value: string;
3396
+ label: string;
3397
+ description?: string;
3398
+ disabled?: boolean;
2622
3399
  }
2623
3400
 
2624
3401
  export declare function reactText(node: ReactNode): string | undefined;
@@ -2836,7 +3613,7 @@ export declare const segmentedControlMeta: {
2836
3613
  };
2837
3614
  readonly options: {
2838
3615
  readonly kind: "array";
2839
- readonly description: "The choices in display order: { value, label, count? }. The label is what a person sees and what the select tool accepts, so an agent never has to know the value. count renders as a muted chip beside the label and reaches the agent view as part state, so never fold a count into the label.";
3616
+ readonly description: "The choices in display order: { value, label, count?, disabled? }. The label is what a person sees and what the select tool accepts, so an agent never has to know the value. count renders as a muted chip beside the label and reaches the agent view as part state, so never fold a count into the label. disabled keeps one option visible but unselectable: it is skipped by the arrow keys, left out of the tool's enum, and marked disabled in the agent view.";
2840
3617
  readonly required: true;
2841
3618
  };
2842
3619
  readonly value: {
@@ -2921,10 +3698,14 @@ export declare const segmentedControlMeta: {
2921
3698
  readonly title: "A disabled control";
2922
3699
  readonly description: "Disabled unregisters the tool, so an agent cannot select an option a person could not.";
2923
3700
  readonly code: "<SegmentedControl\n label=\"Density\"\n disabled\n value=\"dense\"\n onChange={setDensity}\n options={[\n { value: \"dense\", label: \"dense\" },\n { value: \"roomy\", label: \"roomy\" },\n ]}\n/>";
3701
+ }, {
3702
+ readonly title: "One option unavailable";
3703
+ readonly description: "A disabled option stays in place so the set of choices reads the same, but nobody can pick it: arrow keys skip it and the select tool does not offer it.";
3704
+ readonly code: "<SegmentedControl\n label=\"Interview\"\n value={status}\n onChange={setStatus}\n options={[\n { value: \"pending\", label: \"Pending\" },\n { value: \"accepted\", label: \"Accepted\", disabled: !interviewed },\n { value: \"declined\", label: \"Declined\", disabled: !interviewed },\n ]}\n/>";
2924
3705
  }];
2925
3706
  readonly a11y: {
2926
3707
  readonly role: "radiogroup";
2927
- readonly keyboard: readonly ["Arrow keys move to the next or previous option and select it", "Home selects the first option", "End selects the last option", "Tab enters and leaves the group once"];
3708
+ readonly keyboard: readonly ["Arrow keys move to the next or previous enabled option and select it", "Home selects the first enabled option", "End selects the last enabled option", "Tab enters and leaves the group once"];
2928
3709
  readonly notes: "Roving tabindex: only the selected option is in the tab order. Selection follows focus, which is the expected behaviour for a radio group. An option with a count is named by its label and its count together; the hint is linked to the group with aria-describedby.";
2929
3710
  };
2930
3711
  };
@@ -2946,12 +3727,17 @@ export declare interface SegmentedOption {
2946
3727
  value: string;
2947
3728
  label: string;
2948
3729
  count?: number;
3730
+ disabled?: boolean;
2949
3731
  }
2950
3732
 
2951
3733
  export declare function Select(props: SelectProps): JSX.Element;
2952
3734
 
2953
3735
  export declare const SELECT_OPTION_TOOL: AgentToolSpec;
2954
3736
 
3737
+ export declare const SELECT_RADIO_TOOL: AgentToolSpec;
3738
+
3739
+ export declare const SELECT_TAB_TOOL: AgentToolSpec;
3740
+
2955
3741
  export declare const SELECT_TOOL: AgentToolSpec;
2956
3742
 
2957
3743
  export declare const selectMeta: {
@@ -2959,7 +3745,7 @@ export declare const selectMeta: {
2959
3745
  readonly category: "input";
2960
3746
  readonly summary: "A dropdown of mutually exclusive options: a select-only combobox that opens a listbox on click or keyboard, carrying its own label, hint, and error. It registers a single select tool whose schema enumerates the option labels currently on offer.";
2961
3747
  readonly whenToUse: "Use it when one value is chosen from a list too long to lay out flat: a region, a squad, a category. Options are data ({ value, label }), the tool accepts the visible label, and in agent view every option renders as its own control, so an agent picks one without opening anything.";
2962
- readonly whenNotToUse: "Do not use it for two to four short options a person should compare at a glance; that is a SegmentedControl. Do not use it for an on/off state, which is a Checkbox or a Switch, and never for navigation.";
3748
+ readonly whenNotToUse: "Do not use it for two to four short options a person should compare at a glance; that is a SegmentedControl. Do not use it for a list long enough to need searching, grouping or custom option rendering; that is a Combobox. Do not use it for an on/off state, which is a Checkbox or a Switch, and never for navigation.";
2963
3749
  readonly status: "experimental";
2964
3750
  readonly props: {
2965
3751
  readonly label: {
@@ -3169,6 +3955,14 @@ export declare const shellMeta: {
3169
3955
  readonly kind: "handler";
3170
3956
  readonly description: "Called with the collapsed state the Shell wants. Use it to remember the choice across visits.";
3171
3957
  };
3958
+ readonly drawerOpen: {
3959
+ readonly kind: "boolean";
3960
+ readonly description: "Whether the narrow-viewport drawer is open, when the owner keeps that state: a guided tour that steps into the sidebar, or a page that closes the drawer after an action. Pair it with onDrawerOpenChange.";
3961
+ };
3962
+ readonly onDrawerOpenChange: {
3963
+ readonly kind: "handler";
3964
+ readonly description: "Called with the drawer state the Shell wants: true from the menu toggle, false from the close toggle or a link followed inside the drawer.";
3965
+ };
3172
3966
  readonly hideLabel: {
3173
3967
  readonly kind: "string";
3174
3968
  readonly description: "Label of the wide-viewport toggle while the sidebar is shown.";
@@ -3202,6 +3996,10 @@ export declare const shellMeta: {
3202
3996
  readonly title: "A sidebar that can be hidden";
3203
3997
  readonly description: "With collapsible, a person can put the sidebar away on a wide screen as well as a narrow one, and get the full width for the page.";
3204
3998
  readonly code: "<Shell\n collapsible\n bar={<Link href=\"#/\">ACME</Link>}\n side={\n <Nav label=\"Main\">\n <Link href=\"#/reports\" active>Reports</Link>\n <Link href=\"#/settings\">Settings</Link>\n </Nav>\n }\n>\n <Text>Quarterly numbers land here.</Text>\n</Shell>";
3999
+ }, {
4000
+ readonly title: "A drawer the page controls";
4001
+ readonly description: "drawerOpen and onDrawerOpenChange hand the phone drawer to the page, so a guided tour can open it to point at a link and close it again.";
4002
+ readonly code: "<Shell\n drawerOpen={drawerOpen}\n onDrawerOpenChange={setDrawerOpen}\n bar={<Link href=\"#/\">ACME</Link>}\n side={\n <Nav label=\"Main\">\n <Link href=\"#/reports\" active>Reports</Link>\n </Nav>\n }\n>\n <Button onClick={() => setDrawerOpen(true)}>Show me the menu</Button>\n</Shell>";
3205
4003
  }];
3206
4004
  };
3207
4005
 
@@ -3216,31 +4014,99 @@ export declare interface ShellProps extends ComponentPropsWithRef<"div"> {
3216
4014
  collapsed?: boolean;
3217
4015
  defaultCollapsed?: boolean;
3218
4016
  onCollapsedChange?: (collapsed: boolean) => void;
4017
+ drawerOpen?: boolean;
4018
+ onDrawerOpenChange?: (open: boolean) => void;
3219
4019
  hideLabel?: string;
3220
4020
  showLabel?: string;
3221
4021
  }
3222
4022
 
3223
- export declare function slug(value: string): string;
3224
-
3225
- export declare type SprintAgentControls = "always" | "never";
3226
-
3227
- export declare function SprintProvider(props: SprintProviderProps): JSX.Element;
4023
+ export declare function showFloating(element: HTMLElement): void;
3228
4024
 
3229
- export declare interface SprintProviderProps {
3230
- children: ReactNode;
3231
- label?: string;
3232
- format?: AgentFormatter;
3233
- pageTools?: boolean;
3234
- view?: SprintView;
3235
- defaultView?: SprintView;
3236
- onViewChange?: (view: SprintView) => void;
3237
- agentControls?: SprintAgentControls;
3238
- theme?: SprintTheme;
3239
- }
4025
+ export declare function slug(value: string): string;
3240
4026
 
3241
- export declare type SprintTheme = "dark" | "light" | "calorie" | "calorie-dark" | "trax" | "trax-dark" | "ambient" | "ambient-dark";
4027
+ export declare function Spinner(props: SpinnerProps): JSX.Element;
3242
4028
 
3243
- export declare type SprintView = "human" | "agent";
4029
+ export declare const spinnerMeta: {
4030
+ readonly name: "Spinner";
4031
+ readonly category: "feedback";
4032
+ readonly summary: "A small inline busy mark for work with no measurable progress, sized to sit inside a field or beside a line of text.";
4033
+ readonly whenToUse: "Use where a whole bar or region would be too much: beside a field that is checking a value, at the end of a line that is saving, inside a combobox while results load. The label says what is happening and is announced politely; showLabel prints it beside the mark.";
4034
+ readonly whenNotToUse: "Do not use for a region whose data is loading; that is Pending or a component's loading prop. Do not use when the work can be counted; that is Progress. Do not use for a busy button; Button's loading prop marks the button itself.";
4035
+ readonly status: "experimental";
4036
+ readonly props: {
4037
+ readonly label: {
4038
+ readonly kind: "string";
4039
+ readonly description: "What is happening, such as \"Checking availability\".";
4040
+ readonly required: true;
4041
+ };
4042
+ readonly size: {
4043
+ readonly kind: "enum";
4044
+ readonly description: "medium matches body text; small fits inside a dense row or a field.";
4045
+ readonly values: readonly ["small", "medium"];
4046
+ readonly default: "medium";
4047
+ };
4048
+ readonly showLabel: {
4049
+ readonly kind: "boolean";
4050
+ readonly description: "Print the label beside the mark instead of keeping it for assistive technology only.";
4051
+ readonly default: false;
4052
+ };
4053
+ };
4054
+ readonly state: {
4055
+ readonly loading: {
4056
+ readonly description: "Always present: a spinner is only rendered while work is running.";
4057
+ readonly attribute: "data-sprint-loading";
4058
+ };
4059
+ readonly size: {
4060
+ readonly description: "Present as small on the compact mark.";
4061
+ readonly attribute: "data-sprint-size";
4062
+ readonly values: readonly ["small"];
4063
+ };
4064
+ };
4065
+ readonly agentView: {
4066
+ readonly example: "- **Spinner** \"Saving note\" [loading]";
4067
+ };
4068
+ readonly examples: readonly [{
4069
+ readonly title: "Saving beside a line";
4070
+ readonly description: "The mark sits in the line, and the label is printed beside it.";
4071
+ readonly code: "<Spinner label=\"Saving note\" showLabel />";
4072
+ }, {
4073
+ readonly title: "A silent mark in a field";
4074
+ readonly description: "A small mark with the label kept for assistive technology and agents.";
4075
+ readonly code: "<Spinner label=\"Searching members\" size=\"small\" />";
4076
+ }];
4077
+ readonly a11y: {
4078
+ readonly role: "status";
4079
+ readonly notes: "A role=status element, so the label is announced politely when the spinner appears. Render it only while work runs, rather than toggling its visibility. The mark is a stepped four-cell pulse, still under reduced motion.";
4080
+ };
4081
+ };
4082
+
4083
+ export declare interface SpinnerProps extends Omit<ComponentPropsWithRef<"span">, "children"> {
4084
+ label: string;
4085
+ size?: SpinnerSize;
4086
+ showLabel?: boolean;
4087
+ }
4088
+
4089
+ export declare type SpinnerSize = "small" | "medium";
4090
+
4091
+ export declare type SprintAgentControls = "always" | "never";
4092
+
4093
+ export declare function SprintProvider(props: SprintProviderProps): JSX.Element;
4094
+
4095
+ export declare interface SprintProviderProps {
4096
+ children: ReactNode;
4097
+ label?: string;
4098
+ format?: AgentFormatter;
4099
+ pageTools?: boolean;
4100
+ view?: SprintView;
4101
+ defaultView?: SprintView;
4102
+ onViewChange?: (view: SprintView) => void;
4103
+ agentControls?: SprintAgentControls;
4104
+ theme?: SprintTheme;
4105
+ }
4106
+
4107
+ export declare type SprintTheme = "dark" | "light" | "calorie" | "calorie-dark" | "trax" | "trax-dark" | "ambient" | "ambient-dark";
4108
+
4109
+ export declare type SprintView = "human" | "agent";
3244
4110
 
3245
4111
  export declare interface SprintViewValue {
3246
4112
  view: SprintView;
@@ -3258,7 +4124,7 @@ export declare type StackAlign = "start" | "center" | "end" | "stretch" | "basel
3258
4124
 
3259
4125
  export declare type StackDirection = "row" | "column" | "grid";
3260
4126
 
3261
- export declare type StackGap = "none" | "tight" | "normal" | "loose";
4127
+ export declare type StackGap = "none" | "hairline" | "snug" | "tight" | "medium" | "normal" | "loose" | "wide" | "vast";
3262
4128
 
3263
4129
  export declare type StackJustify = "start" | "center" | "end" | "between";
3264
4130
 
@@ -3283,10 +4149,16 @@ export declare const stackMeta: {
3283
4149
  };
3284
4150
  readonly gap: {
3285
4151
  readonly kind: "enum";
3286
- readonly description: "Space between items, from the space scale.";
3287
- readonly values: readonly ["none", "tight", "normal", "loose"];
4152
+ readonly description: "Space between items, from the space scale: none, hairline, snug, tight, medium, normal, loose, wide, vast, smallest to largest.";
4153
+ readonly values: readonly ["none", "hairline", "snug", "tight", "medium", "normal", "loose", "wide", "vast"];
3288
4154
  readonly default: "normal";
3289
4155
  };
4156
+ readonly padding: {
4157
+ readonly kind: "enum";
4158
+ readonly description: "Space inside the stack's edges, on the same scale as gap. Use it for a stack that is itself a bordered or filled region; between siblings, prefer the parent's gap.";
4159
+ readonly values: readonly ["none", "hairline", "snug", "tight", "medium", "normal", "loose", "wide", "vast"];
4160
+ readonly default: "none";
4161
+ };
3290
4162
  readonly align: {
3291
4163
  readonly kind: "enum";
3292
4164
  readonly description: "Cross-axis alignment.";
@@ -3323,7 +4195,12 @@ export declare const stackMeta: {
3323
4195
  readonly gap: {
3324
4196
  readonly description: "The spacing step in use.";
3325
4197
  readonly attribute: "data-sprint-gap";
3326
- readonly values: readonly ["none", "tight", "normal", "loose"];
4198
+ readonly values: readonly ["none", "hairline", "snug", "tight", "medium", "normal", "loose", "wide", "vast"];
4199
+ };
4200
+ readonly padding: {
4201
+ readonly description: "The inner spacing step, when there is one.";
4202
+ readonly attribute: "data-sprint-padding";
4203
+ readonly values: readonly ["hairline", "snug", "tight", "medium", "normal", "loose", "wide", "vast"];
3327
4204
  };
3328
4205
  readonly align: {
3329
4206
  readonly description: "Cross-axis alignment, when one was asked for.";
@@ -3359,6 +4236,10 @@ export declare const stackMeta: {
3359
4236
  }, {
3360
4237
  readonly title: "A header bar that stacks on a phone";
3361
4238
  readonly code: "<Stack direction=\"row\" justify=\"between\" align=\"center\" collapse>\n <Heading level={1}>Button</Heading>\n <Tag tone=\"warning\">experimental</Tag>\n</Stack>";
4239
+ }, {
4240
+ readonly title: "A padded, dense list";
4241
+ readonly description: "padding insets the stack's own content, and a small gap packs rows closely, without a wrapper element or a class.";
4242
+ readonly code: "<Stack gap=\"snug\" padding=\"medium\">\n <Text>Opening hymn</Text>\n <Text>Invocation</Text>\n <Text>Sacrament hymn</Text>\n</Stack>";
3362
4243
  }];
3363
4244
  };
3364
4245
 
@@ -3367,6 +4248,7 @@ export declare type StackMin = (typeof STACK_MINS)[number];
3367
4248
  export declare interface StackProps extends ComponentPropsWithRef<"div"> {
3368
4249
  direction?: StackDirection;
3369
4250
  gap?: StackGap;
4251
+ padding?: StackGap;
3370
4252
  align?: StackAlign;
3371
4253
  justify?: StackJustify;
3372
4254
  wrap?: boolean;
@@ -3551,6 +4433,14 @@ export declare interface SwitchProps extends Omit<ComponentPropsWithRef<"button"
3551
4433
  agentTool?: boolean;
3552
4434
  }
3553
4435
 
4436
+ export declare interface TabItem {
4437
+ value: string;
4438
+ label: string;
4439
+ panel: ReactNode;
4440
+ count?: number;
4441
+ disabled?: boolean;
4442
+ }
4443
+
3554
4444
  export declare function Table(props: TableProps): JSX.Element;
3555
4445
 
3556
4446
  export declare const TABLE_WIDTHS: readonly ["4rem", "5rem", "6rem", "7rem", "8rem", "9rem", "10rem", "12rem", "14rem", "16rem", "20rem", "24rem"];
@@ -3674,6 +4564,91 @@ export declare interface TableRow {
3674
4564
 
3675
4565
  export declare type TableWidth = (typeof TABLE_WIDTHS)[number];
3676
4566
 
4567
+ export declare function Tabs(props: TabsProps): JSX.Element;
4568
+
4569
+ export declare const tabsMeta: {
4570
+ readonly name: "Tabs";
4571
+ readonly category: "navigation";
4572
+ readonly summary: "Sibling views of one subject, one shown at a time, with real tablist, tab and tabpanel semantics. Registers one select tool enumerating the tabs.";
4573
+ readonly whenToUse: "Use to switch between a few views of the same thing without leaving the page: coming up, past and by member for one set of speakers; details and history for one record. Tabs hold data: each is { value, label, panel, count?, disabled? }, and only the selected tab's panel is mounted. An optional actions slot sits at the end of the tab row.";
4574
+ readonly whenNotToUse: "Do not use to pick a value that filters or changes something else on the page; that is a SegmentedControl, a radio group. Do not use for navigation between pages with their own URLs; that is a Nav. Do not use when a person needs to compare the views side by side.";
4575
+ readonly status: "experimental";
4576
+ readonly props: {
4577
+ readonly label: {
4578
+ readonly kind: "string";
4579
+ readonly description: "What the tabs switch between, such as \"Speakers\". Names the tablist and derives the select tool name.";
4580
+ readonly required: true;
4581
+ };
4582
+ readonly tabs: {
4583
+ readonly kind: "array";
4584
+ readonly description: "The tabs in order: { value, label, panel, count?, disabled? }. panel is the content shown while the tab is selected and may hold any components. count renders as a chip beside the label and reaches the agent view as part state.";
4585
+ readonly required: true;
4586
+ };
4587
+ readonly value: {
4588
+ readonly kind: "string";
4589
+ readonly description: "The selected tab's value, when the page owns that state. Pair it with onChange. A disabled or unknown value falls back to the first enabled tab.";
4590
+ };
4591
+ readonly defaultValue: {
4592
+ readonly kind: "string";
4593
+ readonly description: "The tab selected first when the Tabs keep their own state.";
4594
+ };
4595
+ readonly onChange: {
4596
+ readonly kind: "handler";
4597
+ readonly description: "Called with the value of the tab a person or an agent selects.";
4598
+ };
4599
+ readonly actions: {
4600
+ readonly kind: "node";
4601
+ readonly description: "Controls at the end of the tab row, such as an add Button that applies to every tab.";
4602
+ };
4603
+ readonly agentName: {
4604
+ readonly kind: "string";
4605
+ readonly description: "Override the label used to derive the tool name.";
4606
+ };
4607
+ readonly agentTool: {
4608
+ readonly kind: "boolean";
4609
+ readonly description: "Set false to render the tabs without registering the select tool.";
4610
+ readonly default: true;
4611
+ };
4612
+ };
4613
+ readonly state: {
4614
+ readonly value: {
4615
+ readonly description: "The selected tab's label.";
4616
+ readonly attribute: "data-sprint-value";
4617
+ };
4618
+ };
4619
+ readonly tools: {
4620
+ readonly select: AgentToolSpec;
4621
+ };
4622
+ readonly agentView: {
4623
+ readonly example: "- **Tabs** \"Speakers\" [value=Coming up] → tool `select-speakers`\n - part `tab` \"Coming up\" [count=4, selected]\n - part `tab` \"Past\"";
4624
+ };
4625
+ readonly examples: readonly [{
4626
+ readonly title: "Views of one subject";
4627
+ readonly description: "Only the selected panel is on the page. An agent switches with the select tool or the tab controls in the agent view, then reads the new panel.";
4628
+ readonly code: "<Tabs\n label=\"Speakers\"\n tabs={[\n { value: \"upcoming\", label: \"Coming up\", count: 4, panel: <UpcomingSpeakers /> },\n { value: \"past\", label: \"Past\", panel: <PastSpeakers /> },\n { value: \"member\", label: \"By member\", panel: <SpeakersByMember /> },\n ]}\n/>";
4629
+ }, {
4630
+ readonly title: "Tabs with an action";
4631
+ readonly description: "actions sits at the end of the tab row and stays put while the panels change. A disabled tab is visible but cannot be selected.";
4632
+ readonly code: "<Tabs\n label=\"Record\"\n value={view}\n onChange={setView}\n actions={<Button size=\"small\">Export</Button>}\n tabs={[\n { value: \"details\", label: \"Details\", panel: <Details /> },\n { value: \"history\", label: \"History\", panel: <History /> },\n { value: \"audit\", label: \"Audit\", disabled: true, panel: null },\n ]}\n/>";
4633
+ }];
4634
+ readonly a11y: {
4635
+ readonly role: "tablist";
4636
+ readonly keyboard: readonly ["Left and Right Arrow move to the previous or next enabled tab and show it", "Home and End show the first and last enabled tab", "Tab moves from the selected tab into its panel"];
4637
+ readonly notes: "Roving tabindex across the tabs, with selection following focus. The panel is a focusable tabpanel labelled by its tab. A count chip is hidden from assistive technology, so the tab is named by its label alone.";
4638
+ };
4639
+ };
4640
+
4641
+ export declare interface TabsProps extends Omit<ComponentPropsWithRef<"div">, "onChange"> {
4642
+ label: string;
4643
+ tabs: readonly TabItem[];
4644
+ value?: string;
4645
+ defaultValue?: string;
4646
+ onChange?: (value: string) => void;
4647
+ actions?: ReactNode;
4648
+ agentName?: string;
4649
+ agentTool?: boolean;
4650
+ }
4651
+
3677
4652
  export declare function Tag(props: TagProps): JSX.Element;
3678
4653
 
3679
4654
  export declare const tagMeta: {
@@ -3700,6 +4675,11 @@ export declare const tagMeta: {
3700
4675
  readonly description: "Render as a solid field of the tone with inverted ink, instead of a keyline. Use for the one chip that must be read first.";
3701
4676
  readonly default: false;
3702
4677
  };
4678
+ readonly provisional: {
4679
+ readonly kind: "boolean";
4680
+ readonly description: "Draw a dashed keyline for something that is not real yet: a draft, a scenario, a proposed change. It reads as provisional to agents too.";
4681
+ readonly default: false;
4682
+ };
3703
4683
  };
3704
4684
  readonly state: {
3705
4685
  readonly tone: {
@@ -3711,11 +4691,19 @@ export declare const tagMeta: {
3711
4691
  readonly description: "Present when the chip is a solid field rather than a keyline.";
3712
4692
  readonly attribute: "data-sprint-filled";
3713
4693
  };
4694
+ readonly provisional: {
4695
+ readonly description: "Present on a chip for something that is not real yet.";
4696
+ readonly attribute: "data-sprint-provisional";
4697
+ };
3714
4698
  };
3715
4699
  readonly agentView: {
3716
4700
  readonly example: "- **Tag** \"experimental\" [filled, tone=warning]";
3717
4701
  };
3718
4702
  readonly examples: readonly [{
4703
+ readonly title: "A provisional chip";
4704
+ readonly description: "A dashed keyline separates a scenario from the live roster without spending a colour.";
4705
+ readonly code: "<Tag tone=\"info\" provisional>Scenario: fall reshuffle</Tag>";
4706
+ }, {
3719
4707
  readonly title: "A release status";
3720
4708
  readonly code: "<Tag tone=\"warning\" filled>experimental</Tag>";
3721
4709
  }, {
@@ -3731,6 +4719,7 @@ export declare const tagMeta: {
3731
4719
  export declare interface TagProps extends ComponentPropsWithRef<"span"> {
3732
4720
  tone?: TagTone;
3733
4721
  filled?: boolean;
4722
+ provisional?: boolean;
3734
4723
  }
3735
4724
 
3736
4725
  export declare type TagTone = "neutral" | "action" | "danger" | "info" | "warning" | "inert";
@@ -3738,8 +4727,12 @@ export declare type TagTone = "neutral" | "action" | "danger" | "info" | "warnin
3738
4727
  declare function Text_2(props: TextProps): JSX.Element;
3739
4728
  export { Text_2 as Text }
3740
4729
 
4730
+ declare type TextAlign = "start" | "center" | "end";
4731
+
3741
4732
  export declare function Textarea(props: TextareaProps): JSX.Element;
3742
4733
 
4734
+ declare type TextareaFieldProps = Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "id" | "rows" | "value" | "defaultValue" | "onChange" | "placeholder" | "name" | "disabled" | "required" | "aria-invalid" | "aria-describedby">;
4735
+
3743
4736
  export declare const textareaMeta: {
3744
4737
  readonly name: "Textarea";
3745
4738
  readonly category: "input";
@@ -3765,9 +4758,14 @@ export declare const textareaMeta: {
3765
4758
  };
3766
4759
  readonly rows: {
3767
4760
  readonly kind: "number";
3768
- readonly description: "The visible line count before scrolling.";
4761
+ readonly description: "The visible line count before scrolling. With autoGrow it is the minimum height.";
3769
4762
  readonly default: 4;
3770
4763
  };
4764
+ readonly autoGrow: {
4765
+ readonly kind: "boolean";
4766
+ readonly description: "Grow the area with its content instead of scrolling, never shorter than rows.";
4767
+ readonly default: false;
4768
+ };
3771
4769
  readonly placeholder: {
3772
4770
  readonly kind: "string";
3773
4771
  readonly description: "Ghost text shown while the area is empty.";
@@ -3794,6 +4792,19 @@ export declare const textareaMeta: {
3794
4792
  readonly description: "Mark the area required, visually and in the agent view.";
3795
4793
  readonly default: false;
3796
4794
  };
4795
+ readonly hideLabel: {
4796
+ readonly kind: "boolean";
4797
+ readonly description: "Hide the label visually while keeping it as the field's accessible name and agent label. Pair it with a placeholder or a nearby heading so sighted people still know what the field is for.";
4798
+ readonly default: false;
4799
+ };
4800
+ readonly inputRef: {
4801
+ readonly kind: "object";
4802
+ readonly description: "A ref to the underlying <textarea>, for focusing or measuring it. The component's own ref points at the wrapper.";
4803
+ };
4804
+ readonly inputProps: {
4805
+ readonly kind: "object";
4806
+ readonly description: "Extra native attributes and handlers for the <textarea> itself, such as autoFocus, maxLength, inputMode, onKeyDown, or onBlur. Props spread on the component land on the wrapper; these land on the area. Anything the component manages (value, onChange, disabled, the error wiring) cannot be overridden here.";
4807
+ };
3797
4808
  readonly agentName: {
3798
4809
  readonly kind: "string";
3799
4810
  readonly description: "Override the label used to derive the tool name, when two areas on a page would otherwise collide.";
@@ -3840,6 +4851,10 @@ export declare const textareaMeta: {
3840
4851
  readonly title: "A required area with an error";
3841
4852
  readonly description: "The error replaces the hint and marks the area invalid on every surface.";
3842
4853
  readonly code: "<Textarea\n label=\"Abort reason\"\n value={reason}\n onChange={setReason}\n required\n rows={3}\n error=\"State the reason before aborting.\"\n/>";
4854
+ }, {
4855
+ readonly title: "An area that grows";
4856
+ readonly description: "autoGrow drops the scrollbar: the area starts at rows lines and grows with what is typed. The label is hidden because a heading above already names it, and inputProps puts a length cap and a key handler on the textarea itself.";
4857
+ readonly code: "<Textarea\n label=\"Log entry\"\n hideLabel\n autoGrow\n rows={2}\n value={entry}\n onChange={setEntry}\n placeholder=\"What happened on this pass\"\n inputProps={{ maxLength: 500, onKeyDown: submitOnModEnter }}\n/>";
3843
4858
  }];
3844
4859
  readonly a11y: {
3845
4860
  readonly role: "textbox";
@@ -3859,12 +4874,18 @@ export declare interface TextareaProps extends Omit<ComponentPropsWithRef<"div">
3859
4874
  name?: string;
3860
4875
  disabled?: boolean;
3861
4876
  required?: boolean;
4877
+ autoGrow?: boolean;
4878
+ hideLabel?: boolean;
4879
+ inputRef?: Ref<HTMLTextAreaElement>;
4880
+ inputProps?: TextareaFieldProps;
3862
4881
  agentName?: string;
3863
4882
  agentTool?: boolean;
3864
4883
  }
3865
4884
 
3866
4885
  export declare function TextInput(props: TextInputProps): JSX.Element;
3867
4886
 
4887
+ declare type TextInputFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, "id" | "type" | "value" | "defaultValue" | "onChange" | "placeholder" | "name" | "autoComplete" | "disabled" | "readOnly" | "required" | "aria-invalid" | "aria-describedby">;
4888
+
3868
4889
  export declare const textInputMeta: {
3869
4890
  readonly name: "TextInput";
3870
4891
  readonly category: "input";
@@ -3890,8 +4911,8 @@ export declare const textInputMeta: {
3890
4911
  };
3891
4912
  readonly type: {
3892
4913
  readonly kind: "enum";
3893
- readonly description: "The input type. \"password\" masks the field everywhere: the value never appears in agent attributes, the agent view, or tool results.";
3894
- readonly values: readonly ["text", "email", "url", "search", "password"];
4914
+ readonly description: "The input type. \"password\" masks the field everywhere: the value never appears in agent attributes, the agent view, or tool results. \"number\" keeps value a string, so the page parses it; pass min, max and step through inputProps.";
4915
+ readonly values: readonly ["text", "email", "url", "search", "password", "number", "tel"];
3895
4916
  readonly default: "text";
3896
4917
  };
3897
4918
  readonly placeholder: {
@@ -3929,6 +4950,27 @@ export declare const textInputMeta: {
3929
4950
  readonly description: "Mark the field required, visually and in the agent view.";
3930
4951
  readonly default: false;
3931
4952
  };
4953
+ readonly hideLabel: {
4954
+ readonly kind: "boolean";
4955
+ readonly description: "Hide the label visually while keeping it as the field's accessible name and agent label. Pair it with a placeholder or a nearby heading so sighted people still know what the field is for.";
4956
+ readonly default: false;
4957
+ };
4958
+ readonly icon: {
4959
+ readonly kind: "node";
4960
+ readonly description: "A decorative icon drawn inside the start of the field, such as a magnifier on a search box. Hidden from assistive technology.";
4961
+ };
4962
+ readonly trailing: {
4963
+ readonly kind: "node";
4964
+ readonly description: "Controls drawn inside the end of the field, such as a clear Button with hideLabel and size=\"small\". It renders only in the human view; give an agent the same action some other way, or rely on the fill tool, which can set the field to empty.";
4965
+ };
4966
+ readonly inputRef: {
4967
+ readonly kind: "object";
4968
+ readonly description: "A ref to the underlying <input>, for focusing or measuring it. The component's own ref points at the wrapper.";
4969
+ };
4970
+ readonly inputProps: {
4971
+ readonly kind: "object";
4972
+ readonly description: "Extra native attributes and handlers for the <input> itself, such as autoFocus, maxLength, inputMode, onKeyDown, or onBlur. Props spread on the component land on the wrapper; these land on the field. Anything the component manages (value, onChange, disabled, the error wiring) cannot be overridden here.";
4973
+ };
3932
4974
  readonly agentName: {
3933
4975
  readonly kind: "string";
3934
4976
  readonly description: "Override the label used to derive the tool name, when two fields on a page would otherwise collide.";
@@ -3991,6 +5033,14 @@ export declare const textInputMeta: {
3991
5033
  readonly title: "A read-only value";
3992
5034
  readonly description: "A value shown in the shape of a form field that nobody may edit. It stays focusable and selectable, reads as text in the agent view, and registers no fill tool. To hand someone a value to paste elsewhere, a CopyField adds the copy control.";
3993
5035
  readonly code: "<TextInput\n label=\"Station ID\"\n value=\"KX-2209-ALPHA\"\n onChange={() => {}}\n readOnly\n hint=\"Assigned at registration\"\n/>";
5036
+ }, {
5037
+ readonly title: "A number with field attributes";
5038
+ readonly description: "type=\"number\" keeps value a string. Native attributes for the input itself, such as min, max and autoFocus, go through inputProps, and inputRef reaches the input for focusing it later.";
5039
+ readonly code: "<TextInput\n label=\"Link expiry in days\"\n type=\"number\"\n value={days}\n onChange={setDays}\n inputRef={daysField}\n inputProps={{ min: 1, max: 90, step: 1 }}\n/>";
5040
+ }, {
5041
+ readonly title: "A search field with a clear control";
5042
+ readonly description: "The icon sits inside the start of the field and a small icon-only Button clears it from the end. An agent clears it by filling an empty string.";
5043
+ readonly code: "<TextInput\n label=\"Filter the board\"\n hideLabel\n placeholder=\"Filter by name or calling\"\n icon={<SearchIcon />}\n value={query}\n onChange={setQuery}\n trailing={\n query === \"\" ? null : (\n <Button size=\"small\" icon={<CloseIcon />} hideLabel onClick={() => setQuery(\"\")}>\n Clear filter\n </Button>\n )\n }\n/>";
3994
5044
  }];
3995
5045
  readonly a11y: {
3996
5046
  readonly role: "textbox";
@@ -4012,11 +5062,18 @@ export declare interface TextInputProps extends Omit<ComponentPropsWithRef<"div"
4012
5062
  disabled?: boolean;
4013
5063
  readOnly?: boolean;
4014
5064
  required?: boolean;
5065
+ hideLabel?: boolean;
5066
+ icon?: ReactNode;
5067
+ trailing?: ReactNode;
5068
+ inputRef?: Ref<HTMLInputElement>;
5069
+ inputProps?: TextInputFieldProps;
4015
5070
  agentName?: string;
4016
5071
  agentTool?: boolean;
4017
5072
  }
4018
5073
 
4019
- export declare type TextInputType = "text" | "email" | "url" | "search" | "password";
5074
+ export declare type TextInputType = "text" | "email" | "url" | "search" | "password" | "number" | "tel";
5075
+
5076
+ declare type TextLines = 1 | 2 | 3 | 4 | 5 | 6;
4020
5077
 
4021
5078
  export declare const textMeta: {
4022
5079
  readonly name: "Text";
@@ -4043,6 +5100,27 @@ export declare const textMeta: {
4043
5100
  readonly values: readonly ["small", "normal"];
4044
5101
  readonly default: "normal";
4045
5102
  };
5103
+ readonly weight: {
5104
+ readonly kind: "enum";
5105
+ readonly description: "bold for a line that needs to stand out among its neighbours, such as a name in a list row. Prefer a Heading for a title.";
5106
+ readonly values: readonly ["normal", "bold"];
5107
+ readonly default: "normal";
5108
+ };
5109
+ readonly align: {
5110
+ readonly kind: "enum";
5111
+ readonly description: "Horizontal alignment, in the writing direction.";
5112
+ readonly values: readonly ["start", "center", "end"];
5113
+ readonly default: "start";
5114
+ };
5115
+ readonly lines: {
5116
+ readonly kind: "number";
5117
+ readonly description: "Clamp the text to this many lines, 1 to 6, ending in an ellipsis. Only the screen is clamped: assistive technology and the agent view still get the whole text. Pair it with a Tooltip when a sighted person needs the rest.";
5118
+ };
5119
+ readonly italic: {
5120
+ readonly kind: "boolean";
5121
+ readonly description: "Set in italic, for a quotation, a title of a work, or a scripture reference.";
5122
+ readonly default: false;
5123
+ };
4046
5124
  readonly as: {
4047
5125
  readonly kind: "enum";
4048
5126
  readonly description: "The element to render. Use span when the text sits inside another line of text.";
@@ -4061,6 +5139,24 @@ export declare const textMeta: {
4061
5139
  readonly attribute: "data-sprint-size";
4062
5140
  readonly values: readonly ["small", "normal"];
4063
5141
  };
5142
+ readonly weight: {
5143
+ readonly description: "Present as bold on emphasised text.";
5144
+ readonly attribute: "data-sprint-weight";
5145
+ readonly values: readonly ["bold"];
5146
+ };
5147
+ readonly align: {
5148
+ readonly description: "The alignment, when it is not start.";
5149
+ readonly attribute: "data-sprint-align";
5150
+ readonly values: readonly ["center", "end"];
5151
+ };
5152
+ readonly lines: {
5153
+ readonly description: "The line clamp, when one is set. The text itself is never shortened.";
5154
+ readonly attribute: "data-sprint-lines";
5155
+ };
5156
+ readonly italic: {
5157
+ readonly description: "Present on italic text.";
5158
+ readonly attribute: "data-sprint-italic";
5159
+ };
4064
5160
  };
4065
5161
  readonly agentView: {
4066
5162
  readonly example: "- **Text** \"Tools stay registered across a view switch.\" [tone=muted]";
@@ -4076,12 +5172,23 @@ export declare const textMeta: {
4076
5172
  readonly title: "A live status line";
4077
5173
  readonly description: "Tone is the whole message here, so an agent reading the attribute learns the same thing a person learns from the colour.";
4078
5174
  readonly code: "<Text tone={ready ? \"action\" : \"warning\"} size=\"small\">\n {ready ? \"WebMCP is available in this browser.\" : \"WebMCP is unavailable here.\"}\n</Text>";
5175
+ }, {
5176
+ readonly title: "A clamped line";
5177
+ readonly description: "lines clamps the screen to two lines with an ellipsis. The agent view and screen readers still get every word.";
5178
+ readonly code: "<Text lines={2}>{member.notes}</Text>";
5179
+ }, {
5180
+ readonly title: "Emphasis and alignment";
5181
+ readonly code: "<Text weight=\"bold\" align=\"center\" italic>Come, follow me.</Text>";
4079
5182
  }];
4080
5183
  };
4081
5184
 
4082
5185
  export declare interface TextProps extends Omit<ComponentPropsWithRef<"p">, "ref"> {
4083
5186
  tone?: TextTone;
4084
5187
  size?: TextSize;
5188
+ weight?: TextWeight;
5189
+ align?: TextAlign;
5190
+ lines?: TextLines;
5191
+ italic?: boolean;
4085
5192
  as?: "p" | "span" | "div";
4086
5193
  ref?: Ref<HTMLParagraphElement> | Ref<HTMLSpanElement> | Ref<HTMLDivElement>;
4087
5194
  }
@@ -4090,8 +5197,104 @@ export declare type TextSize = "small" | "normal";
4090
5197
 
4091
5198
  export declare type TextTone = "default" | "muted" | "action" | "info" | "warning" | "danger";
4092
5199
 
5200
+ declare type TextWeight = "normal" | "bold";
5201
+
4093
5202
  export declare const THEME_ATTRIBUTE = "data-sprint-theme";
4094
5203
 
5204
+ export declare function Toast(props: ToastProps): JSX.Element | null;
5205
+
5206
+ export declare interface ToastAction {
5207
+ label: string;
5208
+ onSelect: () => void;
5209
+ shortcut?: string;
5210
+ }
5211
+
5212
+ export declare const toastMeta: {
5213
+ readonly name: "Toast";
5214
+ readonly category: "feedback";
5215
+ readonly summary: "A brief message that floats at the bottom of the screen after something happened, with an optional single action such as Undo, and dismisses itself.";
5216
+ readonly whenToUse: "Use to confirm an action that already took effect and can still be reversed or followed up: a person moved, a note saved, a share link copied. Render one Toast with open, message and onDismiss; the page owns which toast is showing. The action is data, { label, onSelect, shortcut? }, rendered as a Button that registers its own press tool, and the shortcut is shown as key caps.";
5217
+ readonly whenNotToUse: "Do not use for an error the person must act on, or anything that must stay visible; that is an Alert in the page. Do not use for a decision; that is a Dialog. Do not show several at once: replace the message instead. Inside an open modal Dialog, render the Toast inside the Dialog, because the page behind it is inert.";
5218
+ readonly status: "experimental";
5219
+ readonly props: {
5220
+ readonly open: {
5221
+ readonly kind: "boolean";
5222
+ readonly description: "Whether the toast is showing. A closed toast renders nothing.";
5223
+ readonly required: true;
5224
+ };
5225
+ readonly message: {
5226
+ readonly kind: "string";
5227
+ readonly description: "What happened, in a sentence, such as \"Moved Sister Amaral to Primary.\"";
5228
+ readonly required: true;
5229
+ };
5230
+ readonly onDismiss: {
5231
+ readonly kind: "handler";
5232
+ readonly description: "Called when the toast should go: after duration, or when the dismiss control is pressed. Set open to false in response.";
5233
+ readonly required: true;
5234
+ };
5235
+ readonly label: {
5236
+ readonly kind: "string";
5237
+ readonly description: "A short title above the message, and the toast's accessible name.";
5238
+ };
5239
+ readonly tone: {
5240
+ readonly kind: "enum";
5241
+ readonly description: "neutral and info announce politely; warning too. danger announces assertively, but prefer an Alert for errors.";
5242
+ readonly values: readonly ["neutral", "info", "warning", "danger"];
5243
+ readonly default: "neutral";
5244
+ };
5245
+ readonly action: {
5246
+ readonly kind: "object";
5247
+ readonly description: "One follow-up action: { label, onSelect, shortcut? }. It is a Button with its own press tool, named from the label. shortcut is shown as key caps and published as aria-keyshortcuts; the page binds the key itself.";
5248
+ };
5249
+ readonly duration: {
5250
+ readonly kind: "number";
5251
+ readonly description: "Milliseconds before onDismiss is called. The timer pauses while the pointer or focus is on the toast. Pass null to keep it until dismissed.";
5252
+ readonly default: 6000;
5253
+ };
5254
+ readonly dismissLabel: {
5255
+ readonly kind: "string";
5256
+ readonly description: "Accessible name of the dismiss control.";
5257
+ readonly default: "Dismiss";
5258
+ };
5259
+ };
5260
+ readonly state: {
5261
+ readonly tone: {
5262
+ readonly description: "The toast's tone.";
5263
+ readonly attribute: "data-sprint-tone";
5264
+ readonly values: readonly ["neutral", "info", "warning", "danger"];
5265
+ };
5266
+ };
5267
+ readonly agentView: {
5268
+ readonly example: "- **Toast** [tone=neutral]\n - part `message` \"Moved Sister Amaral to Primary.\"\n - part `dismiss` \"Dismiss\"\n - **Button** \"Undo\" [size=small, tone=action]";
5269
+ };
5270
+ readonly examples: readonly [{
5271
+ readonly title: "An undo toast";
5272
+ readonly description: "The action is a real Button, so an agent can press Undo through its tool while the toast is up.";
5273
+ readonly code: "<Toast\n open={moved !== null}\n message={movedMessage}\n action={{ label: \"Undo\", onSelect: undo, shortcut: \"Ctrl+Z\" }}\n onDismiss={() => setMoved(null)}\n/>";
5274
+ }, {
5275
+ readonly title: "A toast that stays";
5276
+ readonly description: "duration={null} keeps it until dismissed.";
5277
+ readonly code: "<Toast\n open={offline}\n label=\"Offline\"\n tone=\"warning\"\n duration={null}\n message=\"Changes are saved on this device until the connection returns.\"\n onDismiss={() => setOffline(false)}\n/>";
5278
+ }];
5279
+ readonly a11y: {
5280
+ readonly role: "status";
5281
+ readonly notes: "A role=status region, or role=alert for danger, so the message is announced when the toast appears. Render it when the event happens rather than toggling visibility. It opens as a popover in the top layer, at the bottom edge on a phone and the bottom corner on a wide screen. The timer pauses while the pointer or keyboard focus is on it, so nobody loses the action mid-reach.";
5282
+ };
5283
+ };
5284
+
5285
+ export declare interface ToastProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
5286
+ open: boolean;
5287
+ message: string;
5288
+ onDismiss: () => void;
5289
+ label?: string;
5290
+ tone?: ToastTone;
5291
+ action?: ToastAction;
5292
+ duration?: number | null;
5293
+ dismissLabel?: string;
5294
+ }
5295
+
5296
+ export declare type ToastTone = "neutral" | "info" | "warning" | "danger";
5297
+
4095
5298
  export declare interface Token {
4096
5299
  kind: TokenKind;
4097
5300
  text: string;
@@ -4130,6 +5333,69 @@ export declare interface ToolExecuteContext {
4130
5333
 
4131
5334
  export declare function toolName(scope: readonly string[], verb: string, label: string): string | null;
4132
5335
 
5336
+ export declare function Tooltip(props: TooltipProps): JSX.Element;
5337
+
5338
+ export declare const TOOLTIP_ATTRIBUTE = "data-sprint-tooltip";
5339
+
5340
+ export declare const tooltipMeta: {
5341
+ readonly name: "Tooltip";
5342
+ readonly category: "overlay";
5343
+ readonly summary: "A short hint that appears beside one control on hover or keyboard focus. A human affordance only: it adds nothing to the agent view.";
5344
+ readonly whenToUse: "Use to name an icon-only control for sighted mouse users, or to show the whole of a line that is truncated on screen. Wrap exactly one focusable element. The hint appears after a short hover or at once on keyboard focus, and Escape dismisses it.";
5345
+ readonly whenNotToUse: "Never put information only in a tooltip: touch users never see it and agents never read it, so the text must repeat something the wrapped element already says through its label or its own content. Do not use it for anything interactive; that is a Menu or a Dialog. For an icon-only Button, pass hideLabel instead, which adds the tooltip itself.";
5346
+ readonly status: "experimental";
5347
+ readonly props: {
5348
+ readonly label: {
5349
+ readonly kind: "string";
5350
+ readonly description: "The hint text. Keep it to a few words.";
5351
+ readonly required: true;
5352
+ };
5353
+ readonly children: {
5354
+ readonly kind: "node";
5355
+ readonly description: "Exactly one focusable element, such as a Button or a link. The tooltip is anchored to it and, unless describe is false, linked to it with aria-describedby.";
5356
+ readonly required: true;
5357
+ };
5358
+ readonly side: {
5359
+ readonly kind: "enum";
5360
+ readonly description: "Where the hint prefers to appear. It flips to the other side when there is no room.";
5361
+ readonly values: readonly ["above", "below"];
5362
+ readonly default: "above";
5363
+ };
5364
+ readonly describe: {
5365
+ readonly kind: "boolean";
5366
+ readonly description: "Link the hint to the element with aria-describedby. Set false when the hint repeats the element's accessible name, so a screen reader does not read it twice.";
5367
+ readonly default: true;
5368
+ };
5369
+ readonly disabled: {
5370
+ readonly kind: "boolean";
5371
+ readonly description: "Stop the hint from appearing.";
5372
+ readonly default: false;
5373
+ };
5374
+ };
5375
+ readonly examples: readonly [{
5376
+ readonly title: "Naming a truncated line";
5377
+ readonly description: "The line is cut short on screen, so the tooltip shows it whole. The element's own text already carries the full value for assistive technology and agents.";
5378
+ readonly code: "<Tooltip label=\"Elder Kestrel, second counselor in the elders quorum presidency\">\n <Link href=\"/callings/42\">Elder Kestrel, second counselor…</Link>\n</Tooltip>";
5379
+ }, {
5380
+ readonly title: "A hint below its control";
5381
+ readonly description: "side moves the hint below the control; it still flips when the control sits at the bottom of the screen.";
5382
+ readonly code: "<Tooltip label=\"Opens in the planner\" side=\"below\">\n <Button>Plan Sunday</Button>\n</Tooltip>";
5383
+ }];
5384
+ readonly a11y: {
5385
+ readonly role: "tooltip";
5386
+ readonly keyboard: readonly ["Focus shows the hint", "Escape hides it"];
5387
+ readonly notes: "The hint is a role=tooltip element linked to the wrapped element with aria-describedby. It appears after a hover delay or immediately on keyboard focus, never on touch, and is dismissed by Escape, blur, or moving the pointer away. It is a popover in the top layer, rendered inside the wrapped element's DOM, so it shows above a modal Dialog.";
5388
+ };
5389
+ };
5390
+
5391
+ export declare interface TooltipProps extends Omit<ComponentPropsWithRef<"span">, "children"> {
5392
+ label: string;
5393
+ children: ReactElement;
5394
+ side?: FloatingSide;
5395
+ describe?: boolean;
5396
+ disabled?: boolean;
5397
+ }
5398
+
4133
5399
  export declare const TURN_PAGE_TOOL: AgentToolSpec;
4134
5400
 
4135
5401
  export declare function useAgentControls(): SprintAgentControls;
@@ -4149,6 +5415,19 @@ export declare interface UseAgentToolOptions {
4149
5415
  execute: (inputs: Record<string, unknown>, context: ToolExecuteContext) => Promise<string | null> | string | null;
4150
5416
  }
4151
5417
 
5418
+ export declare function useFloating(options: UseFloatingOptions): void;
5419
+
5420
+ export declare interface UseFloatingOptions {
5421
+ open: boolean;
5422
+ anchor: RefObject<HTMLElement | null>;
5423
+ floating: RefObject<HTMLElement | null>;
5424
+ side?: FloatingSide;
5425
+ align?: FloatingAlign;
5426
+ matchWidth?: boolean;
5427
+ boundary?: RefObject<HTMLElement | null>;
5428
+ onDismiss?: () => void;
5429
+ }
5430
+
4152
5431
  export declare function useSprintView(): SprintView;
4153
5432
 
4154
5433
  export declare function useSprintViewControl(): SprintViewValue;
@@ -4161,4 +5440,52 @@ export declare const VIEW_ATTRIBUTE = "data-sprint-view";
4161
5440
 
4162
5441
  export declare const VIEW_COPY_ATTRIBUTE = "data-sprint-view-copy";
4163
5442
 
5443
+ export declare function VisuallyHidden(props: VisuallyHiddenProps): JSX.Element;
5444
+
5445
+ export declare const visuallyHiddenMeta: {
5446
+ readonly name: "VisuallyHidden";
5447
+ readonly category: "typography";
5448
+ readonly summary: "Text that is off screen but still read by screen readers and agents. For context a sighted person gets from the layout.";
5449
+ readonly whenToUse: "Use to say in words what the layout says visually: \"(opens in a new tab)\" after a link, a count's unit, a heading for a region whose purpose is obvious on screen. With focusable, it wraps a skip link that appears only while it has focus.";
5450
+ readonly whenNotToUse: "Do not use to hide a field's label; TextInput, Textarea, SearchField and Progress take hideLabel, and an icon-only Button takes hideLabel too. Do not hide content that a sighted person also needs.";
5451
+ readonly status: "experimental";
5452
+ readonly props: {
5453
+ readonly children: {
5454
+ readonly kind: "node";
5455
+ readonly description: "The hidden text, or a skip link when focusable is set.";
5456
+ readonly required: true;
5457
+ };
5458
+ readonly focusable: {
5459
+ readonly kind: "boolean";
5460
+ readonly description: "Show the content while something inside it has keyboard focus, for a skip link.";
5461
+ readonly default: false;
5462
+ };
5463
+ };
5464
+ readonly state: {
5465
+ readonly focusable: {
5466
+ readonly description: "Present when the content appears while focused.";
5467
+ readonly attribute: "data-sprint-focusable";
5468
+ };
5469
+ };
5470
+ readonly agentView: {
5471
+ readonly example: "- **VisuallyHidden** \"opens in a new tab\"";
5472
+ };
5473
+ readonly examples: readonly [{
5474
+ readonly title: "Extra words for a screen reader";
5475
+ readonly description: "The arrow says it to a sighted person; the hidden text says it to everyone else.";
5476
+ readonly code: "<Link href=\"https://churchofjesuschrist.org\" external>\n Gospel Library ↗<VisuallyHidden> (opens in a new tab)</VisuallyHidden>\n</Link>";
5477
+ }, {
5478
+ readonly title: "A skip link";
5479
+ readonly description: "Off screen until a keyboard user tabs to it.";
5480
+ readonly code: "<VisuallyHidden focusable>\n <Link href=\"#main\">Skip to the board</Link>\n</VisuallyHidden>";
5481
+ }];
5482
+ readonly a11y: {
5483
+ readonly notes: "Hidden with a one-pixel clip rather than display or visibility, so screen readers still read it. The agent view renders its text as an ordinary line.";
5484
+ };
5485
+ };
5486
+
5487
+ export declare interface VisuallyHiddenProps extends ComponentPropsWithRef<"span"> {
5488
+ focusable?: boolean;
5489
+ }
5490
+
4164
5491
  export { }