@crewlethq/ui 0.15.0 → 0.16.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 (113) hide show
  1. package/README.md +36 -6
  2. package/dist/AddPill/index.js +1 -1
  3. package/dist/{AddPill-V4TQXJ53.css → AddPill-RYCY56P4.css} +3 -1
  4. package/dist/AppShell/AccountRow.d.ts +40 -0
  5. package/dist/AppShell/AppShell.d.ts +1 -0
  6. package/dist/AppShell/index.d.ts +2 -0
  7. package/dist/AppShell/index.js +1 -1
  8. package/dist/{AppShell-6QUD7NWM.css → AppShell-RUNVM4WH.css} +93 -0
  9. package/dist/AuthLayout/AuthLayout.d.ts +35 -0
  10. package/dist/AuthLayout/index.d.ts +3 -0
  11. package/dist/AuthLayout/index.js +8 -0
  12. package/dist/AuthLayout-ODHBUPHQ.css +54 -0
  13. package/dist/Avatar/Avatar.d.ts +22 -2
  14. package/dist/Avatar/index.d.ts +1 -1
  15. package/dist/Avatar/index.js +2 -2
  16. package/dist/{Avatar-BPJ36QZR.css → Avatar-MBUPR5E5.css} +22 -3
  17. package/dist/AvatarPicker/index.js +4 -4
  18. package/dist/Callout/Callout.d.ts +7 -1
  19. package/dist/Callout/index.js +1 -1
  20. package/dist/{Callout-S7QDWHDL.css → Callout-SXXK4B5R.css} +3 -1
  21. package/dist/CharacterPicker/index.js +2 -2
  22. package/dist/Checkbox/CheckboxGroup.d.ts +30 -0
  23. package/dist/Checkbox/index.d.ts +2 -0
  24. package/dist/Checkbox/index.js +5 -1
  25. package/dist/{Checkbox-UE7TEPAM.css → Checkbox-KT7RS6QQ.css} +16 -0
  26. package/dist/CodeBlock/index.js +4 -3
  27. package/dist/Combobox/index.js +3 -3
  28. package/dist/CommandPalette/index.js +5 -5
  29. package/dist/Composer/index.js +3 -3
  30. package/dist/ConfirmField/ConfirmField.d.ts +43 -0
  31. package/dist/ConfirmField/index.d.ts +2 -0
  32. package/dist/ConfirmField/index.js +7 -0
  33. package/dist/Copyable/CopyButton.d.ts +13 -6
  34. package/dist/Copyable/index.js +3 -2
  35. package/dist/DataTable/index.js +7 -5
  36. package/dist/DataView/index.js +10 -9
  37. package/dist/DownloadButton/DownloadButton.d.ts +52 -0
  38. package/dist/DownloadButton/index.d.ts +2 -0
  39. package/dist/DownloadButton/index.js +6 -0
  40. package/dist/EntityChip/index.js +3 -3
  41. package/dist/ImageUpload/index.js +3 -3
  42. package/dist/InlineCode/InlineCode.d.ts +8 -1
  43. package/dist/InlineCode/index.js +1 -1
  44. package/dist/{InlineCode-6WSR5COL.css → InlineCode-ASYHLN3E.css} +9 -0
  45. package/dist/Input/index.js +2 -2
  46. package/dist/{Input-RAKNQJCV.css → Input-3KVSNUO5.css} +11 -0
  47. package/dist/ListInput/index.js +3 -3
  48. package/dist/MarkdownEditor/index.js +1 -1
  49. package/dist/Modal/index.js +2 -2
  50. package/dist/ObjectHeader/ObjectHeader.d.ts +138 -0
  51. package/dist/ObjectHeader/index.d.ts +3 -0
  52. package/dist/ObjectHeader/index.js +4 -0
  53. package/dist/ObjectHeader-UNVYM2KD.css +174 -0
  54. package/dist/OrgLabel/index.js +3 -3
  55. package/dist/{OrgLabel-6WSKJQOT.css → OrgLabel-R5KY7C3W.css} +6 -1
  56. package/dist/OrgNode/index.js +4 -4
  57. package/dist/OrgTable/index.js +5 -5
  58. package/dist/QrCode/QrCode.d.ts +58 -0
  59. package/dist/QrCode/index.d.ts +3 -0
  60. package/dist/QrCode/index.js +2 -0
  61. package/dist/QrCode-JWJTWMU5.css +19 -0
  62. package/dist/Switcher/index.js +4 -4
  63. package/dist/Text/Text.d.ts +7 -1
  64. package/dist/Text/index.js +1 -1
  65. package/dist/{Text-XIPYYZX2.css → Text-S3Q4O3IG.css} +11 -0
  66. package/dist/TimeWindowPicker/index.js +3 -3
  67. package/dist/{chunk-FTEJPHBI.js → chunk-4GB57WIT.js} +2 -2
  68. package/dist/{chunk-NN62JQLR.js → chunk-5CLW2CXT.js} +3 -1
  69. package/dist/{chunk-2GXTIK37.js → chunk-5EFRWWGX.js} +3 -3
  70. package/dist/{chunk-6OF2DBKY.js → chunk-6DQWF5Y4.js} +2 -2
  71. package/dist/{chunk-QSLIN64M.js → chunk-6NIMZ5XC.js} +1 -1
  72. package/dist/{chunk-YCTMKKRA.js → chunk-75CLJNDQ.js} +12 -2
  73. package/dist/chunk-7QFAORCY.js +37 -0
  74. package/dist/chunk-BJCFNRGH.js +53 -0
  75. package/dist/{chunk-IHGKPEEV.js → chunk-CGNQPGJL.js} +1 -1
  76. package/dist/{chunk-DDTDN5L2.js → chunk-CKB3OHL3.js} +1 -1
  77. package/dist/chunk-CXBAIVYZ.js +39 -0
  78. package/dist/chunk-DTIW4CHZ.js +37 -0
  79. package/dist/{chunk-YXMCUKFI.js → chunk-DZ2YR6MV.js} +1 -1
  80. package/dist/{chunk-QV266R6G.js → chunk-EIOTVNYX.js} +4 -4
  81. package/dist/chunk-F6NZOVWL.js +68 -0
  82. package/dist/{chunk-LCF4WPYS.js → chunk-FA2V2VT6.js} +2 -2
  83. package/dist/{chunk-EFNHOVC2.js → chunk-FAEN3HYI.js} +2 -2
  84. package/dist/{chunk-HI6Q2UXQ.js → chunk-FY4SP3HF.js} +11 -3
  85. package/dist/{chunk-KGTQ5CVD.js → chunk-GDDE4UM7.js} +1 -1
  86. package/dist/chunk-I7WXZOWA.js +90 -0
  87. package/dist/{chunk-23DHIMJA.js → chunk-IKBK4A5O.js} +1 -1
  88. package/dist/{chunk-7BZPYA5P.js → chunk-JF7X673Z.js} +1 -1
  89. package/dist/{chunk-DJMLHCLD.js → chunk-KLESCRTS.js} +1 -1
  90. package/dist/{chunk-DYHSQHAZ.js → chunk-OFMPIRGD.js} +2 -2
  91. package/dist/{chunk-H3XOMT7W.js → chunk-OYOQU7O5.js} +44 -3
  92. package/dist/{chunk-IYZZL3MZ.js → chunk-PQ63NLFC.js} +3 -3
  93. package/dist/{chunk-5VNUYO76.js → chunk-STJ7QBC3.js} +8 -25
  94. package/dist/{chunk-R6FBDTP3.js → chunk-SVSCLHHI.js} +1 -1
  95. package/dist/{chunk-GARACZAH.js → chunk-T3ZWBZ65.js} +1 -1
  96. package/dist/{chunk-74WGARJE.js → chunk-TROLVEZY.js} +1 -1
  97. package/dist/{chunk-6NIZCQUM.js → chunk-UAMDRJD6.js} +2 -2
  98. package/dist/{chunk-YACKC3K2.js → chunk-W46LPU4R.js} +8 -8
  99. package/dist/chunk-Y56FM3K4.js +57 -0
  100. package/dist/{chunk-4Y7WKXAW.js → chunk-ZFH7H7GS.js} +1 -1
  101. package/dist/generated/index.d.ts +5 -0
  102. package/dist/generated/index.js +66 -59
  103. package/dist/index.js +66 -59
  104. package/dist/styles.css +439 -15
  105. package/dist/utils/index.d.ts +4 -0
  106. package/dist/utils/index.js +4 -2
  107. package/dist/utils/useClipboard.d.ts +6 -3
  108. package/dist/utils/useDownload.d.ts +63 -0
  109. package/dist/utils/useOutcome.d.ts +44 -0
  110. package/package.json +4 -3
  111. package/dist/{chunk-6AR2T5Y4.js → chunk-CRVETI5L.js} +0 -0
  112. package/dist/{chunk-K4VORB64.js → chunk-DEEVA6TV.js} +1 -1
  113. /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.16.0
