@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,146 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /*
3
+ * A record's activity as a log: a row per thing that happened, under a heading per day. A row is
4
+ * the time, who did it, what happened and a line more about it, and a trailing badge such as
5
+ * whether a person, a model or an agent did it. `comfortable` is a page's log, `compact` puts the
6
+ * line more beside what happened, and `panel` is the log in a drawer or side panel.
7
+ */
8
+ import { css, cx } from "#primitives/css";
9
+ import { Avatar } from "./data-display.js";
10
+ import { useCiridaeLocale } from "./locale.js";
11
+ const entry = css({
12
+ display: "flex",
13
+ alignItems: "flex-start",
14
+ gap: "layout.clusterGap",
15
+ width: "100%",
16
+ minWidth: "0",
17
+ paddingBlock: "log.entryBlock",
18
+ paddingInline: "log.entryInline",
19
+ color: "fg.primary",
20
+ _hover: { background: "token(gradients.rowHover)" },
21
+ "&[data-selected], &[data-selected]:hover": { background: "token(gradients.selected)" },
22
+ "&[role=button]": { cursor: "pointer" },
23
+ _focusVisible: { focusRing: "inset" },
24
+ "&[data-divider]": { borderBottom: "1px solid token(colors.border.divider)" },
25
+ "&[data-density=compact]": { alignItems: "center", paddingBlock: "log.entryCompactBlock" },
26
+ // A compact row's rule sits inside its padding, so it stays the kit's 40px with or without one.
27
+ "&[data-density=compact][data-divider]": {
28
+ paddingBottom: "calc(token(spacing.log.entryCompactBlock) - token(sizes.border.hairline))",
29
+ },
30
+ "&[data-density=panel]": {
31
+ paddingBlock: "log.entryPanelBlock",
32
+ paddingInline: "log.entryPanelInline",
33
+ },
34
+ // The time, the actor and the badge each sit in a line as tall as the actor, level with the
35
+ // headline beside them.
36
+ "& > [data-time], & > [data-actor], & > [data-badge]": {
37
+ display: "flex",
38
+ flexShrink: "0",
39
+ alignItems: "center",
40
+ height: "avatar.menu",
41
+ },
42
+ "& > [data-time]": {
43
+ width: "log.timeColumn",
44
+ color: "fg.subtle",
45
+ fontSize: "code",
46
+ fontWeight: "medium",
47
+ lineHeight: "tight",
48
+ fontVariantNumeric: "tabular-nums",
49
+ whiteSpace: "nowrap",
50
+ },
51
+ "& > [data-actor]": { justifyContent: "center", width: "avatar.menu" },
52
+ "& > [data-body]": {
53
+ display: "grid",
54
+ gridTemplateColumns: "minmax(0, 1fr)",
55
+ flex: "1",
56
+ minWidth: "0",
57
+ },
58
+ // One line as tall as the actor, ending in an ellipsis where the row is too narrow for it.
59
+ "& [data-headline]": {
60
+ display: "flex",
61
+ alignItems: "center",
62
+ minHeight: "avatar.menu",
63
+ fontSize: "body",
64
+ fontWeight: "medium",
65
+ lineHeight: "tight",
66
+ "& > span": {
67
+ minWidth: "0",
68
+ overflow: "hidden",
69
+ textOverflow: "ellipsis",
70
+ whiteSpace: "nowrap",
71
+ },
72
+ },
73
+ "& [data-detail]": {
74
+ color: "fg.label",
75
+ fontSize: "code",
76
+ fontWeight: "medium",
77
+ lineHeight: "log",
78
+ },
79
+ "&[data-density=compact] > [data-body]": {
80
+ display: "flex",
81
+ alignItems: "center",
82
+ gap: "layout.controlGap",
83
+ "& > *": { minWidth: "0", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
84
+ // Block, so a long headline ends in an ellipsis; its own line height, so it centres with
85
+ // the detail.
86
+ "& > [data-headline]": { display: "block", minHeight: "auto" },
87
+ "& > [data-detail]": { flex: "1", lineHeight: "tight" },
88
+ },
89
+ });
90
+ // The log's actor mark: a person's photo or initials, or an icon for an agent, a sync or a rule,
91
+ // on the raised glass circle. The icon is the small icon's size at every density.
92
+ const actorIcon = css({
93
+ display: "inline-grid",
94
+ placeItems: "center",
95
+ flexShrink: "0",
96
+ width: "avatar.menu",
97
+ height: "avatar.menu",
98
+ borderRadius: "round",
99
+ background: "token(gradients.raised)",
100
+ color: "fg.label",
101
+ "--edge": "token(gradients.border.raised)",
102
+ "& > svg": { fontSize: "icon.small" },
103
+ });
104
+ // Initials sit in the label grey here, where the avatar's own are the primary text colour; the
105
+ // extra attribute outranks the avatar's kind rule.
106
+ const actorPerson = css({ "&[data-ciridae-ui][data-kind=user]": { color: "fg.label" } });
107
+ const groupHeader = css({
108
+ display: "flex",
109
+ alignItems: "center",
110
+ gap: "layout.controlGap",
111
+ paddingTop: "panel.comfortable",
112
+ paddingInline: "panel.default",
113
+ paddingBottom: "layout.tightGap",
114
+ fontSize: "code",
115
+ fontWeight: "medium",
116
+ lineHeight: "tight",
117
+ "& > [data-label]": { color: "fg.primary" },
118
+ "& > [data-meta]": { color: "fg.subtle" },
119
+ });
120
+ /** One thing that happened, as a row of a log. */
121
+ export function LogEntry({ at, actor, headline, detail, badge, density = "comfortable", selected = false, divider = true, onSelect, className, ...props }) {
122
+ const { formatTime } = useCiridaeLocale();
123
+ const selectOnKey = (event) => {
124
+ if (event.target !== event.currentTarget || (event.key !== "Enter" && event.key !== " "))
125
+ return;
126
+ event.preventDefault();
127
+ onSelect?.();
128
+ };
129
+ return (_jsxs("div", { ...props, ...(onSelect && {
130
+ role: "button",
131
+ tabIndex: 0,
132
+ "aria-pressed": selected,
133
+ onClick: onSelect,
134
+ onKeyDown: selectOnKey,
135
+ }), className: cx(entry, className), "data-ciridae-ui": "", "data-density": density, "data-selected": selected || undefined, "data-divider": divider || undefined, children: [_jsx("time", { "data-time": "", dateTime: new Date(at).toISOString(), children: formatTime(at) }), _jsx("span", { "data-actor": "", children: actor }), _jsxs("div", { "data-body": "", children: [_jsx("span", { "data-headline": "", children: _jsx("span", { children: headline }) }), detail && _jsx("span", { "data-detail": "", children: detail })] }), badge && _jsx("span", { "data-badge": "", children: badge })] }));
136
+ }
137
+ /** A log's heading over a group of its rows, such as a day's: "Today", then "Oct 6". */
138
+ export function LogGroupHeader({ label, meta, className, ...props }) {
139
+ return (_jsxs("div", { ...props, className: cx(groupHeader, className), "data-ciridae-ui": "", children: [_jsx("span", { "data-label": "", children: label }), meta && _jsx("span", { "data-meta": "", children: meta })] }));
140
+ }
141
+ /** Who did a log entry's thing, as the mark for its `actor`. */
142
+ export function LogActor({ name, src, icon }) {
143
+ if (icon)
144
+ return (_jsx("span", { role: "img", "aria-label": name, className: actorIcon, "data-ciridae-ui": "", "data-edge": "gradient", children: icon }));
145
+ return _jsx(Avatar, { label: name, src: src, size: "menu", className: actorPerson });
146
+ }
@@ -6,7 +6,7 @@ import type { ComponentProps, Ref } from "react";
6
6
  export declare function LogList({ fill, className, ref, ...props }: ComponentProps<"div"> & {
7
7
  fill?: boolean;
8
8
  ref?: Ref<HTMLDivElement>;
9
- }): import("@types/react").JSX.Element;
9
+ }): import("react").JSX.Element;
10
10
  /**
11
11
  * Monospace block of raw log text. `framed` gives it the log list's own scroll frame, for output
12
12
  * that is the whole list rather than one entry's detail.
@@ -15,6 +15,6 @@ export declare function LogOutput({ tone, framed, className, ref, ...props }: Co
15
15
  tone?: "error";
16
16
  framed?: boolean;
17
17
  ref?: Ref<HTMLPreElement>;
18
- }): import("@types/react").JSX.Element;
18
+ }): import("react").JSX.Element;
19
19
  /** Placeholder for plain monospace output such as Docker Compose logs. */
