camox 0.71.0 → 0.74.1

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 (104) hide show
  1. package/dist/core/createApp.d.ts +4 -103
  2. package/dist/core/createApp.js +3 -24
  3. package/dist/core/createBlock.d.ts +2 -2
  4. package/dist/core/createBlock.js +41 -185
  5. package/dist/core/createLayout.d.ts +6 -77
  6. package/dist/core/createLayout.js +12 -50
  7. package/dist/core/editing/createEditableBlock.d.ts +9 -75
  8. package/dist/core/editing/createEditableBlock.js +258 -640
  9. package/dist/core/hooks/useIsEditable.js +2 -2
  10. package/dist/core/lib/artifactData.js +8 -3
  11. package/dist/core/lib/contentType.d.ts +23 -38
  12. package/dist/core/lib/contentType.js +34 -62
  13. package/dist/core/lib/iconTypes.d.ts +0 -3
  14. package/dist/core/lib/icons.js +63 -51
  15. package/dist/core/lib/recordScope.js +1 -1
  16. package/dist/features/canvas/CanvasOverlays.js +94 -176
  17. package/dist/features/canvas/CanvasPageFrame.js +62 -68
  18. package/dist/features/canvas/CanvasPreviewPageSlot.js +1 -3
  19. package/dist/features/canvas/canvasOverlayGeometry.js +1 -7
  20. package/dist/features/canvas/sidebar/CuratedPageInspector.js +3 -21
  21. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +1 -2
  22. package/dist/features/canvas/useCanvasCamera.js +1 -1
  23. package/dist/features/canvas/useCanvasFrame.js +1 -1
  24. package/dist/features/content/ContentCollection.js +2 -2
  25. package/dist/features/content/collection-form.js +6 -7
  26. package/dist/features/page/CuratedPageContent.js +15 -24
  27. package/dist/features/page/UnplacedBlockContent.js +1 -6
  28. package/dist/features/preview/CamoxPreview.js +30 -33
  29. package/dist/features/preview/EditablePageContent.js +131 -151
  30. package/dist/features/preview/components/BlockActionsPopover.js +43 -152
  31. package/dist/features/preview/components/CanvasPreview.js +2 -2
  32. package/dist/features/preview/components/Frame.js +3 -3
  33. package/dist/features/preview/components/IconFieldEditor.js +1 -1
  34. package/dist/features/preview/components/ItemFieldsEditor.js +1 -3
  35. package/dist/features/preview/components/Overlays.js +18 -84
  36. package/dist/features/preview/components/PageInfo.js +121 -98
  37. package/dist/features/preview/components/PageTree.js +57 -103
  38. package/dist/features/preview/components/PreviewPanel.js +8 -11
  39. package/dist/features/preview/components/RecordCombobox.js +65 -78
  40. package/dist/features/preview/components/ReferenceFieldEditor.js +1 -19
  41. package/dist/features/preview/components/ReferenceListFieldEditor.js +1 -19
  42. package/dist/features/preview/components/RemovedBlockPlaceholder.js +2 -2
  43. package/dist/features/preview/components/SelectionEditor.js +6 -5
  44. package/dist/features/preview/components/useRepeatableItemActions.js +18 -24
  45. package/dist/features/preview/components/useUpdateBlockPosition.js +1 -1
  46. package/dist/features/preview/overlayMessages.js +1 -4
  47. package/dist/features/preview/previewStore.js +3 -23
  48. package/dist/features/renderer/RendererApp.js +17 -141
  49. package/dist/features/renderer/definitionAnswers.js +1 -8
  50. package/dist/features/renderer/rendererHost.js +1 -31
  51. package/dist/features/renderer/studioConnection.js +1 -25
  52. package/dist/features/routes/pageRuntime.js +15 -4
  53. package/dist/features/runtime/ClaimCard.js +192 -0
  54. package/dist/features/runtime/pageApp.js +5 -4
  55. package/dist/features/runtime/runtime.js +29 -8
  56. package/dist/features/runtime/worker.js +2 -2
  57. package/dist/features/runtime/workerRequest.d.ts +6 -0
  58. package/dist/features/runtime/workerRequest.js +13 -1
  59. package/dist/features/runtime/workerRuntime.d.ts +2 -2
  60. package/dist/features/runtime/workerRuntime.js +2 -2
  61. package/dist/features/studio/components/AgentSidebar.js +7 -7
  62. package/dist/features/studio/components/ChatWidget.js +142 -106
  63. package/dist/features/studio/components/ContentFindings.js +127 -230
  64. package/dist/features/studio/components/DeleteDraftDialog.js +112 -0
  65. package/dist/features/studio/components/DraftBar.js +98 -53
  66. package/dist/features/studio/components/DraftsControl.js +203 -190
  67. package/dist/features/studio/components/ReviewDraftDialog.js +670 -0
  68. package/dist/features/studio/components/{Patrol.js → Specialists.js} +291 -330
  69. package/dist/features/studio/components/Squareling.js +5 -5
  70. package/dist/features/studio/components/ThreadComposer.js +106 -127
  71. package/dist/features/studio/components/ThreadMessages.js +137 -45
  72. package/dist/features/studio/findingSentence.js +14 -12
  73. package/dist/features/studio/fixMessage.js +23 -0
  74. package/dist/features/studio/reportSentence.js +1 -1
  75. package/dist/features/studio/{patrollerBio.js → specialistBio.js} +8 -5
  76. package/dist/features/studio/studioStore.js +29 -15
  77. package/dist/features/studio/workingSentence.js +1 -1
  78. package/dist/features/vite/blockBoilerplate.js +1 -1
  79. package/dist/features/vite/definitionsSync.js +1 -8
  80. package/dist/lib/api-client.js +5 -1
  81. package/dist/lib/queries.js +5 -6
  82. package/dist/lib/studio-definitions.js +0 -2
  83. package/dist/lib/studio-reads.d.ts +1 -1
  84. package/dist/lib/studio-reads.js +5 -10
  85. package/dist/lib/use-thread.js +196 -138
  86. package/dist/package.js +1 -1
  87. package/dist/studio-overlays.css +0 -29
  88. package/dist/studio.css +1 -1
  89. package/package.json +4 -4
  90. package/skills/camox/SKILL.md +1 -1
  91. package/skills/camox/references/artifacts.md +4 -4
  92. package/skills/camox/references/block-definitions.md +21 -25
  93. package/skills/camox/references/dom-integrations.md +1 -1
  94. package/skills/camox/references/layout-definitions.md +3 -12
  95. package/skills/camox/references/routed-layouts.md +2 -2
  96. package/skills/camox-expand/SKILL.md +5 -5
  97. package/skills/camoxify/SKILL.md +4 -4
  98. package/dist/features/canvas/canvasInsertionSeams.js +0 -44
  99. package/dist/features/preview/components/AddBlockSheet.js +0 -314
  100. package/dist/features/preview/components/BlockPeek.js +0 -66
  101. package/dist/features/preview/components/BlockThumbnail.js +0 -281
  102. package/dist/features/preview/components/blockGridNavigation.js +0 -15
  103. package/dist/features/preview/referencePickerFocus.js +0 -29
  104. package/dist/features/studio/components/ReviewDraftSheet.js +0 -1587
