@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
@@ -3,23 +3,28 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  * The feedback sheet. New: what it is about, where it was sent from, what happened, whether to
4
4
  * attach a screenshot, with numbered pointers and notes on it, and files dropped on the form,
5
5
  * picked or pasted; the form owns its validation and the sending, sent and failed states.
6
- * Sent: everything the person filed, and each item's conversation with the Ciridae team as a
7
- * thread they can reply in, with the item's actions, such as withdrawing it, behind its menu.
8
- * The sheet is presentational: the caller owns the requests behind `onSubmit`, `items`, `item`,
9
- * `onReply` and `onWithdraw`.
6
+ * Tickets: everything the person filed, and each item's conversation with the Ciridae team as a
7
+ * thread they can reply in, with the item's actions, such as withdrawing it, behind its menu. For
8
+ * an operator it lists everyone's instead, filtered (their own among the filters) and searched,
9
+ * each item the same thread with a status to set. The sheet is presentational: the caller owns the requests behind
10
+ * `onSubmit`, `sent` and `operator`; `useFeedbackOperatorSheet` builds `operator` from the API.
10
11
  */
11
12
  import { ArrowClockwiseIcon, ArrowLeftIcon, ChatCircleTextIcon, DotsThreeIcon, SquaresFourIcon, } from "@phosphor-icons/react";
12
13
  import { createContext, useContext, useId, useState } from "react";
13
14
  import { css, cx } from "#primitives/css";
14
- import { feedbackAttachmentLimits, } from "../feedback/contract.js";
15
+ import { feedbackAttachmentLimits, feedbackStatusSchema, feedbackTypeSchema, } from "../feedback/contract.js";
16
+ import { feedbackTriageStatusSchema, } from "../feedback/operator-contract.js";
15
17
  import { Button, IconButton } from "./button.js";
16
18
  import { ChatComposer, ChatThread } from "./chat.js";
19
+ import { useCopy } from "./copyable-id.js";
17
20
  import { DateTime } from "./date-time.js";
18
21
  import { Drawer, DrawerBody, DrawerField, DrawerFooter, DrawerForm } from "./drawer.js";
19
22
  import { Dropzone, DropzoneFileList, DropzonePad } from "./dropzone.js";
20
23
  import { FeedbackScreenshot, } from "./feedback-screenshot.js";
24
+ import { FilterBar, matchesFilter } from "./filter-bar.js";
21
25
  import { Checkbox, Textarea } from "./form-controls.js";
22
26
  import { directional, icon, Spinner } from "./icon.js";
27
+ import { SearchInput } from "./input.js";
23
28
  import { Eyebrow } from "./layout.js";
24
29
  import { useCiridaeLocale } from "./locale.js";
25
30
  import { Menu } from "./menu.js";
@@ -205,6 +210,11 @@ const sentEmpty = css({
205
210
  textAlign: "center",
206
211
  });
207
212
  const padded = css({ padding: "panel.comfortable" });
213
+ const listToolbar = css({
214
+ paddingBlock: "panel.compact",
215
+ paddingInline: "panel.comfortable",
216
+ borderBottom: "1px solid token(colors.border.subtle)",
217
+ });
208
218
  /* An item: its subject and status pinned over the thread, which scrolls; the reply below. */