202
+
203
+ 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.
204
+
205
+ **`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.
206
+
207
+ **`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.
208
+
209
+ **`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.
210
+
211
+ **`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.
212
+
213
+ **`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.
214
+
215
+ **`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.
216
+
217
+ **`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.
218
+
219
+ **`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.
220
+
221
+ **`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.
222
+
223
+ **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.
224
+
225
+ 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.
226
+
227
+ ## Changes in 0.15.1
228
+
229
+ `@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.
230
+
201
231
  ## Changes in 0.15.0
202
232
 
203
233
  0.15.0 gives the rail's foot a row that opens a panel, which is what the engine console's engine panel is built from. It also keeps an unread failure on screen, lets the column chart fill a stretched card, and wraps its legend on a narrow one.
@@ -1,3 +1,3 @@
1
- export { ADD_PILL_MS, AddPill } from '../chunk-74WGARJE.js';
1
+ export { ADD_PILL_MS, AddPill } from '../chunk-TROLVEZY.js';
2
2
  import '../chunk-BZFWDIV6.js';
3
3
  import '../chunk-ODEQTPFM.js';
@@ -408,7 +408,9 @@
408
408
  /* THE FILL IS FOR A FILLED DRAWING, and a glyph is a stroke. Painting the
409
409
  inside of a Lucide glyph turns every closed shape in it solid and every
410
410
  open one into a smear, so a `.crewlet-glyph` keeps the fill its own frame
411
- decides and takes the ink through its stroke, which is `currentColor`. */
411
+ decides and takes the ink through its stroke, which is `currentColor`.
412
+ A drawing here cuts its holes as geometry, never through a mask: this rule
413
+ repaints a mask's paths and groups as well, and fills its holes with ink. */
412
414
  .crewlet-add-pill__section svg:not(.crewlet-glyph),
