camox 0.63.0 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/core/createApp.d.ts +28 -28
  2. package/dist/core/createLayout.d.ts +1 -1
  3. package/dist/core/derivedRoutes.js +3 -30
  4. package/dist/core/editing/createEditableBlock.d.ts +604 -604
  5. package/dist/features/canvas/CamoxCanvas.js +195 -173
  6. package/dist/features/canvas/canvasCamera.js +11 -2
  7. package/dist/features/canvas/canvasFrameCapture.js +5 -12
  8. package/dist/features/canvas/canvasFrameDocument.js +90 -29
  9. package/dist/features/canvas/canvasItems.js +22 -6
  10. package/dist/features/canvas/sidebar/ContentSidebar.js +19 -21
  11. package/dist/features/canvas/sidebar/LayoutPageOverview.js +22 -49
  12. package/dist/features/canvas/sidebar/LayoutPageSeo.js +73 -76
  13. package/dist/features/canvas/sidebar/PageInspector.js +61 -50
  14. package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +4 -4
  15. package/dist/features/canvas/useCanvasCamera.js +36 -15
  16. package/dist/features/page/CuratedPageContent.js +2 -1
  17. package/dist/features/page/UnplacedBlockContent.js +52 -0
  18. package/dist/features/preview/components/PageInfo.js +103 -100
  19. package/dist/features/preview/components/PreviewPanel.js +40 -12
  20. package/dist/features/preview/components/SidebarStack.js +78 -9
  21. package/dist/features/preview/strayCaretGuard.js +34 -0
  22. package/dist/features/provider/CoreCamoxProvider.js +5 -3
  23. package/dist/features/routes/pageRuntime.d.ts +2 -0
  24. package/dist/features/routes/pageRuntime.js +8 -5
  25. package/dist/features/runtime/RuntimeChrome.js +66 -40
  26. package/dist/features/runtime/pageApp.js +111 -74
  27. package/dist/features/runtime/pageClient.js +22 -2
  28. package/dist/features/runtime/runtime.d.ts +10 -0
  29. package/dist/features/runtime/runtime.js +314 -4
  30. package/dist/features/runtime/runtimeRoutes.d.ts +5 -0
  31. package/dist/features/runtime/runtimeRoutes.js +5 -0
  32. package/dist/features/studio/components/AgentSidebar.js +3 -3
  33. package/dist/features/studio/components/ChatWidget.js +106 -75
  34. package/dist/features/studio/components/ConnectAgent.js +313 -0
  35. package/dist/features/studio/components/DraftsControl.js +172 -159
  36. package/dist/features/studio/components/Patrol.js +605 -350
  37. package/dist/features/studio/components/ReviewDraftSheet.js +566 -430
  38. package/dist/features/studio/components/Squareling.js +74 -41
  39. package/dist/features/studio/components/ThreadComposer.js +192 -107
  40. package/dist/features/studio/components/ThreadMessages.js +95 -18
  41. package/dist/features/studio/components/UnnamedDraftBar.js +349 -0
  42. package/dist/features/studio/components/clientLogoPaths.js +10 -0
  43. package/dist/features/studio/findingSentence.js +37 -0
  44. package/dist/features/studio/patrollerBio.js +40 -0
  45. package/dist/features/studio/reportSentence.js +15 -0
  46. package/dist/features/studio/studioStore.js +36 -1
  47. package/dist/features/studio/workingSentence.js +10 -7
  48. package/dist/lib/api-client.d.ts +1 -0
  49. package/dist/lib/auth.js +34 -32
  50. package/dist/lib/queries.js +9 -1
  51. package/dist/lib/revisions.js +25 -3
  52. package/dist/lib/server-auth-cookie.js +17 -4
  53. package/dist/lib/studio-reads.d.ts +1 -1
  54. package/dist/lib/studio-reads.js +5 -1
  55. package/dist/lib/use-thread.js +1 -1
  56. package/dist/package.js +1 -1
  57. package/dist/studio.css +1 -1
  58. package/package.json +15 -4
  59. package/dist/features/canvas/sidebar/DerivedPageInspector.js +0 -49
  60. package/dist/features/canvas/sidebar/SingletonPageInspector.js +0 -39
@@ -5,20 +5,25 @@ import { useNavigate } from "../../navigation/navigation.js";
5
5
  import { AuthContext } from "../../../lib/auth.js";
6
6
  import { reads, useReadQuery } from "../../../lib/studio-reads.js";
7
7
  import { cn } from "../../../lib/utils.js";
8
+ import { patrolMutations } from "../../../lib/queries.js";
8
9
  import { AssetLightbox } from "../../preview/components/AssetLightbox.js";
9
10
  import { BOTTOM_CARD_EDGES, CARD_BODY_EDGES } from "../../preview/components/SidebarStack.js";
10
11
  import { editCollectionItemPath } from "../routes.js";
11
- import { timeAgo } from "../draftLabel.js";
12
+ import { studioStore } from "../studioStore.js";
13
+ import { askAgentMessage, dismissLabel, dismissedSentence, findingSentence, fixLabel } from "../findingSentence.js";
12
14
  import { useShowInCanvas } from "../showInCanvas.js";
13
15
  import { Squareling } from "./Squareling.js";
16
+ import { aboutSentence, whenTag } from "../patrollerBio.js";
14
17
  import { moreSentence, reportSentence, shortTimeAgo, whereReportLeads } from "../reportSentence.js";
15
- import { singleLocaleSentence, workingSentence } from "../workingSentence.js";
18
+ import { workingSentence } from "../workingSentence.js";
16
19
  import { c } from "react/compiler-runtime";
17
20
  import * as React from "react";
18
- import { jsx, jsxs } from "react/jsx-runtime";
21
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
22
+ import { reportLocale } from "@camox/api-contract/patrol";
23
+ import { useMutation } from "@tanstack/react-query";
24
+ import { Button } from "@camox/ui/button";
19
25
  import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from "@camox/ui/popover";
20
26
  import { Badge } from "@camox/ui/badge";
21
- import { reportLocale } from "@camox/api-contract/patrol";
22
27
 
23
28
  //#region src/features/studio/components/Patrol.tsx