209
219
  const itemHeader = css({
210
220
  position: "sticky",
@@ -347,25 +357,39 @@ function FailedNotice({ title, children, action, }) {
347
357
  export function FeedbackSheet({ labels, ...props }) {
348
358
  return (_jsx(FeedbackLabelsContext, { value: labels, children: _jsx(FeedbackSheetContent, { ...props }) }));
349
359
  }
350
- function FeedbackSheetContent({ open, onOpenChange, actions, sent, errorDetail, ...formProps }) {
360
+ function FeedbackSheetContent({ open, onOpenChange, actions, sent, operator, errorDetail, ...formProps }) {
351
361
  const labels = useFeedbackLabels();
362
+ const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
352
363
  const [tab, setTab] = useState("new");
353
364
  const [openItemId, setOpenItemId] = useState(null);
354
- const showSent = tab === "sent" && sent !== undefined;
365
+ const showAll = tab === "tickets" && operator !== undefined;
366
+ const showSent = tab === "tickets" && !showAll && sent !== undefined;
355
367
  const close = () => {
356
368
  onOpenChange(false);
357
369
  setOpenItemId(null);
358
370
  };
359
- return (_jsx(Drawer, { open: open, onOpenChange: (next) => (next ? onOpenChange(true) : close()), title: showSent ? labels.sentTitle : labels.sendTitle, icon: _jsx(ChatCircleTextIcon, { className: icon({ size: "small" }), weight: "fill" }), actions: _jsxs(_Fragment, { children: [sent && (_jsx(TabsRoot, { value: tab, onValueChange: (value) => {
371
+ const back = () => setOpenItemId(null);
372
+ return (_jsx(Drawer, { open: open, onOpenChange: (next) => (next ? onOpenChange(true) : close()), title: showAll ? operatorLabels.allTitle : showSent ? labels.sentTitle : labels.sendTitle, icon: _jsx(ChatCircleTextIcon, { className: icon({ size: "small" }), weight: "fill" }), actions: _jsxs(_Fragment, { children: [(sent || operator) && (_jsx(TabsRoot, { value: tab, onValueChange: (value) => {
360
373
  setTab(value);
361
374
  setOpenItemId(null);
362
- }, children: _jsxs(TabsList, { variant: "contained", children: [_jsx(TabsTab, { value: "new", children: labels.newTab }), _jsx(TabsTab, { value: "sent", children: labels.sentTab })] }) })), actions] }), children: !showSent ? (_jsx(FeedbackForm, { ...formProps, errorDetail: errorDetail, onClose: close })) : openItemId === null ? (_jsx(SentList, { items: sent.items, errorDetail: errorDetail, onOpen: (feedbackId) => {
375
+ }, children: _jsxs(TabsList, { variant: "contained", children: [_jsx(TabsTab, { value: "new", children: labels.newTab }), _jsx(TabsTab, { value: "tickets", children: labels.ticketsTab })] }) })), actions] }), children: showAll ? (openItemId === null ? (_jsx(OperatorList, { operator: operator, ownIds: sent?.items.status === "loaded"
376
+ ? new Set(sent.items.data.map((item) => item.id))
377
+ : undefined, errorDetail: errorDetail, onOpen: (feedbackId) => {
378
+ operator.onOpenItem(feedbackId);
379
+ setOpenItemId(feedbackId);
380
+ } })) : (_jsx(OperatorItem, { feedbackId: openItemId, operator: operator, errorDetail: errorDetail, onBack: back }, openItemId))) : !showSent ? (_jsx(FeedbackForm, { ...formProps, errorDetail: errorDetail, onClose: close })) : openItemId === null ? (_jsx(FeedbackList, { items: sent.items, rows: (items) => items.map((item) => ({
381
+ id: item.id,
382
+ subject: item.subject,
383
+ status: item.status,
384
+ meta: (_jsxs(_Fragment, { children: [typeLabel(item.type, labels), " \u00B7", " ", _jsx(DateTime, { value: item.updatedAt_epoch_milliseconds })] })),
385
+ })), empty: labels.noneSent, errorDetail: errorDetail, onOpen: (feedbackId) => {
363
386
  sent.onOpenItem(feedbackId);
364
387
  setOpenItemId(feedbackId);
365
- } })) : (_jsx(SentItem, { feedbackId: openItemId, item: sent.item, errorDetail: errorDetail, onReply: sent.onReply, onWithdraw: sent.onWithdraw, onBack: () => setOpenItemId(null) }, openItemId)) }));
388
+ } })) : (_jsx(SentItem, { feedbackId: openItemId, item: sent.item, errorDetail: errorDetail, onReply: sent.onReply, onWithdraw: sent.onWithdraw, onBack: back }, openItemId)) }));
366
389
  }
367
390
  const placeholderWidths = ["58%", "42%", "50%"];
368
- function SentList({ items, errorDetail, onOpen, }) {
391
+ /** A list of feedback rows: loading, failed, empty, or each row's subject, meta and status. */
392
+ function FeedbackList({ items, rows, empty, toolbar, errorDetail, onOpen, }) {
369
393
  const labels = useFeedbackLabels();
370
394
  const { common } = useCiridaeLocale().labels;
371
395
  if (items.status === "error") {
@@ -374,13 +398,68 @@ function SentList({ items, errorDetail, onOpen, }) {
374
398
  if (items.status === "loading") {
375
399
  return (_jsx(DrawerBody, { flush: true, children: _jsx("ul", { className: sentList, "aria-busy": "true", children: placeholderWidths.map((width) => (_jsxs("li", { className: sentRow, children: [_jsxs("span", { className: sentRowCopy, children: [_jsx(Skeleton, { shape: "text", style: { width } }), _jsx(Skeleton, { shape: "text", style: { width: "30%" } })] }), _jsx(Skeleton, { shape: "text", style: { width: 64 } })] }, width))) }) }));
376
400
  }
377
- if (items.data.length === 0) {
378
- return (_jsx(DrawerBody, { flush: true, children: _jsx("p", { className: sentEmpty, children: labels.noneSent }) }));
379
- }
380
- return (_jsx(DrawerBody, { flush: true, children: _jsx("ul", { className: sentList, children: items.data.map((item) => {
381
- const needsReply = item.status === "discussion_required";
382
- return (_jsx("li", { children: _jsxs("button", { type: "button", className: sentRow, "data-needs-reply": needsReply ? "" : undefined, onClick: () => onOpen(item.id), children: [_jsxs("span", { className: sentRowCopy, children: [_jsxs("span", { className: sentRowSubject, children: [needsReply && _jsx("span", { className: needsReplyDot, "aria-hidden": "true" }), _jsx("span", { children: item.subject })] }), _jsxs("span", { className: sentRowMeta, children: [typeLabel(item.type, labels), " \u00B7", " ", _jsx(DateTime, { value: item.updatedAt_epoch_milliseconds })] })] }), _jsx(Status, { variant: statusDisplay(item.status, labels).variant, children: statusDisplay(item.status, labels).label })] }) }, item.id));
383
- }) }) }));
401
+ const shown = rows(items.data);
402
+ return (_jsxs(DrawerBody, { flush: true, children: [toolbar, shown.length === 0 ? (_jsx("p", { className: sentEmpty, children: empty })) : (_jsx("ul", { className: sentList, children: shown.map((row) => {
403
+ const needsReply = row.status === "discussion_required";
404
+ const display = statusDisplay(row.status, labels);
405
+ return (_jsx("li", { children: _jsxs("button", { type: "button", className: sentRow, "data-needs-reply": needsReply ? "" : undefined, onClick: () => onOpen(row.id), children: [_jsxs("span", { className: sentRowCopy, children: [_jsxs("span", { className: sentRowSubject, children: [needsReply && _jsx("span", { className: needsReplyDot, "aria-hidden": "true" }), _jsx("span", { children: row.subject })] }), _jsx("span", { className: sentRowMeta, children: row.meta })] }), _jsx(Status, { variant: display.variant, children: display.label })] }) }, row.id));
406
+ }) }))] }));
407
+ }
408
+ /** Everyone's feedback, latest change first, narrowed by status, type and a search. */
409
+ function OperatorList({ operator, ownIds, errorDetail, onOpen, }) {
410
+ const labels = useFeedbackLabels();
411
+ const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
412
+ const [filters, setFilters] = useState([]);
413
+ const [search, setSearch] = useState("");
414
+ const query = search.trim().toLowerCase();
415
+ const fields = [
416
+ ...(ownIds
417
+ ? [
418
+ {
419
+ key: "reporter",
420
+ label: operatorLabels.reporter,
421
+ kind: "multi",
422
+ options: [{ value: "mine", label: operatorLabels.mine }],
423
+ },
424
+ ]
425
+ : []),
426
+ {
427
+ key: "status",
428
+ label: operatorLabels.status,
429
+ kind: "multi",
430
+ options: feedbackStatusSchema.options.map((value) => ({
431
+ value,
432
+ label: statusDisplay(value, labels).label,
433
+ })),
434
+ },
435
+ {
436
+ key: "type",
437
+ label: operatorLabels.type,
438
+ kind: "multi",
439
+ options: feedbackTypeSchema.options.map((value) => ({
440
+ value,
441
+ label: typeLabel(value, labels),
442
+ })),
443
+ },
444
+ ];
445
+ return (_jsx(FeedbackList, { items: operator.items, rows: (items) => items
446
+ .filter((item) => filters.every((filter) => matchesFilter(filter.field === "reporter"
447
+ ? ownIds?.has(item.id)
448
+ ? "mine"
449
+ : "others"
450
+ : filter.field === "status"
451
+ ? item.status
452
+ : item.type, filter)) &&
453
+ (query === "" ||
454
+ [item.subject, item.description, item.reporterEmail].some((value) => value?.toLowerCase().includes(query))))
455
+ // ISO timestamps in one format sort as text.
456
+ .sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))
457
+ .map((item) => ({
458
+ id: item.id,
459
+ subject: item.subject,
460
+ status: item.status,
461
+ meta: (_jsxs(_Fragment, { children: [item.reporterEmail ?? operatorLabels.reporter, " \u00B7 ", typeLabel(item.type, labels), " \u00B7", " ", _jsx(DateTime, { value: item.updatedAt })] })),
462
+ })), empty: filters.length > 0 || query !== "" ? operatorLabels.noMatches : operatorLabels.noneReceived, toolbar: _jsx(FilterBar, { className: listToolbar, fields: fields, value: filters, onValueChange: setFilters, children: _jsx(SearchInput, { variant: "ghost", "aria-label": operatorLabels.search, placeholder: operatorLabels.search, value: search, onValueCommit: setSearch }) }), errorDetail: errorDetail, onOpen: onOpen }));
384
463
  }
385
464
  /* What the thread shows in time order: the description, each status change, each message. */
386
465
  function listThreadEntries(item, labels) {
@@ -413,37 +492,60 @@ function listThreadEntries(item, labels) {
413
492
  ];
414
493
  return entries.sort((earlier, later) => earlier.at - later.at);
415
494
  }
416
- function SentItem({ feedbackId, item, errorDetail, onReply, onWithdraw, onBack, }) {
495
+ /**
496
+ * The operator's side of the same thread: the reporter's description and messages across from
497
+ * them, their own team's replies on their side.
498
+ */
499
+ function listOperatorThreadEntries(item, reporter) {
500
+ const entries = [
501
+ {
502
+ kind: "message",
503
+ id: "description",
504
+ side: "assistant",
505
+ author: { name: reporter },
506
+ at: Date.parse(item.createdAt),
507
+ body: item.description,
508
+ attachments: item.attachments,
509
+ },
510
+ ...item.messages.map((message) => {
511
+ const fromReporter = message.author.role === "reporter";
512
+ return {
513
+ kind: "message",
514
+ id: message.id,
515
+ side: fromReporter ? "assistant" : "user",
516
+ author: fromReporter ? { name: message.author.name } : undefined,
517
+ at: message.createdAt_epoch_milliseconds,
518
+ body: message.body,
519
+ };
520
+ }),
521
+ ];
522
+ return entries.sort((earlier, later) => earlier.at - later.at);
523
+ }
524
+ /** Runs `action`, reporting it pending, then idle, or failed with its error. */
525
+ function usePendingAction(action) {
526
+ const [state, setState] = useState({ status: "idle" });
527
+ const run = async (input) => {
528
+ setState({ status: "pending" });
529
+ try {
530
+ await action(input);
531
+ setState({ status: "idle" });
532
+ }
533
+ catch (error) {
534
+ setState({ status: "failed", error });
535
+ }
536
+ };
537
+ return { state, run: (input) => void run(input) };
538
+ }
539
+ /**
540
+ * One item as a thread: its header pinned over the conversation, then the failures and the reply
541
+ * below. Loading and failing to load keep the back button.
542
+ */
543
+ function FeedbackThreadItem({ feedbackId, item, loaded, headerEnd, notices, replyDisabled, errorDetail, onReply, onBack, }) {
417
544
  const labels = useFeedbackLabels();
418
545
  const { common } = useCiridaeLocale().labels;
419
546
  const [draft, setDraft] = useState("");
420
547
  const [reply, setReply] = useState({ status: "idle" });
421
- const [withdraw, setWithdraw] = useState({ status: "idle" });
422
- // The item the caller last opened is this one only once its load carries this id.
423
- const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
424
- const entries = loaded ? listThreadEntries(loaded, labels) : [];
425
- if (reply.status === "sending") {
426
- entries.push({
427
- kind: "message",
428
- id: "sending",
429
- side: "user",
430
- at: reply.at,
431
- body: reply.body,
432
- sending: true,
433
- });
434
- }
435
548
  const back = (_jsx(IconButton, { "aria-label": labels.back, variant: "ghost", size: "small", onClick: onBack, children: _jsx(ArrowLeftIcon, { className: cx(icon({ size: "small" }), directional) }) }));
436
- const actions = [
437
- { label: labels.copyId, onSelect: () => void navigator.clipboard.writeText(feedbackId) },
438
- ];
439
- if (loaded?.canWithdraw) {
440
- actions.push({
441
- label: labels.withdraw,
442
- variant: "danger",
443
- disabled: reply.status === "sending",
444
- onSelect: () => void sendWithdraw(),
445
- });
446
- }
447
549
  const sendReply = async (body) => {
448
550
  setReply({ status: "sending", body, at: Date.now() });
449
551
  try {
@@ -455,21 +557,80 @@ function SentItem({ feedbackId, item, errorDetail, onReply, onWithdraw, onBack,
455
557
  setReply({ status: "failed", error });
456
558
  }
457
559
  };
458
- const sendWithdraw = async () => {
459
- setWithdraw({ status: "pending" });
460
- try {
461
- await onWithdraw(feedbackId);
462
- setWithdraw({ status: "idle" });
463
- }
464
- catch (error) {
465
- setWithdraw({ status: "failed", error });
466
- }
467
- };
468
560
  if (item?.status === "error") {
469
561
  return (_jsxs(DrawerBody, { flush: true, children: [_jsx("div", { className: itemHeader, children: back }), _jsx("div", { className: padded, children: _jsx(FailedNotice, { title: labels.loadItemFailed, action: _jsx("button", { type: "button", onClick: item.retry, children: common.tryAgain }), children: errorDetail?.(item.error) }) })] }));
470
562
  }
471
563
  if (!loaded) {
472
564
  return (_jsxs(DrawerBody, { flush: true, "aria-busy": "true", children: [_jsxs("div", { className: itemHeader, children: [back, _jsxs("span", { className: itemTitle, children: [_jsx(Skeleton, { shape: "text", style: { width: "60%" } }), _jsx(Skeleton, { shape: "text", style: { width: "30%" } })] })] }), _jsx("div", { className: padded, children: _jsxs("span", { className: placeholderLines, children: [_jsx(Skeleton, { shape: "text" }), _jsx(Skeleton, { shape: "text", style: { width: "80%" } }), _jsx(Skeleton, { shape: "text", style: { width: "50%" } })] }) })] }));
473
565
  }
474
- return (_jsxs(_Fragment, { children: [_jsxs(DrawerBody, { flush: true, children: [_jsxs("div", { className: itemHeader, children: [back, _jsxs("span", { className: itemTitle, children: [_jsx("span", { className: itemSubject, children: loaded.subject }), _jsxs("span", { className: sentRowMeta, children: [typeLabel(loaded.type, labels), " \u00B7", " ", _jsx(DateTime, { value: loaded.createdAt_epoch_milliseconds })] })] }), _jsx(Status, { className: itemStatus, variant: statusDisplay(loaded.status, labels).variant, children: statusDisplay(loaded.status, labels).label }), _jsx(Menu, { align: "end", items: actions, trigger: _jsx(IconButton, { "aria-label": labels.actions, variant: "ghost", size: "small", disabled: withdraw.status === "pending", children: withdraw.status === "pending" ? (_jsx(Spinner, { label: labels.withdrawing })) : (_jsx(DotsThreeIcon, { className: icon({ size: "small" }) })) }) })] }), _jsx(ChatThread, { entries: entries })] }), (loaded.canReply || reply.status === "failed" || withdraw.status === "failed") && (_jsxs("div", { className: itemFoot, children: [reply.status === "failed" && (_jsx(FailedNotice, { title: labels.replyFailed, children: errorDetail?.(reply.error) })), withdraw.status === "failed" && (_jsx(FailedNotice, { title: labels.withdrawFailed, children: errorDetail?.(withdraw.error) })), loaded.canReply && (_jsx(ChatComposer, { variant: "sheet", placeholder: labels.reply, ariaLabel: labels.reply, submitLabel: labels.sendReply, value: draft, onValueChange: setDraft, sendDisabled: reply.status === "sending", onSend: (body) => void sendReply(body.trim()) }))] }))] }));
566
+ const entries = reply.status === "sending"
567
+ ? [
568
+ ...loaded.entries,
569
+ {
570
+ kind: "message",
571
+ id: "sending",
572
+ side: "user",
573
+ at: reply.at,
574
+ body: reply.body,
575
+ sending: true,
576
+ },
577
+ ]
578
+ : loaded.entries;
579
+ return (_jsxs(_Fragment, { children: [_jsxs(DrawerBody, { flush: true, children: [_jsxs("div", { className: itemHeader, children: [back, _jsxs("span", { className: itemTitle, children: [_jsx("span", { className: itemSubject, children: loaded.subject }), _jsx("span", { className: sentRowMeta, children: loaded.meta })] }), headerEnd] }), _jsx(ChatThread, { entries: entries })] }), (loaded.canReply || reply.status === "failed" || notices) && (_jsxs("div", { className: itemFoot, children: [reply.status === "failed" && (_jsx(FailedNotice, { title: labels.replyFailed, children: errorDetail?.(reply.error) })), notices, loaded.canReply && (_jsx(ChatComposer, { variant: "sheet", placeholder: labels.reply, ariaLabel: labels.reply, submitLabel: labels.sendReply, value: draft, onValueChange: setDraft, sendDisabled: replyDisabled || reply.status === "sending", onSend: (body) => void sendReply(body.trim()) }))] }))] }));
580
+ }
581
+ function SentItem({ feedbackId, item, errorDetail, onReply, onWithdraw, onBack, }) {
582
+ const labels = useFeedbackLabels();
583
+ const withdraw = usePendingAction(() => onWithdraw(feedbackId));
584
+ const copyId = useCopy(feedbackId);
585
+ // The item the caller last opened is this one only once its load carries this id.
586
+ const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
587
+ const actions = [{ label: labels.copyId, onSelect: copyId.copy }];
588
+ if (loaded?.canWithdraw) {
589
+ actions.push({
590
+ label: labels.withdraw,
591
+ variant: "danger",
592
+ onSelect: () => withdraw.run(undefined),
593
+ });
594
+ }
595
+ const display = loaded && statusDisplay(loaded.status, labels);
596
+ return (_jsx(FeedbackThreadItem, { feedbackId: feedbackId, item: item, loaded: loaded && {
597
+ subject: loaded.subject,
598
+ meta: (_jsxs(_Fragment, { children: [typeLabel(loaded.type, labels), " \u00B7", " ", _jsx(DateTime, { value: loaded.createdAt_epoch_milliseconds })] })),
599
+ entries: listThreadEntries(loaded, labels),
600
+ canReply: loaded.canReply,
601
+ }, headerEnd: _jsxs(_Fragment, { children: [display && (_jsx(Status, { className: itemStatus, variant: display.variant, children: display.label })), _jsx(Menu, { align: "end", items: actions, trigger: _jsx(IconButton, { "aria-label": labels.actions, variant: "ghost", size: "small", disabled: withdraw.state.status === "pending", children: withdraw.state.status === "pending" ? (_jsx(Spinner, { label: labels.withdrawing })) : (_jsx(DotsThreeIcon, { className: icon({ size: "small" }) })) }) })] }), notices: withdraw.state.status === "failed" && (_jsx(FailedNotice, { title: labels.withdrawFailed, children: errorDetail?.(withdraw.state.error) })), replyDisabled: withdraw.state.status === "pending", errorDetail: errorDetail, onReply: onReply, onBack: onBack }));
602
+ }
603
+ /** An item as its operator sees it: the reporter's thread, a status to set, and a reply. */
604
+ function OperatorItem({ feedbackId, operator, errorDetail, onBack, }) {
605
+ const labels = useFeedbackLabels();
606
+ const operatorLabels = useCiridaeLocale().labels.feedbackOperator;
607
+ const [pendingStatus, setPendingStatus] = useState(null);
608
+ const statusChange = usePendingAction(async (status) => {
609
+ setPendingStatus(status);
610
+ try {
611
+ await operator.onStatusChange(feedbackId, status);
612
+ }
613
+ finally {
614
+ setPendingStatus(null);
615
+ }
616
+ });
617
+ const { item } = operator;
618
+ const loaded = item?.status === "loaded" && item.data.id === feedbackId ? item.data : null;
619
+ const status = pendingStatus ?? loaded?.status;
620
+ const reporter = loaded?.reporterEmail ?? operatorLabels.reporter;
621
+ const statusOptions = [
622
+ // A withdrawn item is the reporter's call; it shows, but cannot be chosen.
623
+ ...(status === "withdrawn" ? ["withdrawn"] : []),
624
+ ...feedbackTriageStatusSchema.options,
625
+ ].map((value) => ({ value, label: statusDisplay(value, labels).label }));
626
+ return (_jsx(FeedbackThreadItem, { feedbackId: feedbackId, item: item, loaded: loaded && {
627
+ subject: loaded.subject,
628
+ meta: (_jsxs(_Fragment, { children: [reporter, " \u00B7 ", typeLabel(loaded.type, labels), " \u00B7 ", _jsx(DateTime, { value: loaded.createdAt })] })),
629
+ entries: listOperatorThreadEntries(loaded, reporter),
630
+ canReply: loaded.canReply,
631
+ }, headerEnd: _jsx(Select, { label: operatorLabels.status, options: statusOptions, value: status, disabled: status === "withdrawn" || statusChange.state.status === "pending", onValueChange: (value) => {
632
+ const next = feedbackTriageStatusSchema.safeParse(value);
633
+ if (next.success && next.data !== status)
634
+ statusChange.run(next.data);
635
+ } }), notices: statusChange.state.status === "failed" && (_jsx(FailedNotice, { title: operatorLabels.statusFailed, children: errorDetail?.(statusChange.state.error) })), errorDetail: errorDetail, onReply: operator.onReply, onBack: onBack }));
475
636
  }
@@ -8,7 +8,7 @@ export type FieldControl = {
8
8
  descriptionId?: string;
9
9
  invalid: boolean;
10
10
  };
11
- export declare const FieldControlContext: import("@types/react").Context<FieldControl | null>;
11
+ export declare const FieldControlContext: import("react").Context<FieldControl | null>;
12
12
  /** The ids and state a control takes from its field; its own props win. */
13
13
  export declare function useFieldControl(own: {
14
14
  id?: string;
@@ -49,7 +49,7 @@ export declare function FilterChip({ label, icon: fieldIcon, operator, values, o
49
49
  values: ReactNode;
50
50
  onEdit?: () => void;
51
51
  onRemove: () => void;
52
- }): import("@types/react").JSX.Element;
52
+ }): import("react").JSX.Element;
53
53
  /**
54
54
  * The set filters as chips, Clear all, and the Add filter builder: pick a field, then its operator
55
55
  * and values, which apply as they are picked. Pressing a chip reopens the builder on its field.
@@ -63,7 +63,7 @@ export declare function FilterBar({ fields, value, defaultValue, onValueChange,
63
63
  onValueChange?: (value: FilterValue[]) => void;
64
64
  open?: boolean;
65
65
  onOpenChange?: (open: boolean) => void;
66
- }): import("@types/react").JSX.Element;
66
+ }): import("react").JSX.Element;
67
67
  /**
68
68
  * One field's operator and values, reporting every change so the list follows along. `onBack`
69
69
  * adds the way back to the builder's field list; without it the field is fixed, as under a
@@ -74,4 +74,4 @@ export declare function FilterValueEditor({ field, value, onChange, onBack, }: {
74
74
  value: FilterValue | undefined;
75
75
  onChange: (filter: FilterValue) => void;
76
76
  onBack?: () => void;
77
- }): import("@types/react").JSX.Element;
77
+ }): import("react").JSX.Element;
@@ -7,14 +7,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  * alone, such as a column head's "Filter by". Nothing here filters rows: `matchesFilter` tests one
8
8
  * value for a list that filters on the client.
9
9
  */
10
- import { CaretDownIcon, CaretLeftIcon, PlusIcon, XIcon } from "@phosphor-icons/react";
10
+ import { CaretDownIcon, CaretLeftIcon, CaretRightIcon, PlusIcon, XIcon, } from "@phosphor-icons/react";
11
11
  import { useState } from "react";
12
12
  import { css, cx } from "#primitives/css";
13
13
  import { Button, IconButton } from "./button.js";
14
14
  import { CheckboxItem } from "./checkbox-item.js";
15
15
  import { useControllableState } from "./controllable-state.js";
16
- import { Divider } from "./data-display.js";
17
- import { icon } from "./icon.js";
16
+ import { glassMenuDivider, glassMenuRow } from "./glass-menu.js";
17
+ import { faceEdge } from "./gradient-edge.js";
18
+ import { directional, icon } from "./icon.js";
18
19
  import { Input, SearchField } from "./input.js";
19
20
  import { useCiridaeLocale } from "./locale.js";
20
21
  import { Menu } from "./menu.js";
@@ -90,6 +91,10 @@ export function describeFilter(field, filter, locale) {
90
91
  : values.join(", "),
91
92
  };
92
93
  }
94
+ /*
95
+ * The slicer's face: the selected tint, hovering to the raised face. The field reads in the
96
+ * secondary grey, the operator a step quieter.
97
+ */
93
98
  const chip = css({
94
99
  display: "inline-flex",
95
100
  alignItems: "center",
@@ -99,7 +104,9 @@ const chip = css({
99
104
  paddingInlineStart: "control.compactInline",
100
105
  paddingInlineEnd: "control.iconGap",
101
106
  borderRadius: "pill",
102
- background: "bg.raised",
107
+ background: "token(gradients.selected)",
108
+ _hover: { background: "token(gradients.raisedHover)" },
109
+ _light: { color: "fg.secondary" },
103
110
  color: "fg.muted",
104
111
  fontSize: "caption",
105
112
  fontWeight: "medium",
@@ -122,6 +129,7 @@ const chipBody = css({
122
129
  _focusVisible: { focusRing: "outer" },
123
130
  "& svg": { flexShrink: "0" },
124
131
  });
132
+ const chipOperator = css({ color: "fg.muted" });
125
133
  const chipValue = css({ color: "fg.primary", overflow: "hidden", textOverflow: "ellipsis" });
126
134
  const chipRemove = css({
127
135
  display: "inline-grid",
@@ -138,10 +146,11 @@ const chipRemove = css({
138
146
  _hover: { color: "fg.primary", background: "bg.hover" },
139
147
  _focusVisible: { focusRing: "outer" },
140
148
  });
149
+ const toolbarEdge = faceEdge("toolbar");
141
150
  /** One set filter: the field and operator muted, its values in full; the text reopens it. */
142
151
  export function FilterChip({ label, icon: fieldIcon, operator, values, onEdit, onRemove, className, ...props }) {
143
152
  const { labels } = useCiridaeLocale();
144
- return (_jsxs("span", { ...props, className: cx(chip, className), "data-ciridae-ui": "", "data-edge": "glass", children: [_jsxs("button", { type: "button", className: chipBody, onClick: onEdit, children: [fieldIcon, label, " ", operator, _jsx("span", { className: chipValue, children: values })] }), _jsx("button", { type: "button", className: chipRemove, "aria-label": labels.filters.remove(label), onClick: onRemove, children: _jsx(XIcon, { className: icon({ size: "small" }) }) })] }));
153
+ return (_jsxs("span", { ...props, className: cx(chip, toolbarEdge.className, className), "data-ciridae-ui": "", "data-edge": toolbarEdge["data-edge"], children: [_jsxs("button", { type: "button", className: chipBody, onClick: onEdit, children: [fieldIcon, label, " ", _jsx("span", { className: chipOperator, children: operator }), _jsx("span", { className: chipValue, children: values })] }), _jsx("button", { type: "button", className: chipRemove, "aria-label": labels.filters.remove(label), onClick: onRemove, children: _jsx(XIcon, { className: icon({ size: "small" }) }) })] }));
145
154
  }
146
155
  const bar = css({
147
156
  display: "flex",
@@ -158,37 +167,41 @@ const chips = css({
158
167
  gap: "layout.controlGap",
159
168
  minWidth: "0",
160
169
  });
170
+ // One line: the buttons keep their size and the search after them takes what the row leaves.
161
171
  const tools = css({
162
172
  display: "flex",
173
+ flex: "1",
163
174
  alignItems: "center",
164
175
  justifyContent: "flex-end",
165
- flexWrap: "wrap",
166
176
  gap: "layout.controlGap",
167
177
  minWidth: "0",
168
178
  marginInlineStart: "auto",
179
+ "& > button": { flexShrink: "0" },
180
+ "& > :not(button)": { minWidth: "0" },
169
181
  });
170
- const builder = css({ display: "flex", flexDirection: "column", width: "300px", maxWidth: "100%" });
171
- const fieldItem = css({
182
+ const builder = css({
172
183
  display: "flex",
173
- alignItems: "center",
174
- gap: "layout.controlGap",
184
+ flexDirection: "column",
185
+ width: "overlay.popover",
186
+ maxWidth: "100%",
187
+ });
188
+ // A menu row: the field's icon, its name, and an arrow on to its operator and values.
189
+ const fieldItem = css(glassMenuRow, {
175
190
  width: "100%",
176
- minHeight: "control.compact",
177
- paddingInline: "menu.itemInline",
178
191
  border: "0",
179
- borderRadius: "control",
180
192
  background: "transparent",
181
- color: "fg.primary",
182
- font: "inherit",
183
- fontSize: "body",
184
- fontWeight: "medium",
185
- letterSpacing: "compact",
193
+ fontFamily: "inherit",
186
194
  textAlign: "start",
187
- cursor: "pointer",
188
- _hover: { background: "bg.hover" },
189
- _focusVisible: { background: "bg.hover", outline: "none" },
190
- "& svg": { color: "fg.muted", width: "icon.small", height: "icon.small" },
195
+ "& > span": { flex: "1", minWidth: "0" },
196
+ "& svg": {
197
+ flexShrink: "0",
198
+ color: "fg.muted",
199
+ fontSize: "icon.small",
200
+ width: "1em",
201
+ height: "1em",
202
+ },
191
203
  });
204
+ const menuDivider = css(glassMenuDivider);
192
205
  const groupLabel = css({
193
206
  paddingBlockStart: "control.block",
194
207
  paddingBlockEnd: "layout.tightGap",
@@ -198,7 +211,8 @@ const groupLabel = css({
198
211
  fontWeight: "medium",
199
212
  letterSpacing: "compact",
200
213
  });
201
- const searchWrap = css({ padding: "layout.tightGap" });
214
+ // The quiet search's hairline is inset as the dividers are.
215
+ const searchWrap = css({ paddingInline: "menu.itemInline", paddingBlockEnd: "layout.tightGap" });
202
216
  const noMatches = css({
203
217
  paddingBlock: "control.block",
204
218
  paddingInline: "menu.itemInline",
@@ -232,7 +246,7 @@ export function FilterBar({ fields, value, defaultValue = [], onValueChange, ope
232
246
  setEditing(field.key);
233
247
  setOpen(true);
234
248
  }, onRemove: () => setFilters(filters.filter((candidate) => candidate.field !== filter.field)) }, filter.field));
235
- }) })), _jsxs("div", { className: tools, children: [filters.length > 0 && (_jsx(Button, { variant: "ghost", onClick: () => setFilters([]), children: labels.filters.clearAll })), _jsx(Popover, { open: open, onOpenChange: changeOpen, density: "compact", trigger: _jsx(Button, { variant: "ghost", icon: _jsx(PlusIcon, { className: icon({ size: "small" }) }), onClick: () => setEditing(null), children: labels.filters.addFilter }), children: open && (_jsx(FilterBuilder, { fields: fields, startField: editing, filters: filters, onChange: (filter) => setFilters(replaceFilter(filters, filter)) }, editing ?? "")) }), children] })] }));
249
+ }) })), _jsxs("div", { className: tools, children: [filters.length > 0 && (_jsx(Button, { variant: "ghost", onClick: () => setFilters([]), children: labels.filters.clearAll })), _jsx(Popover, { open: open, onOpenChange: changeOpen, density: "compact", trigger: _jsx(Button, { variant: "secondary", size: "medium", icon: _jsx(PlusIcon, { className: icon({ size: "small" }) }), onClick: () => setEditing(null), children: labels.filters.addFilter }), children: open && (_jsx(FilterBuilder, { fields: fields, startField: editing, filters: filters, onChange: (filter) => setFilters(replaceFilter(filters, filter)) }, editing ?? "")) }), children] })] }));
236
250
  }