20
- export declare function LogOutputSkeleton(): import("@types/react").JSX.Element;
20
+ export declare function LogOutputSkeleton(): import("react").JSX.Element;
@@ -29,7 +29,7 @@ export declare function MentionChip({ mention, mark, href, }: {
29
29
  mention: Mention;
30
30
  mark?: ReactNode;
31
31
  href?: string;
32
- }): import("@types/react").JSX.Element;
32
+ }): import("react").JSX.Element;
33
33
  export type MentionInputProps = Omit<ComponentProps<"div">, "children" | "defaultValue" | "onChange" | "onSubmit" | "placeholder"> & {
34
34
  /** The text, each mention in it as `[@label](mention://kind/id)`. */
35
35
  value: string;
@@ -47,4 +47,4 @@ export type MentionInputProps = Omit<ComponentProps<"div">, "children" | "defaul
47
47
  /** Names the picker's list; the locale's "Mentions" by default. */
48
48
  listLabel?: string;
49
49
  };
50
- export declare function MentionInput({ value, onValueChange, triggers, mentionMark, placeholder, submitOn, onSubmit, disabled, autoFocus, listLabel, ref, className, onKeyDown, onFocus, onBlur, onPointerDown, ...props }: MentionInputProps): import("@types/react").JSX.Element;
50
+ export declare function MentionInput({ value, onValueChange, triggers, mentionMark, placeholder, submitOn, onSubmit, disabled, autoFocus, listLabel, ref, className, onKeyDown, onFocus, onBlur, onPointerDown, ...props }: MentionInputProps): import("react").JSX.Element;
@@ -244,7 +244,7 @@ const field = css({
244
244
  },
245
245
  "&[aria-disabled=true]": { cursor: "default", opacity: "0.5" },
246
246
  });
