@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
@@ -65,16 +65,23 @@ export type EditableFieldProps = Omit<ComponentProps<"div">, "children"> & {
65
65
  badge?: ReactNode;
66
66
  /** Under the value while the row is idle. */
67
67
  note?: ReactNode;
68
- /** The field needs the reader: the row lifts and this button sits at its end. */
68
+ /** The field needs the reader: the row lifts on the selected glass and this button sits at its end. */
69
69
  action?: {
70
70
  label: ReactNode;
71
71
  onClick: () => void;
72
72
  disabled?: boolean;
73
73
  };
74
+ /** `bottom` by default; `none` for the last row of a list whose container draws its own edge. */
75
+ divider?: "bottom" | "none";
76
+ /**
77
+ * `selected` washes the row with the selected glass, such as the field a side panel is
78
+ * showing; `inset` sets it into the list between hairlines, such as a field set apart.
79
+ */
80
+ emphasis?: "selected" | "inset" | "highlighted";
74
81
  edit?: FieldEdit;
75
82
  labels?: Partial<CiridaeLabels["editableField"]>;
76
83
  };
77
- export declare function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("@types/react").JSX.Element;
84
+ export declare function EditableField({ label, value, provenance, badge, note, action, divider, emphasis, edit, labels: ownLabels, className, ...props }: EditableFieldProps): import("react").JSX.Element;
78
85
  export type ConflictingSource = {
79
86
  id: string;
80
87
  label: ReactNode;
@@ -92,8 +99,10 @@ export type SourceConflictProps = Omit<ComponentProps<"div">, "children"> & {
92
99
  };
93
100
  /** At the effective row's end, such as a Resolve button. */
94
101
  action?: ReactNode;
102
+ /** `highlighted` while the agent points at the field; `neutral` by default. */
103
+ tone?: "highlighted" | "neutral";
95
104
  labels?: Partial<{
96
105
  effectiveValue: string;
97
106
  }>;
98
107
  };
99
- export declare function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }: SourceConflictProps): import("@types/react").JSX.Element;
108
+ export declare function SourceConflict({ sources, effective, action, tone, labels: ownLabels, className, ...props }: SourceConflictProps): import("react").JSX.Element;
@@ -10,6 +10,7 @@ import { useEffect, useId, useRef, useState, } from "react";
10
10
  import { css, cx } from "#primitives/css";
11
11
  import { Button } from "./button.js";
12
12
  import { Calendar } from "./calendar.js";
13
+ import { glassMenuSurface } from "./glass-menu.js";
13
14
  import { useHotkey } from "./hotkeys.js";
14
15
  import { icon, Spinner } from "./icon.js";
15
16
  import { Input } from "./input.js";
@@ -18,24 +19,48 @@ import { Tooltip } from "./overlays.js";
18
19
  import { Combobox } from "./select.js";
19
20
  /* The row stacks its label over the value in a narrow box, so each field is its own container. */
20
21
  const fieldBox = css({ containerType: "inline-size" });
