@crewlethq/ui 0.15.1 → 0.17.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.
Files changed (111) hide show
  1. package/README.md +36 -6
  2. package/dist/AppShell/AccountRow.d.ts +40 -0
  3. package/dist/AppShell/AppShell.d.ts +1 -0
  4. package/dist/AppShell/index.d.ts +2 -0
  5. package/dist/AppShell/index.js +1 -1
  6. package/dist/{AppShell-6QUD7NWM.css → AppShell-RUNVM4WH.css} +93 -0
  7. package/dist/AuthLayout/AuthLayout.d.ts +35 -0
  8. package/dist/AuthLayout/index.d.ts +3 -0
  9. package/dist/AuthLayout/index.js +8 -0
  10. package/dist/AuthLayout-ODHBUPHQ.css +54 -0
  11. package/dist/Avatar/Avatar.d.ts +37 -2
  12. package/dist/Avatar/index.d.ts +1 -1
  13. package/dist/Avatar/index.js +2 -2
  14. package/dist/{Avatar-I4NFP3WQ.css → Avatar-3C2H2WA4.css} +34 -0
  15. package/dist/AvatarPicker/index.js +4 -4
  16. package/dist/Callout/Callout.d.ts +7 -1
  17. package/dist/Callout/index.js +1 -1
  18. package/dist/{Callout-S7QDWHDL.css → Callout-SXXK4B5R.css} +3 -1
  19. package/dist/CharacterPicker/index.js +2 -2
  20. package/dist/Checkbox/CheckboxGroup.d.ts +30 -0
  21. package/dist/Checkbox/index.d.ts +2 -0
  22. package/dist/Checkbox/index.js +5 -1
  23. package/dist/{Checkbox-UE7TEPAM.css → Checkbox-KT7RS6QQ.css} +16 -0
  24. package/dist/CodeBlock/index.js +4 -3
  25. package/dist/Combobox/index.js +3 -3
  26. package/dist/CommandPalette/index.js +5 -5
  27. package/dist/Composer/index.js +3 -3
  28. package/dist/ConfirmField/ConfirmField.d.ts +43 -0
  29. package/dist/ConfirmField/index.d.ts +2 -0
  30. package/dist/ConfirmField/index.js +7 -0
  31. package/dist/Copyable/CopyButton.d.ts +13 -6
  32. package/dist/Copyable/index.js +3 -2
  33. package/dist/DataTable/index.js +7 -5
  34. package/dist/DataView/index.js +12 -11
  35. package/dist/DateTimePicker/index.js +2 -2
  36. package/dist/DownloadButton/DownloadButton.d.ts +52 -0
  37. package/dist/DownloadButton/index.d.ts +2 -0
  38. package/dist/DownloadButton/index.js +6 -0
  39. package/dist/EntityChip/index.js +3 -3
  40. package/dist/ImageUpload/index.js +3 -3
  41. package/dist/InlineCode/InlineCode.d.ts +8 -1
  42. package/dist/InlineCode/index.js +1 -1
  43. package/dist/{InlineCode-6WSR5COL.css → InlineCode-ASYHLN3E.css} +9 -0
  44. package/dist/Input/index.js +2 -2
  45. package/dist/{Input-RAKNQJCV.css → Input-3KVSNUO5.css} +11 -0
  46. package/dist/ListInput/index.js +3 -3
  47. package/dist/MarkdownEditor/index.js +1 -1
  48. package/dist/Modal/index.js +2 -2
  49. package/dist/ObjectHeader/ObjectHeader.d.ts +138 -0
  50. package/dist/ObjectHeader/index.d.ts +3 -0
  51. package/dist/ObjectHeader/index.js +4 -0
  52. package/dist/ObjectHeader-UNVYM2KD.css +174 -0
  53. package/dist/OrgLabel/index.js +3 -3
  54. package/dist/OrgNode/index.js +4 -4
  55. package/dist/OrgTable/index.js +4 -4
  56. package/dist/QrCode/QrCode.d.ts +58 -0
  57. package/dist/QrCode/index.d.ts +3 -0
  58. package/dist/QrCode/index.js +2 -0
  59. package/dist/QrCode-JWJTWMU5.css +19 -0
  60. package/dist/Switcher/index.js +4 -4
  61. package/dist/Text/Text.d.ts +7 -1
  62. package/dist/Text/index.js +1 -1
  63. package/dist/{Text-XIPYYZX2.css → Text-S3Q4O3IG.css} +11 -0
  64. package/dist/TimeWindowPicker/index.js +4 -4
  65. package/dist/{chunk-FTEJPHBI.js → chunk-4GB57WIT.js} +2 -2
  66. package/dist/{chunk-DDTDN5L2.js → chunk-4KWOKX2B.js} +1 -1
  67. package/dist/{chunk-NN62JQLR.js → chunk-5CLW2CXT.js} +3 -1
  68. package/dist/{chunk-HI6Q2UXQ.js → chunk-5OM447RW.js} +19 -7
  69. package/dist/{chunk-YXMCUKFI.js → chunk-5PSXBL73.js} +1 -1
  70. package/dist/{chunk-QSLIN64M.js → chunk-6NIMZ5XC.js} +1 -1
  71. package/dist/{chunk-YCTMKKRA.js → chunk-75CLJNDQ.js} +12 -2
  72. package/dist/chunk-7QFAORCY.js +37 -0
  73. package/dist/{chunk-22M7OXKW.js → chunk-AKEZ3G5V.js} +1 -1
  74. package/dist/chunk-BJCFNRGH.js +53 -0
  75. package/dist/chunk-CXBAIVYZ.js +39 -0
  76. package/dist/chunk-DTIW4CHZ.js +37 -0
  77. package/dist/chunk-F6NZOVWL.js +68 -0
  78. package/dist/{chunk-LCF4WPYS.js → chunk-FA2V2VT6.js} +2 -2
  79. package/dist/{chunk-KGTQ5CVD.js → chunk-GDDE4UM7.js} +1 -1
  80. package/dist/{chunk-EFNHOVC2.js → chunk-HENOTGUV.js} +2 -2
  81. package/dist/chunk-I7WXZOWA.js +90 -0
  82. package/dist/{chunk-23DHIMJA.js → chunk-IKBK4A5O.js} +1 -1
  83. package/dist/{chunk-DY77H372.js → chunk-JF7X673Z.js} +2 -2
  84. package/dist/{chunk-YYBD73OI.js → chunk-L4ZLXYPO.js} +1 -1
  85. package/dist/{chunk-H3XOMT7W.js → chunk-OYOQU7O5.js} +44 -3
  86. package/dist/{chunk-IYZZL3MZ.js → chunk-PQ63NLFC.js} +3 -3
  87. package/dist/{chunk-TK6YC5QT.js → chunk-RF5O4FYL.js} +4 -4
  88. package/dist/{chunk-5VNUYO76.js → chunk-STJ7QBC3.js} +8 -25
  89. package/dist/{chunk-R6FBDTP3.js → chunk-SVSCLHHI.js} +1 -1
  90. package/dist/{chunk-GARACZAH.js → chunk-T3ZWBZ65.js} +1 -1
  91. package/dist/{chunk-FY2R4U4Q.js → chunk-TBMDMXEX.js} +1 -1
  92. package/dist/{chunk-ZPWUVVIB.js → chunk-UAN5NE7M.js} +2 -2
  93. package/dist/{chunk-YACKC3K2.js → chunk-W46LPU4R.js} +8 -8
  94. package/dist/{chunk-4EOJR52M.js → chunk-XU4H6JQB.js} +4 -4
  95. package/dist/chunk-Y56FM3K4.js +57 -0
  96. package/dist/{chunk-DJMLHCLD.js → chunk-YDBC4DNW.js} +1 -1
  97. package/dist/{chunk-4Y7WKXAW.js → chunk-ZFH7H7GS.js} +1 -1
  98. package/dist/generated/index.d.ts +5 -0
  99. package/dist/generated/index.js +67 -60
  100. package/dist/index.js +67 -60
  101. package/dist/styles.css +440 -8
  102. package/dist/utils/index.d.ts +4 -0
  103. package/dist/utils/index.js +4 -2
  104. package/dist/utils/useClipboard.d.ts +6 -3
  105. package/dist/utils/useDownload.d.ts +63 -0
  106. package/dist/utils/useOutcome.d.ts +44 -0
  107. package/package.json +4 -3
  108. package/dist/{chunk-GUHP5JHC.js → chunk-2UU5IGZI.js} +1 -1
  109. package/dist/{chunk-6AR2T5Y4.js → chunk-CRVETI5L.js} +0 -0
  110. package/dist/{chunk-K4VORB64.js → chunk-DEEVA6TV.js} +1 -1
  111. /package/dist/{chunk-AER7UDD4.js → chunk-U5LQ2AS5.js} +0 -0