24
29
  /**
@@ -54,7 +59,7 @@ function Patrol(t0) {
54
59
  children: "Patrol"
55
60
  })
56
61
  });
57
- t4 = cn("bg-background min-h-0 flex-1 overflow-y-auto", CARD_BODY_EDGES);
62
+ t4 = cn("bg-background flex min-h-0 flex-1 flex-col overflow-y-auto", CARD_BODY_EDGES);
58
63
  $[4] = t3;
59
64
  $[5] = t4;
60
65
  } else {
@@ -95,7 +100,7 @@ function Patrol(t0) {
95
100
  return t7;
96
101
  }
97
102
  function ProjectPatrol(t0) {
98
- const $ = c(36);
103
+ const $ = c(32);
99
104
  const { projectId, inDraft } = t0;
100
105
  let t1;
101
106
  if ($[0] !== projectId) {
@@ -113,95 +118,100 @@ function ProjectPatrol(t0) {
113
118
  const { data: locales } = useReadQuery(t2);
114
119
  const [open, setOpen] = React.useState(null);
115
120
  const now = useNow(3e4);
116
- const [heldOrder, setHeldOrder] = React.useState(null);
117
- const pointerIn = React.useRef(false);
118
- let t3;
119
- if ($[4] !== heldOrder || $[5] !== view) {
120
- t3 = view && inHeldOrder(view, heldOrder);
121
- $[4] = heldOrder;
122
- $[5] = view;
123
- $[6] = t3;
124
- } else t3 = $[6];
125
- const shown = t3;
126
- let t4;
127
- if ($[7] !== shown?.patrollers) {
128
- t4 = shown?.patrollers.map(_temp).join(" ");
129
- $[7] = shown?.patrollers;
130
- $[8] = t4;
131
- } else t4 = $[8];
132
- const roster = useMovingRows(t4);
133
121
  const [openFileId, setOpenFileId] = React.useState(null);
134
122
  const showReport = useShowReport(inDraft, setOpenFileId);
135
- if (!shown) return null;
136
- let t5;
137
- if ($[9] !== shown) {
138
- t5 = () => setHeldOrder((held) => held ?? shown.patrollers.map(_temp2));
139
- $[9] = shown;
140
- $[10] = t5;
141
- } else t5 = $[10];
142
- const hold = t5;
123
+ const navigate = useNavigate();
124
+ let t3;
125
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
126
+ t3 = patrolMutations.applyFix();
127
+ $[4] = t3;
128
+ } else t3 = $[4];
129
+ const applyFix = useMutation(t3);
130
+ let t4;
131
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
132
+ t4 = patrolMutations.dismissFinding();
133
+ $[5] = t4;
134
+ } else t4 = $[5];
135
+ const dismissFinding = useMutation(t4);
136
+ if (!view) return null;
137
+ const t5 = locales?.length === 1;
143
138
  let t6;
144
- if ($[11] !== hold) {
145
- t6 = (id_1) => {
146
- setOpen(id_1);
147
- if (id_1) hold();
148
- else if (!pointerIn.current) setHeldOrder(null);
139
+ if ($[6] !== showReport) {
140
+ t6 = (shown) => {
141
+ setOpen(null);
142
+ showReport(shown);
149
143
  };
150
- $[11] = hold;
151
- $[12] = t6;
152
- } else t6 = $[12];
153
- const openReports = t6;
144
+ $[6] = showReport;
145
+ $[7] = t6;
146
+ } else t6 = $[7];
154
147
  let t7;
155
- if ($[13] !== hold) {
156
- t7 = () => {
157
- pointerIn.current = true;
158
- hold();
159
- };
160
- $[13] = hold;
161
- $[14] = t7;
162
- } else t7 = $[14];
148
+ if ($[8] !== applyFix || $[9] !== projectId) {
149
+ t7 = (finding) => applyFix.mutate({
150
+ projectId,
151
+ findingId: finding.id
152
+ });
153
+ $[8] = applyFix;
154
+ $[9] = projectId;
155
+ $[10] = t7;
156
+ } else t7 = $[10];
163
157
  let t8;
164
- if ($[15] !== open) {
165
- t8 = () => {
166
- pointerIn.current = false;
167
- if (!open) setHeldOrder(null);
158
+ if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
159
+ t8 = (patroller, finding_0) => {
160
+ setOpen(null);
161
+ studioStore.trigger.askAgent({ text: askAgentMessage(finding_0, patroller.role) });
168
162
  };
169
- $[15] = open;
170
- $[16] = t8;
171
- } else t8 = $[16];
172
- const t9 = locales?.length === 1;
163
+ $[11] = t8;
164
+ } else t8 = $[11];
165
+ let t9;
166
+ if ($[12] !== dismissFinding || $[13] !== projectId) {
167
+ t9 = (finding_1) => dismissFinding.mutate({
168
+ projectId,
169
+ findingId: finding_1.id
170
+ });
171
+ $[12] = dismissFinding;
172
+ $[13] = projectId;
173
+ $[14] = t9;
174
+ } else t9 = $[14];
173
175
  let t10;
174
- if ($[17] !== openReports || $[18] !== showReport) {
175
- t10 = (report) => {
176
- openReports(null);
177
- showReport(report);
176
+ if ($[15] !== navigate) {
177
+ t10 = (draft) => {
178
+ setOpen(null);
179
+ navigate({
180
+ to: window.location.href,
181
+ draft: draft.id
182
+ });
178
183
  };
179
- $[17] = openReports;
180
- $[18] = showReport;
181
- $[19] = t10;
182
- } else t10 = $[19];
184
+ $[15] = navigate;
185
+ $[16] = t10;
186
+ } else t10 = $[16];
183
187
  let t11;
184
- if ($[20] !== inDraft || $[21] !== now || $[22] !== open || $[23] !== openReports || $[24] !== shown || $[25] !== t10 || $[26] !== t9) {
188
+ if ($[17] !== inDraft || $[18] !== now || $[19] !== open || $[20] !== t10 || $[21] !== t5 || $[22] !== t6 || $[23] !== t7 || $[24] !== t9 || $[25] !== view) {
185
189
  t11 = /* @__PURE__ */ jsx(PatrolRoster, {
186
- view: shown,
190
+ view,
187
191
  now,
188
192
  inDraft,
189
- singleLocale: t9,
193
+ singleLocale: t5,
190
194
  open,
191
- onOpenChange: openReports,
192
- onShow: t10
193
- });
194
- $[20] = inDraft;
195
- $[21] = now;
196
- $[22] = open;
197
- $[23] = openReports;
198
- $[24] = shown;
199
- $[25] = t10;
200
- $[26] = t9;
201
- $[27] = t11;
202
- } else t11 = $[27];
195
+ onOpenChange: setOpen,
196
+ onShow: t6,
197
+ onApply: t7,
198
+ onAsk: t8,
199
+ onDismiss: t9,
200
+ onOpenPatrolDraft: t10
201
+ });
202
+ $[17] = inDraft;
203
+ $[18] = now;
204
+ $[19] = open;
205
+ $[20] = t10;
206
+ $[21] = t5;
207
+ $[22] = t6;
208
+ $[23] = t7;
209
+ $[24] = t9;
210
+ $[25] = view;
211
+ $[26] = t11;
212
+ } else t11 = $[26];
203
213
  let t12;