413
415
  .crewlet-add-pill__section svg:not(.crewlet-glyph) g,
414
416
  .crewlet-add-pill__section svg:not(.crewlet-glyph) path {
@@ -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
  /**
@@ -130,7 +130,27 @@ export interface AvatarProps extends HTMLAttributes<HTMLElement> {
130
130
  * neutral badge's ink, on its plate.
131
131
  */
132
132
  hue?: NodeHue | undefined;
133
+ /**
134
+ * A glyph drawn in place of the initials, for a principal that is not a
135
+ * person or an agent and whose name's initials would mean nothing: an API
136
+ * token, a service account. "TO" from `token:reader` reads as somebody's
137
+ * initials. A component, not a name; it is drawn at the badge's own scale.
138
+ * An image or an agent's character still wins over it.
139
+ */
140
+ icon?: ReactNode;
133
141
  }
142
+ /**
143
+ * The name an agent's initials are drawn from: its leading "agent" word
144
+ * dropped (`Agent CEO` gives CEO, `agent-cto` gives cto), and every other
145
+ * name as it is.
146
+ *
147
+ * THE SHAPE ALREADY SAYS "AGENT". A company that names its seats for what
148
+ * they are ("Agent CEO", "Agent CTO", "Agent PM") otherwise got AC, AC and
149
+ * AP: the two seats a chart draws most were the same badge side by side. The
150
+ * badge's accessible name keeps the whole name. A name that is nothing but
151
+ * the word keeps it, since an empty one draws the anonymous badge.
152
+ */
153
+ export declare function avatarInitialsName(name: string | undefined, kind: AvatarKind): string | undefined;
134
154
  /**
135
155
  * The smallest badge, in px, a character is drawn at full detail: its keyline
136
156
  * and its visor gap. Under it both are hairlines finer than a pixel, which a
@@ -198,4 +218,4 @@ export declare function avatarCorner(pixels: number): number;
198
218
  * knowing what colour that ground is. The frame takes the caller's class and
199
219
  * style, which is where a layout reaches for the badge.
200
220
  */
201
- export declare const Avatar: ({ src, name, size, kind, tone, ring, status, decorative, colorSeed, character, hue, className, style, ...rest }: AvatarProps) => import("react").JSX.Element;
221
+ export declare const Avatar: ({ src, name, size, kind, tone, ring, status, 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-6NIZCQUM.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-UAMDRJD6.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-FY4SP3HF.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 {
@@ -160,9 +179,9 @@
160
179
 
161
180
  /*
162
181
  * THE CHARACTER FILLS MOST OF THE PLATE, centred by its own frame (it frames
163
- * every silhouette square on what it draws), with the plate's corner clear of
164
- * it: a wide character such as the original Crewlet reaches the plate's
165
- * sides, a tall one its top and its foot, and none touches the curve.
182
+ * every silhouette square on its optical centre), with the plate's corner
183
+ * clear of it: a wide character such as the original Crewlet reaches the
184
+ * plate's sides, a tall one its top and its foot, and none touches the curve.
166
185
  */
167
186
  .crewlet-avatar--character {
168
187
  border: 1px solid transparent;
@@ -1,14 +1,14 @@
1
- export { AvatarPicker } from '../chunk-EFNHOVC2.js';
1
+ export { AvatarPicker } from '../chunk-FAEN3HYI.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-DZ2YR6MV.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-FY4SP3HF.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-DZ2YR6MV.js';
3
3
  import '../chunk-CBOICT64.js';
4
- import '../chunk-HI6Q2UXQ.js';
4
+ import '../chunk-FY4SP3HF.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';