package/README.md CHANGED
@@ -40,17 +40,17 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
40
40
 
41
41
  | Area | Components |
42
42
  | ---- | ---------- |
43
- | Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `Kbd` |
44
- | Content and text | `Text` (the eight registers), `Prose`, `Document` and `DocumentTitle`, `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime` |
45
- | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor` |
46
- | Data display | `DataTable`, `CopyableCell`, `Table`, `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `Skeleton`, `Eyebrow` |
43
+ | Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `DownloadButton`, `Kbd` |
44
+ | Content and text | `Text` (the eight registers and `inherit`), `Prose`, `Document` and `DocumentTitle`, `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime` |
45
+ | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor` |
46
+ | Data display | `DataTable`, `CopyableCell`, `Table`, `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `QrCode`, `ObjectHeader`, `FactLine`, `SetByLine`, `Skeleton`, `Eyebrow` |
47
47
  | List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
48
48
  | Feedback and overlays | `Callout`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
49
49
  | Charts | `Legend`, `BarList`, `StackedBar`, `StackedColumns`, `ActivityStrip`, `TimeSeries`, `Sparkline`, the reading layer they share (`ChartTooltip`, `useChartHover`), and the ramp (`DATA_COLORS`, `DATA_COLOR_OTHER`, `dataColor`) |
50
50
  | Marks | `Count`, `StatusDot`, `EmptyValue`, `VisuallyHidden` |
51
- | Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useHighlightRest` and `HIGHLIGHT_REST_MS`, `useClipboard`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx`, the node hues (`NODE_HUES`, `NODE_HUE_NAMES`, `isNodeHue`) |
51
+ | Hooks and seams | `useModalLayer`, `usePopupLayer`, `LayerHost`, `useListbox`, `useOptionKeys`, `useHighlightRest` and `HIGHLIGHT_REST_MS`, `useClipboard`, `useDownload`, `downloadText`, `safeFilename`, `useOutcome`, `useAnnouncer`, `useNow`, `formatRelative`, `HeadingLevelProvider`, `cx`, the node hues (`NODE_HUES`, `NODE_HUE_NAMES`, `isNodeHue`) |
52
52
  | Layout | `Container`, `Section`, `Card`, `Tabs`, `Accordion`, `Disclosure`, `Outline` (with `useActiveHeading` and `scrollToHeading`), `TreeGrid` |