237
251
  function FilterBuilder({ fields, startField, filters, onChange, }) {
238
252
  const { labels } = useCiridaeLocale();
@@ -251,10 +265,10 @@ function FilterBuilder({ fields, startField, filters, onChange, }) {
251
265
  else
252
266
  groups.push({ label: candidate.group, fields: [candidate] });
253
267
  }
254
- return (_jsxs("div", { className: builder, children: [_jsx("div", { className: searchWrap, children: _jsx(SearchField, { size: "compact", "aria-label": labels.filters.filterBy, placeholder: labels.filters.filterBy, value: query, onChange: (event) => setQuery(event.target.value) }) }), groups.map((group, index) => (_jsxs("div", { className: css({ display: "grid" }), children: [index > 0 && _jsx(Divider, { inset: "menu" }), group.label && _jsx("span", { className: groupLabel, children: group.label }), group.fields.map((candidate) => (_jsxs("button", { type: "button", className: fieldItem, onClick: () => {
268
+ return (_jsxs("div", { className: builder, children: [_jsx("div", { className: searchWrap, children: _jsx(SearchField, { size: "compact", quiet: true, "aria-label": labels.filters.filterBy, placeholder: labels.filters.filterBy, value: query, onChange: (event) => setQuery(event.target.value) }) }), groups.map((group, index) => (_jsxs("div", { className: css({ display: "grid" }), children: [index > 0 && _jsx("div", { className: menuDivider, role: "separator" }), group.label && _jsx("span", { className: groupLabel, children: group.label }), group.fields.map((candidate) => (_jsxs("button", { type: "button", className: fieldItem, onClick: () => {
255
269
  setFieldKey(candidate.key);
256
270
  setQuery("");
257
- }, children: [candidate.icon, candidate.label] }, candidate.key)))] }, group.label ?? ""))), matching.length === 0 && _jsx("span", { className: noMatches, children: labels.filters.noFields })] }));
271
+ }, children: [candidate.icon, _jsx("span", { children: candidate.label }), _jsx(CaretRightIcon, { className: directional, "aria-hidden": "true" })] }, candidate.key)))] }, group.label ?? ""))), matching.length === 0 && _jsx("span", { className: noMatches, children: labels.filters.noFields })] }));
258
272
  }