204
- if ($[28] !== openFileId) {
214
+ if ($[27] !== openFileId) {
205
215
  t12 = openFileId && /* @__PURE__ */ jsx(AssetLightbox, {
206
216
  open: true,
207
217
  onOpenChange: (open_0) => {
@@ -209,39 +219,27 @@ function ProjectPatrol(t0) {
209
219
  },
210
220
  fileId: openFileId
211
221
  });
212
- $[28] = openFileId;
213
- $[29] = t12;
214
- } else t12 = $[29];
222
+ $[27] = openFileId;
223
+ $[28] = t12;
224
+ } else t12 = $[28];
215
225
  let t13;
216
- if ($[30] !== roster || $[31] !== t11 || $[32] !== t12 || $[33] !== t7 || $[34] !== t8) {
217
- t13 = /* @__PURE__ */ jsxs("div", {
218
- ref: roster,
219
- className: "relative",
220
- onPointerEnter: t7,
221
- onPointerLeave: t8,
222
- children: [t11, t12]
223
- });
224
- $[30] = roster;
225
- $[31] = t11;
226
- $[32] = t12;
227
- $[33] = t7;
228
- $[34] = t8;
229
- $[35] = t13;
230
- } else t13 = $[35];
226
+ if ($[29] !== t11 || $[30] !== t12) {
227
+ t13 = /* @__PURE__ */ jsxs("div", { children: [t11, t12] });
228
+ $[29] = t11;
229
+ $[30] = t12;
230
+ $[31] = t13;
231
+ } else t13 = $[31];
231
232
  return t13;
232
233
  }
233
- function _temp2(t0) {
234
- const { id: id_0 } = t0;
235
- return id_0;
236
- }
237
- function _temp(t0) {
238
- const { id } = t0;
239
- return id;
240
- }
234
+ /** A report, as the Patrol shows its subject. */
235
+ const shownOf = (report) => ({
236
+ ...report,
237
+ locale: reportLocale(report.summary)
238
+ });
241
239
  /**
242
- * Shows a report's subject where it is, in the report's locale, and on Live for a Live report seen
243
- * from a draft: a page, block or item selected on the Canvas, a record in its collection, or a
244
- * file, opened over the Canvas.
240
+ * Shows a report's or a finding's subject where it is, in its locale, and on Live for a Live one
241
+ * seen from a draft: a page, block or item selected on the Canvas, a record in its collection, or
242
+ * a file, opened over the Canvas.
245
243
  */