53
- | Application shell | `AppShell` with `AppShell.Rail`, `AppShell.Topbar` and `useAppShell`, `SidebarNav` and `NavItem`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen` |
53
+ | Application shell | `AppShell` with `AppShell.Rail`, `AppShell.Topbar`, `AppShell.AccountRow` and `useAppShell`, `AuthLayout`, `SidebarNav` and `NavItem`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen` |
54
54
  | Preferences | `ThemeToggle`, `ThemeSwitcher`, `DensitySwitcher`, `ThemeGallery`, `ThemeSwatch`, `useThemePreference`, `useDensityPreference`, `useSystemTheme`, `resolveTheme`, `applyStoredPreferences` |
55
55
 
56
56
  Storybook stories live under `apps/storybook/src/stories`.
@@ -198,6 +198,36 @@ places:
198
198
  - A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
199
199
  when the content beneath it moves, and every panel anchored inside follows.
200
200
 
201
+ ## Changes in 0.17.0
202
+
203
+ **`Avatar` takes `mark`**, a glyph on the lower-right corner that says how a principal is present rather than what it is doing: a seat's badge acting through an API token keeps its own initials and wears a key there. It sits in the notch a status uses, drawn quiet in the neutral badge's raised fill, strong hairline and secondary ink, and a `status` wins over it. **`markLabel`** is said after the name by a badge that names itself (`Miles avatar, signed in with an API token`); a decorative one says nothing. A marked badge, like one with a status, is drawn inside a `.crewlet-avatar-frame` and draws no ring. What to change: nothing.
204
+
205
+ ## Changes in 0.16.0
206
+
207
+ 0.16.0 adds the pieces the engine console's sign-in, Account and People & access screens were drawing on their own, so every one of them is the package's now.
208
+
209
+ **`QrCode`** draws a value as inline SVG from the `uqr` encoder: one path of run rectangles on one plate, four modules of quiet zone, error-correction level M, named by `label`, and nothing for a value too long to encode. Nothing is fetched and no markup is injected, so it works under a strict Content-Security-Policy. Its inks are the new **`color.qr.dark` and `color.qr.light` tokens** in `@crewlethq/tokens`, black and white in every palette, because a code inverted for a dark theme is one some authenticator apps cannot read. `@crewlethq/ui` now depends on `uqr` 0.1.3.
210
+
211
+ **`DownloadButton`** hands text to the reader as a file and says whether the browser took it, under the name it offered: "Downloading", and "download started: {name}" to a screen reader. **`useDownload`** is its hook, and **`downloadText`** is the same save for a control that cannot speak for itself, such as a menu item. **`safeFilename`** decides the name once: separators and invisibles become a hyphen in any script, the extension survives, and the stem is cut to 120 bytes on whole code points. `CopyButton` and `DownloadButton` share **`useOutcome`**, one state machine whose status message is keyed on the attempt, so a second identical outcome is still announced.
212
+
213
+ **`AuthLayout`** is the page a sign-in screen is drawn on, outside the application shell: a main landmark, the product's `BrandLockup` with the Crewlet mark inline, and one card named by the page's only `h1`, with its description and a body that stacks a form one field step apart.
214
+
215
+ **`ObjectHeader`, `FactLine` and `SetByLine`** are the header of an object on its page, in a peek and in a hover card: the eyebrow with its kind, glyph and key, a two-line level-two title beside the object's state and actions, and the facts as label-over-value cells on a 7.5rem track. A value gets two lines before it is cut, a `token` keeps one line across two tracks, a `set` of chips is never clamped, and a fact's note and by-line follow its own value. `SetByLine` says provenance in one wording, "set by ada · 2h ago · turn", with the turn as a link.
216
+
217
+ **`CheckboxGroup`** is several checkboxes under one legend, in the field label's register, with the group's help and error on the fieldset and an `emptyMessage` for a group with nothing to offer.
218
+
219
+ **`ConfirmField`** is the field a gesture that cannot be undone asks the reader to type a value back into: a `FormField` labelled "Type {phrase} to confirm", with spelling checks off, and **`confirmMatches`** to decide when the gesture may go ahead. Inline code inside a field label now keeps its case, so the phrase reads as the value the field accepts.
220
+
221
+ **`AppShell.AccountRow`** is the signed-in reader at the foot of the rail: their badge, name and detail as the link to their account, ending in a chevron at rest, marked current on the account page, with the row's controls beside it.
222
+
223
+ **`Avatar` takes `icon`**, a glyph drawn in place of the initials for a principal whose initials mean nothing, such as an API token or a service account. **An agent's initials skip a leading "agent" word**, which its squircle already says: Agent CEO and Agent CTO draw CE and CT rather than AC twice. The accessible name keeps the whole name. `avatarInitialsName` is exported.
224
+
225
+ **`InlineCode` takes `whole`**, for a short token that must not break across lines. **`Text` takes `variant="inherit"`**, which keeps the register of the words around it, for a mono literal inside a caption or a chip.
226
+
227
+ **A `Callout`'s title is a line of its own** above the message, rather than a bold lead-in that read as one run-on sentence with it.
228
+
229
+ What to change: a `CopyButton` refusal now holds until the next press by default, as `ClipboardOptions.failedResetMs` recommends; pass `failedResetMs` a number to give it a clock again. A callout written with a `title` that read as the start of its sentence may want its body reworded. An `AvatarStack` member or an `Avatar` handed a pre-shortened agent name can be handed the whole name. Everything else is new.
230
+
201
231
  ## Changes in 0.15.1
202
232
 