259
273
  const editorHeader = css({
260
274
  display: "flex",
@@ -326,13 +340,13 @@ export function FilterValueEditor({ field, value, onChange, onBack, }) {
326
340
  const keepsValues = multiValueOperators.includes(candidate) === multiple && candidate !== "between";
327
341
  report(candidate, keepsValues ? values : values.slice(0, 1));
328
342
  },
329
- })) })) : (_jsx("span", { className: fixedOperator, children: operatorLabel }))] }), options ? (_jsxs(_Fragment, { children: [field.options && field.options.length > 6 && (_jsx("div", { className: searchWrap, children: _jsx(SearchField, { size: "compact", "aria-label": labels.filters.searchValues(field.label), placeholder: labels.filters.searchValues(field.label), value: query, onChange: (event) => setQuery(event.target.value) }) })), _jsxs("div", { className: valueList, children: [options.map((option) => (_jsxs(CheckboxItem, { checked: values.includes(option.value), onCheckedChange: (checked) => report(operator, multiple
343
+ })) })) : (_jsx("span", { className: fixedOperator, children: operatorLabel }))] }), options ? (_jsxs(_Fragment, { children: [field.options && field.options.length > 6 && (_jsx("div", { className: searchWrap, children: _jsx(SearchField, { size: "compact", quiet: true, "aria-label": labels.filters.searchValues(field.label), placeholder: labels.filters.searchValues(field.label), value: query, onChange: (event) => setQuery(event.target.value) }) })), _jsxs("div", { className: valueList, children: [options.map((option) => (_jsxs(CheckboxItem, { checked: values.includes(option.value), onCheckedChange: (checked) => report(operator, multiple
330
344
  ? checked
331
345
  ? [...values, option.value]
332
346
  : values.filter((candidate) => candidate !== option.value)
333
347
  : checked
334
348
  ? [option.value]
335
- : []), meta: option.count, children: [option.icon, option.label] }, option.value))), options.length === 0 && _jsx("span", { className: noMatches, children: labels.filters.noValues })] })] })) : (_jsx("div", { className: inputs, children: (operator === "between" ? [0, 1] : [0]).map((index) => (_jsx(Input, { type: field.kind === "date" ? "date" : field.kind === "number" ? "number" : "text", "aria-label": operator === "between"
349
+ : []), meta: option.count, children: [option.icon, option.label] }, option.value))), options.length === 0 && _jsx("span", { className: noMatches, children: labels.filters.noValues })] })] })) : (_jsx("div", { className: inputs, children: (operator === "between" ? [0, 1] : [0]).map((index) => (_jsx(Input, { size: "small", type: field.kind === "date" ? "date" : field.kind === "number" ? "number" : "text", "aria-label": operator === "between"
336
350
  ? index === 0
337
351
  ? labels.filters.from
338
352
  : labels.filters.to
@@ -344,7 +358,7 @@ export function FilterValueEditor({ field, value, onChange, onBack, }) {
344
358
  const next = [...values];
345
359
  next[index] = event.target.value;
346
360
  report(operator, next);
347
- } }, index))) })), _jsx(Divider, { inset: "menu" }), _jsxs("div", { className: editorFooter, children: [options
361
+ } }, index))) })), _jsx("div", { className: menuDivider, role: "separator" }), _jsxs("div", { className: editorFooter, children: [options
348
362
  ? `${labels.filters.selectedCount(picked)} · ${labels.filters.updatesAsYouPick}`
349
363
  : labels.filters.updatesAsYouPick, _jsx(Button, { variant: "ghost", onClick: () => report(operator, []), children: labels.common.clear })] })] }));