22
+ /*
23
+ * The kit's Detail field: 12 · 16 padding, a hairline under it. A field that needs the reader
24
+ * (the inline edit's Needs confirm) and an inset row wear the selected glass between the selected
25
+ * edge's hairlines; a selected row wears the glass alone; a highlighted row, while the agent points
26
+ * at it, is the flat yellow band between hairlines.
27
+ */
21
28
  const fieldRow = css({
22
29
  position: "relative",
23
30
  display: "grid",
24
31
  gridTemplateColumns: "150px minmax(0, 1fr) auto",
25
- alignItems: "center",
32
+ // The label sits on the value's first line, as the kit's does; the trailing control centres.
33
+ alignItems: "baseline",
26
34
  gap: "layout.contentGap",
27
35
  paddingBlock: "panel.default",
28
36
  paddingInline: "panel.comfortable",
29
- borderBottom: "1px solid token(colors.border.panel)",
37
+ borderBottom: "1px solid token(colors.border.divider)",
30
38
  fontSize: "caption",
31
39
  letterSpacing: "compact",
40
+ "& > :last-child": { alignSelf: "center" },
32
41
  "&[data-needs]": {
33
- background: "bg.raised",
34
- borderBlock: "1px solid token(colors.border.glass)",
42
+ background: "token(gradients.selected)",
43
+ borderBlock: "1px solid",
44
+ borderImage: "token(gradients.border.selected) 1",
45
+ },
46
+ // After the band's hairlines, so a last highlighted row keeps only its top one, as the kit's.
47
+ "&[data-divider=none]": { borderBottom: "0" },
48
+ "&[data-emphasis=selected]": { background: "token(gradients.selected)" },
49
+ "&[data-emphasis=inset]": {
50
+ background: "token(gradients.selected)",
51
+ borderBlock: "1px solid",
52
+ borderImage: "token(gradients.border.selected) 1",
53
+ },
54
+ // The kit's Highlighted detail field: the yellow band while the agent points at the field.
55
+ "&[data-emphasis=highlighted]": {
56
+ background: "bg.warningTint",
57
+ borderBlock: "1px solid token(colors.border.divider)",
58
+ },
59
+ "&[data-editable]": {
60
+ cursor: "pointer",
61
+ _hover: { background: "token(gradients.card)", _light: { background: "bg.rowHover" } },
35
62
  },
36
- "&[data-editable]": { cursor: "pointer", _hover: { background: "bg.rowHover" } },
37
63
  "&[data-editable]:is(:hover, :focus-within) [data-affordance], &[data-editing] [data-affordance]": { opacity: "1" },
38
- "&[data-editable]:hover > [data-label]": { color: "fg.secondary" },
39
64
  "@container (max-width: 520px)": {
40
65
  gridTemplateColumns: "minmax(0, 1fr) auto",
41
66
  rowGap: "layout.tightGap",
@@ -43,10 +68,10 @@ const fieldRow = css({
43
68
  },
44
69
  });
45
70
  const fieldLabel = css({
46
- color: "fg.muted",
71
+ color: "fg.secondary",
47
72
  fontSize: "code",
48
73
  fontWeight: "medium",
49
- "[data-needs] > &": { color: "fg.secondary" },
74
+ lineHeight: "tight",
50
75
  });
51
76
  const fieldValue = css({
52
77
  display: "flex",
@@ -101,9 +126,10 @@ const provenanceLine = css({
101
126
  gap: "layout.contentGap",
102
127
  marginTop: "control.iconGap",
103
128
  minHeight: "control.knob",
104
- color: "fg.muted",
129
+ color: "fg.label",
105
130
  fontSize: "code",
106
131
  fontWeight: "medium",
132
+ lineHeight: "tight",
107
133
  "& > span, & > button, & > a": {
108
134
  display: "inline-flex",
109
135
  alignItems: "center",
@@ -133,31 +159,29 @@ const fieldNote = css({
133
159
  color: "fg.secondary",
134
160
  fontSize: "code",
135
161
  fontWeight: "medium",
162
+ lineHeight: "tight",
136
163
  });
137
- const editor = css({ width: "200px", maxWidth: "100%", fontSize: "body" });
164
+ const editor = css({ width: "200px", maxWidth: "100%" });
138
165
  const editorValue = css({ position: "relative", display: "flex", alignItems: "center" });
139
166
  const tooltipTitle = css({ display: "block", fontWeight: "medium" });
140
167
  const tooltipDetail = css({ display: "block", fontSize: "code", opacity: "0.8" });
141
168
  const trailingActions = css({ display: "inline-flex", gap: "layout.tightGap" });
142
- /* The calendar opens above the date editor. */
143
- const calendarPicker = css({
169
+ /* The calendar opens above the date editor, on the menus' glass sheet. */
170
+ const calendarPicker = css(glassMenuSurface, {
144
171
  position: "absolute",
145
172
  bottom: "calc(100% + token(spacing.control.iconGap))",
146
173
  insetInlineStart: "0",
147
174
  zIndex: "popover",
148
175
  cursor: "default",
149
- border: "1px solid token(colors.border.control)",
150
- borderRadius: "inset",
151
- background: "bg.card",
152
- boxShadow: "card",
153
- "& > section": { border: "0", background: "transparent" },
154
176
  });
177
+ // Under the days, inset as the calendar is, such as the value it had before.
155
178
  const calendarFooter = css({
156
- paddingInline: "panel.default",
179
+ paddingInline: "panel.compact",
157
180
  paddingBottom: "panel.compact",
158
181
  color: "fg.muted",
159
182
  fontSize: "code",
160
183
  fontWeight: "medium",
184
+ lineHeight: "body",
161
185
  });
162
186
  /** An editor opens with its text selected, so typing replaces the value. */
163
187
  function selectTyped(event) {
@@ -167,7 +191,7 @@ function selectTyped(event) {
167
191
  function localMidnight(date) {
168
192
  return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
169
193
  }
170
- export function EditableField({ label, value, provenance, badge, note, action, edit, labels: ownLabels, className, ...props }) {
194
+ export function EditableField({ label, value, provenance, badge, note, action, divider = "bottom", emphasis, edit, labels: ownLabels, className, ...props }) {
171
195
  const locale = useCiridaeLocale();
172
196
  const labels = { ...locale.labels.editableField, ...ownLabels };
173
197
  const formatDate = (timestamp) => locale.formatDate(timestamp);
@@ -307,7 +331,7 @@ export function EditableField({ label, value, provenance, badge, note, action, e
307
331
  cancel();
308
332
  } }) }) }));
309
333
  }
310
- return (_jsxs("span", { className: editorValue, onFocus: selectTyped, children: [_jsx(Input, { ref: inputRef, className: editor, "aria-labelledby": labelId, "aria-invalid": status.mode === "error" || undefined, value: draft, onChange: (event) => {
334
+ return (_jsxs("span", { className: editorValue, onFocus: selectTyped, children: [_jsx(Input, { ref: inputRef, size: "inline", className: editor, "aria-labelledby": labelId, "aria-invalid": status.mode === "error" || undefined, value: draft, onChange: (event) => {
311
335
  setDraft(event.target.value);
312
336
  if (status.mode === "error")
313
337
  setStatus({ mode: "editing" });
@@ -364,18 +388,24 @@ export function EditableField({ label, value, provenance, badge, note, action, e
364
388
  }
365
389
  return _jsx("span", {});
366
390
  };
367
- return (_jsx("div", { ...props, className: cx(fieldBox, className), "data-ciridae-ui": "", children: _jsxs("div", { ref: rowRef, className: fieldRow, "data-needs": action && !editing ? "" : undefined, "data-editable": editable && !editing ? "" : undefined, "data-editing": editing ? "" : undefined, onClick: begin, children: [_jsx("span", { className: fieldLabel, id: labelId, "data-label": "", children: label }), _jsxs("span", { className: css({ minWidth: "0" }), children: [valueLine(), _jsx("span", { role: "status", children: statusLine() }), note !== undefined && status.mode === "idle" && (_jsx("span", { className: fieldNote, children: note }))] }), trailing()] }) }));
391
+ return (_jsx("div", { ...props, className: cx(fieldBox, className), "data-ciridae-ui": "", children: _jsxs("div", { ref: rowRef, className: fieldRow, "data-needs": action && !editing ? "" : undefined, "data-divider": divider, "data-emphasis": emphasis, "data-editable": editable && !editing ? "" : undefined, "data-editing": editing ? "" : undefined, onClick: begin, children: [_jsx("span", { className: fieldLabel, id: labelId, "data-label": "", children: label }), _jsxs("span", { className: css({ minWidth: "0" }), children: [valueLine(), _jsx("span", { role: "status", children: statusLine() }), note !== undefined && status.mode === "idle" && (_jsx("span", { className: fieldNote, children: note }))] }), trailing()] }) }));
368
392
  }
369
- /* Source conflict: each source's value under a disputed field, then the value in effect. */
393
+ /*
394
+ * Source conflict: each source's value under a disputed field, then the value in effect. The
395
+ * kit's Contested sources: flat and square between hairlines, the yellow band while the agent
396
+ * points at the field, the panel's face otherwise (the grey well in light).
397
+ */
370
398
  const conflictBlock = css({
371
399
  display: "grid",
372
400
  gap: "layout.controlGap",
373
401
  paddingBlock: "panel.default",
374
402
  paddingInline: "panel.comfortable",
375
- borderBottom: "1px solid token(colors.border.panel)",
376
- background: "bg.panel",
403
+ borderBlock: "1px solid token(colors.border.divider)",
377
404
  letterSpacing: "compact",
405
+ lineHeight: "tight",
378
406
  containerType: "inline-size",
407
+ "&[data-tone=neutral]": { background: "bg.panel", _light: { background: "bg.field" } },
408
+ "&[data-tone=highlighted]": { background: "bg.warningTint" },
379
409
  });
380
410
  const conflictRow = css({
381
411
  display: "grid",
@@ -406,7 +436,7 @@ const sourceMeta = css({
406
436
  });
407
437
  const effectiveRow = css({
408
438
  paddingTop: "panel.default",
409
- borderTop: "1px solid token(colors.border.panel)",
439
+ borderTop: "1px solid token(colors.border.divider)",
410
440
  });
411
441
  const effectiveLabel = css({ fontSize: "code", fontWeight: "medium" });
412
442
  const effectiveNote = css({
@@ -416,7 +446,7 @@ const effectiveNote = css({
416
446
  fontSize: "code",
417
447
  fontWeight: "medium",
418
448
  });
419
- export function SourceConflict({ sources, effective, action, labels: ownLabels, className, ...props }) {
449
+ export function SourceConflict({ sources, effective, action, tone = "neutral", labels: ownLabels, className, ...props }) {
420
450
  const labels = { ...useCiridaeLocale().labels.editableField, ...ownLabels };
421
- return (_jsxs("div", { ...props, className: cx(conflictBlock, className), "data-ciridae-ui": "", children: [sources.map((source) => (_jsxs("div", { className: conflictRow, children: [_jsxs("span", { className: sourceName, children: [source.icon, source.label] }), _jsx("span", { className: sourceValue, children: source.value }), _jsx("span", { className: sourceMeta, children: source.meta })] }, source.id))), _jsxs("div", { className: cx(conflictRow, effectiveRow), children: [_jsx("span", { className: effectiveLabel, children: labels.effectiveValue }), _jsxs("span", { children: [_jsx("span", { className: sourceValue, children: effective.value }), effective.note !== undefined && _jsx("span", { className: effectiveNote, children: effective.note })] }), action ?? _jsx("span", {})] })] }));
451
+ return (_jsxs("div", { ...props, className: cx(conflictBlock, className), "data-ciridae-ui": "", "data-tone": tone, children: [sources.map((source) => (_jsxs("div", { className: conflictRow, children: [_jsxs("span", { className: sourceName, children: [source.icon, source.label] }), _jsx("span", { className: sourceValue, children: source.value }), _jsx("span", { className: sourceMeta, children: source.meta })] }, source.id))), _jsxs("div", { className: cx(conflictRow, effectiveRow), children: [_jsx("span", { className: effectiveLabel, children: labels.effectiveValue }), _jsxs("span", { children: [_jsx("span", { className: sourceValue, children: effective.value }), effective.note !== undefined && _jsx("span", { className: effectiveNote, children: effective.note })] }), action ?? _jsx("span", {})] })] }));
422
452
  }
@@ -9,4 +9,4 @@ export type EmailInputProps = Omit<InputProps, "value" | "defaultValue" | "onCha
9
9
  invalidEmails?: readonly string[];
10
10
  };
11
11
  /** Comma, semicolon, whitespace, Enter, Tab, or blur commits addresses; duplicates are ignored. */
12
- export declare function EmailInput({ value, onValueChange, inputValue, onInputValueChange, invalidEmails, disabled, className, onKeyDown, onBlur, onPaste, ...props }: EmailInputProps): import("@types/react").JSX.Element;
12
+ export declare function EmailInput({ value, onValueChange, inputValue, onInputValueChange, invalidEmails, disabled, className, onKeyDown, onBlur, onPaste, ...props }: EmailInputProps): import("react").JSX.Element;
@@ -2,18 +2,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { css, cx } from "#primitives/css";
3
3
  import { Chip } from "./chip.js";
4
4
  import { useFieldControl } from "./field-context.js";
5
+ // The field face `Input` wears, holding chips that wrap onto more lines as they fill it.
5
6
  const emailField = css({
6
7
  display: "flex",
7
8
  flexWrap: "wrap",
8
9
  alignItems: "center",
9
10
  gap: "layout.tightGap",
10
11
  padding: "control.compactBlock",
11
- minHeight: "control.compact",
12
+ minHeight: "control.default",
12
13
  border: "1px solid token(colors.border.control)",
13
- borderRadius: "field",
14
- background: "bg.control",
14
+ borderRadius: "control",
15
+ background: "bg.formField",
15
16
  _focusWithin: { focusRing: "outer" },
16
- "&[data-invalid]": { borderColor: "accent.red" },
17
+ "&[data-invalid]": { borderColor: "border.error" },
17
18
  "&[data-disabled]": { opacity: "disabled" },
18
19
  });
19
20
  const draftInput = css({
@@ -28,7 +29,7 @@ const draftInput = css({
28
29
  height: "control.shortcut",
29
30
  paddingInline: "control.compactInline",
30
31
  outline: "none",
31
- _placeholder: { color: "fg.muted" },
32
+ _placeholder: { color: "fg.subtle" },
32
33
  _focusVisible: { outline: "none", boxShadow: "none" },
33
34
  });
34
35
  /** Comma, semicolon, whitespace, Enter, Tab, or blur commits addresses; duplicates are ignored. */
@@ -9,5 +9,5 @@ export type EnvironmentFrameProps = ComponentProps<"div"> & {
9
9
  /** The strip's tint: `warning` for a staging build, `info` for dev. */
10
10
  tone?: SiteBannerProps["tone"];
11
11
  };
12
- /** A persistent environment strip and an inset AppShell viewport. */
13
- export declare function EnvironmentFrame({ label, tone, children, className, ...props }: EnvironmentFrameProps): import("@types/react").JSX.Element;
12
+ /** An environment strip, which the reader can dismiss for the session, and an inset AppShell viewport. */
13
+ export declare function EnvironmentFrame({ label, tone, children, className, ...props }: EnvironmentFrameProps): import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { css, cx } from "#primitives/css";
3
4
  import { useCiridaeLocale } from "./locale.js";
4
5
  import { SiteBanner } from "./site-banner.js";
@@ -17,26 +18,56 @@ const viewport = css({
17
18
  flex: "1",
18
19
  minHeight: "0",
19
20
  overflow: "hidden",
20
- background: "bg.canvas",
21
+ background: "token(gradients.canvas)",
21
22
  // Only under the environment strip does the app read as inset below it.
22
23
  "&[data-under-banner]": {
23
24
  borderTopRadius: "field",
24
25
  boxShadow: "inset 0 2px 4px rgba(0, 0, 0, 0.18)",
25
26
  },
26
- // Keep the shell and its phone layout within the space below the site banner.
27
+ // Keep the shell and its phone layout within the space below the site banner. On a desktop the
28
+ // shell's row sizes the main panel inside its block margins, so a full height would push it past
29
+ // them; a phone's main panel is a full window tall, so there it takes the space under the strip.
27
30
  "& > [data-ciridae-ui]": { height: "100%", background: "transparent" },
28
31
  "& > [data-ciridae-ui] > main": {
29
- height: "100%",
30
32
  borderRadius: "inset",
31
- _phone: { borderRadius: "0" },
33
+ _phone: { borderRadius: "0", height: "100%" },
32
34
  },
33
35
  "& > [data-ciridae-ui] > main > header": {
34
36
  paddingInlineStart: "page.inline",
35
37
  _phone: { paddingInlineStart: "panel.default" },
36
38
  },
37
39
  });
38
- /** A persistent environment strip and an inset AppShell viewport. */
40
+ /*
41
+ * Dismissing the strip hides it for the rest of the browser tab's session. Where the browser blocks
42
+ * storage, such as a sandboxed frame or site data turned off, even reading `sessionStorage` throws;
43
+ * the strip then shows on each load and a dismissal lasts until the page reloads.
44
+ */
45
+ const dismissedStorageKey = "ciridae-ui-environment-banner-dismissed";
46
+ function readDismissedForSession() {
47
+ try {
48
+ return sessionStorage.getItem(dismissedStorageKey) === "1";
49
+ }
50
+ catch (error) {
51
+ console.warn(`Showing the environment strip: could not read ${dismissedStorageKey}`, error);
52
+ return false;
53
+ }
54
+ }
55
+ function rememberDismissedForSession() {
56
+ try {
57
+ sessionStorage.setItem(dismissedStorageKey, "1");
58
+ }
59
+ catch (error) {
60
+ console.warn(`Could not save ${dismissedStorageKey}; the strip stays hidden until the page reloads`, error);
61
+ }
62
+ }
63
+ /** An environment strip, which the reader can dismiss for the session, and an inset AppShell viewport. */
39
64
  export function EnvironmentFrame({ label, tone = "info", children, className, ...props }) {
40
65
  const { labels } = useCiridaeLocale();
41
- return (_jsxs("div", { ...props, className: cx(frame, className), children: [label !== undefined && (_jsx(SiteBanner, { className: environmentBanner, tone: tone, children: typeof label === "string" ? labels.shell.environment(label) : label })), _jsx("div", { className: viewport, "data-under-banner": label !== undefined || undefined, children: children })] }));
66
+ const [dismissed, setDismissed] = useState(readDismissedForSession);
67
+ const shown = label !== undefined && !dismissed;
68
+ const dismiss = () => {
69
+ rememberDismissedForSession();
70
+ setDismissed(true);
71
+ };
72
+ return (_jsxs("div", { ...props, className: cx(frame, className), children: [shown && (_jsx(SiteBanner, { className: environmentBanner, tone: tone, onDismiss: dismiss, children: typeof label === "string" ? labels.shell.environment(label) : label })), _jsx("div", { className: viewport, "data-under-banner": shown || undefined, children: children })] }));
42
73
  }
@@ -6,4 +6,4 @@ import { type ReactNode } from "react";
6
6
  export declare function ExpandableText({ children, className, }: {
7
7
  children: ReactNode;
8
8
  className?: string;
9
- }): import("@types/react").JSX.Element;
9
+ }): import("react").JSX.Element;
@@ -16,4 +16,4 @@ export declare function FeedbackScreenshot({ src, pointers, onPointersChange, di
16
16
  pointers: readonly PlacedScreenshotPointer[];
17
17
  onPointersChange: (pointers: readonly PlacedScreenshotPointer[]) => void;
18
18
  disabled?: boolean;
19
- }): import("@types/react").JSX.Element;
19
+ }): import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { type FeedbackItem, type FeedbackSummary } from "../feedback/contract.js";
3
+ import { type FeedbackTriageStatus, type OperatorFeedbackItem, type OperatorFeedbackSummary } from "../feedback/operator-contract.js";
3
4
  import { type FeedbackScreenshotPointer } from "./feedback-screenshot.js";
4
5
  import { type CiridaeLabels } from "./locale.js";
5
6
  export type FeedbackKind = "wrong" | "idea" | "question";
@@ -42,7 +43,7 @@ export type FeedbackFormProps = {
42
43
  onClose: () => void;
43
44
  };
44
45
  /** The sheet's form, body and footer, for a `Drawer` that holds more than the form. */
45
- export declare function FeedbackForm({ where: about, screenshot, onSubmit, errorDetail, onClose, }: FeedbackFormProps): import("@types/react").JSX.Element;
46
+ export declare function FeedbackForm({ where: about, screenshot, onSubmit, errorDetail, onClose, }: FeedbackFormProps): import("react").JSX.Element;
46
47
  /** Something the caller loads for the sheet: in flight, failed with a way to try again, or here. */
47
48
  export type FeedbackLoadState<Data> = {
48
49
  status: "loading";
@@ -66,15 +67,29 @@ export type FeedbackSentProps = {
66
67
  /** Resolves once `item` is withdrawn. */
67
68
  onWithdraw: (feedbackId: string) => Promise<void>;
68
69
  };
70
+ /** The All tab, for the people who answer feedback: everyone's items, triaged and replied to. */
71
+ export type FeedbackOperatorProps = {
72
+ /** Every reporter's feedback, in any order; the tab lists the latest change first. */
73
+ items: FeedbackLoadState<readonly OperatorFeedbackSummary[]>;
74
+ /** Loads the item into `item`; the sheet shows it once the ids match. */
75
+ onOpenItem: (feedbackId: string) => void;
76
+ item?: FeedbackLoadState<OperatorFeedbackItem>;
77
+ /** Resolves once `item` carries the reply; rejecting keeps the reply in the composer. */
78
+ onReply: (feedbackId: string, body: string) => Promise<void>;
79
+ /** Resolves once `item` carries the status; withdrawing stays the reporter's. */
80
+ onStatusChange: (feedbackId: string, status: FeedbackTriageStatus) => Promise<void>;
81
+ };
69
82
  export type FeedbackSheetProps = Omit<FeedbackFormProps, "onClose"> & {
70
83
  open: boolean;
71
84
  onOpenChange: (open: boolean) => void;
72
85
  /** Controls in the sheet's header, before the close button. */
73
86
  actions?: ReactNode;
74
- /** The Sent tab; without it the sheet is the form alone. */
87
+ /** The person's own tickets; without it or `operator` the sheet is the form alone. */
75
88
  sent?: FeedbackSentProps;
89
+ /** For an operator, the Tickets tab lists everyone's, with a filter for their own (`sent`). */
90
+ operator?: FeedbackOperatorProps;
76
91
  /** Rewords the sheet's labels over the locale's. */
77
92
  labels?: Partial<FeedbackLabels>;
78
93
  };
79
94
  /** The floating feedback sheet. Closing it drops the draft and the open item. */
80
- export declare function FeedbackSheet({ labels, ...props }: FeedbackSheetProps): import("@types/react").JSX.Element;
95
+ export declare function FeedbackSheet({ labels, ...props }: FeedbackSheetProps): import("react").JSX.Element;