@@ -9,12 +9,12 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  * mark, or at the editor while idle. Its face and motion say what it is doing. The animations
10
10
  * live in `studio.css`.
11
11
  *
12
- * The agent's (the default) is the logo's square with its soft corner, in emerald. Each patroller
12
+ * The agent's (the default) is the logo's square with its soft corner, in emerald. Each specialist
13
13
  * has its own, one family: the Translator is the logo's wide wedge, in violet, glancing across
14
14
  * while it works; the SEO manager is a tall block with the small shape's soft corner, in amber,
15
15
  * scanning down while it works.
16
16
  *
17
- * A patroller off duty where the editor is sleeps, eyes closed, breathing slowly.
17
+ * A specialist off duty where the editor is sleeps, eyes closed, breathing slowly.
18
18
  *
19
19
  * One face flows into the next: its eyes glide to where the next face holds them, opening or
20
20
  * narrowing, and a looping motion settles before the next one starts (`useFaceChange`).
@@ -67,7 +67,7 @@ function Squareling(props) {
67
67
  children: props.variant === void 0 || props.variant === "agent" ? /* @__PURE__ */ jsx(AgentEyes, {
68
68
  eyes: look.eyes,
69
69
  status: props.status
70
- }) : /* @__PURE__ */ jsx(PatrollerEyes, {
70
+ }) : /* @__PURE__ */ jsx(SpecialistEyes, {
71
71
  eyes: look.eyes,
72
72
  working: props.status === "working",
73
73
  asleep: props.status === "asleep"
@@ -306,10 +306,10 @@ function AgentEyes(t0) {
306
306
  return t5;
307
307
  }
308
308
  /**
309
- * A patroller's eyes: open while idle, narrowed on the work while working, closed while asleep,
309
+ * A specialist's eyes: open while idle, narrowed on the work while working, closed while asleep,
310
310
  * when they don't blink.
311
311
  */
312
- function PatrollerEyes({ eyes, working, asleep }) {
312
+ function SpecialistEyes({ eyes, working, asleep }) {
313
313
  return eyes.x.map((x) => /* @__PURE__ */ jsx(Eye, {
314
314
  x,
315
315
  y: eyes.y,
@@ -8,14 +8,13 @@ import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupTextarea } fro
8
8
  /**
9
9
  * Where an editor writes to the agent: Enter sends, Shift+Enter adds a line. While a run is
10
10
  * going, sending steers it, and a Stop button sits beside Send. Over the message, what the
11
- * editor has selected, which their messages carry to the agent until they clear it. A message
12
- * filled in from elsewhere in the studio replaces what the editor wrote, for them to send. Given a
11
+ * editor has selected, which their messages carry to the agent until they clear it. Given a
13
12
  * `carry`, the composer of the next thread goes on from this one, as when the studio enters the
14
13
  * draft the editor's request was handed off to: with what they were writing, focused if it was.
15
14
  */
16
15
  function ThreadComposer(t0) {
17
- const $ = c(48);
18
- const { onSend, onStop, selection, onClearSelection, running: t1, disabled: t2, filled, carry } = t0;
16
+ const $ = c(45);
17
+ const { onSend, onStop, selection, onClearSelection, running: t1, disabled: t2, carry } = t0;
19
18
  const running = t1 === void 0 ? false : t1;
20
19
  const disabled = t2 === void 0 ? false : t2;
21
20
  let t3;
@@ -25,11 +24,6 @@ function ThreadComposer(t0) {
25
24
  $[1] = t3;
26
25
  } else t3 = $[1];
27
26
  const [text, setText] = useState(t3);
28
- const [filledId, setFilledId] = useState();
29
- if (filled && filled.id !== filledId) {
30
- setFilledId(filled.id);
31
- setText(filled.text);
32
- }
33
27
  const box = useRef(null);
34
28
  let t4;
35
29
  let t5;
@@ -72,53 +66,38 @@ function ThreadComposer(t0) {
72
66
  }
73
67
  useLayoutEffect(t6, t7);
74
68
  let t8;
75
- let t9;
76
- if ($[9] !== filledId) {
77
- t8 = () => {
78
- if (filledId !== void 0) box.current?.focus();
79
- };
80
- t9 = [filledId];
81
- $[9] = filledId;
69
+ if ($[9] !== text) {
70
+ t8 = text.trim();
71
+ $[9] = text;
82
72
  $[10] = t8;
83
- $[11] = t9;
84
- } else {
85
- t8 = $[10];
86
- t9 = $[11];
87
- }
88
- useEffect(t8, t9);
89
- let t10;
90
- if ($[12] !== text) {
91
- t10 = text.trim();
92
- $[12] = text;
93
- $[13] = t10;
94
- } else t10 = $[13];
95
- const message = t10;
73
+ } else t8 = $[10];
74
+ const message = t8;
96
75
  const canSend = !disabled && message !== "" && onSend !== void 0;
97
- let t11;
98
- if ($[14] !== canSend || $[15] !== message || $[16] !== onSend) {
99
- t11 = () => {
76
+ let t9;
77
+ if ($[11] !== canSend || $[12] !== message || $[13] !== onSend) {
78
+ t9 = () => {
100
79
  if (!canSend) return;
101
80
  onSend(message);
102
81
  setText("");
103
82
  };
104
- $[14] = canSend;
105
- $[15] = message;
106
- $[16] = onSend;
107
- $[17] = t11;
108
- } else t11 = $[17];
109
- const submit = t11;
110
- let t12;
111
- if ($[18] !== submit) {
112
- t12 = (event) => {
83
+ $[11] = canSend;
84
+ $[12] = message;
85
+ $[13] = onSend;
86
+ $[14] = t9;
87
+ } else t9 = $[14];
88
+ const submit = t9;
89
+ let t10;
90
+ if ($[15] !== submit) {
91
+ t10 = (event) => {
113
92
  event.preventDefault();
114
93
  submit();
115
94
  };
116
- $[18] = submit;
117
- $[19] = t12;
118
- } else t12 = $[19];
119
- let t13;
120
- if ($[20] !== onClearSelection || $[21] !== selection) {
121
- t13 = selection && /* @__PURE__ */ jsx(InputGroupAddon, {
95
+ $[15] = submit;
96
+ $[16] = t10;
97
+ } else t10 = $[16];
98
+ let t11;
99
+ if ($[17] !== onClearSelection || $[18] !== selection) {
100
+ t11 = selection && /* @__PURE__ */ jsx(InputGroupAddon, {
122
101
  align: "block-start",
123
102
  className: "border-input border-b",
124
103
  children: /* @__PURE__ */ jsx(SelectionChip, {
@@ -126,45 +105,45 @@ function ThreadComposer(t0) {
126
105
  onClear: onClearSelection
127
106
  })
128
107
  });
129
- $[20] = onClearSelection;
130
- $[21] = selection;
131
- $[22] = t13;
132
- } else t13 = $[22];
133
- let t14;
134
- if ($[23] === Symbol.for("react.memo_cache_sentinel")) {
135
- t14 = (event_0) => setText(event_0.target.value);
136
- $[23] = t14;
137
- } else t14 = $[23];
138
- let t15;
139
- if ($[24] !== submit) {
140
- t15 = (event_1) => {
108
+ $[17] = onClearSelection;
109
+ $[18] = selection;
110
+ $[19] = t11;
111
+ } else t11 = $[19];
112
+ let t12;
113
+ if ($[20] === Symbol.for("react.memo_cache_sentinel")) {
114
+ t12 = (event_0) => setText(event_0.target.value);
115
+ $[20] = t12;
116
+ } else t12 = $[20];
117
+ let t13;
118
+ if ($[21] !== submit) {
119
+ t13 = (event_1) => {
141
120
  if (event_1.key !== "Enter" || event_1.shiftKey || event_1.nativeEvent.isComposing) return;
142
121
  event_1.preventDefault();
143
122
  submit();
144
123
  };
145
- $[24] = submit;
146
- $[25] = t15;
147
- } else t15 = $[25];
148
- let t16;
149
- if ($[26] !== disabled || $[27] !== t15 || $[28] !== text) {
150
- t16 = /* @__PURE__ */ jsx(InputGroupTextarea, {
124
+ $[21] = submit;
125
+ $[22] = t13;
126
+ } else t13 = $[22];
127
+ let t14;
128
+ if ($[23] !== disabled || $[24] !== t13 || $[25] !== text) {
129
+ t14 = /* @__PURE__ */ jsx(InputGroupTextarea, {
151
130
  ref: box,
152
131
  "aria-label": "Message",
153
132
  placeholder: "Message Camox…",
154
133
  value: text,
155
134
  disabled,
156
- onChange: t14,
157
- onKeyDown: t15,
135
+ onChange: t12,
136
+ onKeyDown: t13,
158
137
  className: "max-h-48 min-h-14"
159
138
  });
160
- $[26] = disabled;
161
- $[27] = t15;
162
- $[28] = text;
163
- $[29] = t16;
164
- } else t16 = $[29];
165
- let t17;
166
- if ($[30] !== disabled || $[31] !== onStop || $[32] !== running) {
167
- t17 = running && onStop && /* @__PURE__ */ jsxs(InputGroupButton, {
139
+ $[23] = disabled;
140
+ $[24] = t13;
141
+ $[25] = text;
142
+ $[26] = t14;
143
+ } else t14 = $[26];
144
+ let t15;
145
+ if ($[27] !== disabled || $[28] !== onStop || $[29] !== running) {
146
+ t15 = running && onStop && /* @__PURE__ */ jsxs(InputGroupButton, {
168
147
  type: "button",
169
148
  variant: "outline",
170
149
  size: "icon-sm",
@@ -175,73 +154,73 @@ function ThreadComposer(t0) {
175
154
  children: "Stop"
176
155
  })]
177
156
  });
178
- $[30] = disabled;
179
- $[31] = onStop;
180
- $[32] = running;
181
- $[33] = t17;
182
- } else t17 = $[33];
183
- const t18 = !canSend;
184
- let t19;
185
- let t20;
186
- if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
187
- t19 = /* @__PURE__ */ jsx(ArrowUp, {});
188
- t20 = /* @__PURE__ */ jsx("span", {
157
+ $[27] = disabled;
158
+ $[28] = onStop;
159
+ $[29] = running;
160
+ $[30] = t15;
161
+ } else t15 = $[30];
162
+ const t16 = !canSend;
163
+ let t17;
164
+ let t18;
165
+ if ($[31] === Symbol.for("react.memo_cache_sentinel")) {
166
+ t17 = /* @__PURE__ */ jsx(ArrowUp, {});
167
+ t18 = /* @__PURE__ */ jsx("span", {
189
168
  className: "sr-only",
190
169
  children: "Send"
191
170
  });
192
- $[34] = t19;
193
- $[35] = t20;
171
+ $[31] = t17;
172
+ $[32] = t18;
194
173
  } else {
195
- t19 = $[34];
196
- t20 = $[35];
174
+ t17 = $[31];
175
+ t18 = $[32];
197
176
  }
198
- let t21;
199
- if ($[36] !== t18) {
200
- t21 = /* @__PURE__ */ jsxs(InputGroupButton, {
177
+ let t19;
178
+ if ($[33] !== t16) {
179
+ t19 = /* @__PURE__ */ jsxs(InputGroupButton, {
201
180
  type: "submit",
202
181
  variant: "default",
203
182
  size: "icon-sm",
204
- disabled: t18,
205
- children: [t19, t20]
183
+ disabled: t16,
184
+ children: [t17, t18]
206
185
  });
207
- $[36] = t18;
208
- $[37] = t21;
209
- } else t21 = $[37];
210
- let t22;
211
- if ($[38] !== t17 || $[39] !== t21) {
212
- t22 = /* @__PURE__ */ jsxs(InputGroupAddon, {
186
+ $[33] = t16;
187
+ $[34] = t19;
188
+ } else t19 = $[34];
189
+ let t20;
190
+ if ($[35] !== t15 || $[36] !== t19) {
191
+ t20 = /* @__PURE__ */ jsxs(InputGroupAddon, {
213
192
  align: "block-end",
214
193
  className: "justify-end",
215
- children: [t17, t21]
194
+ children: [t15, t19]
216
195
  });
217
- $[38] = t17;
218
- $[39] = t21;
219
- $[40] = t22;
220
- } else t22 = $[40];
221
- let t23;
222
- if ($[41] !== t13 || $[42] !== t16 || $[43] !== t22) {
223
- t23 = /* @__PURE__ */ jsxs(InputGroup, { children: [
224
- t13,
225
- t16,
226
- t22
196
+ $[35] = t15;
197
+ $[36] = t19;
198
+ $[37] = t20;
199
+ } else t20 = $[37];
200
+ let t21;
201
+ if ($[38] !== t11 || $[39] !== t14 || $[40] !== t20) {
202
+ t21 = /* @__PURE__ */ jsxs(InputGroup, { children: [
203
+ t11,
204
+ t14,
205
+ t20
227
206
  ] });
228
- $[41] = t13;
229
- $[42] = t16;
230
- $[43] = t22;
231
- $[44] = t23;
232
- } else t23 = $[44];
233
- let t24;
234
- if ($[45] !== t12 || $[46] !== t23) {
235
- t24 = /* @__PURE__ */ jsx("form", {
207
+ $[38] = t11;
208
+ $[39] = t14;
209
+ $[40] = t20;
210
+ $[41] = t21;
211
+ } else t21 = $[41];
212
+ let t22;
213
+ if ($[42] !== t10 || $[43] !== t21) {
214
+ t22 = /* @__PURE__ */ jsx("form", {
236
215
  className: "p-2",
237
- onSubmit: t12,
238
- children: t23
216
+ onSubmit: t10,
217
+ children: t21
239
218
  });
240
- $[45] = t12;
241
- $[46] = t23;
242
- $[47] = t24;
243
- } else t24 = $[47];
244
- return t24;
219
+ $[42] = t10;
220
+ $[43] = t21;
221
+ $[44] = t22;
222
+ } else t22 = $[44];
223
+ return t22;
245
224
  }
246
225
  /** A carrier that lasts as long as the component that keeps it. */
247
226
  function useComposerCarrier() {
@@ -18,7 +18,8 @@ import { MessageScroller, MessageScrollerButton, MessageScrollerContent, Message
18
18
  * agent does, never how: thinking, looking around, making changes, building, fixing the build,
19
19
  * or starting a draft. Each editor's message shows its
20
20
  * sender's avatar, without their name. A Live turn handed off to a draft ends on the draft's
21
- * card, which `onOpenDraft` opens. The thread keeps its end in view, unless the editor scrolls up.
21
+ * card, which `onOpenDraft` opens, and the draft's thread shows the hand-off's Brief collapsed
22
+ * under the editor's messages. The thread keeps its end in view, unless the editor scrolls up.
22
23
  */
23
24
  function ThreadMessages(t0) {
24
25
  const $ = c(20);
@@ -297,7 +298,7 @@ const PUSH_OUTCOMES = {
297
298
  notPreviewed: "Built, but the preview can’t show it."
298
299
  };
299
300
  function ThreadMessageRow(t0) {
300
- const $ = c(44);
301
+ const $ = c(50);
301
302
  const { message, onOpenDraft } = t0;
302
303
  if (message.from === "draft") {
303
304
  let t1;
@@ -406,86 +407,117 @@ function ThreadMessageRow(t0) {
406
407
  }
407
408
  if (message.from === "connectedAgent") {
408
409
  const { member, client } = message;
410
+ const sender = `${member.name}’s ${client.verified ? client.name : "agent"}`;
411
+ if (message.answer) {
412
+ const t1 = `${sender} added context`;
413
+ const t2 = client.verified ? void 0 : client.name;
414
+ let t3;
415
+ if ($[26] !== message.text || $[27] !== t1 || $[28] !== t2) {
416
+ t3 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(CollapsedNote, {
417
+ label: t1,
418
+ hideLabel: "Hide the context",
419
+ title: t2,
420
+ text: message.text
421
+ }) }) });
422
+ $[26] = message.text;
423
+ $[27] = t1;
424
+ $[28] = t2;
425
+ $[29] = t3;
426
+ } else t3 = $[29];
427
+ return t3;
428
+ }
409
429
  const t1 = client.verified ? void 0 : client.name;
410
- const t2 = `${member.name}’s ${client.verified ? client.name : "agent"}`;
411
- let t3;
412
- if ($[26] !== t1 || $[27] !== t2) {
413
- t3 = /* @__PURE__ */ jsx(MessageHeader, {
430
+ let t2;
431
+ if ($[30] !== sender || $[31] !== t1) {
432
+ t2 = /* @__PURE__ */ jsx(MessageHeader, {
414
433
  className: "text-muted-foreground text-xs",
415
434
  title: t1,
416
- children: t2
435
+ children: sender
417
436
  });
418
- $[26] = t1;
419
- $[27] = t2;
420
- $[28] = t3;
421
- } else t3 = $[28];
422
- let t4;
423
- if ($[29] !== message.text) {
424
- t4 = /* @__PURE__ */ jsx(Bubble, {
437
+ $[30] = sender;
438
+ $[31] = t1;
439
+ $[32] = t2;
440
+ } else t2 = $[32];
441
+ let t3;
442
+ if ($[33] !== message.text) {
443
+ t3 = /* @__PURE__ */ jsx(Bubble, {
425
444
  variant: "muted",
426
445
  className: "max-w-full",
427
446
  children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
428
447
  });
429
- $[29] = message.text;
430
- $[30] = t4;
431
- } else t4 = $[30];
432
- let t5;
433
- if ($[31] !== t3 || $[32] !== t4) {
434
- t5 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t3, t4] }) });
435
- $[31] = t3;
436
- $[32] = t4;
437
- $[33] = t5;
438
- } else t5 = $[33];
439
- return t5;
448
+ $[33] = message.text;
449
+ $[34] = t3;
450
+ } else t3 = $[34];
451
+ let t4;
452
+ if ($[35] !== t2 || $[36] !== t3) {
453
+ t4 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t2, t3] }) });
454
+ $[35] = t2;
455
+ $[36] = t3;
456
+ $[37] = t4;
457
+ } else t4 = $[37];
458
+ return t4;
459
+ }
460
+ if (message.from === "brief") {
461
+ let t1;
462
+ if ($[38] !== message.text) {
463
+ t1 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(CollapsedNote, {
464
+ label: "Camox briefed the agent",
465
+ hideLabel: "Hide the brief",
466
+ text: message.text
467
+ }) }) });
468
+ $[38] = message.text;
469
+ $[39] = t1;
470
+ } else t1 = $[39];
471
+ return t1;
440
472
  }
441
473
  if (message.from === "camox") {
442
474
  let t1;
443
- if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
475
+ if ($[40] === Symbol.for("react.memo_cache_sentinel")) {
444
476
  t1 = /* @__PURE__ */ jsx(MessageHeader, {
445
477
  className: "text-muted-foreground text-xs",
446
478
  children: "Camox"
447
479
  });
448
- $[34] = t1;
449
- } else t1 = $[34];
480
+ $[40] = t1;
481
+ } else t1 = $[40];
450
482
  let t2;
451
- if ($[35] !== message.text) {
483
+ if ($[41] !== message.text) {
452
484
  t2 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t1, /* @__PURE__ */ jsx(Bubble, {
453
485
  variant: "muted",
454
486
  className: "max-w-full",
455
487
  children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
456
488
  })] }) });
457
- $[35] = message.text;
458
- $[36] = t2;
459
- } else t2 = $[36];
489
+ $[41] = message.text;
490
+ $[42] = t2;
491
+ } else t2 = $[42];
460
492
  return t2;
461
493
  }
462
494
  let t1;
463
- if ($[37] !== message.sender) {
495
+ if ($[43] !== message.sender) {
464
496
  t1 = /* @__PURE__ */ jsx(MessageAvatar, { children: /* @__PURE__ */ jsx(SenderAvatar, { sender: message.sender }) });
465
- $[37] = message.sender;
466
- $[38] = t1;
467
- } else t1 = $[38];
497
+ $[43] = message.sender;
498
+ $[44] = t1;
499
+ } else t1 = $[44];
468
500
  let t2;
469
- if ($[39] !== message.text) {
501
+ if ($[45] !== message.text) {
470
502
  t2 = /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Bubble, {
471
503
  align: "end",
472
504
  variant: "muted",
473
505
  className: "max-w-full",
474
506
  children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx(CroppedText, { text: message.text }) })
475
507
  }) });
476
- $[39] = message.text;
477
- $[40] = t2;
478
- } else t2 = $[40];
508
+ $[45] = message.text;
509
+ $[46] = t2;
510
+ } else t2 = $[46];
479
511
  let t3;
480
- if ($[41] !== t1 || $[42] !== t2) {
512
+ if ($[47] !== t1 || $[48] !== t2) {
481
513
  t3 = /* @__PURE__ */ jsxs(Message, {
482
514
  align: "end",
483
515
  children: [t1, t2]
484
516
  });
485
- $[41] = t1;
486
- $[42] = t2;
487
- $[43] = t3;
488
- } else t3 = $[43];
517
+ $[47] = t1;
518
+ $[48] = t2;
519
+ $[49] = t3;
520
+ } else t3 = $[49];
489
521
  return t3;
490
522
  }
491
523
  function _temp(subject, index) {
@@ -575,6 +607,66 @@ function CroppedText(t0) {
575
607
  } else t9 = $[16];
576
608
  return t9;
577
609
  }
610
+ /**
611
+ * Context the agent was given beside the editors' requests, such as the Brief the agent on Live
612
+ * handed the request off with, or a Connected Agent's answers to its questions: a quiet line,
613
+ * `label`, so the requests read on as they were sent, which shows `text` once clicked.
614
+ */
615
+ function CollapsedNote(t0) {
616
+ const $ = c(11);
617
+ const { label, hideLabel, title, text } = t0;
618
+ const [open, setOpen] = React.useState(false);
619
+ let t1;
620
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
621
+ t1 = () => setOpen(_temp2);
622
+ $[0] = t1;
623
+ } else t1 = $[0];
624
+ const t2 = open ? hideLabel : label;
625
+ let t3;
626
+ if ($[1] !== open || $[2] !== t2 || $[3] !== title) {
627
+ t3 = /* @__PURE__ */ jsx("button", {
628
+ type: "button",
629
+ "aria-expanded": open,
630
+ title,
631
+ onClick: t1,
632
+ className: "focus-visible:ring-ring self-start rounded-sm hover:underline focus-visible:ring-2 focus-visible:outline-none",
633
+ children: t2
634
+ });
635
+ $[1] = open;
636
+ $[2] = t2;
637
+ $[3] = title;
638
+ $[4] = t3;
639
+ } else t3 = $[4];
640
+ let t4;
641
+ if ($[5] !== open || $[6] !== text) {
642
+ t4 = open && /* @__PURE__ */ jsx(Bubble, {
643
+ variant: "muted",
644
+ className: "max-w-full",
645
+ children: /* @__PURE__ */ jsx(BubbleContent, { children: /* @__PURE__ */ jsx("div", {
646
+ className: "whitespace-pre-wrap",
647
+ children: text
648
+ }) })
649
+ });
650
+ $[5] = open;
651
+ $[6] = text;
652
+ $[7] = t4;
653
+ } else t4 = $[7];
654
+ let t5;
655
+ if ($[8] !== t3 || $[9] !== t4) {
656
+ t5 = /* @__PURE__ */ jsxs("div", {
657
+ "data-thread-collapsed": true,
658
+ className: "text-muted-foreground flex flex-col gap-2 text-xs",
659
+ children: [t3, t4]
660
+ });
661
+ $[8] = t3;
662
+ $[9] = t4;
663
+ $[10] = t5;
664
+ } else t5 = $[10];
665
+ return t5;
666
+ }
667
+ function _temp2(current) {
668
+ return !current;
669
+ }
578
670
  const initials = (name) => name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
579
671
  const DRAFT_CARD_CLASSES = "bg-muted/50 flex w-full flex-col items-start gap-0.5 rounded-lg border px-3 py-2 text-left text-sm";
580
672
  /**
@@ -1,14 +1,13 @@
1
- import { localeName } from "./localeOptions.js";
2
- import { findingSentence, findingSentenceInPlace } from "@camox/api-contract/patrol";
1
+ import { fixMessage, whereSentence } from "./fixMessage.js";
2
+ import { findingSentence, findingSentenceInPlace } from "@camox/api-contract/specialists";
3
3
 
4
4
  //#region src/features/studio/findingSentence.ts
5
- /** Quoted as editors read it. */
6
- const quoted = (text) => `“${text}”`;
7
- /** What a dismissal dismissed, briefly: "Links to About". */
5
+ /** What a dismissal dismissed, briefly: "Links to About", or "No meta title". */
8
6
  function dismissedSentence(about) {
9
7
  switch (about.kind) {
10
8
  case "missingPage": return `Links to ${about.page ?? "a missing page"}`;
11
9
  case "brokenLink": return `Links to ${about.url}`;
10
+ case "missingMetaTitle": return "No meta title";
12
11
  }
13
12
  }
14
13
  /** What dismissing a finding says, as its button says it: a broken link's is "This link is fine". */
@@ -22,14 +21,17 @@ function fixLabel(fix) {
22
21
  }
23
22
  }
24
23
  /**
25
- * What "Ask the agent" fills the draft's chat with: who found what, where, and the fix it
26
- * suggests, for the agent to find among the draft's findings and fix.
24
+ * What Fix with agent sends to the chat of what the studio views: who found what, where, and the
25
+ * fix it suggests, for the agent to find among the findings it sees and fix.
27
26
  */
28
- function askAgentMessage(finding, role) {
29
- const where = `(in ${localeName(finding.locale)}, field ${quoted(finding.field)})`;
30
- const fix = finding.fix ? ` Suggested fix: ${fixLabel(finding.fix)}.` : "";
31
- return `${role} finding: ${findingSentence(finding)} ${where}.${fix}`;
27
+ function fixWithAgentMessage(finding, role) {
28
+ return fixMessage({
29
+ from: `${role} finding`,
30
+ what: findingSentence(finding),
31
+ places: [whereSentence(finding)],
32
+ fix: finding.fix ? fixLabel(finding.fix) : void 0
33
+ });
32
34
  }
33
35
 
34
36
  //#endregion
35
- export { askAgentMessage, dismissLabel, dismissedSentence, findingSentence, findingSentenceInPlace, fixLabel };
37
+ export { dismissLabel, dismissedSentence, findingSentence, findingSentenceInPlace, fixLabel, fixWithAgentMessage };
@@ -0,0 +1,23 @@
1
+ import { localeName } from "./localeOptions.js";
2
+
3
+ //#region src/features/studio/fixMessage.ts
4
+ /** Quoted as editors read it. */
5
+ const quoted = (text) => `“${text}”`;
6
+ /** Where something is in content, in a sentence: "Hero on Home, in German, field “image”". */
7
+ function whereSentence({ label, locale, field }) {
8
+ return [
9
+ label,
10
+ locale && `in ${localeName(locale)}`,
11
+ field && `field ${quoted(field)}`
12
+ ].filter(Boolean).join(", ");
13
+ }
14
+ /**
15
+ * The message: "Link checker finding: … (in French, field “body”). Suggested fix: Remove the
16
+ * link." Each of `places` is a `whereSentence`.
17
+ */
18
+ function fixMessage({ from, what, places = [], fix }) {
19
+ return `${from}: ${what}${places.length > 0 ? ` (${places.join("; ")})` : ""}.${fix ? ` Suggested fix: ${fix}.` : ""}`;
20
+ }
21
+
22
+ //#endregion
23
+ export { fixMessage, quoted, whereSentence };
@@ -1,5 +1,5 @@
1
1
  import { localeName } from "./localeOptions.js";
2
- import { reportLocale } from "@camox/api-contract/patrol";
2
+ import { reportLocale } from "@camox/api-contract/specialists";
3
3
 
4
4
  //#region src/features/studio/reportSentence.ts
5
5
  const PHRASES = {