@ciridae/ui 1.0.2 → 1.0.4

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 (209) hide show
  1. package/dist/app/auth.d.ts +10 -0
  2. package/dist/app/auth.js +60 -0
  3. package/dist/app/error-boundary.d.ts +18 -0
  4. package/dist/app/error-boundary.js +45 -0
  5. package/dist/app/feedback.d.ts +12 -0
  6. package/dist/app/feedback.js +128 -0
  7. package/dist/app/index.d.ts +8 -0
  8. package/dist/app/index.js +8 -0
  9. package/dist/app/request-errors.d.ts +63 -0
  10. package/dist/app/request-errors.js +172 -0
  11. package/dist/app/sentry.d.ts +18 -0
  12. package/dist/app/sentry.js +52 -0
  13. package/dist/app/telemetry.d.ts +7 -0
  14. package/dist/app/telemetry.js +28 -0
  15. package/dist/app/trail.d.ts +9 -0
  16. package/dist/app/trail.js +18 -0
  17. package/dist/chat/chat-query-keys.d.ts +3 -3
  18. package/dist/chat/contract.d.ts +15 -15
  19. package/dist/chat/conversation-items-query-options.d.ts +114 -114
  20. package/dist/chat/conversation-source.d.ts +89 -0
  21. package/dist/chat/conversation-source.js +1 -0
  22. package/dist/chat/describe-tool-call.d.ts +3 -3
  23. package/dist/chat/describe-tool-call.js +3 -3
  24. package/dist/chat/message-text.d.ts +1 -1
  25. package/dist/chat/message-text.js +116 -48
  26. package/dist/chat/use-conversation-list.d.ts +6 -6
  27. package/dist/ciridae.css +16 -4
  28. package/dist/components/activity-feed.d.ts +3 -2
  29. package/dist/components/activity-feed.js +1 -0
  30. package/dist/components/agent-work.d.ts +3 -3
  31. package/dist/components/app-frame.d.ts +91 -0
  32. package/dist/components/app-frame.js +134 -0
  33. package/dist/components/app-navigation.d.ts +30 -3
  34. package/dist/components/app-navigation.js +105 -14
  35. package/dist/components/badge.d.ts +30 -5
  36. package/dist/components/badge.js +134 -15
  37. package/dist/components/brand-icons.d.ts +3 -3
  38. package/dist/components/bulk-action-bar.d.ts +1 -1
  39. package/dist/components/bulk-action-bar.js +3 -3
  40. package/dist/components/button.d.ts +12 -5
  41. package/dist/components/button.js +77 -44
  42. package/dist/components/calendar.d.ts +1 -1
  43. package/dist/components/calendar.js +44 -17
  44. package/dist/components/centered-page.d.ts +1 -1
  45. package/dist/components/centered-page.js +2 -2
  46. package/dist/components/chart.d.ts +22 -2
  47. package/dist/components/chart.js +251 -51
  48. package/dist/components/chat-client-source.d.ts +16 -0
  49. package/dist/components/chat-client-source.js +297 -0
  50. package/dist/components/chat-page.d.ts +9 -8
  51. package/dist/components/chat-page.js +7 -11
  52. package/dist/components/chat-sheet-controller.d.ts +11 -14
  53. package/dist/components/chat-sheet-controller.js +3 -3
  54. package/dist/components/chat-sheet.d.ts +18 -11
  55. package/dist/components/chat-sheet.js +188 -289
  56. package/dist/components/chat.d.ts +109 -19
  57. package/dist/components/chat.js +520 -124
  58. package/dist/components/checkbox-item.d.ts +6 -3
  59. package/dist/components/checkbox-item.js +9 -5
  60. package/dist/components/checkbox-list.d.ts +1 -1
  61. package/dist/components/chip.d.ts +6 -1
  62. package/dist/components/chip.js +27 -1
  63. package/dist/components/collapsible.d.ts +1 -1
  64. package/dist/components/color-theme.d.ts +1 -1
  65. package/dist/components/column-settings.d.ts +1 -1
  66. package/dist/components/column-settings.js +1 -1
  67. package/dist/components/command-menu.d.ts +5 -1
  68. package/dist/components/command-menu.js +98 -32
  69. package/dist/components/command-palette.d.ts +7 -3
  70. package/dist/components/command-palette.js +20 -11
  71. package/dist/components/copyable-id.d.ts +22 -5
  72. package/dist/components/copyable-id.js +46 -6
  73. package/dist/components/data-display.d.ts +39 -8
  74. package/dist/components/data-display.js +138 -19
  75. package/dist/components/data-grid.d.ts +23 -11
  76. package/dist/components/data-grid.js +61 -26
  77. package/dist/components/date-range-picker.d.ts +1 -1
  78. package/dist/components/date-range-picker.js +9 -20
  79. package/dist/components/date-time.d.ts +1 -1
  80. package/dist/components/dialog-height-context.d.ts +1 -1
  81. package/dist/components/document-viewer.d.ts +2 -2
  82. package/dist/components/drawer.d.ts +11 -6
  83. package/dist/components/drawer.js +91 -54
  84. package/dist/components/dropzone.d.ts +4 -4
  85. package/dist/components/editable-field.d.ts +12 -3
  86. package/dist/components/editable-field.js +57 -27
  87. package/dist/components/email-input.d.ts +1 -1
  88. package/dist/components/email-input.js +6 -5
  89. package/dist/components/environment-frame.d.ts +2 -2
  90. package/dist/components/environment-frame.js +37 -6
  91. package/dist/components/expandable-text.d.ts +1 -1
  92. package/dist/components/feedback-screenshot.d.ts +1 -1
  93. package/dist/components/feedback-sheet.d.ts +18 -3
  94. package/dist/components/feedback-sheet.js +216 -55
  95. package/dist/components/field-context.d.ts +1 -1
  96. package/dist/components/filter-bar.d.ts +3 -3
  97. package/dist/components/filter-bar.js +43 -29
  98. package/dist/components/form-controls.d.ts +12 -3
  99. package/dist/components/form-controls.js +46 -22
  100. package/dist/components/form-layout.d.ts +14 -10
  101. package/dist/components/form-layout.js +68 -23
  102. package/dist/components/glass-menu.d.ts +3 -0
  103. package/dist/components/glass-menu.js +63 -0
  104. package/dist/components/gradient-edge.d.ts +9 -0
  105. package/dist/components/gradient-edge.js +31 -0
  106. package/dist/components/hotkeys.d.ts +2 -2
  107. package/dist/components/icon.d.ts +1 -1
  108. package/dist/components/input.d.ts +7 -5
  109. package/dist/components/input.js +29 -17
  110. package/dist/components/keyboard-shortcuts.d.ts +2 -2
  111. package/dist/components/keycap.d.ts +10 -6
  112. package/dist/components/keycap.js +57 -19
  113. package/dist/components/layout.d.ts +13 -13
  114. package/dist/components/lightbox.d.ts +9 -3
  115. package/dist/components/lightbox.js +13 -9
  116. package/dist/components/link.d.ts +1 -1
  117. package/dist/components/locale.d.ts +42 -2
  118. package/dist/components/locale.js +43 -3
  119. package/dist/components/log-entry.d.ts +40 -0
  120. package/dist/components/log-entry.js +146 -0
  121. package/dist/components/log-list.d.ts +3 -3
  122. package/dist/components/mention-input.d.ts +2 -2
  123. package/dist/components/mention-input.js +4 -3
  124. package/dist/components/menu.d.ts +7 -3
  125. package/dist/components/menu.js +34 -37
  126. package/dist/components/meter.d.ts +10 -2
  127. package/dist/components/meter.js +64 -6
  128. package/dist/components/navigation.d.ts +21 -4
  129. package/dist/components/navigation.js +84 -19
  130. package/dist/components/notification-tray.d.ts +1 -1
  131. package/dist/components/notification-tray.js +5 -4
  132. package/dist/components/overlays.d.ts +6 -4
  133. package/dist/components/overlays.js +69 -40
  134. package/dist/components/page-frame.d.ts +4 -4
  135. package/dist/components/page-frame.js +1 -1
  136. package/dist/components/panel.d.ts +4 -4
  137. package/dist/components/panel.js +18 -12
  138. package/dist/components/profile-header.d.ts +1 -1
  139. package/dist/components/progress.d.ts +1 -1
  140. package/dist/components/project-avatar.d.ts +1 -1
  141. package/dist/components/radio-card.d.ts +2 -2
  142. package/dist/components/radio-card.js +10 -9
  143. package/dist/components/record.d.ts +29 -5
  144. package/dist/components/record.js +147 -18
  145. package/dist/components/resolved-file-url.d.ts +19 -0
  146. package/dist/components/resolved-file-url.js +49 -0
  147. package/dist/components/scroll-area.d.ts +1 -1
  148. package/dist/components/secret-dialog.d.ts +1 -1
  149. package/dist/components/segmented-bar.d.ts +4 -2
  150. package/dist/components/segmented-bar.js +16 -6
  151. package/dist/components/select.d.ts +9 -6
  152. package/dist/components/select.js +103 -127
  153. package/dist/components/settings.d.ts +4 -4
  154. package/dist/components/shell-skeleton.d.ts +1 -1
  155. package/dist/components/shell-skeleton.js +1 -1
  156. package/dist/components/shell.d.ts +21 -21
  157. package/dist/components/shell.js +43 -43
  158. package/dist/components/side-panel.d.ts +3 -3
  159. package/dist/components/sidebar.d.ts +13 -8
  160. package/dist/components/sidebar.js +55 -27
  161. package/dist/components/single-line-text.d.ts +2 -0
  162. package/dist/components/single-line-text.js +16 -0
  163. package/dist/components/site-banner.d.ts +5 -2
  164. package/dist/components/site-banner.js +17 -6
  165. package/dist/components/skeleton.d.ts +8 -7
  166. package/dist/components/skeleton.js +28 -16
  167. package/dist/components/skeletons.d.ts +17 -5
  168. package/dist/components/skeletons.js +71 -8
  169. package/dist/components/stages.d.ts +83 -11
  170. package/dist/components/stages.js +512 -156
  171. package/dist/components/stat-card.d.ts +12 -4
  172. package/dist/components/stat-card.js +92 -37
  173. package/dist/components/status-banner.d.ts +1 -1
  174. package/dist/components/status-banner.js +1 -1
  175. package/dist/components/status.d.ts +2 -2
  176. package/dist/components/steps.d.ts +1 -1
  177. package/dist/components/switch.d.ts +1 -1
  178. package/dist/components/tabs.d.ts +8 -8
  179. package/dist/components/tabs.js +26 -13
  180. package/dist/components/theme-customizer.d.ts +2 -2
  181. package/dist/components/theme-toggle.d.ts +4 -0
  182. package/dist/components/theme-toggle.js +67 -0
  183. package/dist/components/toast.d.ts +10 -3
  184. package/dist/components/toast.js +78 -29
  185. package/dist/components/use-new-version-available.d.ts +6 -0
  186. package/dist/components/use-new-version-available.js +29 -0
  187. package/dist/components/user-chip.d.ts +3 -3
  188. package/dist/components/wizard.d.ts +2 -2
  189. package/dist/feedback/contract.d.ts +136 -136
  190. package/dist/feedback/feedback-controller.d.ts +6 -6
  191. package/dist/feedback/generated/zod.gen.d.ts +28 -28
  192. package/dist/feedback/operator-contract.d.ts +11 -11
  193. package/dist/feedback/operator-controller.d.ts +32 -13
  194. package/dist/feedback/operator-controller.js +44 -11
  195. package/dist/preset.js +289 -33
  196. package/dist/styled-system/styles.css +3294 -1079
  197. package/dist/styled-system/tokens/index.mjs +598 -22
  198. package/dist/styled-system/tokens/tokens.d.ts +11 -11
  199. package/dist/styled-system/types/prop-type.d.ts +2 -2
  200. package/dist/styles.css +16 -4
  201. package/dist/theme/color.d.ts +3 -0
  202. package/dist/theme/color.js +5 -0
  203. package/dist/theme/derive.d.ts +9 -2
  204. package/dist/theme/derive.js +72 -12
  205. package/dist/theme/palettes.d.ts +49 -6
  206. package/dist/theme/palettes.js +84 -15
  207. package/dist/theme/tokens.d.ts +3 -3
  208. package/dist/theme/tokens.js +107 -32
  209. package/package.json +40 -3