203
233
  `@crewlethq/icons`: every Crewlet character is centred on its optical centre rather than its bounding box, and its face and holes are cut into its own paths rather than masked, so a slit shows the badge's plate in every hue and theme, and inside `OrgLabel` or `AddPill` no longer fills with ink. The page's fold is drawn at full detail only, as the keyline is. What to change: nothing.
@@ -0,0 +1,40 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface AccountRowProps {
3
+ /** The reader's badge, drawn decorative: the name beside it says who it is. */
4
+ avatar: ReactNode;
5
+ /** Who the reader is. */
6
+ name: ReactNode;
7
+ /** The line under the name: a login, a seat. Left out where it would repeat the name. */
8
+ detail?: ReactNode;
9
+ /** The reader's account page. Without it the row states who is signed in and goes nowhere. */
10
+ href?: string | undefined;
11
+ /** Whether the reader is on that page, which marks the row as the rail marks its current row. */
12
+ current?: boolean | undefined;
13
+ /** What the link opens, as its tooltip. */
14
+ title?: string | undefined;
15
+ /**
16
+ * Said after the name to a screen reader, so the link names its target as
17
+ * well as its owner. Defaults to ", your account".
18
+ */
19
+ linkSuffix?: string | undefined;
20
+ /** Controls at the row's end: a theme toggle, the trigger of a preferences panel. */
21
+ actions?: ReactNode;
22
+ className?: string | undefined;
23
+ }
24
+ /**
25
+ * The signed-in reader at the foot of the rail: their badge, their name and
26
+ * the line under it, as the way to their own account, with the row's
27
+ * controls beside it.
28
+ *
29
+ * THE LINK ENDS IN A CHEVRON AT REST. A name says nothing about being the way
30
+ * to a password and a second factor, and a row that drew no mark until it was
31
+ * hovered was one nobody found.
32
+ *
33
+ * WHERE THE READER IS, as the rail says it: on the account page the row wears
34
+ * the lift and hairline the rail's current row wears, with no hue, and an
35
+ * outline rather than a shadow so forced colours keep it.
36
+ *
37
+ * The controls are siblings of the link, never inside it, so each keeps its
38
+ * own name and the link's name stays the reader's.
39
+ */
40
+ export declare function AccountRow({ avatar, name, detail, href, current, title, linkSuffix, actions, className, }: AccountRowProps): import("react").JSX.Element;
@@ -105,6 +105,7 @@ export declare namespace AppShell {
105
105
  var RailRow: import("react").ForwardRefExoticComponent<AppShellRailRowProps & import("react").RefAttributes<HTMLElement>>;
106
106
  var Topbar: typeof AppShellTopbar;
107
107
  var Toggle: typeof AppShellToggle;
108
+ var AccountRow: typeof import("./AccountRow.js").AccountRow;
108
109
  }
109
110
  export interface AppShellRailProps {
110
111
  /** The head of the rail, drawn at the top bar's own height. */
@@ -1,3 +1,5 @@
1
1
  import './AppShell.css';
2
2
  export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from './AppShell.js';
3
+ export { AccountRow } from './AccountRow.js';
4
+ export type { AccountRowProps } from './AccountRow.js';
3
5
  export type { ActionRailRowProps, AppShellContextValue, AppShellProps, AppShellRailProps, AppShellRailRowProps, AppShellToggleProps, AppShellTopbarProps, RailRowCount, StaticRailRowProps, } from './AppShell.js';
@@ -1,4 +1,4 @@
1
- export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from '../chunk-H3XOMT7W.js';
1
+ export { AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from '../chunk-OYOQU7O5.js';
2
2
  import '../chunk-IG75YMUN.js';
3
3
  import '../chunk-4FQT6ZAY.js';
4
4
  import '../chunk-HMPFGIWM.js';
@@ -621,3 +621,96 @@
621
621
  transition: none;
622
622
  }
623
623
  }