247
- /* The picker: a flush menu over the caret, its rows edge to edge. */
247
+ /* The picker: a flush menu over the caret on the popovers' glass, its rows edge to edge. */
248
248
  const picker = css({
249
249
  zIndex: "popover",
250
250
  display: "grid",
@@ -254,9 +254,10 @@ const picker = css({
254
254
  overflowY: "auto",
255
255
  border: "1px solid token(colors.border.control)",
256
256
  borderRadius: "action",
257
- background: "bg.card",
257
+ background: "bg.popover",
258
+ backdropFilter: "blur(token(blurs.popover))",
258
259
  color: "fg.primary",
259
- boxShadow: "card",
260
+ boxShadow: "soft",
260
261
  outline: "none",
261
262
  });
262
263
  const pickerRow = css({
@@ -12,7 +12,7 @@ export type MenuItem = {
12
12
  variant?: "default" | "danger";
13
13
  /** Shows a check mark, for the active choice in a set. */
14
14
  checked?: boolean;
15
- /** Muted after the label, such as what a choice covers. */
15
+ /** Muted at the row's end, such as what a choice covers. */
16
16
  meta?: string;
17
17
  /** The hotkey that does what the item does, at the row's end; the host binds it. */
18
18
  shortcut?: Hotkey;
@@ -59,8 +59,12 @@ export type MenuProps = Omit<ComponentProps<typeof BaseMenu.Root>, "children"> &
59
59
  items: readonly MenuItem[];
60
60
  /** Which edge of the trigger the menu lines up with; `end` keeps a right-hand menu inside its container. */
61
61
  align?: "start" | "end";
62
+ /** How far the menu floats from its trigger, in px. Defaults to the kit's 4. */
63
+ sideOffset?: number;
64
+ /** Makes the menu exactly as wide as its trigger rather than at least `overlay.menu`. */
65
+ matchTriggerWidth?: boolean;
62
66
  };
63
- export declare function Menu({ trigger, items, align, ...rootProps }: MenuProps): import("@types/react").JSX.Element;
67
+ export declare function Menu({ trigger, items, align, sideOffset, matchTriggerWidth, ...rootProps }: MenuProps): import("react").JSX.Element;
64
68
  export type OverflowMenuProps = Omit<MenuProps, "trigger" | "align"> & {
65
69
  /** The button's accessible name, such as "Record actions" or "More for INV-4821". */
66
70
  label: string;
@@ -73,4 +77,4 @@ export type OverflowMenuProps = Omit<MenuProps, "trigger" | "align"> & {
73
77
  ref?: Ref<HTMLButtonElement>;
74
78
  };
75
79
  /** The "more" button: an `IconButton` with three dots that opens a `Menu` of `items`. */
76
- export declare function OverflowMenu({ label, size, variant, align, disabled, ref, ...props }: OverflowMenuProps): import("@types/react").JSX.Element;
80
+ export declare function OverflowMenu({ label, size, variant, align, disabled, ref, ...props }: OverflowMenuProps): import("react").JSX.Element;
@@ -3,21 +3,14 @@ import { Menu as BaseMenu } from "@base-ui/react/menu";
3
3
  import { CaretRightIcon, CheckIcon, DotsThreeIcon } from "@phosphor-icons/react";
4
4
  import { css, cx } from "#primitives/css";
5
5
  import { IconButton } from "./button.js";
6
+ import { glassMenuDivider, glassMenuRow, glassMenuSurface } from "./glass-menu.js";
6
7
  import { HotkeyLayer } from "./hotkeys.js";
7
8
  import { directional, icon, Spinner } from "./icon.js";
8
9
  import { ShortcutHint } from "./keycap.js";
9
- /* Flush, like the select menu: rows run edge to edge and the popup's corners clip them. */
10
- const popup = css({
11
- minWidth: "overlay.menu",
12
- padding: "none",
13
- overflow: "hidden",
14
- border: "1px solid token(colors.border.control)",
15
- borderRadius: "action",
16
- background: "bg.card",
17
- color: "fg.primary",
18
- textStyle: "body",
19
- boxShadow: "card",
20
- });
10
+ import { singleLineTextClass } from "./single-line-text.js";
11
+ const popup = css(glassMenuSurface, { minWidth: "overlay.menu" });
12
+ // As wide as its trigger, such as a switcher whose menu hangs under a full-width button.
13
+ const triggerWidthPopup = css(glassMenuSurface, { width: "var(--anchor-width)" });
21
14
  // A submenu joins its parent: the corner it opens from goes square, so the two read as one panel.
22
15
  const submenuPopup = css({
23
16
  "&:is([data-side=right], [data-side=inline-end])": { borderStartStartRadius: "0" },
@@ -25,28 +18,34 @@ const submenuPopup = css({
25
18
  });
26
19
  /** The positioner is the positioned element, so it carries the stacking order. */
27
20
  const positioner = css({ zIndex: "popover" });
28
- const item = css({
29
- display: "flex",
30
- alignItems: "center",
31
- gap: "layout.controlGap",
32
- minHeight: "menu.item",
33
- paddingInline: "control.compactInline",
34
- fontSize: "caption",
35
- lineHeight: "body",
36
- cursor: "pointer",
37
- outline: "none",
38
- "&[data-highlighted], &[data-popup-open]": { background: "bg.rowHover" },
39
- "&[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
21
+ // A submenu's row stays lit while its submenu is open. A label stays on the row's one line, as the
22
+ // design's 32px rows set it, and ends in an ellipsis in a menu held to its trigger's width.
23
+ const itemStyle = css.raw(glassMenuRow, {
24
+ "&[data-popup-open]": {
25
+ background: "bg.hover",
26
+ boxShadow: "inset 0 0 0 1px token(colors.border.menuHover)",
27
+ },
40
28
  "& > [data-label]": { flex: "1", minWidth: "0" },
41
29
  "& > [data-trailing]": { color: "fg.muted" },
42
30
  });
43
- const danger = css({ color: "accent.red" });
31
+ const item = css(itemStyle);
32
+ const dangerItem = css(itemStyle, { color: "accent.red" });
33
+ // Every icon sits in one 14px slot, so labels line up whatever the icon's own size. A wider mark,
34
+ // such as an avatar, widens its slot instead of spilling into the gap before the label.
35
+ const iconSlot = css({
36
+ display: "inline-grid",
37
+ placeItems: "center",
38
+ flexShrink: "0",
39
+ minWidth: "mark.inline",
40
+ minHeight: "mark.inline",
41
+ fontSize: "icon.small",
42
+ });
44
43
  /* Not an item: a row that names what the menu belongs to, such as the signed-in account. */
45
44
  const header = css({
46
45
  display: "flex",
47
46
  alignItems: "center",
48
47
  gap: "layout.controlGap",
49
- paddingInline: "control.compactInline",
48
+ paddingInline: "menu.itemInline",
50
49
  paddingBlock: "control.compactBlock",
51
50
  fontSize: "caption",
52
51
  lineHeight: "compact",
@@ -64,7 +63,7 @@ const groupHeading = css({
64
63
  display: "flex",
65
64
  alignItems: "center",
66
65
  minHeight: "menu.item",
67
- paddingInline: "control.compactInline",
66
+ paddingInline: "menu.itemInline",
68
67
  color: "fg.muted",
69
68
  fontSize: "code",
70
69
  fontWeight: "medium",
@@ -72,18 +71,16 @@ const groupHeading = css({
72
71
  cursor: "default",
73
72
  });
74
73
  const itemMeta = css({
74
+ flexShrink: "1",
75
75
  minWidth: "0",
76
76
  overflow: "hidden",
77
77
  textOverflow: "ellipsis",
78
78
  whiteSpace: "nowrap",
79
- color: "fg.muted",
80
- });
81
- const separator = css({
82
- height: "border.hairline",
83
- background: "border.subtle",
79
+ fontSize: "caption",
84
80
  });
85
- export function Menu({ trigger, items, align = "start", ...rootProps }) {
86
- return (_jsxs(BaseMenu.Root, { ...rootProps, children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: 6, align: align, children: _jsx(BaseMenu.Popup, { className: popup, "data-ciridae-ui": "", children: _jsx(MenuItems, { items: items }) }) }) }) })] }));
81
+ const separator = css(glassMenuDivider);
82
+ export function Menu({ trigger, items, align = "start", sideOffset = 4, matchTriggerWidth = false, ...rootProps }) {
83
+ return (_jsxs(BaseMenu.Root, { ...rootProps, children: [_jsx(BaseMenu.Trigger, { render: trigger }), _jsx(BaseMenu.Portal, { children: _jsx(HotkeyLayer, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: sideOffset, align: align, children: _jsx(BaseMenu.Popup, { className: matchTriggerWidth ? triggerWidthPopup : popup, "data-ciridae-ui": "", children: _jsx(MenuItems, { items: items }) }) }) }) })] }));
87
84
  }
88
85
  /** The "more" button: an `IconButton` with three dots that opens a `Menu` of `items`. */
89
86
  export function OverflowMenu({ label, size = "medium", variant = "raised", align = "end", disabled, ref, ...props }) {
@@ -100,10 +97,10 @@ function MenuItems({ items }) {
100
97
  return (_jsxs("div", { className: header, role: "presentation", children: [menuItem.icon, _jsxs("span", { className: headerCopy, children: [_jsx("span", { children: menuItem.label }), menuItem.detail && _jsx("span", { children: menuItem.detail })] })] }, index));
101
98
  }
102
99
  if (menuItem.items) {
103
- return (_jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: item, disabled: menuItem.disabled, children: [menuItem.icon, _jsx("span", { "data-label": "", children: menuItem.label }), _jsx(CaretRightIcon, { "data-trailing": "", className: cx(icon({ size: "tiny" }), directional) })] }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: 0, align: "start", alignOffset: -1, children: _jsx(BaseMenu.Popup, { className: cx(popup, submenuPopup), "data-ciridae-ui": "", children: _jsx(MenuItems, { items: menuItem.items }) }) }) })] }, menuItem.label));
100
+ return (_jsxs(BaseMenu.SubmenuRoot, { children: [_jsxs(BaseMenu.SubmenuTrigger, { className: item, disabled: menuItem.disabled, children: [menuItem.icon && _jsx("span", { className: iconSlot, children: menuItem.icon }), _jsx("span", { "data-label": "", className: singleLineTextClass, children: menuItem.label }), _jsx(CaretRightIcon, { "data-trailing": "", className: cx(icon({ size: "small" }), directional) })] }), _jsx(BaseMenu.Portal, { children: _jsx(BaseMenu.Positioner, { className: positioner, sideOffset: 0, align: "start", alignOffset: -5, children: _jsx(BaseMenu.Popup, { className: cx(popup, submenuPopup), "data-ciridae-ui": "", children: _jsx(MenuItems, { items: menuItem.items }) }) }) })] }, menuItem.label));
104
101
  }