@@ -0,0 +1,134 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * The app's frame, composed from the shell's pieces: the sidebar that folds to a rail and then
4
+ * into the phone menu, its footer rows and account menu drawn for each width, and, given a
5
+ * `title`, the main panel's header too. `AccountMenu` is the signed-in account's menu: the app's
6
+ * own entries, the color theme and palette, and sign out. Auth stays with the host, which passes
7
+ * sign-out as an action. `AssistantLauncher` opens the app's assistant from the sidebar's foot.
8
+ */
9
+ import { ArrowClockwiseIcon, CaretUpIcon, CircleHalfIcon, PaletteIcon, PulseIcon, SignOutIcon, } from "@phosphor-icons/react";
10
+ import { css, cx } from "#primitives/css";
11
+ import { PALETTE_NAMES } from "../theme/index.js";
12
+ import { AppNavigation } from "./app-navigation.js";
13
+ import { Button, IconButton } from "./button.js";
14
+ import { useColorTheme } from "./color-theme.js";
15
+ import { icon } from "./icon.js";
16
+ import { KeyboardShortcuts } from "./keyboard-shortcuts.js";
17
+ import { ShortcutHint } from "./keycap.js";
18
+ import { Link } from "./link.js";
19
+ import { useCiridaeLocale } from "./locale.js";
20
+ import { Menu } from "./menu.js";
21
+ import { AppAccountButton, AppBreadcrumbs, AppContent, AppHeader, AppHeaderActions, AppHeaderCompact, AppMain, AppShell, AppSidebar, AppSidebarFooter, AppSidebarFootnote, AppSidebarGroup, AppSidebarNav, AppSidebarOnly, AppSidebarRail, } from "./shell.js";
22
+ import { SidebarItem } from "./sidebar.js";
23
+ import { useNewVersionAvailable } from "./use-new-version-available.js";
24
+ /** The color theme submenu: system, light or dark. */
25
+ export function useColorThemeMenuItem() {
26
+ const colorTheme = useColorTheme();
27
+ const { labels } = useCiridaeLocale();
28
+ const choices = [
29
+ { value: "system", label: labels.themeCustomizer.system },
30
+ { value: "light", label: labels.themeCustomizer.light },
31
+ { value: "dark", label: labels.themeCustomizer.dark },
32
+ ];
33
+ return {
34
+ label: labels.themeCustomizer.colorTheme,
35
+ icon: _jsx(CircleHalfIcon, { className: icon({ size: "small" }) }),
36
+ items: choices.map((choice) => ({
37
+ label: choice.label,
38
+ checked: colorTheme.preference === choice.value,
39
+ onSelect: () => colorTheme.setPreference(choice.value),
40
+ })),
41
+ };
42
+ }
43
+ /** The palette submenu over the kit's palettes. */
44
+ export function usePaletteMenuItem(choice) {
45
+ return paletteMenuItem(choice, useCiridaeLocale().labels);
46
+ }
47
+ function paletteMenuItem({ value, onValueChange }, labels) {
48
+ const selected = value ?? "ciridae";
49
+ return {
50
+ label: labels.themeCustomizer.palette,
51
+ icon: _jsx(PaletteIcon, { className: icon({ size: "small" }) }),
52
+ items: PALETTE_NAMES.map((name) => ({
53
+ label: labels.themeCustomizer.paletteName(name),
54
+ checked: selected === name,
55
+ onSelect: () => onValueChange(name),
56
+ })),
57
+ };
58
+ }
59
+ /** The signed-in account's menu: the app's entries, the color theme and palette, then sign out. */
60
+ export function AccountMenu({ avatar, name, detail, items = [], palette, signOut, compact = false, }) {
61
+ const { labels } = useCiridaeLocale();
62
+ const colorThemeItem = useColorThemeMenuItem();
63
+ return (_jsx(Menu
64
+ // In the sidebar the menu is as wide as the account row it opens from, as the design draws
65
+ // it, so it covers whatever sits above that row; the avatar alone opens a menu of its own width.
66
+ , {
67
+ // In the sidebar the menu is as wide as the account row it opens from, as the design draws
68
+ // it, so it covers whatever sits above that row; the avatar alone opens a menu of its own width.
69
+ matchTriggerWidth: !compact, trigger: _jsx(AppAccountButton, { "aria-label": labels.accountMenu.menu, avatar: avatar, compact: compact, detail: detail, name: name, trailing: _jsx(CaretUpIcon, { className: icon({ size: "small" }) }) }), items: [
70
+ ...items,
71
+ colorThemeItem,
72
+ ...(palette ? [paletteMenuItem(palette, labels)] : []),
73
+ ...(signOut
74
+ ? [
75
+ { separator: true },
76
+ {
77
+ ...signOut,
78
+ label: labels.accountMenu.signOut,
79
+ icon: _jsx(SignOutIcon, { className: icon({ size: "small" }) }),
80
+ variant: "danger",
81
+ },
82
+ ]
83
+ : []),
84
+ ] }));
85
+ }
86
+ // A raised pill on the canvas; in the rail, a round button with the same face.
87
+ const assistantLauncher = css({
88
+ display: "flex",
89
+ alignItems: "center",
90
+ gap: "control.iconGap",
91
+ width: "100%",
92
+ height: "control.default",
93
+ paddingInlineStart: "control.compactInline",
94
+ paddingInlineEnd: "control.launcherInlineEnd",
95
+ border: "1px solid token(colors.border.canvas)",
96
+ borderRadius: "shell",
97
+ background: "token(gradients.raised)",
98
+ color: "fg.primary",
99
+ font: "inherit",
100
+ fontWeight: "medium",
101
+ letterSpacing: "compact",
102
+ lineHeight: "tight",
103
+ cursor: "pointer",
104
+ _hover: { background: "bg.raisedHover" },
105
+ "&[aria-pressed=true]": { background: "bg.field" },
106
+ _focusVisible: { focusRing: "outer" },
107
+ "& > [data-label]": { flex: "1", minWidth: "0", textAlign: "start" },
108
+ "&[data-compact]": {
109
+ justifyContent: "center",
110
+ width: "control.compact",
111
+ height: "control.compact",
112
+ padding: "none",
113
+ borderRadius: "round",
114
+ },
115
+ });
116
+ /** Opens the app's assistant from the sidebar's foot: a raised pill, or a round icon in the rail. */
117
+ export function AssistantLauncher({ label, icon: launcherIcon, hotkey, compact = false, className, ...props }) {
118
+ return (_jsxs("button", { type: "button", "aria-label": compact ? label : undefined, ...props, className: cx(assistantLauncher, className), "data-ciridae-ui": "", "data-compact": compact || undefined, children: [launcherIcon, !compact && (_jsxs(_Fragment, { children: [_jsx("span", { "data-label": "", children: label }), hotkey && _jsx(ShortcutHint, { boxed: true, hotkey: hotkey })] }))] }));
119
+ }
120
+ /** The app's frame: the sidebar for every width, its footer and account, and the page. */
121
+ export function AppFrame({ title, actions, sidebarTop, sidebarRail, navigation, sidebar, sidebarFooter, items = [], footnote, buildSha = null, statusUrl, railActions, compactActions, account, overlays, children, }) {
122
+ const { labels } = useCiridaeLocale();
123
+ const statusLink = statusUrl && (_jsx(IconButton, { "aria-label": labels.appFrame.status, nativeButton: false, render: _jsx("a", { href: statusUrl, target: "_blank", rel: "noreferrer" }), children: _jsx(PulseIcon, { className: icon() }) }));
124
+ const sidebarFoot = (_jsxs(_Fragment, { children: [(sidebarFooter || buildSha !== null || items.length > 0) && (_jsxs(AppSidebarOnly, { children: [sidebarFooter, buildSha !== null && _jsx(BuildUpdateButton, { buildSha: buildSha }), items.map((item) => (_jsx(SidebarItem, { "aria-pressed": item.pressed, icon: item.icon, onClick: item.onClick, children: item.label }, item.id)))] })), _jsxs(AppSidebarFootnote, { children: [footnote, statusUrl && (_jsxs(_Fragment, { children: [_jsx(Link, { external: true, tone: "muted", href: statusUrl, children: labels.appFrame.status }), _jsx("span", { "aria-hidden": "true", children: "\u00B7" })] })), _jsx("code", { title: buildSha ?? undefined, children: buildSha ? buildSha.slice(0, 7) : labels.appFrame.localBuild })] }), (railActions || statusLink || items.length > 0) && (_jsxs(AppSidebarOnly, { rail: true, children: [items.map((item) => (_jsx(SidebarItem, { "aria-label": item.label, "aria-pressed": item.pressed, icon: item.icon, onClick: item.onClick }, item.id))), railActions, statusLink] })), account && (_jsxs(_Fragment, { children: [_jsx(AppSidebarOnly, { children: _jsx(AccountMenu, { ...account }) }), _jsx(AppSidebarOnly, { rail: true, children: _jsx(AccountMenu, { ...account, compact: true }) })] }))] }));
125
+ return (_jsxs(AppShell, { children: [_jsx(KeyboardShortcuts, {}), navigation ? (_jsx(AppNavigation, { ...navigation, header: sidebarTop, railHeader: sidebarRail, footer: sidebarFoot })) : (_jsxs(AppSidebar, { children: [_jsx(AppSidebarGroup, { children: sidebarTop }), _jsx(AppSidebarRail, { children: sidebarRail }), sidebar && _jsx(AppSidebarNav, { children: sidebar }), _jsx(AppSidebarFooter, { children: sidebarFoot })] })), title === undefined ? (children) : (_jsxs(AppMain, { children: [_jsx(AppHeader, { actions: _jsxs(AppHeaderActions, { children: [actions, _jsxs(AppHeaderCompact, { children: [compactActions, statusLink, items.map((item) => (_jsx(IconButton, { "aria-label": item.label, "aria-pressed": item.pressed, onClick: item.onClick, children: item.icon }, item.id))), account && _jsx(AccountMenu, { ...account, compact: true })] })] }), children: _jsx(AppBreadcrumbs, { children: title }) }), _jsx(AppContent, { children: children })] })), overlays] }));
126
+ }
127
+ /** Only mounted for a named build, so local previews do not need a query provider. */
128
+ function BuildUpdateButton({ buildSha }) {
129
+ const hasNewVersion = useNewVersionAvailable(buildSha);
130
+ const { labels } = useCiridaeLocale();
131
+ if (!hasNewVersion)
132
+ return null;
133
+ return (_jsx(Button, { variant: "secondary", fullWidth: true, icon: _jsx(ArrowClockwiseIcon, { className: icon({ size: "small" }) }), onClick: () => window.location.reload(), children: labels.appFrame.updateAvailable }));
134
+ }
@@ -47,6 +47,10 @@ export type AppNavigationProps = Omit<ComponentProps<"aside">, "children"> & {
47
47
  footer?: ReactNode;
48
48
  /** Unfold every section while the sidebar has room for all of its rows. Defaults to true. */
49
49
  autoUnfold?: boolean;
50
+ /** Show the rail at any width, as the reader chose; the host keeps the choice. */
51
+ collapsed?: boolean;
52
+ /** With `collapsed`, a round button at the rail's foot that unfolds it. */
53
+ onExpand?: () => void;
50
54
  labels?: Partial<{
51
55
  more: string;
52
56
  less: string;
@@ -59,8 +63,31 @@ export type AppNavigationProps = Omit<ComponentProps<"aside">, "children"> & {
59
63
  * out of room, so it does not flicker at the threshold.
60
64
  */
61
65
  export declare function useSidebarRoom(foldedRows: number): {
62
- navRef: import("@types/react").RefObject<HTMLDivElement | null>;
63
- footerRef: import("@types/react").RefObject<HTMLDivElement | null>;
66
+ navRef: import("react").RefObject<HTMLDivElement | null>;
67
+ footerRef: import("react").RefObject<HTMLDivElement | null>;
64
68
  roomy: boolean;
65
69
  };
66
- export declare function AppNavigation({ sections, activeId: ownActiveId, renderLink, header, railHeader, footer, autoUnfold, labels: ownLabels, ...props }: AppNavigationProps): import("@types/react").JSX.Element;
70
+ export declare function AppNavigation({ sections, activeId: ownActiveId, renderLink, header, railHeader, footer, autoUnfold, collapsed, onExpand, labels: ownLabels, ...props }: AppNavigationProps): import("react").JSX.Element;
71
+ export type Workspace = {
72
+ id: string;
73
+ name: string;
74
+ };
75
+ export type WorkspaceMarkProps = {
76
+ name: string;
77
+ /** The workspace the reader is in; the others are drawn quieter. */
78
+ current?: boolean;
79
+ /** `small` for a row in the workspace menu. */
80
+ size?: "regular" | "small";
81
+ className?: string;
82
+ };
83
+ /** A workspace's mark: its name's first letter on a square tile. */
84
+ export declare function WorkspaceMark({ name, current, size, className, }: WorkspaceMarkProps): import("react").JSX.Element;
85
+ export type WorkspaceSwitcherProps = {
86
+ workspaces: readonly Workspace[];
87
+ currentId: string;
88
+ onSelect: (id: string) => void;
89
+ /** The mark alone, for the rail. */
90
+ compact?: boolean;
91
+ };
92
+ /** The current workspace, opening a menu of every workspace the reader belongs to, current checked. */
93
+ export declare function WorkspaceSwitcher({ workspaces, currentId, onSelect, compact, }: WorkspaceSwitcherProps): import("react").JSX.Element;
@@ -1,19 +1,31 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * The app's sidebar from data. `AppNavigation` draws its sections twice from the same list: as the
4
- * expanded sidebar, and as the icon rail the shell shows under 1000px. A section can fold past
5
- * `maxVisible` behind a More row, and unfolds on its own while the item it holds is open or, with
6
- * `autoUnfold`, while the sidebar has room for every row. Pinned and recent records are sections
7
- * like any other, which the caller builds from its own state.
4
+ * expanded sidebar, and as the icon rail the shell shows under 1000px or while `collapsed`. A
5
+ * section can fold past `maxVisible` behind a More row, and unfolds on its own while the item it
6
+ * holds is open or, with `autoUnfold`, while the sidebar has room for every row. Pinned and recent
7
+ * records are sections like any other, which the caller builds from its own state.
8
+ * `WorkspaceSwitcher` heads the sidebar: the current workspace's mark and name, opening a menu of
9
+ * the others.
8
10
  */
9
- import { CaretDownIcon, CaretUpIcon } from "@phosphor-icons/react";
11
+ import { CaretDownIcon, CaretLineRightIcon, CaretUpDownIcon, CaretUpIcon, } from "@phosphor-icons/react";
10
12
  import { Fragment, useLayoutEffect, useRef, useState, } from "react";
11
- import { css } from "#primitives/css";
13
+ import { css, cx } from "#primitives/css";
14
+ import { IconButton } from "./button.js";
12
15
  import { icon } from "./icon.js";
13
16
  import { useCiridaeLocale } from "./locale.js";
17
+ import { Menu } from "./menu.js";
14
18
  import { AppSidebar, AppSidebarFooter, AppSidebarNav, AppSidebarRail } from "./shell.js";
15
- import { SidebarItem, SidebarPanel, SidebarSection } from "./sidebar.js";
16
- const navCount = css({ color: "fg.muted", fontSize: "code", fontVariantNumeric: "tabular-nums" });
19
+ import { SidebarItem, SidebarPanel, SidebarRailRule, SidebarSection } from "./sidebar.js";
20
+ import { singleLineTextClass } from "./single-line-text.js";
21
+ const navCount = css({ color: "fg.subtle", fontSize: "code", fontVariantNumeric: "tabular-nums" });
22
+ // The rail's items, a section at a time: the sections stand apart by the rail's gap.
23
+ const railSection = css({
24
+ display: "flex",
25
+ flexDirection: "column",
26
+ alignItems: "center",
27
+ gap: "layout.tightGap",
28
+ });
17
29
  const sectionFooter = css({
18
30
  display: "grid",
19
31
  gap: "control.trackInset",
@@ -84,8 +96,9 @@ function sectionRuns(sections) {
84
96
  }
85
97
  return runs;
86
98
  }
87
- export function AppNavigation({ sections, activeId: ownActiveId = null, renderLink, header, railHeader, footer, autoUnfold = true, labels: ownLabels, ...props }) {
88
- const labels = { ...useCiridaeLocale().labels.navigation, ...ownLabels };
99
+ export function AppNavigation({ sections, activeId: ownActiveId = null, renderLink, header, railHeader, footer, autoUnfold = true, collapsed = false, onExpand, labels: ownLabels, ...props }) {
100
+ const locale = useCiridaeLocale();
101
+ const labels = { ...locale.labels.navigation, ...ownLabels };
89
102
  const activeIds = ownActiveId === null ? [] : typeof ownActiveId === "string" ? [ownActiveId] : ownActiveId;
90
103
  // Every row a fold hides, less the More row it trades them for.
91
104
  const foldedRows = sections.reduce((rows, section) => rows +
@@ -95,8 +108,86 @@ export function AppNavigation({ sections, activeId: ownActiveId = null, renderLi
95
108
  const room = useSidebarRoom(foldedRows);
96
109
  const roomy = autoUnfold && room.roomy;
97
110
  const sectionBlock = (section) => (_jsxs(SidebarSection, { label: section.label, children: [_jsx(SectionItems, { section: section, activeIds: activeIds, renderLink: renderLink, roomy: roomy, labels: labels }), section.footer && _jsx("div", { className: sectionFooter, children: section.footer })] }, section.id));
98
- return (_jsxs(AppSidebar, { "aria-label": labels.navigation, ...props, children: [_jsxs(AppSidebarNav, { ref: room.navRef, children: [header, sectionRuns(sections).map((run) => run.panel ? (_jsx(SidebarPanel, { children: run.sections.map(sectionBlock) }, run.sections[0].id)) : (_jsx(Fragment, { children: run.sections.map(sectionBlock) }, run.sections[0].id)))] }), _jsxs(AppSidebarRail, { children: [railHeader, sections
99
- .filter((section) => section.rail !== false)
100
- .flatMap((section) => section.items)
101
- .map((item) => (_jsx(SidebarItem, { "aria-label": item.label, icon: item.icon, active: activeIds.includes(item.id), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect }, item.id)))] }), _jsx(AppSidebarFooter, { ref: room.footerRef, children: footer })] }));
111
+ return (_jsxs(AppSidebar, { "aria-label": labels.navigation, collapsed: collapsed, ...props, children: [_jsxs(AppSidebarNav, { ref: room.navRef, children: [header, sectionRuns(sections).map((run) => run.panel ? (_jsx(SidebarPanel, { children: run.sections.map(sectionBlock) }, run.sections[0].id)) : (_jsx(Fragment, { children: run.sections.map(sectionBlock) }, run.sections[0].id)))] }), _jsxs(AppSidebarRail, { children: [railHeader, sections
112
+ .filter((section) => section.rail !== false && section.items.length > 0)
113
+ .map((section) => (_jsxs("div", { className: railSection, children: [section.label && _jsx(SidebarRailRule, {}), section.items.map((item) => (_jsx(SidebarItem, { "aria-label": item.label, icon: item.icon, active: activeIds.includes(item.id), href: item.href, render: item.render ?? renderLink?.(item), onClick: item.onSelect }, item.id)))] }, section.id)))] }), _jsxs(AppSidebarFooter, { ref: room.footerRef, children: [collapsed && onExpand && (_jsx(AppSidebarRail, { children: _jsx(IconButton, { "aria-label": locale.labels.common.expand, size: "medium", onClick: onExpand, children: _jsx(CaretLineRightIcon, { className: cx(icon({ size: "small" }), css({ _rtl: { transform: "scaleX(-1)" } })) }) }) })), footer] })] }));
114
+ }
115
+ /*
116
+ * A workspace's square mark with its initial: the primary face for the current one, the raised
117
+ * face with a quieter initial for the others. Its edge is a gradient, so it is drawn under the face
118
+ * through a transparent border.
119
+ */
120
+ const workspaceMark = css({
121
+ display: "inline-grid",
122
+ placeItems: "center",
123
+ flexShrink: "0",
124
+ width: "workspace.mark",
125
+ height: "workspace.mark",
126
+ border: "1px solid transparent",
127
+ borderRadius: "keycap",
128
+ background: "token(gradients.raised) padding-box, token(gradients.border.otherWorkspaceMark) border-box",
129
+ color: "fg.label",
130
+ fontSize: "code",
131
+ fontWeight: "medium",
132
+ letterSpacing: "compact",
133
+ lineHeight: "tight",
134
+ "&[data-current]": {
135
+ background: "token(gradients.primary) padding-box, token(gradients.border.primary) border-box",
136
+ color: "bg.canvas",
137
+ _light: { color: "fg.onAccent" },
138
+ },
139
+ "&[data-size=small]": { width: "control.mark", height: "control.mark", borderRadius: "control" },
140
+ });
141
+ /** A workspace's mark: its name's first letter on a square tile. */
142
+ export function WorkspaceMark({ name, current = false, size = "regular", className, }) {
143
+ return (_jsx("span", { "aria-hidden": "true", className: cx(workspaceMark, className), "data-ciridae-ui": "", "data-current": current || undefined, "data-size": size, children: name.trim().charAt(0).toLocaleUpperCase() }));
144
+ }
145
+ // The sidebar's first row: hovered it takes the nav row's wash, open a grey well in the canvas's edge.
146
+ const workspaceSwitcher = css({
147
+ display: "flex",
148
+ alignItems: "center",
149
+ gap: "layout.controlGap",
150
+ width: "100%",
151
+ height: "control.default",
152
+ padding: "control.compactBlock",
153
+ border: "0",
154
+ borderRadius: "keycap",
155
+ background: "transparent",
156
+ color: "fg.primary",
157
+ font: "inherit",
158
+ fontSize: "body",
159
+ fontWeight: "medium",
160
+ letterSpacing: "compact",
161
+ lineHeight: "tight",
162
+ textAlign: "start",
163
+ cursor: "pointer",
164
+ transitionProperty: "background",
165
+ transitionDuration: "fast",
166
+ _hover: { background: "token(gradients.selected)", _light: { background: "bg.rowHover" } },
167
+ // Open, it keeps its open face under the pointer too.
168
+ "&[data-popup-open], &[data-popup-open]:hover": {
169
+ background: "bg.openTrigger",
170
+ _light: { background: "bg.openTrigger" },
171
+ boxShadow: "inset 0 0 0 1px token(colors.border.canvas)",
172
+ },
173
+ _focusVisible: { focusRing: "outer" },
174
+ "& > [data-name]": { flex: "1" },
175
+ "& > svg": { flexShrink: "0", color: "fg.muted" },
176
+ "&[data-compact]": {
177
+ justifyContent: "center",
178
+ width: "control.compact",
179
+ height: "control.compact",
180
+ padding: "none",
181
+ },
182
+ });
183
+ /** The current workspace, opening a menu of every workspace the reader belongs to, current checked. */
184
+ export function WorkspaceSwitcher({ workspaces, currentId, onSelect, compact = false, }) {
185
+ const current = workspaces.find((workspace) => workspace.id === currentId);
186
+ const name = current?.name ?? "";
187
+ return (_jsx(Menu, { trigger: _jsxs("button", { type: "button", "aria-label": compact ? name : undefined, className: workspaceSwitcher, "data-ciridae-ui": "", "data-compact": compact || undefined, children: [_jsx(WorkspaceMark, { name: name, current: true }), !compact && (_jsxs(_Fragment, { children: [_jsx("span", { className: singleLineTextClass, "data-name": "", children: name }), _jsx(CaretUpDownIcon, { className: icon({ size: "small" }) })] }))] }), items: workspaces.map((workspace) => ({
188
+ label: workspace.name,
189
+ icon: (_jsx(WorkspaceMark, { name: workspace.name, current: workspace.id === currentId, size: "small" })),
190
+ checked: workspace.id === currentId,
191
+ onSelect: () => onSelect(workspace.id),
192
+ })), matchTriggerWidth: true }));
102
193
  }
@@ -1,14 +1,39 @@
1
1
  import { useRender } from "@base-ui/react/use-render";
2
- import type { ReactNode } from "react";
2
+ import type { ComponentProps, ReactNode } from "react";
3
3
  /** What a color says, wherever color carries meaning. */
4
4
  export type Tone = "neutral" | "info" | "success" | "warning" | "danger";
5
+ /** A badge's tone: one of the five, or `ink` for an outcome, which no other system uses. */
6
+ export type BadgeTone = Tone | "ink";
5
7
  export type BadgeProps = Omit<useRender.ComponentProps<"span">, "children"> & {
6
- tone?: Tone;
8
+ tone?: BadgeTone;
7
9
  /** `pill` for a judgment or state; `square` for a fact, such as a value's source. */
8
10
  shape?: "pill" | "square";
9
- /** `bare` drops the box: the icon and the words, muted, the tone on the icon alone. */
10
- appearance?: "filled" | "bare";
11
+ /**
12
+ * `filled` by default; `outline` is the card's face in the tone's edge; `bare` drops the box:
13
+ * the icon and the words, muted, the tone on the icon alone.
14
+ */
15
+ appearance?: "filled" | "outline" | "bare";
11
16
  icon?: ReactNode;
12
17
  children: ReactNode;
13
18
  };
14
- export declare function Badge({ tone, shape, appearance, icon, children, render, className, ...props }: BadgeProps): import("@types/react").ReactElement<unknown, string | import("@types/react").JSXElementConstructor<any>>;
19
+ export declare function Badge({ tone, shape, appearance, icon, children, render, className, ...props }: BadgeProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
20
+ export type BadgeGlyphKind = "notReached" | "hereNow" | "terminal" | "contested" | "noChange" | "check" | "stop";
21
+ export type BadgeGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
22
+ kind: BadgeGlyphKind;
23
+ };
24
+ /** A badge's drawn glyph: a lifecycle position, a contested verdict, an unchanged outcome. */
25
+ export declare function BadgeGlyph({ kind, className, ...props }: BadgeGlyphProps): import("react").JSX.Element;
26
+ /** How strongly a value has been checked: none, by a model, by a person, against the world. */
27
+ export type AssuranceLevel = 0 | 1 | 2 | 3;
28
+ export type AssuranceGlyphProps = Omit<ComponentProps<"svg">, "children"> & {
29
+ level: AssuranceLevel;
30
+ };
31
+ /** A three-bar meter, lit to the level, so a value's assurance reads without color. */
32
+ export declare function AssuranceGlyph({ level, className, ...props }: AssuranceGlyphProps): import("react").JSX.Element;
33
+ /** Where a record sits in its stages. The glyph is fixed per position; the words name the stage. */
34
+ export type LifecycleState = "notReached" | "hereNow" | "done" | "blocked" | "agentWaiting" | "expired" | "terminal" | "awaiting";
35
+ export type LifecycleBadgeProps = Omit<BadgeProps, "tone" | "shape" | "appearance" | "icon"> & {
36
+ state: LifecycleState;
37
+ };
38
+ /** A lifecycle pill: the state picks the glyph and the hue; the words are the stage's name. */
39
+ export declare function LifecycleBadge({ state, ...props }: LifecycleBadgeProps): import("react").JSX.Element;
@@ -1,12 +1,21 @@
1
- import { Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  /*
3
3
  * A badge: a short label, usually with an icon, that says what state something is in. Its
4
4
  * `tone` carries the meaning: `warning`, `danger`, `success` and `info` tint the glass toward
5
- * their hue, `neutral` stays grey. A `pill` carries a judgment, such as a verdict or a run mode;
6
- * a `square` badge carries a fact, such as where a value came from, in a field's flat well.
5
+ * their hue, `neutral` stays grey, and `ink` is the solid face of what a run actually did. A
6
+ * `pill` carries a judgment, such as a verdict or a run mode; a `square` badge carries a fact,
7
+ * such as where a value came from, in a field's flat well. `LifecycleBadge` is the pill for where
8
+ * a record sits in its stages, and `BadgeGlyph` and `AssuranceGlyph` the small marks badges and
9
+ * fields put in their icon slot.
7
10
  */
8
11
  import { useRender } from "@base-ui/react/use-render";
9
- import { cva, cx } from "#primitives/css";
12
+ import { CheckIcon, ClockIcon, HourglassIcon, SparkleIcon, StopIcon } from "@phosphor-icons/react";
13
+ import { css, cva, cx } from "#primitives/css";
14
+ /*
15
+ * A tinted pill is its hue's glass, falling from light to its hue, inside that hue's gradient
16
+ * hairline, which the `[data-edge="gradient"]` ring draws from `--edge`. Neutral is the raised
17
+ * glass, warning the tinted button's warm glass, and ink the primary face.
18
+ */
10
19
  const badge = cva({
11
20
  base: {
12
21
  display: "inline-flex",
@@ -29,16 +38,23 @@ const badge = cva({
29
38
  variants: {
30
39
  shape: {
31
40
  pill: { borderRadius: "pill" },
41
+ // The well of a form field in dark, the card's face in light, lettered in the label grey.
32
42
  square: {
33
43
  borderRadius: "control",
34
44
  borderColor: "border.control",
35
- background: "bg.field",
36
- _light: { borderColor: "border.mark", background: "bg.card" },
45
+ background: "bg.formField",
46
+ color: "fg.label",
47
+ _light: { background: "bg.card" },
37
48
  },
38
49
  },
39
- // A fact told in passing: the icon and the words, muted, with no box; a tone tints the icon.
50
+ /*
51
+ * `filled` is the tone's glass; `outline` the card's face inside the tone's edge, as an
52
+ * outcome that changed nothing; `bare` a fact told in passing: the icon and the words, muted,
53
+ * with no box, the tone on the icon alone.
54
+ */
40
55
  appearance: {
41
56
  filled: {},
57
+ outline: {},
42
58
  bare: {
43
59
  height: "auto",
44
60
  gap: "layout.tightGap",
@@ -54,21 +70,80 @@ const badge = cva({
54
70
  neutral: {},
55
71
  info: { color: "accent.blue" },
56
72
  success: { color: "accent.green" },
57
- warning: { color: "accent.yellow" },
73
+ warning: { color: "accent.yellow", "& svg": { color: "accent.yellowIcon" } },
58
74
  danger: { color: "accent.red" },
75
+ ink: { color: "fg.primary" },
59
76
  },
60
77
  },
61
78
  compoundVariants: [
62
- { appearance: "filled", shape: "pill", tone: "neutral", css: { background: "bg.raised" } },
63
- { appearance: "filled", shape: "pill", tone: "info", css: { background: "bg.infoSubtle" } },
79
+ {
80
+ appearance: "filled",
81
+ shape: "pill",
82
+ tone: "neutral",
83
+ css: {
84
+ background: "token(gradients.raised)",
85
+ color: "fg.label",
86
+ "--edge": "token(gradients.border.raised)",
87
+ },
88
+ },
89
+ {
90
+ appearance: "filled",
91
+ shape: "pill",
92
+ tone: "info",
93
+ css: {
94
+ background: "token(gradients.infoGlass)",
95
+ "--edge": "token(gradients.border.infoGlass)",
96
+ },
97
+ },
64
98
  {
65
99
  appearance: "filled",
66
100
  shape: "pill",
67
101
  tone: "success",
68
- css: { background: "bg.successSubtle" },
102
+ css: {
103
+ background: "token(gradients.successGlass)",
104
+ "--edge": "token(gradients.border.successGlass)",
105
+ },
106
+ },
107
+ {
108
+ appearance: "filled",
109
+ shape: "pill",
110
+ tone: "warning",
111
+ css: {
112
+ background: "token(gradients.tinted)",
113
+ "--edge": "token(gradients.border.raised)",
114
+ _light: { "--edge": "token(gradients.border.tinted)" },
115
+ },
116
+ },
117
+ {
118
+ appearance: "filled",
119
+ shape: "pill",
120
+ tone: "danger",
121
+ css: {
122
+ background: "token(gradients.dangerGlass)",
123
+ "--edge": "token(gradients.border.dangerGlass)",
124
+ },
125
+ },
126
+ {
127
+ appearance: "filled",
128
+ shape: "pill",
129
+ tone: "ink",
130
+ css: {
131
+ background: "token(gradients.primary)",
132
+ color: "bg.canvas",
133
+ "--edge": "token(gradients.border.primary)",
134
+ _light: { color: "fg.onAccent" },
135
+ },
136
+ },
137
+ {
138
+ appearance: "outline",
139
+ shape: "pill",
140
+ css: {
141
+ background: "token(gradients.raised)",
142
+ color: "fg.primary",
143
+ "--edge": "token(gradients.border.raised)",
144
+ _light: { background: "bg.card", "--edge": "token(gradients.border.primary)" },
145
+ },
69
146
  },
70
- { appearance: "filled", shape: "pill", tone: "warning", css: { background: "bg.tinted" } },
71
- { appearance: "filled", shape: "pill", tone: "danger", css: { background: "bg.errorSubtle" } },
72
147
  {
73
148
  appearance: "bare",
74
149
  tone: "info",
@@ -82,7 +157,7 @@ const badge = cva({
82
157
  {
83
158
  appearance: "bare",
84
159
  tone: "warning",
85
- css: { color: "fg.muted", "& svg": { color: "accent.yellow" } },
160
+ css: { color: "fg.muted", "& svg": { color: "accent.yellowIcon" } },
86
161
  },
87
162
  {
88
163
  appearance: "bare",
@@ -100,8 +175,52 @@ export function Badge({ tone = "neutral", shape = "pill", appearance = "filled",
100
175
  ...props,
101
176
  className: cx(badge({ tone, shape, appearance }), className),
102
177
  "data-ciridae-ui": "",
103
- "data-edge": shape === "pill" && appearance === "filled" ? "glass" : undefined,
178
+ "data-edge": shape === "pill" && appearance !== "bare" ? "gradient" : undefined,
104
179
  children: (_jsxs(_Fragment, { children: [icon, children] })),
105
180
  },
106
181
  });
107
182
  }
183
+ /*
184
+ * The glyphs the kit draws itself, in a 14px box (12px for the progress step's check and stop),
185
+ * in the color of the badge's or field's label.
186
+ */
187
+ const glyph = css({ flexShrink: "0", width: "1em", height: "1em", fontSize: "icon.small" });
188
+ const smallGlyph = css({ flexShrink: "0", width: "1em", height: "1em", fontSize: "icon.compact" });
189
+ /** A badge's drawn glyph: a lifecycle position, a contested verdict, an unchanged outcome. */
190
+ export function BadgeGlyph({ kind, className, ...props }) {
191
+ const small = kind === "check" || kind === "stop";
192
+ return (_jsxs("svg", { "aria-hidden": "true", viewBox: small ? "0 0 12 12" : "0 0 14 14", fill: "none", ...props, className: cx(small ? smallGlyph : glyph, className), children: [kind === "notReached" && (_jsx("circle", { cx: "7", cy: "7", r: "3.875", stroke: "currentColor", strokeWidth: "1.25", strokeDasharray: "2 1.6" })), kind === "hereNow" && _jsx("circle", { cx: "7", cy: "7", r: "3", fill: "currentColor" }), kind === "terminal" && (_jsx("rect", { x: "3.5", y: "3.5", width: "7", height: "7", rx: "1.5", fill: "currentColor" })), kind === "contested" && (_jsx("circle", { cx: "7", cy: "7", r: "3.875", fill: "currentColor", stroke: "currentColor", strokeWidth: "1.25" })), kind === "noChange" && (_jsx("circle", { cx: "7", cy: "7", r: "3.375", stroke: "currentColor", strokeWidth: "1.25" })), kind === "check" && (_jsx("path", { d: "M2.4 6.2 4.6 8.5 9.7 3.4", stroke: "currentColor", strokeWidth: "0.857", strokeLinecap: "round", strokeLinejoin: "round" })), kind === "stop" && (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "6", cy: "6", r: "3.95", stroke: "currentColor", strokeWidth: "0.857" }), _jsx("path", { d: "M4.1 6h3.8", stroke: "currentColor", strokeWidth: "0.857", strokeLinecap: "round" })] }))] }));
193
+ }
194
+ const ASSURANCE_BARS = [
195
+ { x: 2, y: 7.5, height: 5 },
196
+ { x: 5.8, y: 4.5, height: 8 },
197
+ { x: 9.5, y: 1.5, height: 11 },
198
+ ];
199
+ /** A three-bar meter, lit to the level, so a value's assurance reads without color. */
200
+ export function AssuranceGlyph({ level, className, ...props }) {
201
+ return (_jsx("svg", { "aria-hidden": "true", viewBox: "0 0 14 14", fill: "currentColor", ...props, className: cx(glyph, className), children: ASSURANCE_BARS.map((bar, index) => (_jsx("rect", { x: bar.x, y: bar.y, width: "2.5", height: bar.height, rx: "0.75", opacity: index < level ? 1 : 0.28 }, bar.x))) }));
202
+ }
203
+ const LIFECYCLE_TONES = {
204
+ notReached: "neutral",
205
+ hereNow: "info",
206
+ done: "success",
207
+ blocked: "warning",
208
+ agentWaiting: "info",
209
+ expired: "neutral",
210
+ terminal: "neutral",
211
+ awaiting: "warning",
212
+ };
213
+ const LIFECYCLE_GLYPHS = {
214
+ notReached: _jsx(BadgeGlyph, { kind: "notReached" }),
215
+ hereNow: _jsx(BadgeGlyph, { kind: "hereNow" }),
216
+ done: _jsx(CheckIcon, { weight: "bold" }),
217
+ blocked: _jsx(StopIcon, { weight: "bold" }),
218
+ agentWaiting: _jsx(SparkleIcon, { weight: "fill" }),
219
+ expired: _jsx(ClockIcon, { weight: "bold" }),
220
+ terminal: _jsx(BadgeGlyph, { kind: "terminal" }),
221
+ awaiting: _jsx(HourglassIcon, { weight: "bold" }),
222
+ };
223
+ /** A lifecycle pill: the state picks the glyph and the hue; the words are the stage's name. */
224
+ export function LifecycleBadge({ state, ...props }) {
225
+ return _jsx(Badge, { ...props, tone: LIFECYCLE_TONES[state], icon: LIFECYCLE_GLYPHS[state] });
226
+ }
@@ -3,15 +3,15 @@ import type { SVGProps } from "react";
3
3
  * GitHub's own mark, rather than a lookalike from an icon set: it carries the brand, so it should
4
4
  * be the shape people recognize. Path from Octicons `mark-github` (MIT).
5
5
  */
6
- export declare function GithubMarkIcon(props: SVGProps<SVGSVGElement>): import("@types/react").JSX.Element;
6
+ export declare function GithubMarkIcon(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
7
7
  /**
8
8
  * The mark in the current text color, so it follows the theme. Decorative unless given a
9
9
  * `title`, which names it for assistive technology.
10
10
  */
11
11
  export declare function CiridaeMark({ title, ...props }: SVGProps<SVGSVGElement> & {
12
12
  title?: string;
13
- }): import("@types/react").JSX.Element;
13
+ }): import("react").JSX.Element;
14
14
  /** The mark over its wordmark, in the current text color; `title` names it for assistive technology. */
15
15
  export declare function CiridaeLogo({ title, ...props }: SVGProps<SVGSVGElement> & {
16
16
  title: string;
17
- }): import("@types/react").JSX.Element;
17
+ }): import("react").JSX.Element;
@@ -12,4 +12,4 @@ export declare function BulkActionBar({ count, onClear, actions, onSelectAll, to
12
12
  /** Every row the list holds, as the Select all button counts it. */
13
13
  total?: string;
14
14
  labels?: Partial<CiridaeLabels["table"]>;
15
- }): import("@types/react").JSX.Element;
15
+ }): import("react").JSX.Element;
@@ -14,13 +14,13 @@ const bar = css({
14
14
  paddingBlock: "panel.compact",
15
15
  paddingInlineStart: "panel.comfortable",
16
16
  paddingInlineEnd: "panel.compact",
17
- border: "1px solid token(colors.border.panel)",
17
+ border: "1px solid token(colors.border.divider)",
18
18
  borderRadius: "action",
19
- background: "bg.hover",
19
+ background: "token(gradients.card)",
20
20
  fontSize: "body",
21
21
  fontWeight: "medium",
22
22
  letterSpacing: "compact",
23
- _light: { background: "bg.card" },
23
+ lineHeight: "tight",
24
24
  });
25
25
  const leading = css({
26
26
  display: "flex",