624
+
625
+ /* ── the account row ───────────────────────────────────────────────────── */
626
+
627
+ /*
628
+ * The signed-in reader at the foot of the rail, with the row's controls at
629
+ * its end. The link takes the room, so a long name is cut rather than pushing
630
+ * the controls off the rail.
631
+ */
632
+ .crewlet-account-row {
633
+ display: flex;
634
+ align-items: center;
635
+ gap: var(--spacing-1);
636
+ min-width: 0;
637
+ padding: var(--spacing-1) 0;
638
+ }
639
+
640
+ .crewlet-account-row__who {
641
+ display: flex;
642
+ flex: 1 1 auto;
643
+ align-items: center;
644
+ gap: var(--spacing-2);
645
+ min-width: 0;
646
+ padding: var(--spacing-1);
647
+ border-radius: var(--radius-md);
648
+ color: var(--color-text-primary);
649
+ text-decoration: none;
650
+ /* The fill eases under the pointer, as the rail's rows do. */
651
+ transition: background var(--motion-duration-fast) var(--motion-easing-default);
652
+ }
653
+
654
+ a.crewlet-account-row__who:hover {
655
+ background: var(--color-surface-hover);
656
+ }
657
+
658
+ /* After :hover, so the current row shows the lift rather than the overlay. */
659
+ a.crewlet-account-row__who[aria-current='page'] {
660
+ background: var(--color-surface-elevated);
661
+ outline: 1px solid var(--color-border-default);
662
+ outline-offset: -1px;
663
+ }
664
+
665
+ a.crewlet-account-row__who:focus-visible {
666
+ outline: 2px solid var(--color-focus);
667
+ outline-offset: var(--size-focus-ring-inset-offset);
668
+ }
669
+
670
+ .crewlet-account-row__avatar {
671
+ display: inline-flex;
672
+ flex: none;
673
+ }
674
+
675
+ .crewlet-account-row__text {
676
+ display: flex;
677
+ flex-direction: column;
678
+ min-width: 0;
679
+ line-height: var(--font-line-height-snug);
680
+ }
681
+
682
+ .crewlet-account-row__name,
683
+ .crewlet-account-row__detail {
684
+ overflow: hidden;
685
+ text-overflow: ellipsis;
686
+ white-space: nowrap;
687
+ }
688
+
689
+ .crewlet-account-row__name {
690
+ font-size: var(--font-size-xs);
691
+ font-weight: var(--font-weight-medium);
692
+ }
693
+
694
+ .crewlet-account-row__detail {
695
+ font-size: var(--font-size-2xs);
696
+ color: var(--color-text-tertiary);
697
+ }
698
+
699
+ .crewlet-account-row__go {
700
+ flex: none;
701
+ margin-left: auto;
702
+ color: var(--color-text-tertiary);
703
+ }
704
+
705
+ .crewlet-account-row__actions {
706
+ display: flex;
707
+ flex: none;
708
+ align-items: center;
709
+ gap: var(--spacing-1);
710
+ }
711
+
712
+ @media (prefers-reduced-motion: reduce) {
713
+ .crewlet-account-row__who {
714
+ transition: none;
715
+ }
716
+ }
@@ -0,0 +1,35 @@
1
+ import { type ReactNode } from 'react';
2
+ export interface AuthLayoutProps {
3
+ /** What this screen is: "Sign in", "Set your password". The page's one `h1`. */
4
+ title: ReactNode;
5
+ /** One or two sentences under the title saying what the screen is for. */
6
+ description?: ReactNode;
7
+ /** The product's name beside its mark. */
8
+ productName?: string | undefined;
9
+ /** The mark. Defaults to the Crewlet mark; decoration, since the name says it. */
10
+ mark?: ReactNode;
11
+ /** Home, for a screen a reader can leave. Without it the lockup is not a link. */
12
+ homeHref?: string | undefined;
13
+ /** The form, or the message and its one action. */
14
+ children?: ReactNode;
15
+ className?: string | undefined;
16
+ }
17
+ /**
18
+ * The page a sign-in screen is drawn on: the product's lockup, and one card.
19
+ *
20
+ * OUTSIDE THE APPLICATION SHELL, deliberately. A browser here holds no
21
+ * session, so a rail, a top bar and their rows would be chrome over questions
22
+ * the server will refuse, and a row offering a workspace to somebody who has
23
+ * not said who they are is a promise the next click breaks.
24
+ *
25
+ * ONE COLUMN, NARROW, AT THE TOP. The column is a sentence's measure wide and
26
+ * starts near the top of the window rather than its middle, so the form does
27
+ * not jump when an error line appears under a field. The card is a section
28
+ * named by its title, which is the page's `h1`; the description is its lede,
29
+ * and the body stacks the form's parts one field step apart.
30
+ *
31
+ * The mark is the design system's own drawing, inline, through
32
+ * [BrandLockup], so nothing is fetched and the lockup reads as it does at the
33
+ * head of the rail.
34
+ */
35
+ export declare function AuthLayout({ title, description, productName, mark, homeHref, children, className, }: AuthLayoutProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './AuthLayout.css';
2
+ export { AuthLayout } from './AuthLayout.js';
3
+ export type { AuthLayoutProps } from './AuthLayout.js';
@@ -0,0 +1,8 @@
1
+ export { AuthLayout } from '../chunk-7QFAORCY.js';
2
+ import '../chunk-7KBNFFM3.js';
3
+ import '../chunk-EG6NYWVK.js';
4
+ import '../chunk-AV2DXAEL.js';
5
+ import '../chunk-PQADV6P6.js';
6
+ import '../chunk-BHHIIK3M.js';
7
+ import '../chunk-BZFWDIV6.js';
8
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,54 @@
1
+ /*
2
+ * The sign-in page: one narrow column near the top of the window.
3
+ *
4
+ * THE PAGE SCROLLS ITSELF, at the viewport's dynamic height, because it is
5
+ * drawn outside any shell that would: on a phone with the keyboard up, a form
6
+ * taller than what is left of the window has to scroll, not be cut off.
7
+ */
8
+ .crewlet-auth-layout {
9
+ display: flex;
10
+ justify-content: center;
11
+ height: 100dvh;
12
+ overflow-y: auto;
13
+ padding: var(--spacing-8) var(--spacing-4);
14
+ background: var(--color-surface-background);
15
+ }
16
+
17
+ /* A sentence's measure wide, and held at the top: the form does not jump when
18
+ an error line appears under a field. */
19
+ .crewlet-auth-layout__column {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: var(--spacing-5);
23
+ width: 100%;
24
+ max-width: 52ch;
25
+ margin-block-end: auto;
26
+ }
27
+
28
+ /* The lockup is centred over the card, and takes none of the rail's inset,
29
+ which is what its own padding is for. */
30
+ .crewlet-auth-layout__brand {
31
+ align-self: center;
32
+ padding: 0;
33
+ }
34
+
35
+ /* The form's parts stand one field step apart, under the title and lede. */
36
+ .crewlet-auth-layout__body {
37
+ display: flex;
38
+ flex-direction: column;
39
+ gap: var(--spacing-4);
40
+ margin-top: var(--spacing-4);
41
+ }
42
+
43
+ /* A submit does not stretch: every button here states one gesture, and a
44
+ full-width bar reads as the page's edge rather than a control. */
45
+ .crewlet-auth-layout__body > form {
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: var(--spacing-4);
49
+ }
50
+
51
+ .crewlet-auth-layout__body > form > button,
52
+ .crewlet-auth-layout__body > button {
53
+ align-self: flex-start;
54
+ }
@@ -1,4 +1,4 @@
1
- import type { HTMLAttributes } from 'react';
1
+ import type { HTMLAttributes, ReactNode } from 'react';
2
2
  import { type CrewletCharacterId } from '@crewlethq/icons/characters';
3
3
  import type { NodeHue } from '../utils/nodeHue.js';
4
4
  /**
@@ -108,6 +108,21 @@ export interface AvatarProps extends HTMLAttributes<HTMLElement> {
108
108
  ring?: AvatarRing | undefined;
109
109
  /** What the agent is doing, as a corner badge; none is idle. */
110
110
  status?: AvatarStatus | undefined;
111
+ /**
112
+ * A glyph on the corner saying HOW a principal is present rather than what
113
+ * it is doing: a person's or a seat's badge acting through an API token
114
+ * wears a key there, so the face stays theirs and the credential is still
115
+ * shown. It sits where a status sits, cut into the same notch, in the
116
+ * neutral badge's raised fill, and a `status` wins over it. A component,
117
+ * drawn at the corner's own scale.
118
+ */
119
+ mark?: ReactNode;
120
+ /**
121
+ * What the mark means, said after the name by a badge that names itself
122
+ * ("Miles avatar, signed in with an API token"). A decorative badge says
123
+ * nothing, as it says nothing of a status.
124
+ */
125
+ markLabel?: string | undefined;
111
126
  /**
112
127
  * The name is already printed beside this badge, so the badge itself says
113
128
  * nothing. Without it a row reads "Carlos Diaz avatar, Carlos Diaz".
@@ -130,7 +145,27 @@ export interface AvatarProps extends HTMLAttributes<HTMLElement> {
130
145
  * neutral badge's ink, on its plate.
131
146
  */
132
147
  hue?: NodeHue | undefined;
148
+ /**
149
+ * A glyph drawn in place of the initials, for a principal that is not a
150
+ * person or an agent and whose name's initials would mean nothing: an API
151
+ * token, a service account. "TO" from `token:reader` reads as somebody's
152
+ * initials. A component, not a name; it is drawn at the badge's own scale.
153
+ * An image or an agent's character still wins over it.
154
+ */
155
+ icon?: ReactNode;
133
156
  }