350
364
  }
@@ -23,13 +23,22 @@ export declare const checkMark: import("../styled-system/types/index.js").Recipe
23
23
  };
24
24
  };
25
25
  }>;
26
+ /** What a check mark's element carries, so its filled face can draw the gradient edge. */
27
+ export declare const checkMarkEdge: {
28
+ readonly "data-ciridae-ui": "";
29
+ readonly "data-edge": "gradient";
30
+ };
26
31
  export declare const checkIndicator: string;
32
+ /** The tick, or the dash of a mixed checkbox. */
33
+ export declare function CheckGlyph({ mixed }: {
34
+ mixed: boolean | undefined;
35
+ }): import("react").JSX.Element;
27
36
  export declare function Checkbox({ children, className, wrapperClassName, ...props }: Omit<ComponentProps<typeof BaseCheckbox.Root>, "children" | "className"> & {
28
37
  children?: ReactNode;
29
38
  className?: string;
30
39
  wrapperClassName?: string;
31
- }): import("@types/react").JSX.Element;
32
- export declare function Textarea({ className, ...props }: ComponentProps<"textarea">): import("@types/react").JSX.Element;
40
+ }): import("react").JSX.Element;
41
+ export declare function Textarea({ className, ...props }: ComponentProps<"textarea">): import("react").JSX.Element;
33
42
  export declare const radioDot: string;
34
43
  export type RadioGroupProps = Omit<ComponentProps<typeof BaseRadioGroup>, "children" | "defaultValue" | "name" | "onValueChange" | "value"> & {
35
44
  name: string;
@@ -44,7 +53,7 @@ export type RadioGroupProps = Omit<ComponentProps<typeof BaseRadioGroup>, "child
44
53
  value?: string;
45
54
  onValueChange?: (value: string) => void;
46
55
  };
47
- export declare function RadioGroup({ name, options, label: groupLabel, defaultValue, value, onValueChange, layout, className, ...props }: RadioGroupProps): import("@types/react").JSX.Element;
56
+ export declare function RadioGroup({ name, options, label: groupLabel, defaultValue, value, onValueChange, layout, className, ...props }: RadioGroupProps): import("react").JSX.Element;
48
57
  type SelectOption = {
49
58
  label: string;
50
59
  value: string;