105
- const content = (_jsxs(_Fragment, { children: [menuItem.loading ? _jsx(Spinner, { "aria-hidden": "true" }) : menuItem.icon, _jsxs("span", { "data-label": "", children: [menuItem.label, menuItem.meta !== undefined && _jsxs("span", { className: itemMeta, children: [" ", menuItem.meta] })] }), menuItem.shortcut && _jsx(ShortcutHint, { "data-trailing": "", hotkey: menuItem.shortcut }), menuItem.checked && _jsx(CheckIcon, { "data-trailing": "", className: icon({ size: "small" }) })] }));
106
- const className = cx(item, menuItem.variant === "danger" && danger);
102
+ const content = (_jsxs(_Fragment, { children: [(menuItem.loading || menuItem.icon) && (_jsx("span", { className: iconSlot, children: menuItem.loading ? _jsx(Spinner, { "aria-hidden": "true" }) : menuItem.icon })), _jsx("span", { "data-label": "", className: singleLineTextClass, children: menuItem.label }), menuItem.meta !== undefined && (_jsx("span", { "data-trailing": "", className: itemMeta, children: menuItem.meta })), menuItem.shortcut && _jsx(ShortcutHint, { "data-trailing": "", boxed: true, hotkey: menuItem.shortcut }), menuItem.checked && _jsx(CheckIcon, { "data-trailing": "", className: icon({ size: "small" }) })] }));
103
+ const className = menuItem.variant === "danger" ? dangerItem : item;
107
104
  if (menuItem.href !== undefined || menuItem.render !== undefined) {
108
105
  return (_jsx(BaseMenu.LinkItem, { className: className, href: menuItem.href, render: menuItem.render, closeOnClick: true, onClick: menuItem.onSelect, children: content }, menuItem.label));
109
106
  }