157
+ /**
158
+ * The name an agent's initials are drawn from: its leading "agent" word
159
+ * dropped (`Agent CEO` gives CEO, `agent-cto` gives cto), and every other
160
+ * name as it is.
161
+ *
162
+ * THE SHAPE ALREADY SAYS "AGENT". A company that names its seats for what
163
+ * they are ("Agent CEO", "Agent CTO", "Agent PM") otherwise got AC, AC and
164
+ * AP: the two seats a chart draws most were the same badge side by side. The
165
+ * badge's accessible name keeps the whole name. A name that is nothing but
166
+ * the word keeps it, since an empty one draws the anonymous badge.
167
+ */
168
+ export declare function avatarInitialsName(name: string | undefined, kind: AvatarKind): string | undefined;
134
169
  /**
135
170
  * The smallest badge, in px, a character is drawn at full detail: its keyline
136
171
  * and its visor gap. Under it both are hairlines finer than a pixel, which a
@@ -198,4 +233,4 @@ export declare function avatarCorner(pixels: number): number;
198
233
  * knowing what colour that ground is. The frame takes the caller's class and
199
234
  * style, which is where a layout reaches for the badge.
200
235
  */
201
- export declare const Avatar: ({ src, name, size, kind, tone, ring, status, decorative, colorSeed, character, hue, className, style, ...rest }: AvatarProps) => import("react").JSX.Element;
236
+ export declare const Avatar: ({ src, name, size, kind, tone, ring, status, mark, markLabel, decorative, colorSeed, character, hue, icon, className, style, ...rest }: AvatarProps) => import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import './Avatar.css';
2
2
  import './AvatarStack.css';
3
- export { AVATAR_CHARACTER_FULL_DETAIL, AVATAR_CORNER_RATIO, AVATAR_SIZES, AVATAR_STATUS_LABELS, AVATAR_STATUSES, Avatar, avatarCorner, avatarPixels, avatarTint, getInitials, } from './Avatar.js';
3
+ export { AVATAR_CHARACTER_FULL_DETAIL, AVATAR_CORNER_RATIO, AVATAR_SIZES, AVATAR_STATUS_LABELS, AVATAR_STATUSES, Avatar, avatarCorner, avatarInitialsName, avatarPixels, avatarTint, getInitials, } from './Avatar.js';
4
4
  export type { AvatarKind, AvatarProps, AvatarRing, AvatarSize, AvatarSizeStep, AvatarStatus, AvatarTone, } from './Avatar.js';
5
5
  export { AvatarStack, avatarStackLabel } from './AvatarStack.js';
6
6
  export type { AvatarStackMember, AvatarStackProps } from './AvatarStack.js';
@@ -1,3 +1,3 @@
1
- export { AvatarStack, avatarStackLabel } from '../chunk-ZPWUVVIB.js';
2
- export { AVATAR_CHARACTER_FULL_DETAIL, AVATAR_CORNER_RATIO, AVATAR_SIZES, AVATAR_STATUSES, AVATAR_STATUS_LABELS, Avatar, avatarCorner, avatarPixels, avatarTint, getInitials } from '../chunk-HI6Q2UXQ.js';
1
+ export { AvatarStack, avatarStackLabel } from '../chunk-UAN5NE7M.js';
2
+ export { AVATAR_CHARACTER_FULL_DETAIL, AVATAR_CORNER_RATIO, AVATAR_SIZES, AVATAR_STATUSES, AVATAR_STATUS_LABELS, Avatar, avatarCorner, avatarInitialsName, avatarPixels, avatarTint, getInitials } from '../chunk-5OM447RW.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -126,6 +126,25 @@
126
126
  object-fit: cover;