246
244
  function useShowReport(inDraft, openFile) {
247
245
  const $ = c(6);
@@ -254,7 +252,7 @@ function useShowReport(inDraft, openFile) {
254
252
  const { place } = report;
255
253
  if (!place) return;
256
254
  const toLive = inDraft && report.live ? { draft: null } : {};
257
- const tag = reportLocale(report.summary);
255
+ const tag = report.locale;
258
256
  if (place.kind === "page") {
259
257
  const { pageId, blockId, itemId } = place;
260
258
  showInCanvas({
@@ -290,70 +288,10 @@ function useShowReport(inDraft, openFile) {
290
288
  } else t0 = $[5];
291
289
  return t0;
292
290
  }
293
- /**
294
- * The view with its patrollers in the order held, if any, and its latest content: patrollers the
295
- * order doesn't hold follow, in the view's order, and those no longer in the view are dropped.
296
- */
297
- function inHeldOrder(view, order) {
298
- if (!order) return view;
299
- const place = (id) => {
300
- const index = order.indexOf(id);
301
- return index === -1 ? order.length : index;
302
- };
303
- return {
304
- ...view,
305
- patrollers: [...view.patrollers].sort((a, b) => place(a.id) - place(b.id))
306
- };
307
- }
308
- /** How long a patroller takes to move to its new place, in ms. */
309
- const MOVE_DURATION = 250;
310
- /** A strong ease-out: the move starts at once and settles gently. */
311
- const MOVE_EASING = "cubic-bezier(0.23, 1, 0.32, 1)";
312
- /**
313
- * Moves each patroller from where it showed to its new place when the order (a key of their ids)
314
- * changes, rather than jumping there. Without motion for editors who reduce it.
315
- */
316
- function useMovingRows(order) {
317
- const $ = c(2);
318
- const node = React.useRef(null);
319
- const last = React.useRef(null);
320
- let t0;
321
- if ($[0] !== order) {
322
- t0 = () => {
323
- const roster = node.current;
324
- if (!roster) return;
325
- const rows = [...roster.querySelectorAll("[data-patroller]")];
326
- const tops = new Map(rows.map(_temp3));
327
- const previous = last.current;
328
- last.current = {
329
- order,
330
- tops
331
- };
332
- if (!previous || previous.order === order) return;
333
- if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
334
- for (const row_0 of rows) {
335
- const from = previous.tops.get(row_0.dataset.patroller);
336
- const to = tops.get(row_0.dataset.patroller);
337
- if (from === void 0 || from === to) continue;
338
- row_0.animate([{ transform: `translateY(${from - to}px)` }, { transform: "none" }], {
339
- duration: MOVE_DURATION,
340
- easing: MOVE_EASING
341
- });
342
- }
343
- };
344
- $[0] = order;
345
- $[1] = t0;
346
- } else t0 = $[1];
347
- React.useLayoutEffect(t0);
348
- return node;
349
- }
350
- function _temp3(row) {
351
- return [row.dataset.patroller, row.offsetTop];
352
- }
353
291
  /** The time now, updated every `intervalMs`. */
354
292
  function useNow(intervalMs) {
355
293
  const $ = c(3);
356
- const [now, setNow] = React.useState(_temp4);
294
+ const [now, setNow] = React.useState(_temp);
357
295
  let t0;
358
296
  let t1;
359
297
  if ($[0] !== intervalMs) {
@@ -372,62 +310,81 @@ function useNow(intervalMs) {
372
310
  React.useEffect(t0, t1);
373
311
  return now;
374
312
  }
375
- function _temp4() {
313
+ function _temp() {
376
314
  return Date.now();
377
315
  }
378
316
  /**
379
- * A `PatrolView`, in the order it gives: each patroller's avatar, role, and when it last worked,
380
- * opening its reports in a popover to the left. Live's reports carry a tag when a draft is viewed. In a
381
- * project with a single locale, the Translator invites the editor to add a language instead.
317
+ * A `PatrolView`, in the order it gives, as avatars in rows of three, each over its role like a
318
+ * story. Each opens its reports in a popover to the left, under its role and when it last worked,
319
+ * or what it's on. Live's reports carry a tag when a draft is viewed. In a project with a single
320
+ * locale, the Translator invites the editor to add a language instead.
382
321
  */
383
322
  function PatrolRoster(t0) {
384
- const $ = c(17);
385
- const { view, now, inDraft, singleLocale, open, onOpenChange, onShow } = t0;
323
+ const $ = c(27);
324
+ const { view, now, inDraft, singleLocale, open, onOpenChange, onShow, onApply, onAsk, onDismiss, onOpenPatrolDraft } = t0;
386
325
  let t1;
387
- if ($[0] !== inDraft || $[1] !== now || $[2] !== onOpenChange || $[3] !== onShow || $[4] !== open || $[5] !== singleLocale || $[6] !== view.patrollers) {
326
+ if ($[0] !== inDraft || $[1] !== now || $[2] !== onApply || $[3] !== onAsk || $[4] !== onDismiss || $[5] !== onOpenChange || $[6] !== onOpenPatrolDraft || $[7] !== onShow || $[8] !== open || $[9] !== singleLocale || $[10] !== view.patrolDraft || $[11] !== view.patrollers) {
388
327
  let t2;
389
- if ($[8] !== inDraft || $[9] !== now || $[10] !== onOpenChange || $[11] !== onShow || $[12] !== open || $[13] !== singleLocale) {
328
+ if ($[13] !== inDraft || $[14] !== now || $[15] !== onApply || $[16] !== onAsk || $[17] !== onDismiss || $[18] !== onOpenChange || $[19] !== onOpenPatrolDraft || $[20] !== onShow || $[21] !== open || $[22] !== singleLocale || $[23] !== view.patrolDraft) {
390
329
  t2 = (patroller) => /* @__PURE__ */ jsx(PatrollerRow, {
391
330
  patroller,
392
331
  now,
393
332
  inDraft,
394
- idle: singleLocale ? singleLocaleSentence(patroller.id) : void 0,
333
+ singleLocale,
395
334
  open: open === patroller.id,
396
335
  onOpenChange: (next) => onOpenChange(next ? patroller.id : null),
397
- onShow
336
+ onShow,
337
+ onApply,
338
+ onAsk,
339
+ onDismiss,
340
+ patrolDraft: view.patrolDraft,
341
+ onOpenPatrolDraft
398
342
  }, patroller.id);
399
- $[8] = inDraft;
400
- $[9] = now;
401
- $[10] = onOpenChange;
402
- $[11] = onShow;
403
- $[12] = open;
404
- $[13] = singleLocale;
405
- $[14] = t2;
406
- } else t2 = $[14];
343
+ $[13] = inDraft;
344
+ $[14] = now;
345
+ $[15] = onApply;
346
+ $[16] = onAsk;
347
+ $[17] = onDismiss;
348
+ $[18] = onOpenChange;
349
+ $[19] = onOpenPatrolDraft;
350
+ $[20] = onShow;
351
+ $[21] = open;
352
+ $[22] = singleLocale;
353
+ $[23] = view.patrolDraft;
354
+ $[24] = t2;
355
+ } else t2 = $[24];
407
356
  t1 = view.patrollers.map(t2);
408
357
  $[0] = inDraft;
409
358
  $[1] = now;
410
- $[2] = onOpenChange;
411
- $[3] = onShow;
412
- $[4] = open;
413
- $[5] = singleLocale;
414
- $[6] = view.patrollers;
415
- $[7] = t1;
416
- } else t1 = $[7];
359
+ $[2] = onApply;
360
+ $[3] = onAsk;
361
+ $[4] = onDismiss;
362
+ $[5] = onOpenChange;
363
+ $[6] = onOpenPatrolDraft;
364
+ $[7] = onShow;
365
+ $[8] = open;
366
+ $[9] = singleLocale;
367
+ $[10] = view.patrolDraft;
368
+ $[11] = view.patrollers;
369
+ $[12] = t1;
370
+ } else t1 = $[12];
417
371
  let t2;
418
- if ($[15] !== t1) {
419
- t2 = /* @__PURE__ */ jsx("ul", {
420
- className: "flex flex-col gap-px p-1",
421
- children: t1
422
- });
423
- $[15] = t1;
424
- $[16] = t2;
425
- } else t2 = $[16];
372
+ if ($[25] !== t1) {
373
+ t2 = /* @__PURE__ */ jsx("div", {
374
+ className: "px-1 py-3",
375
+ children: /* @__PURE__ */ jsx("ul", {
376
+ className: "mx-auto grid w-fit grid-cols-[repeat(3,5rem)] gap-y-3.5",
377
+ children: t1
378
+ })
379
+ });
380
+ $[25] = t1;
381
+ $[26] = t2;
382
+ } else t2 = $[26];
426
383
  return t2;
427
384
  }
428
385
  /**
429
- * A thin, low-contrast arc travelling around a working patroller's avatar circle, or the whole
430
- * circle pulsing slowly under reduced motion. The motion lives in `studio.css`.
386
+ * A thin, low-contrast arc travelling around a working patroller's avatar, where its story ring
387
+ * goes, or the whole ring pulsing slowly under reduced motion. The motion lives in `studio.css`.
431
388
  */
432
389
  function WorkingArc() {
433
390
  const $ = c(1);
@@ -436,12 +393,12 @@ function WorkingArc() {
436
393
  t0 = /* @__PURE__ */ jsx("svg", {
437
394
  "data-working-indicator": true,
438
395
  "aria-hidden": true,
439
- viewBox: "0 0 32 32",
440
- className: "patrol-working text-foreground/30 pointer-events-none absolute inset-0 size-full",
396
+ viewBox: "0 0 36 36",
397
+ className: "patrol-working text-foreground/40 pointer-events-none absolute -inset-[3px] size-[calc(100%+6px)]",
441
398
  children: /* @__PURE__ */ jsx("circle", {
442
- cx: "16",
443
- cy: "16",
444
- r: "15.25",
399
+ cx: "18",
400
+ cy: "18",
401
+ r: "17.25",
445
402
  pathLength: "100",
446
403
  fill: "none",
447
404
  stroke: "currentColor",
@@ -456,7 +413,7 @@ function WorkingArc() {
456
413
  }
457
414
  function PatrollerRow(t0) {
458
415
  const $ = c(53);
459
- const { patroller, now, inDraft, idle, open, onOpenChange, onShow } = t0;
416
+ const { patroller, now, inDraft, singleLocale, open, onOpenChange, onShow, onApply, onAsk, onDismiss, patrolDraft, onOpenPatrolDraft } = t0;
460
417
  const statusId = React.useId();
461
418
  let t1;
462
419
  if ($[0] !== patroller.id || $[1] !== patroller.working) {
@@ -466,174 +423,471 @@ function PatrollerRow(t0) {
466
423
  $[2] = t1;
467
424
  } else t1 = $[2];
468
425
  const working = t1;
469
- let t2;
470
- if ($[3] !== idle || $[4] !== now || $[5] !== patroller.lastActivityAt || $[6] !== working) {
471
- t2 = working ?? idle ?? (patroller.lastActivityAt === null ? "" : timeAgo(patroller.lastActivityAt, now));
472
- $[3] = idle;
473
- $[4] = now;
474
- $[5] = patroller.lastActivityAt;
426
+ const count = patroller.findings.length;
427
+ const t2 = open || void 0;
428
+ const t3 = working ? statusId : void 0;
429
+ let t4;
430
+ if ($[3] !== count || $[4] !== working) {
431
+ t4 = !working && /* @__PURE__ */ jsx("span", {
432
+ "aria-hidden": true,
433
+ className: cn("group-focus-visible/patroller:ring-ring/50 pointer-events-none absolute -inset-[3px] rounded-full border group-focus-visible/patroller:ring-[3px]", count > 0 ? "border-foreground border-[1.5px]" : "border-border")
434
+ });
435
+ $[3] = count;
436
+ $[4] = working;
437
+ $[5] = t4;
438
+ } else t4 = $[5];
439
+ let t5;
440
+ if ($[6] !== working) {
441
+ t5 = working && /* @__PURE__ */ jsx(WorkingArc, {});
475
442
  $[6] = working;
476
- $[7] = t2;
477
- } else t2 = $[7];
478
- const subtitle = t2;
479
- const t3 = open || void 0;
480
- const t4 = working ? statusId : void 0;
481
- const t5 = working ? "working" : "idle";
482
- let t6;
483
- if ($[8] !== patroller.id || $[9] !== t5) {
484
- t6 = /* @__PURE__ */ jsx(Squareling, {
485
- variant: patroller.id,
486
- status: t5,
487
- className: "size-5"
443
+ $[7] = t5;
444
+ } else t5 = $[7];
445
+ const t6 = working ? "working" : patroller.duty === "off" ? "asleep" : "idle";
446
+ let t7;
447
+ if ($[8] !== patroller.id || $[9] !== t6) {
448
+ t7 = /* @__PURE__ */ jsx("span", {
449
+ className: "bg-muted group-hover/patroller:bg-muted-foreground/15 group-data-popup-open/patroller:bg-muted-foreground/15 flex size-full items-center justify-center rounded-full transition-colors",
450
+ children: /* @__PURE__ */ jsx(Squareling, {
451
+ variant: patroller.id,
452
+ status: t6,
453
+ className: "size-6"
454
+ })
488
455
  });
489
456
  $[8] = patroller.id;
490
- $[9] = t5;
491
- $[10] = t6;
492
- } else t6 = $[10];
493
- let t7;
494
- if ($[11] !== working) {
495
- t7 = working && /* @__PURE__ */ jsx(WorkingArc, {});
496
- $[11] = working;
497
- $[12] = t7;
498
- } else t7 = $[12];
457
+ $[9] = t6;
458
+ $[10] = t7;
459
+ } else t7 = $[10];
499
460
  let t8;
500
- if ($[13] !== t6 || $[14] !== t7) {
501
- t8 = /* @__PURE__ */ jsxs("span", {
502
- className: "bg-muted relative flex size-8 shrink-0 items-center justify-center rounded-full",
503
- children: [t6, t7]
461
+ if ($[11] !== count) {
462
+ t8 = count > 0 && /* @__PURE__ */ jsx("span", {
463
+ "data-findings-count": true,
464
+ "aria-label": `${count} open ${count === 1 ? "finding" : "findings"}`,
465
+ className: "bg-foreground text-background ring-background absolute -top-1.5 -right-1.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[10px] font-medium tabular-nums ring-2",
466
+ children: count
504
467
  });
505
- $[13] = t6;
506
- $[14] = t7;
507
- $[15] = t8;
508
- } else t8 = $[15];
468
+ $[11] = count;
469
+ $[12] = t8;
470
+ } else t8 = $[12];
509
471
  let t9;
510
- if ($[16] !== patroller.role) {
511
- t9 = /* @__PURE__ */ jsx("span", {
512
- "data-patroller-role": true,
513
- className: "truncate text-sm font-medium",
514
- children: patroller.role
472
+ if ($[13] !== t4 || $[14] !== t5 || $[15] !== t7 || $[16] !== t8) {
473
+ t9 = /* @__PURE__ */ jsxs("span", {
474
+ className: "relative flex size-10 shrink-0 transition-[scale] duration-200 ease-out group-hover/patroller:scale-105 group-active/patroller:scale-95 motion-reduce:transition-none",
475
+ children: [
476
+ t4,
477
+ t5,
478
+ t7,
479
+ t8
480
+ ]
515
481
  });
516
- $[16] = patroller.role;
482
+ $[13] = t4;
483
+ $[14] = t5;
484
+ $[15] = t7;
485
+ $[16] = t8;
517
486
  $[17] = t9;
518
487
  } else t9 = $[17];
519
- const t10 = working ? true : void 0;
520
- let t11;
521
- if ($[18] !== subtitle || $[19] !== t10) {
522
- t11 = /* @__PURE__ */ jsx("span", {
523
- "data-patroller-subtitle": true,
524
- "aria-hidden": t10,
525
- className: "text-muted-foreground truncate text-xs",
526
- children: subtitle
488
+ let t10;
489
+ if ($[18] !== patroller.role) {
490
+ t10 = /* @__PURE__ */ jsx("span", {
491
+ "data-patroller-role": true,
492
+ className: "max-w-full truncate text-[11px] leading-4 font-medium",
493
+ children: patroller.role
527
494
  });
528
- $[18] = subtitle;
495
+ $[18] = patroller.role;
529
496
  $[19] = t10;
530
- $[20] = t11;
531
- } else t11 = $[20];
532
- let t12;
533
- if ($[21] !== t11 || $[22] !== t9) {
534
- t12 = /* @__PURE__ */ jsxs("span", {
535
- className: "flex min-w-0 flex-col",
536
- children: [t9, t11]
497
+ } else t10 = $[19];
498
+ let t11;
499
+ if ($[20] !== t10 || $[21] !== t3 || $[22] !== t9) {
500
+ t11 = /* @__PURE__ */ jsxs(PopoverTrigger, {
501
+ "aria-describedby": t3,
502
+ className: "group/patroller flex max-w-full min-w-0 cursor-pointer flex-col items-center gap-1 pt-[3px] outline-none",
503
+ children: [t9, t10]
537
504
  });
538
- $[21] = t11;
505
+ $[20] = t10;
506
+ $[21] = t3;
539
507
  $[22] = t9;
540
- $[23] = t12;
541
- } else t12 = $[23];
508
+ $[23] = t11;
509
+ } else t11 = $[23];
510
+ let t12;
511
+ if ($[24] !== onAsk || $[25] !== patroller) {
512
+ t12 = (finding) => onAsk(patroller, finding);
513
+ $[24] = onAsk;
514
+ $[25] = patroller;
515
+ $[26] = t12;
516
+ } else t12 = $[26];
542
517
  let t13;
543
- if ($[24] !== t12 || $[25] !== t4 || $[26] !== t8) {
544
- t13 = /* @__PURE__ */ jsxs(PopoverTrigger, {
545
- "aria-describedby": t4,
546
- className: "hover:bg-muted/60 data-popup-open:bg-muted/60 focus-visible:ring-ring/50 flex cursor-pointer items-center gap-2.5 rounded-lg px-2 py-1.5 text-left outline-none focus-visible:ring-[3px]",
547
- children: [t8, t12]
548
- });
549
- $[24] = t12;
550
- $[25] = t4;
551
- $[26] = t8;
552
- $[27] = t13;
553
- } else t13 = $[27];
554
- let t14;
555
- if ($[28] !== patroller.role) {
556
- t14 = /* @__PURE__ */ jsx(PopoverTitle, {
557
- className: "text-sm",
558
- children: patroller.role
559
- });
560
- $[28] = patroller.role;
561
- $[29] = t14;
562
- } else t14 = $[29];
563
- let t15;
564
- if ($[30] !== inDraft || $[31] !== now || $[32] !== onShow || $[33] !== patroller) {
565
- t15 = /* @__PURE__ */ jsx(PatrollerReports, {
566
- patroller,
567
- now,
568
- inDraft,
569
- onShow
518
+ if ($[27] !== inDraft || $[28] !== now || $[29] !== onApply || $[30] !== onDismiss || $[31] !== onOpenPatrolDraft || $[32] !== onShow || $[33] !== patrolDraft || $[34] !== patroller || $[35] !== singleLocale || $[36] !== t12) {
519
+ t13 = /* @__PURE__ */ jsx(PopoverContent, {
520
+ sideOffset: 8,
521
+ className: "w-96 gap-3 p-4",
522
+ children: /* @__PURE__ */ jsx(PatrollerCard, {
523
+ patroller,
524
+ now,
525
+ inDraft,
526
+ singleLocale,
527
+ onShow,
528
+ onApply,
529
+ onAsk: t12,
530
+ onDismiss,
531
+ patrolDraft,
532
+ onOpenPatrolDraft
533
+ })
570
534
  });
571
- $[30] = inDraft;
572
- $[31] = now;
535
+ $[27] = inDraft;
536
+ $[28] = now;
537
+ $[29] = onApply;
538
+ $[30] = onDismiss;
539
+ $[31] = onOpenPatrolDraft;
573
540
  $[32] = onShow;
574
- $[33] = patroller;
575
- $[34] = t15;
576
- } else t15 = $[34];
577
- let t16;
578
- if ($[35] !== t14 || $[36] !== t15) {
579
- t16 = /* @__PURE__ */ jsxs(PopoverContent, {
580
- side: "left",
581
- align: "start",
582
- sideOffset: 12,
583
- className: "w-80 gap-2 p-3",
584
- children: [t14, t15]
585
- });
586
- $[35] = t14;
587
- $[36] = t15;
588
- $[37] = t16;
589
- } else t16 = $[37];
590
- let t17;
591
- if ($[38] !== onOpenChange || $[39] !== open || $[40] !== t13 || $[41] !== t16) {
592
- t17 = /* @__PURE__ */ jsxs(Popover, {
541
+ $[33] = patrolDraft;
542
+ $[34] = patroller;
543
+ $[35] = singleLocale;
544
+ $[36] = t12;
545
+ $[37] = t13;
546
+ } else t13 = $[37];
547
+ let t14;
548
+ if ($[38] !== onOpenChange || $[39] !== open || $[40] !== t11 || $[41] !== t13) {
549
+ t14 = /* @__PURE__ */ jsxs(Popover, {
593
550
  open,
594
551
  onOpenChange,
595
- children: [t13, t16]
552
+ children: [t11, t13]
596
553
  });
597
554
  $[38] = onOpenChange;
598
555
  $[39] = open;
599
- $[40] = t13;
600
- $[41] = t16;
601
- $[42] = t17;
602
- } else t17 = $[42];
603
- let t18;
556
+ $[40] = t11;
557
+ $[41] = t13;
558
+ $[42] = t14;
559
+ } else t14 = $[42];
560
+ let t15;
604
561
  if ($[43] !== working) {
605
- t18 = working && `Working: ${working.charAt(0).toLowerCase()}${working.slice(1)}`;
562
+ t15 = working && `Working: ${working.charAt(0).toLowerCase()}${working.slice(1)}`;
606
563
  $[43] = working;
607
- $[44] = t18;
608
- } else t18 = $[44];
609
- let t19;
610
- if ($[45] !== statusId || $[46] !== t18) {
611
- t19 = /* @__PURE__ */ jsx("span", {
564
+ $[44] = t15;
565
+ } else t15 = $[44];
566
+ let t16;
567
+ if ($[45] !== statusId || $[46] !== t15) {
568
+ t16 = /* @__PURE__ */ jsx("span", {
612
569
  id: statusId,
613
570
  "data-working-status": true,
614
571
  role: "status",
615
572
  className: "sr-only",
616
- children: t18
573
+ children: t15
617
574
  });
618
575
  $[45] = statusId;
619
- $[46] = t18;
620
- $[47] = t19;
621
- } else t19 = $[47];
622
- let t20;
623
- if ($[48] !== patroller.id || $[49] !== t17 || $[50] !== t19 || $[51] !== t3) {
624
- t20 = /* @__PURE__ */ jsxs("li", {
576
+ $[46] = t15;
577
+ $[47] = t16;
578
+ } else t16 = $[47];
579
+ let t17;
580
+ if ($[48] !== patroller.id || $[49] !== t14 || $[50] !== t16 || $[51] !== t2) {
581
+ t17 = /* @__PURE__ */ jsxs("li", {
625
582
  "data-patroller": patroller.id,
626
- "data-open": t3,
627
- className: "flex flex-col",
628
- children: [t17, t19]
583
+ "data-open": t2,
584
+ className: "flex justify-center",
585
+ children: [t14, t16]
629
586
  });
630
587
  $[48] = patroller.id;
631
- $[49] = t17;
632
- $[50] = t19;
633
- $[51] = t3;
634
- $[52] = t20;
635
- } else t20 = $[52];
636
- return t20;
588
+ $[49] = t14;
589
+ $[50] = t16;
590
+ $[51] = t2;
591
+ $[52] = t17;
592
+ } else t17 = $[52];
593
+ return t17;
594
+ }
595
+ /**
596
+ * A patroller's popover, a profile card: its avatar and role, a tag of when it works where the
597
+ * editor is over what it does, what it's on while it works, then what it did.
598
+ */
599
+ function PatrollerCard(t0) {
600
+ const $ = c(47);
601
+ let props;
602
+ let singleLocale;
603
+ if ($[0] !== t0) {
604
+ ({singleLocale, ...props} = t0);
605
+ $[0] = t0;
606
+ $[1] = props;
607
+ $[2] = singleLocale;
608
+ } else {
609
+ props = $[1];
610
+ singleLocale = $[2];
611
+ }
612
+ const { patroller, inDraft } = props;
613
+ let t1;
614
+ if ($[3] !== patroller.id || $[4] !== patroller.working) {
615
+ t1 = patroller.working && workingSentence(patroller.id, patroller.working);
616
+ $[3] = patroller.id;
617
+ $[4] = patroller.working;
618
+ $[5] = t1;
619
+ } else t1 = $[5];
620
+ const working = t1;
621
+ let t2;
622
+ if ($[6] !== inDraft || $[7] !== patroller.duty || $[8] !== patroller.id || $[9] !== singleLocale) {
623
+ t2 = whenTag(patroller.id, patroller.duty, inDraft, singleLocale);
624
+ $[6] = inDraft;
625
+ $[7] = patroller.duty;
626
+ $[8] = patroller.id;
627
+ $[9] = singleLocale;
628
+ $[10] = t2;
629
+ } else t2 = $[10];
630
+ const when = t2;
631
+ const t3 = working ? "working" : patroller.duty === "off" ? "asleep" : "idle";
632
+ let t4;
633
+ if ($[11] !== patroller.id || $[12] !== t3) {
634
+ t4 = /* @__PURE__ */ jsx(Squareling, {
635
+ variant: patroller.id,
636
+ status: t3,
637
+ className: "size-7"
638
+ });
639
+ $[11] = patroller.id;
640
+ $[12] = t3;
641
+ $[13] = t4;
642
+ } else t4 = $[13];
643
+ let t5;
644
+ if ($[14] !== working) {
645
+ t5 = working && /* @__PURE__ */ jsx(WorkingArc, {});
646
+ $[14] = working;
647
+ $[15] = t5;
648
+ } else t5 = $[15];
649
+ let t6;
650
+ if ($[16] !== t4 || $[17] !== t5) {
651
+ t6 = /* @__PURE__ */ jsxs("span", {
652
+ className: "bg-muted relative flex size-12 shrink-0 items-center justify-center rounded-full",
653
+ children: [t4, t5]
654
+ });
655
+ $[16] = t4;
656
+ $[17] = t5;
657
+ $[18] = t6;
658
+ } else t6 = $[18];
659
+ let t7;
660
+ if ($[19] !== patroller.role) {
661
+ t7 = /* @__PURE__ */ jsx(PopoverTitle, {
662
+ className: "min-w-0 text-base",
663
+ children: patroller.role
664
+ });
665
+ $[19] = patroller.role;
666
+ $[20] = t7;
667
+ } else t7 = $[20];
668
+ let t8;
669
+ if ($[21] !== t6 || $[22] !== t7) {
670
+ t8 = /* @__PURE__ */ jsxs("header", {
671
+ className: "flex items-center gap-3",
672
+ children: [t6, t7]
673
+ });
674
+ $[21] = t6;
675
+ $[22] = t7;
676
+ $[23] = t8;
677
+ } else t8 = $[23];
678
+ const t9 = when.runs ? "bg-emerald-500" : "bg-muted-foreground/40";
679
+ let t10;
680
+ if ($[24] !== t9) {
681
+ t10 = cn("size-1.5 rounded-full", t9);
682
+ $[24] = t9;
683
+ $[25] = t10;
684
+ } else t10 = $[25];
685
+ let t11;
686
+ if ($[26] !== t10) {
687
+ t11 = /* @__PURE__ */ jsx("span", {
688
+ "aria-hidden": true,
689
+ className: t10
690
+ });
691
+ $[26] = t10;
692
+ $[27] = t11;
693
+ } else t11 = $[27];
694
+ let t12;
695
+ if ($[28] !== t11 || $[29] !== when.label) {
696
+ t12 = /* @__PURE__ */ jsxs("span", {
697
+ "data-patroller-when": true,
698
+ className: "bg-muted text-muted-foreground inline-flex items-center gap-1.5 rounded-full py-0.5 pr-2.5 pl-2 text-xs font-medium",
699
+ children: [t11, when.label]
700
+ });
701
+ $[28] = t11;
702
+ $[29] = when.label;
703
+ $[30] = t12;
704
+ } else t12 = $[30];
705
+ let t13;
706
+ if ($[31] !== patroller.id) {
707
+ t13 = aboutSentence(patroller.id);
708
+ $[31] = patroller.id;
709
+ $[32] = t13;
710
+ } else t13 = $[32];
711
+ let t14;
712
+ if ($[33] !== t13) {
713
+ t14 = /* @__PURE__ */ jsx("p", {
714
+ "data-patroller-about": true,
715
+ className: "text-sm",
716
+ children: t13
717
+ });
718
+ $[33] = t13;
719
+ $[34] = t14;
720
+ } else t14 = $[34];
721
+ let t15;
722
+ if ($[35] !== t12 || $[36] !== t14) {
723
+ t15 = /* @__PURE__ */ jsxs("div", {
724
+ className: "flex flex-col items-start gap-1.5",
725
+ children: [t12, t14]
726
+ });
727
+ $[35] = t12;
728
+ $[36] = t14;
729
+ $[37] = t15;
730
+ } else t15 = $[37];
731
+ let t16;
732
+ if ($[38] !== working) {
733
+ t16 = working && /* @__PURE__ */ jsx("p", {
734
+ "data-patroller-working": true,
735
+ className: "bg-muted rounded-md px-2.5 py-1.5 text-xs",
736
+ children: working
737
+ });
738
+ $[38] = working;
739
+ $[39] = t16;
740
+ } else t16 = $[39];
741
+ let t17;
742
+ if ($[40] !== props) {
743
+ t17 = /* @__PURE__ */ jsx("div", {
744
+ className: "flex flex-col gap-2 border-t pt-3",
745
+ children: /* @__PURE__ */ jsx(PatrollerDetails, { ...props })
746
+ });
747
+ $[40] = props;
748
+ $[41] = t17;
749
+ } else t17 = $[41];
750
+ let t18;
751
+ if ($[42] !== t15 || $[43] !== t16 || $[44] !== t17 || $[45] !== t8) {
752
+ t18 = /* @__PURE__ */ jsxs(Fragment, { children: [
753
+ t8,
754
+ t15,
755
+ t16,
756
+ t17
757
+ ] });
758
+ $[42] = t15;
759
+ $[43] = t16;
760
+ $[44] = t17;
761
+ $[45] = t8;
762
+ $[46] = t18;
763
+ } else t18 = $[46];
764
+ return t18;
765
+ }
766
+ /**
767
+ * What a patroller's popover shows: its open findings with their actions, then its reports, then
768
+ * the history of its dismissals.
769
+ */
770
+ function PatrollerDetails(t0) {
771
+ const $ = c(23);
772
+ const { patroller, now, inDraft, onShow, onApply, onAsk, onDismiss, patrolDraft, onOpenPatrolDraft } = t0;
773
+ let t1;
774
+ if ($[0] !== inDraft || $[1] !== onApply || $[2] !== onAsk || $[3] !== onDismiss || $[4] !== onOpenPatrolDraft || $[5] !== onShow || $[6] !== patrolDraft || $[7] !== patroller.findings || $[8] !== patroller.id) {
775
+ t1 = patroller.findings.length > 0 && /* @__PURE__ */ jsx("ul", {
776
+ "data-findings": patroller.id,
777
+ className: "flex flex-col gap-2 text-xs",
778
+ children: patroller.findings.map((finding) => /* @__PURE__ */ jsxs("li", {
779
+ "data-finding": true,
780
+ className: "flex flex-col gap-1",
781
+ children: [/* @__PURE__ */ jsxs("span", {
782
+ className: "flex items-baseline gap-2",
783
+ children: [/* @__PURE__ */ jsx("span", {
784
+ "data-finding-sentence": true,
785
+ className: "min-w-0 flex-1",
786
+ children: findingSentence(finding)
787
+ }), inDraft && finding.live && /* @__PURE__ */ jsx(Badge, {
788
+ variant: "outline",
789
+ size: "sm",
790
+ children: "Live"
791
+ })]
792
+ }), /* @__PURE__ */ jsxs("span", {
793
+ className: "flex flex-wrap gap-1",
794
+ children: [
795
+ finding.place && /* @__PURE__ */ jsx(Button, {
796
+ variant: "outline",
797
+ size: "xs",
798
+ onClick: () => onShow(finding),
799
+ children: "Show"
800
+ }),
801
+ finding.fix && !finding.live && /* @__PURE__ */ jsx(Button, {
802
+ variant: "outline",
803
+ size: "xs",
804
+ onClick: () => onApply(finding),
805
+ children: fixLabel(finding.fix)
806
+ }),
807
+ inDraft && !finding.live && /* @__PURE__ */ jsx(Button, {
808
+ variant: "outline",
809
+ size: "xs",
810
+ onClick: () => onAsk(finding),
811
+ children: "Ask the agent"
812
+ }),
813
+ !inDraft && patrolDraft && /* @__PURE__ */ jsx(Button, {
814
+ variant: "outline",
815
+ size: "xs",
816
+ onClick: () => onOpenPatrolDraft(patrolDraft),
817
+ children: "Open the Patrol Draft"
818
+ }),
819
+ /* @__PURE__ */ jsx(Button, {
820
+ variant: "ghost",
821
+ size: "xs",
822
+ onClick: () => onDismiss(finding),
823
+ children: dismissLabel(finding.about)
824
+ })
825
+ ]
826
+ })]
827
+ }, finding.id))
828
+ });
829
+ $[0] = inDraft;
830
+ $[1] = onApply;
831
+ $[2] = onAsk;
832
+ $[3] = onDismiss;
833
+ $[4] = onOpenPatrolDraft;
834
+ $[5] = onShow;
835
+ $[6] = patrolDraft;
836
+ $[7] = patroller.findings;
837
+ $[8] = patroller.id;
838
+ $[9] = t1;
839
+ } else t1 = $[9];
840
+ let t2;
841
+ if ($[10] !== inDraft || $[11] !== now || $[12] !== onShow || $[13] !== patroller) {
842
+ t2 = /* @__PURE__ */ jsx(PatrollerReports, {
843
+ patroller,
844
+ now,
845
+ inDraft,
846
+ onShow
847
+ });
848
+ $[10] = inDraft;
849
+ $[11] = now;
850
+ $[12] = onShow;
851
+ $[13] = patroller;
852
+ $[14] = t2;
853
+ } else t2 = $[14];
854
+ let t3;
855
+ if ($[15] !== now || $[16] !== patroller.dismissals || $[17] !== patroller.id) {
856
+ t3 = patroller.dismissals.length > 0 && /* @__PURE__ */ jsx("ul", {
857
+ "data-dismissals": patroller.id,
858
+ "aria-label": "Dismissed",
859
+ className: "text-muted-foreground flex flex-col gap-1 border-t pt-2 text-xs",
860
+ children: patroller.dismissals.map((dismissal) => /* @__PURE__ */ jsxs("li", {
861
+ "data-dismissal": true,
862
+ className: "flex items-baseline gap-2",
863
+ children: [/* @__PURE__ */ jsx("span", {
864
+ className: "min-w-0 flex-1",
865
+ children: `${dismissedSentence(dismissal.about)}: dismissed by ${dismissal.by.name}`
866
+ }), /* @__PURE__ */ jsx("time", {
867
+ dateTime: new Date(dismissal.at).toISOString(),
868
+ className: "shrink-0",
869
+ children: shortTimeAgo(dismissal.at, now)
870
+ })]
871
+ }, dismissal.id))
872
+ });
873
+ $[15] = now;
874
+ $[16] = patroller.dismissals;
875
+ $[17] = patroller.id;
876
+ $[18] = t3;
877
+ } else t3 = $[18];
878
+ let t4;
879
+ if ($[19] !== t1 || $[20] !== t2 || $[21] !== t3) {
880
+ t4 = /* @__PURE__ */ jsxs(Fragment, { children: [
881
+ t1,
882
+ t2,
883
+ t3
884
+ ] });
885
+ $[19] = t1;
886
+ $[20] = t2;
887
+ $[21] = t3;
888
+ $[22] = t4;
889
+ } else t4 = $[22];
890
+ return t4;
637
891
  }
638
892
  /**
639
893
  * A patroller's reports, latest first, each with a Live tag when a draft is viewed and how long
@@ -643,6 +897,7 @@ function PatrollerReports(t0) {
643
897
  const $ = c(16);
644
898
  const { patroller, now, inDraft, onShow } = t0;
645
899
  if (patroller.reports.length === 0 && patroller.more.length === 0) {
900
+ if (patroller.findings.length > 0 || patroller.dismissals.length > 0) return null;
646
901
  let t1;
647
902
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
648
903
  t1 = /* @__PURE__ */ jsx("p", {
@@ -753,7 +1008,7 @@ function ReportSentence(t0) {
753
1008
  const t3 = `${sentence}: ${leads}`;
754
1009
  let t4;
755
1010
  if ($[8] !== onShow || $[9] !== report) {
756
- t4 = () => onShow(report);
1011
+ t4 = () => onShow(shownOf(report));
757
1012
  $[8] = onShow;
758
1013
  $[9] = report;
759
1014
  $[10] = t4;
@@ -776,4 +1031,4 @@ function ReportSentence(t0) {
776
1031
  }
777
1032
 
778
1033
  //#endregion
779
- export { Patrol, PatrolRoster, PatrollerReports, inHeldOrder };
1034
+ export { Patrol, PatrolRoster, PatrollerCard, PatrollerDetails, PatrollerReports };