@@ -1,5 +1,5 @@
1
1
  import type { Tone } from "./badge.js";
2
- import type { ComponentProps, ReactNode } from "react";
2
+ import type { ComponentProps, HTMLAttributes, ReactNode } from "react";
3
3
  export type MeterProps = Omit<ComponentProps<"span">, "children"> & {
4
4
  value: number;
5
5
  /** 1 by default, so a share can be passed as is. */
@@ -11,4 +11,12 @@ export type MeterProps = Omit<ComponentProps<"span">, "children"> & {
11
11
  /** The figure shown above the track, such as "92%"; nothing is shown without it. */
12
12
  valueLabel?: ReactNode;
13
13
  };
14
- export declare function Meter({ value, max, tone, label, valueLabel, className, ...props }: MeterProps): import("@types/react").JSX.Element;
14
+ export declare function Meter({ value, max, tone, label, valueLabel, className, ...props }: MeterProps): import("react").JSX.Element;
15
+ /** A button with `onClick`, otherwise a plain row; either way it takes the caller's attributes. */
16
+ export type BudgetRowProps = Omit<HTMLAttributes<HTMLElement>, "children"> & Pick<MeterProps, "value" | "max" | "tone" | "valueLabel"> & {
17
+ icon: ReactNode;
18
+ name: string;
19
+ /** Under the name, such as "$184K of $200K of approved budget". */
20
+ detail: ReactNode;
21
+ };
22
+ export declare function BudgetRow({ icon, name, detail, value, max, tone, valueLabel, className, ...props }: BudgetRowProps): import("react").JSX.Element;
@@ -1,8 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  /*
3
3
  * A measured amount against its limit, such as a budget spent: a thin track filled to
4
4
  * `value / max`, with the figure above it at the end. Past the limit the track stays full. It is
5
- * phrasing content, so it can sit inside a row's button.
5
+ * phrasing content, so it can sit inside a row's button. On track (`info`) and at risk (`warning`)
6
+ * fill as the chart's progress bar does, a gradient over the hue's track.
6
7
  */
7
8
  import { css, cva, cx } from "#primitives/css";
8
9
  const meter = css({
@@ -19,9 +20,9 @@ const track = cva({
19
20
  variants: {
20
21
  tone: {
21
22
  neutral: { background: "bg.hover" },
22
- info: { background: "bg.infoSubtle" },
23
+ info: { background: "chart.progressTrack" },
23
24
  success: { background: "bg.successSubtle" },
24
- warning: { background: "bg.warningSubtle" },
25
+ warning: { background: "chart.atRiskTrack" },
25
26
  danger: { background: "bg.errorSubtle" },
26
27
  },
27
28
  },
@@ -31,9 +32,9 @@ const fill = cva({
31
32
  variants: {
32
33
  tone: {
33
34
  neutral: { background: "fg.subtle" },
34
- info: { background: "accent.blue" },
35
+ info: { background: "token(gradients.chart.progress)" },
35
36
  success: { background: "accent.green" },
36
- warning: { background: "accent.yellow" },
37
+ warning: { background: "token(gradients.chart.atRisk)" },
37
38
  danger: { background: "accent.red" },
38
39
  },
39
40
  },
@@ -42,3 +43,60 @@ export function Meter({ value, max = 1, tone = "info", label, valueLabel, classN
42
43
  const share = max > 0 ? Math.min(Math.max(value / max, 0), 1) : 0;
43
44
  return (_jsxs("span", { role: "meter", "aria-label": label, "aria-valuenow": value, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": typeof valueLabel === "string" ? valueLabel : undefined, ...props, className: cx(meter, className), "data-ciridae-ui": "", children: [valueLabel, _jsx("span", { className: track({ tone }), children: _jsx("span", { className: fill({ tone }), style: { width: `${share * 100}%` } }) })] }));
44
45
  }
46
+ /*
47
+ * Budget row: a job's spend against its budget as a row, the kit's Budget row: the icon on the
48
+ * raised tile, the name over what is spent of what, and a 170px meter at the end. A row with
49
+ * `onClick` is a button that opens the job. Rows in a list part with a hairline above each.
50
+ */
51
+ const budgetRow = css({
52
+ display: "flex",
53
+ alignItems: "center",
54
+ justifyContent: "space-between",
55
+ gap: "meter.rowGap",
56
+ width: "100%",
57
+ paddingBlock: "panel.default",
58
+ paddingInline: "panel.comfortable",
59
+ border: "0",
60
+ borderTop: "1px solid token(colors.border.divider)",
61
+ background: "transparent",
62
+ color: "fg.primary",
63
+ font: "inherit",
64
+ fontWeight: "medium",
65
+ letterSpacing: "compact",
66
+ lineHeight: "tight",
67
+ textAlign: "start",
68
+ _first: { borderTop: "0" },
69
+ "&:is(button)": {
70
+ cursor: "pointer",
71
+ _hover: { background: "bg.rowHover" },
72
+ _focusVisible: { focusRing: "inset" },
73
+ },
74
+ });
75
+ const budgetLeading = css({
76
+ display: "flex",
77
+ alignItems: "center",
78
+ gap: "layout.contentGap",
79
+ flex: "1",
80
+ minWidth: "0",
81
+ });
82
+ const budgetTile = css({
83
+ display: "inline-grid",
84
+ placeItems: "center",
85
+ flexShrink: "0",
86
+ width: "tile.icon",
87
+ height: "tile.icon",
88
+ border: "1px solid transparent",
89
+ borderRadius: "action",
90
+ background: "token(gradients.raised)",
91
+ fontSize: "icon.medium",
92
+ "--edge": "token(gradients.border.raised)",
93
+ });
94
+ const budgetCopy = css({ display: "grid", gap: "layout.tightGap", minWidth: "0" });
95
+ const budgetName = css({ fontSize: "body" });
96
+ const budgetDetail = css({ color: "fg.secondary", fontSize: "code" });
97
+ // Fluid under 170px, so the job's name keeps a readable line on a phone.
98
+ const budgetMeter = css({ width: "min(170px, 40%)", flexShrink: "0" });
99
+ export function BudgetRow({ icon, name, detail, value, max, tone, valueLabel, className, ...props }) {
100
+ const content = (_jsxs(_Fragment, { children: [_jsxs("span", { className: budgetLeading, children: [_jsx("span", { className: budgetTile, "data-ciridae-ui": "", "data-edge": "gradient", children: icon }), _jsxs("span", { className: budgetCopy, children: [_jsx("span", { className: budgetName, children: name }), _jsx("span", { className: budgetDetail, children: detail })] })] }), _jsx(Meter, { className: budgetMeter, value: value, max: max, tone: tone, label: name, valueLabel: valueLabel })] }));
101
+ return props.onClick ? (_jsx("button", { type: "button", ...props, className: cx(budgetRow, className), "data-ciridae-ui": "", children: content })) : (_jsx("div", { ...props, className: cx(budgetRow, className), "data-ciridae-ui": "", children: content }));
102
+ }
@@ -4,18 +4,35 @@ export type PaginationProps = Omit<ComponentProps<"nav">, "children"> & {
4
4
  page: number;
5
5
  pages: number;
6
6
  onPageChange: (page: number) => void;
7
+ /** At the bar's start, such as a `Select` of how many rows a page shows. */
8
+ pageSizeSelect?: ReactNode;
9
+ /** Before the pager, such as "Showing 1 to 25 of 1,284". */
10
+ summary?: ReactNode;
11
+ /** A button per page between Previous and Next, for a short run of pages. */
12
+ numbered?: boolean;
7
13
  labels?: Partial<CiridaeLabels["pagination"]>;
8
14
  };
9
- export declare function Pagination({ page, pages, onPageChange, labels: ownLabels, className, ...props }: PaginationProps): import("@types/react").JSX.Element;
15
+ export declare function Pagination({ page, pages, onPageChange, pageSizeSelect, summary, numbered, labels: ownLabels, className, ...props }: PaginationProps): import("react").JSX.Element;
10
16
  export type EmptyStateProps = Omit<ComponentProps<"section">, "title"> & {
11
17
  icon?: ReactNode;
12
18
  title: ReactNode;
13
19
  description?: ReactNode;
20
+ /** The main way forward: a primary medium button on a page, a secondary small one in a card. */
14
21
  action?: ReactNode;
22
+ /** A second way forward before `action`, such as a secondary medium button on a page. */
23
+ secondaryAction?: ReactNode;
15
24
  /**
16
- * `block` frames a page's or panel's empty state; `bare` is the same without the frame, its icon
17
- * in a ring, for a table or card that frames it; `inline` is one muted line in a list.
25
+ * `block` is a page's or panel's empty state; `bare` the same, tighter, for a table or card
26
+ * that frames it; `inline` is one muted line in a list.
18
27
  */
19
28
  variant?: "block" | "bare" | "inline";
20
29
  };
21
- export declare function EmptyState({ icon, title, description, action, variant, className, ...props }: EmptyStateProps): import("@types/react").JSX.Element;
30
+ export declare function EmptyState({ icon, title, description, action, secondaryAction, variant, className, ...props }: EmptyStateProps): import("react").JSX.Element;
31
+ export type EmptyTileProps = Omit<ComponentProps<"div">, "children"> & {
32
+ /** What the tile would count, as its stat tile names it. */
33
+ label: ReactNode;
34
+ /** Why there is no figure, such as "No invoices this period". */
35
+ note?: ReactNode;
36
+ };
37
+ /** A stat tile with no figure: its label, a dash in the figure's place, and why. */
38
+ export declare function EmptyTile({ label, note, className, ...props }: EmptyTileProps): import("react").JSX.Element;