127
127
  }
128
128
 
129
+ /*
130
+ * A GLYPH IN PLACE OF THE INITIALS, for a principal whose name's initials
131
+ * mean nothing (an API token, a service account). It takes a little over half
132
+ * the box, the share the initials' cap height and width take, so a key badge
133
+ * beside a person's badge reads as the same size of mark.
134
+ */
135
+ .crewlet-avatar__icon {
136
+ display: inline-flex;
137
+ align-items: center;
138
+ justify-content: center;
139
+ width: 55%;
140
+ height: 55%;
141
+ }
142
+
143
+ .crewlet-avatar__icon > * {
144
+ width: 100%;
145
+ height: 100%;
146
+ }
147
+
129
148
  /* ── tones ─────────────────────────────────────────────────────────────── */
130
149
 
131
150
  .crewlet-avatar--fallback {
@@ -320,3 +339,18 @@
320
339
  .crewlet-avatar__status--stopped {
321
340
  background: var(--color-feedback-danger);
322
341
  }
342
+
343
+ /*
344
+ * A MARK (`mark`): how a principal is present, not a state, so it is drawn
345
+ * quiet rather than in a state's fill: the neutral badge's raised rung, its
346
+ * strong hairline and its secondary ink. The hairline is the corner's own
347
+ * outline, inset by its width, so it keeps the corner's size and survives
348
+ * forced colours.
349
+ */
350
+ .crewlet-avatar__status--mark {
351
+ padding: 1px;
352
+ background: var(--color-surface-elevated);
353
+ outline-color: var(--color-border-strong);
354
+ outline-offset: -1px;
355
+ color: var(--color-text-secondary);
356
+ }
@@ -1,14 +1,14 @@
1
- export { AvatarPicker } from '../chunk-EFNHOVC2.js';
1
+ export { AvatarPicker } from '../chunk-HENOTGUV.js';
2
2
  import '../chunk-NXDEPUVG.js';
3
3
  import '../chunk-AYKXMIOR.js';
4
4
  import '../chunk-26PEPONN.js';
5
5
  import '../chunk-4LDTQWS7.js';
6
6
  import '../chunk-BWRGFKJP.js';
7
- import '../chunk-YXMCUKFI.js';
7
+ import '../chunk-5PSXBL73.js';
8
8
  import '../chunk-CBOICT64.js';
9
- import '../chunk-BHHIIK3M.js';
10
9
  import '../chunk-HMPFGIWM.js';
11
10
  import '../chunk-R25RGPUB.js';
11
+ import '../chunk-BHHIIK3M.js';
12
12
  import '../chunk-BZFWDIV6.js';
13
- import '../chunk-HI6Q2UXQ.js';
13
+ import '../chunk-5OM447RW.js';
14
14
  import '../chunk-ODEQTPFM.js';
@@ -18,7 +18,13 @@ export type CalloutVariant = Exclude<Tone, 'brand'>;
18
18
  export type CalloutLayout = 'card' | 'banner';
19
19
  export interface CalloutProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
20
20
  variant?: CalloutVariant | undefined;
21
- /** A bold lead-in before the message. */
21
+ /**
22
+ * The callout's heading, drawn in bold on its own line above the message.
23
+ *
24
+ * A LINE OF ITS OWN, not a lead-in. Run on the same line as the body, the
25
+ * title and the first sentence read as one run-on sentence ("You were signed
26
+ * out Your session ended"), and the reader had to find where one stopped.
27
+ */
22
28
  title?: ReactNode;
23
29
  /** A glyph replacing the variant's own. A component, not a name. */
24
30
  icon?: ReactNode;
@@ -1,2 +1,2 @@
1
- export { Callout } from '../chunk-R6FBDTP3.js';
1
+ export { Callout } from '../chunk-SVSCLHHI.js';
2
2
  import '../chunk-ODEQTPFM.js';
@@ -52,8 +52,10 @@
52
52
  }
53
53
 
54
54
  .crewlet-callout__title {
55
+ /* A LINE OF ITS OWN above the message, so the heading and the first
56
+ sentence are never read as one run-on sentence. */
57
+ display: block;
55
58
  font-weight: var(--font-weight-semibold);
56
- margin-right: var(--spacing-2);
57
59
  }
58
60
 
59
61
  .crewlet-callout__action {
@@ -1,5 +1,5 @@
1
1
  import '../chunk-3L5NHASN.js';
2
- export { CHARACTER_PICKER_COLUMNS, CharacterPicker } from '../chunk-YXMCUKFI.js';
2
+ export { CHARACTER_PICKER_COLUMNS, CharacterPicker } from '../chunk-5PSXBL73.js';
3
3
  import '../chunk-CBOICT64.js';
4
- import '../chunk-HI6Q2UXQ.js';
4
+ import '../chunk-5OM447RW.js';
5
5
  import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,30 @@
1
+ import type { ReactNode } from 'react';
2
+ export interface CheckboxGroupProps {
3
+ /** What the boxes choose between, read as the group's name: "Access". */
4
+ legend: ReactNode;
5
+ /** A sentence about the whole group, read when a reader enters it. */
6
+ helper?: ReactNode;
7
+ /** Why the last choice was refused. */
8
+ error?: ReactNode;
9
+ /**
10
+ * What the group says when it has no box to offer, in place of an empty
11
+ * fieldset that reads as a missing control.
12
+ */
13
+ emptyMessage?: ReactNode;
14
+ /** The boxes: `Checkbox` elements, one per choice. */
15
+ children?: ReactNode;
16
+ className?: string | undefined;
17
+ }
18
+ /**
19
+ * Several checkboxes that answer one question, named by a legend.
20
+ *
21
+ * A FIELDSET, because a `for` attribute names one element and a group is not
22
+ * one: a screen reader entering the group hears the legend once, and then each
23
+ * box by its own label. The legend wears the field label's register, so the
24
+ * group reads as one field among the others on the form, and the boxes stand
25
+ * one step apart, the rhythm a field's own parts keep.
26
+ *
27
+ * The group's help line and error are attached to the fieldset itself rather
28
+ * than to each box, so neither is read again on every box in it.
29
+ */
30
+ export declare function CheckboxGroup({ legend, helper, error, emptyMessage, children, className }: CheckboxGroupProps): import("react").JSX.Element;
@@ -1,3 +1,5 @@
1
1
  import './Checkbox.css';
2
2
  export { Checkbox } from './Checkbox.js';
3
3
  export type { CheckboxProps } from './Checkbox.js';
4
+ export { CheckboxGroup } from './CheckboxGroup.js';
5
+ export type { CheckboxGroupProps } from './CheckboxGroup.js';
@@ -1,2 +1,6 @@
1
- export { Checkbox } from '../chunk-YCTMKKRA.js';
1
+ export { Checkbox, CheckboxGroup } from '../chunk-75CLJNDQ.js';
2
+ import '../chunk-4GB57WIT.js';
3
+ import '../chunk-4FQT6ZAY.js';
4
+ import '../chunk-PQADV6P6.js';
5
+ import '../chunk-BZFWDIV6.js';
2
6
  import '../chunk-ODEQTPFM.js';
@@ -117,3 +117,19 @@
117
117
  outline: 2px solid var(--color-focus);
118
118
  outline-offset: 0;
119
119
  }
120
+
121
+ /*
122
+ * A group of boxes under one legend. The boxes keep a field's inner rhythm,
123
+ * one step apart, so the group reads as one field and its boxes as its parts.
124
+ */
125
+ .crewlet-checkbox-group__options {
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: var(--spacing-1);
129
+ }
130
+
131
+ .crewlet-checkbox-group__empty {
132
+ margin: 0;
133
+ font-size: var(--font-size-xs);
134
+ color: var(--color-text-secondary);
135
+ }
@@ -1,6 +1,7 @@
1
- export { CodeBlock } from '../chunk-KGTQ5CVD.js';
2
- import '../chunk-YACKC3K2.js';
3
- import '../chunk-5VNUYO76.js';
1
+ export { CodeBlock } from '../chunk-GDDE4UM7.js';
2
+ import '../chunk-W46LPU4R.js';
3
+ import '../chunk-STJ7QBC3.js';
4
+ import '../chunk-DTIW4CHZ.js';
4
5
  import '../chunk-OG6CL3EB.js';
5
6
  import '../chunk-4FQT6ZAY.js';
6
7
  import '../chunk-BZFWDIV6.js';
@@ -1,8 +1,8 @@
1
- export { Combobox } from '../chunk-4Y7WKXAW.js';
1
+ export { Combobox } from '../chunk-ZFH7H7GS.js';
2
2
  import '../chunk-F73MC7EZ.js';
3
- import '../chunk-FTEJPHBI.js';
4
- import '../chunk-PQADV6P6.js';
3
+ import '../chunk-4GB57WIT.js';
5
4
  import '../chunk-4FQT6ZAY.js';
5
+ import '../chunk-PQADV6P6.js';
6
6
  import '../chunk-R25RGPUB.js';
7
7
  import '../chunk-BZFWDIV6.js';
8
8
  import '../chunk-ODEQTPFM.js';
@@ -1,13 +1,13 @@
1
- export { CommandPalette } from '../chunk-IYZZL3MZ.js';
2
- import '../chunk-K4VORB64.js';
1
+ export { CommandPalette } from '../chunk-PQ63NLFC.js';
2
+ import '../chunk-DEEVA6TV.js';
3
3
  import '../chunk-F73MC7EZ.js';
4
4
  import '../chunk-SMBKXWML.js';
5
- import '../chunk-FTEJPHBI.js';
5
+ import '../chunk-4GB57WIT.js';
6
6
  import '../chunk-OG6CL3EB.js';
7
- import '../chunk-PQADV6P6.js';
8
7
  import '../chunk-4FQT6ZAY.js';
9
- import '../chunk-BHHIIK3M.js';
8
+ import '../chunk-PQADV6P6.js';
10
9
  import '../chunk-HMPFGIWM.js';
11
10
  import '../chunk-R25RGPUB.js';
11
+ import '../chunk-BHHIIK3M.js';
12
12
  import '../chunk-BZFWDIV6.js';
13
13
  import '../chunk-ODEQTPFM.js';
@@ -1,7 +1,7 @@
1
- export { COMPOSER_COUNTER_AT, Composer } from '../chunk-QSLIN64M.js';
1
+ export { COMPOSER_COUNTER_AT, Composer } from '../chunk-6NIMZ5XC.js';
2
2
  import '../chunk-SMBKXWML.js';
3
- import '../chunk-FTEJPHBI.js';
4
- import '../chunk-PQADV6P6.js';
3
+ import '../chunk-4GB57WIT.js';
5
4
  import '../chunk-4FQT6ZAY.js';
5
+ import '../chunk-PQADV6P6.js';
6
6
  import '../chunk-BZFWDIV6.js';
7
7
  import '../chunk-ODEQTPFM.js';