camox 0.78.0 → 0.80.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.
- package/dist/core/lib/artifact.d.ts +26 -1
- package/dist/core/lib/artifactData.d.ts +5 -1
- package/dist/core/lib/artifactHost.d.ts +43 -0
- package/dist/features/brand/BrandPart.js +116 -0
- package/dist/features/brand/CamoxBrand.js +758 -0
- package/dist/features/brand/VoiceGroup.js +443 -0
- package/dist/features/preview/EditablePageExperience.js +2 -2
- package/dist/features/runtime/artifactReplay.d.ts +3 -0
- package/dist/features/runtime/artifactReplay.js +4 -0
- package/dist/features/runtime/runtime.d.ts +1 -1
- package/dist/features/runtime/runtime.js +4 -4
- package/dist/features/studio/components/DraftReview.js +55 -32
- package/dist/features/studio/components/Navbar.js +9 -7
- package/dist/features/studio/routes.js +3 -3
- package/dist/features/vite/vite.js +1 -0
- package/dist/lib/queries.js +10 -6
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/studio-reads.js +6 -0
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +8 -4
- package/dist/features/memory/CamoxMemory.js +0 -747
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import { useDraftScope } from "../../lib/draft-scope.js";
|
|
2
|
+
import { useNavigate } from "../navigation/navigation.js";
|
|
3
|
+
import { reads, useReadQuery, useStudioCache } from "../../lib/studio-reads.js";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
import { brandMutations } from "../../lib/queries.js";
|
|
6
|
+
import { previewStore } from "../preview/previewStore.js";
|
|
7
|
+
import { useWebsiteHref } from "../studio/components/Navbar.js";
|
|
8
|
+
import { BrandPart } from "./BrandPart.js";
|
|
9
|
+
import { c } from "react/compiler-runtime";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { useMutation } from "@tanstack/react-query";
|
|
13
|
+
import { Button } from "@camox/ui/button";
|
|
14
|
+
import { Skeleton } from "@camox/ui/skeleton";
|
|
15
|
+
import { Spinner } from "@camox/ui/spinner";
|
|
16
|
+
import { Slider } from "@base-ui/react/slider";
|
|
17
|
+
import { VOICE_AXES } from "@camox/api-contract/voice";
|
|
18
|
+
|
|
19
|
+
//#region src/features/brand/VoiceGroup.tsx
|
|
20
|
+
/**
|
|
21
|
+
* The Voice group, at the top of the Brand Section (ADR 0051): a slider per Voice Axis, showing
|
|
22
|
+
* its chosen step and that step's definition, as models read it. Moving sliders saves nothing:
|
|
23
|
+
* Save shows while they differ from the saved Voice, and saves every moved axis as one change.
|
|
24
|
+
* Like any edit to the brand, saving while viewing Live starts a draft. "Save and rewrite", beside it,
|
|
25
|
+
* also asks Camox's agent, as the member, to rewrite the site's copy in the new Voice, and shows
|
|
26
|
+
* the draft with its thread beside the Canvas, where the rewrite happens.
|
|
27
|
+
*/
|
|
28
|
+
function VoiceGroup(t0) {
|
|
29
|
+
const $ = c(37);
|
|
30
|
+
const { projectId } = t0;
|
|
31
|
+
let t1;
|
|
32
|
+
if ($[0] !== projectId) {
|
|
33
|
+
t1 = reads.voice({ projectId });
|
|
34
|
+
$[0] = projectId;
|
|
35
|
+
$[1] = t1;
|
|
36
|
+
} else t1 = $[1];
|
|
37
|
+
const { data: saved } = useReadQuery(t1);
|
|
38
|
+
let t2;
|
|
39
|
+
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
40
|
+
t2 = {};
|
|
41
|
+
$[2] = t2;
|
|
42
|
+
} else t2 = $[2];
|
|
43
|
+
const [moved, setMoved] = React.useState(t2);
|
|
44
|
+
const cache = useStudioCache();
|
|
45
|
+
const draftScope = useDraftScope();
|
|
46
|
+
const navigate = useNavigate();
|
|
47
|
+
const websiteHref = useWebsiteHref();
|
|
48
|
+
const heading = React.useRef(null);
|
|
49
|
+
const actions = React.useRef(null);
|
|
50
|
+
const [justSaved, setJustSaved] = React.useState(false);
|
|
51
|
+
let t3;
|
|
52
|
+
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
|
53
|
+
t3 = brandMutations.saveVoice();
|
|
54
|
+
$[3] = t3;
|
|
55
|
+
} else t3 = $[3];
|
|
56
|
+
let t4;
|
|
57
|
+
if ($[4] !== cache || $[5] !== draftScope || $[6] !== navigate || $[7] !== projectId || $[8] !== websiteHref) {
|
|
58
|
+
t4 = {
|
|
59
|
+
...t3,
|
|
60
|
+
onSuccess: (_voice, t5) => {
|
|
61
|
+
const { rewrite } = t5;
|
|
62
|
+
const focused = document.activeElement;
|
|
63
|
+
const focusWasOnSave = !focused || focused === document.body || actions.current?.contains(focused);
|
|
64
|
+
setMoved({});
|
|
65
|
+
cache.invalidate(reads.voice({ projectId }));
|
|
66
|
+
if (!rewrite) {
|
|
67
|
+
setJustSaved(true);
|
|
68
|
+
if (focusWasOnSave) heading.current?.focus();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
previewStore.send({ type: "showCanvas" });
|
|
72
|
+
navigate({
|
|
73
|
+
to: websiteHref(),
|
|
74
|
+
draft: draftScope.current() ?? void 0
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
$[4] = cache;
|
|
79
|
+
$[5] = draftScope;
|
|
80
|
+
$[6] = navigate;
|
|
81
|
+
$[7] = projectId;
|
|
82
|
+
$[8] = websiteHref;
|
|
83
|
+
$[9] = t4;
|
|
84
|
+
} else t4 = $[9];
|
|
85
|
+
const save = useMutation(t4);
|
|
86
|
+
let t5;
|
|
87
|
+
if ($[10] !== moved || $[11] !== saved) {
|
|
88
|
+
t5 = saved && {
|
|
89
|
+
...saved,
|
|
90
|
+
...moved
|
|
91
|
+
};
|
|
92
|
+
$[10] = moved;
|
|
93
|
+
$[11] = saved;
|
|
94
|
+
$[12] = t5;
|
|
95
|
+
} else t5 = $[12];
|
|
96
|
+
const voice = t5;
|
|
97
|
+
let t6;
|
|
98
|
+
if ($[13] !== saved || $[14] !== voice) {
|
|
99
|
+
t6 = saved && voice ? changedAxes(saved, voice) : {};
|
|
100
|
+
$[13] = saved;
|
|
101
|
+
$[14] = voice;
|
|
102
|
+
$[15] = t6;
|
|
103
|
+
} else t6 = $[15];
|
|
104
|
+
const changed = t6;
|
|
105
|
+
let t7;
|
|
106
|
+
if ($[16] !== changed) {
|
|
107
|
+
t7 = Object.keys(changed);
|
|
108
|
+
$[16] = changed;
|
|
109
|
+
$[17] = t7;
|
|
110
|
+
} else t7 = $[17];
|
|
111
|
+
const dirty = t7.length > 0;
|
|
112
|
+
const t8 = dirty ? "The Voice has unsaved changes." : justSaved ? "Voice saved." : "";
|
|
113
|
+
let t9;
|
|
114
|
+
if ($[18] !== t8) {
|
|
115
|
+
t9 = /* @__PURE__ */ jsx("p", {
|
|
116
|
+
role: "status",
|
|
117
|
+
className: "sr-only",
|
|
118
|
+
children: t8
|
|
119
|
+
});
|
|
120
|
+
$[18] = t8;
|
|
121
|
+
$[19] = t9;
|
|
122
|
+
} else t9 = $[19];
|
|
123
|
+
let t10;
|
|
124
|
+
if ($[20] !== changed || $[21] !== dirty || $[22] !== projectId || $[23] !== save) {
|
|
125
|
+
t10 = dirty && /* @__PURE__ */ jsxs("div", {
|
|
126
|
+
className: "flex flex-wrap gap-2",
|
|
127
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
128
|
+
disabled: save.isPending,
|
|
129
|
+
onClick: () => save.mutate({
|
|
130
|
+
projectId,
|
|
131
|
+
steps: changed
|
|
132
|
+
}),
|
|
133
|
+
children: [save.isPending && !save.variables?.rewrite && /* @__PURE__ */ jsx(Spinner, {}), "Save"]
|
|
134
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
135
|
+
variant: "outline",
|
|
136
|
+
disabled: save.isPending,
|
|
137
|
+
onClick: () => save.mutate({
|
|
138
|
+
projectId,
|
|
139
|
+
steps: changed,
|
|
140
|
+
rewrite: true
|
|
141
|
+
}),
|
|
142
|
+
children: [save.isPending && save.variables?.rewrite && /* @__PURE__ */ jsx(Spinner, {}), "Save and rewrite"]
|
|
143
|
+
})]
|
|
144
|
+
});
|
|
145
|
+
$[20] = changed;
|
|
146
|
+
$[21] = dirty;
|
|
147
|
+
$[22] = projectId;
|
|
148
|
+
$[23] = save;
|
|
149
|
+
$[24] = t10;
|
|
150
|
+
} else t10 = $[24];
|
|
151
|
+
let t11;
|
|
152
|
+
if ($[25] !== save.error) {
|
|
153
|
+
t11 = save.error && /* @__PURE__ */ jsx("p", {
|
|
154
|
+
"data-voice-error": true,
|
|
155
|
+
role: "alert",
|
|
156
|
+
className: "text-destructive text-xs",
|
|
157
|
+
children: save.error.message
|
|
158
|
+
});
|
|
159
|
+
$[25] = save.error;
|
|
160
|
+
$[26] = t11;
|
|
161
|
+
} else t11 = $[26];
|
|
162
|
+
let t12;
|
|
163
|
+
if ($[27] !== t10 || $[28] !== t11 || $[29] !== t9) {
|
|
164
|
+
t12 = /* @__PURE__ */ jsxs("div", {
|
|
165
|
+
ref: actions,
|
|
166
|
+
className: "flex flex-col items-start gap-3",
|
|
167
|
+
children: [
|
|
168
|
+
t9,
|
|
169
|
+
t10,
|
|
170
|
+
t11
|
|
171
|
+
]
|
|
172
|
+
});
|
|
173
|
+
$[27] = t10;
|
|
174
|
+
$[28] = t11;
|
|
175
|
+
$[29] = t9;
|
|
176
|
+
$[30] = t12;
|
|
177
|
+
} else t12 = $[30];
|
|
178
|
+
let t13;
|
|
179
|
+
if ($[31] !== save || $[32] !== voice) {
|
|
180
|
+
t13 = voice ? /* @__PURE__ */ jsx("ul", {
|
|
181
|
+
className: "flex flex-col gap-12",
|
|
182
|
+
children: VOICE_AXES.map((axis) => /* @__PURE__ */ jsx(VoiceAxisRow, {
|
|
183
|
+
axis,
|
|
184
|
+
step: voice[axis.key],
|
|
185
|
+
disabled: save.isPending,
|
|
186
|
+
onStep: (step) => {
|
|
187
|
+
setMoved((current) => ({
|
|
188
|
+
...current,
|
|
189
|
+
[axis.key]: step
|
|
190
|
+
}));
|
|
191
|
+
setJustSaved(false);
|
|
192
|
+
save.reset();
|
|
193
|
+
}
|
|
194
|
+
}, axis.key))
|
|
195
|
+
}) : /* @__PURE__ */ jsx(Skeleton, { className: "h-96" });
|
|
196
|
+
$[31] = save;
|
|
197
|
+
$[32] = voice;
|
|
198
|
+
$[33] = t13;
|
|
199
|
+
} else t13 = $[33];
|
|
200
|
+
let t14;
|
|
201
|
+
if ($[34] !== t12 || $[35] !== t13) {
|
|
202
|
+
t14 = /* @__PURE__ */ jsx(BrandPart, {
|
|
203
|
+
"data-voice": true,
|
|
204
|
+
title: "Voice",
|
|
205
|
+
description: "How the website speaks to visitors.",
|
|
206
|
+
headingRef: heading,
|
|
207
|
+
aside: t12,
|
|
208
|
+
children: t13
|
|
209
|
+
});
|
|
210
|
+
$[34] = t12;
|
|
211
|
+
$[35] = t13;
|
|
212
|
+
$[36] = t14;
|
|
213
|
+
} else t14 = $[36];
|
|
214
|
+
return t14;
|
|
215
|
+
}
|
|
216
|
+
/** The axes `voice` moves away from `saved`, with their new steps. */
|
|
217
|
+
function changedAxes(saved, voice) {
|
|
218
|
+
const changed = {};
|
|
219
|
+
for (const { key } of VOICE_AXES) if (voice[key] !== saved[key]) changed[key] = voice[key];
|
|
220
|
+
return changed;
|
|
221
|
+
}
|
|
222
|
+
const sentenceCase = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
223
|
+
/**
|
|
224
|
+
* One Voice Axis: its name over an oversized slider, with the chosen step's name and definition
|
|
225
|
+
* centered under its stop, and following it as it moves.
|
|
226
|
+
*/
|
|
227
|
+
function VoiceAxisRow(t0) {
|
|
228
|
+
const $ = c(55);
|
|
229
|
+
const { axis, step, disabled, onStep } = t0;
|
|
230
|
+
const steps = axis.steps;
|
|
231
|
+
const last = steps.length - 1;
|
|
232
|
+
let t1;
|
|
233
|
+
if ($[0] !== step || $[1] !== steps) {
|
|
234
|
+
let t2;
|
|
235
|
+
if ($[3] !== step) {
|
|
236
|
+
t2 = (candidate) => candidate.key === step;
|
|
237
|
+
$[3] = step;
|
|
238
|
+
$[4] = t2;
|
|
239
|
+
} else t2 = $[4];
|
|
240
|
+
t1 = steps.findIndex(t2);
|
|
241
|
+
$[0] = step;
|
|
242
|
+
$[1] = steps;
|
|
243
|
+
$[2] = t1;
|
|
244
|
+
} else t1 = $[2];
|
|
245
|
+
const index = Math.max(t1, 0);
|
|
246
|
+
const chosen = steps[index];
|
|
247
|
+
const at = index / last * 100;
|
|
248
|
+
let t2;
|
|
249
|
+
if ($[5] !== axis.name) {
|
|
250
|
+
t2 = /* @__PURE__ */ jsx("h3", {
|
|
251
|
+
"data-voice-axis-name": true,
|
|
252
|
+
className: "text-sm font-medium",
|
|
253
|
+
children: axis.name
|
|
254
|
+
});
|
|
255
|
+
$[5] = axis.name;
|
|
256
|
+
$[6] = t2;
|
|
257
|
+
} else t2 = $[6];
|
|
258
|
+
let t3;
|
|
259
|
+
if ($[7] !== onStep || $[8] !== steps) {
|
|
260
|
+
t3 = (value) => {
|
|
261
|
+
const next = steps[value];
|
|
262
|
+
if (next) onStep(next.key);
|
|
263
|
+
};
|
|
264
|
+
$[7] = onStep;
|
|
265
|
+
$[8] = steps;
|
|
266
|
+
$[9] = t3;
|
|
267
|
+
} else t3 = $[9];
|
|
268
|
+
let t4;
|
|
269
|
+
if ($[10] !== last || $[11] !== steps) {
|
|
270
|
+
let t5;
|
|
271
|
+
if ($[13] !== last) {
|
|
272
|
+
t5 = (candidate_0, stop) => /* @__PURE__ */ jsx("span", {
|
|
273
|
+
"aria-hidden": true,
|
|
274
|
+
className: "bg-muted-foreground/80 absolute top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2 rounded-full",
|
|
275
|
+
style: { left: `${stop / last * 100}%` }
|
|
276
|
+
}, candidate_0.key);
|
|
277
|
+
$[13] = last;
|
|
278
|
+
$[14] = t5;
|
|
279
|
+
} else t5 = $[14];
|
|
280
|
+
t4 = steps.map(t5);
|
|
281
|
+
$[10] = last;
|
|
282
|
+
$[11] = steps;
|
|
283
|
+
$[12] = t4;
|
|
284
|
+
} else t4 = $[12];
|
|
285
|
+
let t5;
|
|
286
|
+
if ($[15] !== t4) {
|
|
287
|
+
t5 = /* @__PURE__ */ jsx(Slider.Track, {
|
|
288
|
+
className: "bg-muted relative h-2 w-full rounded-full",
|
|
289
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
290
|
+
className: "pointer-events-none absolute inset-x-3.5 inset-y-0",
|
|
291
|
+
children: t4
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
$[15] = t4;
|
|
295
|
+
$[16] = t5;
|
|
296
|
+
} else t5 = $[16];
|
|
297
|
+
let t6;
|
|
298
|
+
if ($[17] !== axis.name) {
|
|
299
|
+
t6 = () => axis.name;
|
|
300
|
+
$[17] = axis.name;
|
|
301
|
+
$[18] = t6;
|
|
302
|
+
} else t6 = $[18];
|
|
303
|
+
const t7 = chosen.name;
|
|
304
|
+
const t8 = steps.length;
|
|
305
|
+
let t9;
|
|
306
|
+
if ($[19] !== chosen.definition) {
|
|
307
|
+
t9 = sentenceCase(chosen.definition);
|
|
308
|
+
$[19] = chosen.definition;
|
|
309
|
+
$[20] = t9;
|
|
310
|
+
} else t9 = $[20];
|
|
311
|
+
const t10 = `${t7}, ${index + 1} of ${t8}. ${t9}`;
|
|
312
|
+
let t11;
|
|
313
|
+
if ($[21] !== t10 || $[22] !== t6) {
|
|
314
|
+
t11 = /* @__PURE__ */ jsx(Slider.Thumb, {
|
|
315
|
+
getAriaLabel: t6,
|
|
316
|
+
"aria-valuetext": t10,
|
|
317
|
+
className: "border-primary bg-background ring-ring/50 block size-7 shrink-0 rounded-full border-[3px] shadow-md transition-[left,box-shadow] duration-150 ease-out outline-none hover:ring-4 has-focus-visible:ring-4 data-disabled:pointer-events-none motion-reduce:transition-none"
|
|
318
|
+
});
|
|
319
|
+
$[21] = t10;
|
|
320
|
+
$[22] = t6;
|
|
321
|
+
$[23] = t11;
|
|
322
|
+
} else t11 = $[23];
|
|
323
|
+
let t12;
|
|
324
|
+
if ($[24] !== t11 || $[25] !== t5) {
|
|
325
|
+
t12 = /* @__PURE__ */ jsxs(Slider.Control, {
|
|
326
|
+
className: "relative flex h-8 w-full cursor-pointer touch-none items-center select-none data-disabled:cursor-not-allowed",
|
|
327
|
+
children: [t5, t11]
|
|
328
|
+
});
|
|
329
|
+
$[24] = t11;
|
|
330
|
+
$[25] = t5;
|
|
331
|
+
$[26] = t12;
|
|
332
|
+
} else t12 = $[26];
|
|
333
|
+
let t13;
|
|
334
|
+
if ($[27] !== disabled || $[28] !== index || $[29] !== last || $[30] !== t12 || $[31] !== t3) {
|
|
335
|
+
t13 = /* @__PURE__ */ jsx(Slider.Root, {
|
|
336
|
+
value: index,
|
|
337
|
+
min: 0,
|
|
338
|
+
max: last,
|
|
339
|
+
step: 1,
|
|
340
|
+
thumbAlignment: "edge",
|
|
341
|
+
disabled,
|
|
342
|
+
className: "data-disabled:opacity-50",
|
|
343
|
+
onValueChange: t3,
|
|
344
|
+
children: t12
|
|
345
|
+
});
|
|
346
|
+
$[27] = disabled;
|
|
347
|
+
$[28] = index;
|
|
348
|
+
$[29] = last;
|
|
349
|
+
$[30] = t12;
|
|
350
|
+
$[31] = t3;
|
|
351
|
+
$[32] = t13;
|
|
352
|
+
} else t13 = $[32];
|
|
353
|
+
const t14 = index === 0 ? "text-left" : index === last ? "text-right" : "text-center";
|
|
354
|
+
let t15;
|
|
355
|
+
if ($[33] !== t14) {
|
|
356
|
+
t15 = cn("transition-[margin-left] duration-200 ease-out motion-reduce:transition-none", t14);
|
|
357
|
+
$[33] = t14;
|
|
358
|
+
$[34] = t15;
|
|
359
|
+
} else t15 = $[34];
|
|
360
|
+
const t16 = `clamp(-0.875rem, calc(${at}% - min(8rem, 50%)), calc(100% + 0.875rem - min(16rem, 100%)))`;
|
|
361
|
+
let t17;
|
|
362
|
+
if ($[35] !== t16) {
|
|
363
|
+
t17 = {
|
|
364
|
+
width: "min(16rem, 100%)",
|
|
365
|
+
marginLeft: t16
|
|
366
|
+
};
|
|
367
|
+
$[35] = t16;
|
|
368
|
+
$[36] = t17;
|
|
369
|
+
} else t17 = $[36];
|
|
370
|
+
const t18 = chosen.key;
|
|
371
|
+
let t19;
|
|
372
|
+
if ($[37] !== chosen.name) {
|
|
373
|
+
t19 = /* @__PURE__ */ jsx("p", {
|
|
374
|
+
"data-voice-step": true,
|
|
375
|
+
className: "font-semibold",
|
|
376
|
+
children: chosen.name
|
|
377
|
+
});
|
|
378
|
+
$[37] = chosen.name;
|
|
379
|
+
$[38] = t19;
|
|
380
|
+
} else t19 = $[38];
|
|
381
|
+
let t20;
|
|
382
|
+
if ($[39] !== chosen.definition) {
|
|
383
|
+
t20 = sentenceCase(chosen.definition);
|
|
384
|
+
$[39] = chosen.definition;
|
|
385
|
+
$[40] = t20;
|
|
386
|
+
} else t20 = $[40];
|
|
387
|
+
let t21;
|
|
388
|
+
if ($[41] !== t20) {
|
|
389
|
+
t21 = /* @__PURE__ */ jsx("p", {
|
|
390
|
+
"data-voice-definition": true,
|
|
391
|
+
className: "text-muted-foreground mt-0.5 text-sm",
|
|
392
|
+
children: t20
|
|
393
|
+
});
|
|
394
|
+
$[41] = t20;
|
|
395
|
+
$[42] = t21;
|
|
396
|
+
} else t21 = $[42];
|
|
397
|
+
let t22;
|
|
398
|
+
if ($[43] !== chosen.key || $[44] !== t19 || $[45] !== t21) {
|
|
399
|
+
t22 = /* @__PURE__ */ jsxs("div", {
|
|
400
|
+
className: "animate-in fade-in-0 duration-200",
|
|
401
|
+
children: [t19, t21]
|
|
402
|
+
}, t18);
|
|
403
|
+
$[43] = chosen.key;
|
|
404
|
+
$[44] = t19;
|
|
405
|
+
$[45] = t21;
|
|
406
|
+
$[46] = t22;
|
|
407
|
+
} else t22 = $[46];
|
|
408
|
+
let t23;
|
|
409
|
+
if ($[47] !== t15 || $[48] !== t17 || $[49] !== t22) {
|
|
410
|
+
t23 = /* @__PURE__ */ jsx("div", {
|
|
411
|
+
className: "mx-3.5 min-h-21",
|
|
412
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
413
|
+
className: t15,
|
|
414
|
+
style: t17,
|
|
415
|
+
children: t22
|
|
416
|
+
})
|
|
417
|
+
});
|
|
418
|
+
$[47] = t15;
|
|
419
|
+
$[48] = t17;
|
|
420
|
+
$[49] = t22;
|
|
421
|
+
$[50] = t23;
|
|
422
|
+
} else t23 = $[50];
|
|
423
|
+
let t24;
|
|
424
|
+
if ($[51] !== t13 || $[52] !== t2 || $[53] !== t23) {
|
|
425
|
+
t24 = /* @__PURE__ */ jsxs("li", {
|
|
426
|
+
"data-voice-axis": true,
|
|
427
|
+
className: "flex flex-col gap-3",
|
|
428
|
+
children: [
|
|
429
|
+
t2,
|
|
430
|
+
t13,
|
|
431
|
+
t23
|
|
432
|
+
]
|
|
433
|
+
});
|
|
434
|
+
$[51] = t13;
|
|
435
|
+
$[52] = t2;
|
|
436
|
+
$[53] = t23;
|
|
437
|
+
$[54] = t24;
|
|
438
|
+
} else t24 = $[54];
|
|
439
|
+
return t24;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
//#endregion
|
|
443
|
+
export { VoiceGroup };
|
|
@@ -3,7 +3,7 @@ import { CamoxContent } from "../content/CamoxContent.js";
|
|
|
3
3
|
import { AuthenticatedCamoxProvider } from "../provider/AuthenticatedCamoxProvider.js";
|
|
4
4
|
import { CamoxStudio } from "../studio/CamoxStudio.js";
|
|
5
5
|
import { CompleteBlockEditingRuntimeProvider } from "../../core/editing/CompleteBlockEditingRuntime.js";
|
|
6
|
-
import {
|
|
6
|
+
import { CamoxBrand } from "../brand/CamoxBrand.js";
|
|
7
7
|
import { CamoxSettings } from "../settings/CamoxSettings.js";
|
|
8
8
|
import { c } from "react/compiler-runtime";
|
|
9
9
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -12,7 +12,7 @@ import { jsx } from "react/jsx-runtime";
|
|
|
12
12
|
/** The studio section each studio route shows. */
|
|
13
13
|
const STUDIO_SECTIONS = {
|
|
14
14
|
"studio-content": CamoxContent,
|
|
15
|
-
"studio-
|
|
15
|
+
"studio-brand": CamoxBrand,
|
|
16
16
|
"studio-settings": CamoxSettings
|
|
17
17
|
};
|
|
18
18
|
function EditablePageExperience(t0) {
|
|
@@ -11,7 +11,7 @@ import { LookPlaceholderHeight } from "@camox/api-contract/drafts";
|
|
|
11
11
|
import { UseHeadInput } from "unhead/types";
|
|
12
12
|
//#region src/features/runtime/runtime.d.ts
|
|
13
13
|
/** The studio's own routes, rather than a page or the Canvas. */
|
|
14
|
-
declare const STUDIO_ROUTE_KINDS: readonly ["studio", "studio-content", "studio-
|
|
14
|
+
declare const STUDIO_ROUTE_KINDS: readonly ["studio", "studio-content", "studio-brand", "studio-settings", "studio-nested"];
|
|
15
15
|
export type StudioRouteKind = (typeof STUDIO_ROUTE_KINDS)[number];
|
|
16
16
|
export type RuntimeRouteKind = RuntimeOwnedRouteKind | "canvas" | "page" | StudioRouteKind;
|
|
17
17
|
export interface RuntimeRouteMatch {
|
|
@@ -8,7 +8,7 @@ import { ARTIFACTS_PATH_PREFIX, RUNTIME_ROUTE_PATHS, matchRuntimeOwnedRoute } fr
|
|
|
8
8
|
import { reads } from "../../lib/studio-reads.js";
|
|
9
9
|
import { recordMarkdownTemplate } from "../../core/recordItem.js";
|
|
10
10
|
import { PLATFORM_SCRIPT } from "../../lib/platform.js";
|
|
11
|
-
import { STUDIO_ASSETS_PATH, STUDIO_CANVAS_PATH, STUDIO_CONTENT_PATH,
|
|
11
|
+
import { STUDIO_ASSETS_PATH, STUDIO_BRAND_PATH, STUDIO_CANVAS_PATH, STUDIO_CONTENT_PATH, STUDIO_SETTINGS_PATH, matchCollectionContentPath } from "../studio/routes.js";
|
|
12
12
|
import { STUDIO_THEME_SCRIPT } from "../studio/studioTheme.js";
|
|
13
13
|
import { DefaultPageImage, renderPageImage } from "../og/pageImage.js";
|
|
14
14
|
import { discoverLayoutPages } from "../../core/derivedRoutes.js";
|
|
@@ -39,7 +39,7 @@ const DEFAULT_RUNTIME_BASE_PATH = "";
|
|
|
39
39
|
const STUDIO_ROUTE_KINDS = [
|
|
40
40
|
"studio",
|
|
41
41
|
"studio-content",
|
|
42
|
-
"studio-
|
|
42
|
+
"studio-brand",
|
|
43
43
|
"studio-settings",
|
|
44
44
|
"studio-nested"
|
|
45
45
|
];
|
|
@@ -123,8 +123,8 @@ function matchRuntimeRoute(pathname) {
|
|
|
123
123
|
kind: "studio-content",
|
|
124
124
|
pathname
|
|
125
125
|
};
|
|
126
|
-
if (pathname === "/camox/
|
|
127
|
-
kind: "studio-
|
|
126
|
+
if (pathname === "/camox/brand" || pathname === `${"/camox/brand"}/`) return {
|
|
127
|
+
kind: "studio-brand",
|
|
128
128
|
pathname
|
|
129
129
|
};
|
|
130
130
|
if (pathname === "/camox/settings" || pathname === `${"/camox/settings"}/`) return {
|
|
@@ -16,15 +16,16 @@ import { toast } from "@camox/ui/toaster";
|
|
|
16
16
|
import { Skeleton } from "@camox/ui/skeleton";
|
|
17
17
|
import { Spinner } from "@camox/ui/spinner";
|
|
18
18
|
import { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger } from "@camox/ui/popover";
|
|
19
|
+
import { voiceAxis, voiceStep } from "@camox/api-contract/voice";
|
|
19
20
|
|
|
20
21
|
//#region src/features/studio/components/DraftReview.tsx
|
|
21
22
|
/**
|
|
22
|
-
* Where a subject sits: its page or layout, or the files, the languages or
|
|
23
|
-
* to none.
|
|
23
|
+
* Where a subject sits: its page or layout, or the files, the languages or the brand (the Voice
|
|
24
|
+
* and memory), which belong to none.
|
|
24
25
|
*/
|
|
25
26
|
function ownerName(owner, entity) {
|
|
26
27
|
if (entity === "locale") return "Languages";
|
|
27
|
-
if (entity === "memory") return "
|
|
28
|
+
if (entity === "memory" || entity === "voiceAxis") return "Brand";
|
|
28
29
|
if (!owner) return entity === "file" ? "Files" : "Elsewhere";
|
|
29
30
|
if (owner.kind === "layout") return `Layout ${owner.name}`;
|
|
30
31
|
if (owner.kind === "syncedBlock") return `Synced block ${owner.title}`;
|
|
@@ -219,44 +220,57 @@ const MEMORY_CHANGE_LABELS = {
|
|
|
219
220
|
changed: "Changed",
|
|
220
221
|
deleted: "Deleted"
|
|
221
222
|
};
|
|
223
|
+
/** A Voice Axis the draft moves, as it reads: "Humor: Earnest → Light". */
|
|
224
|
+
function voiceLine({ axis, before, after }) {
|
|
225
|
+
const stepName = (key) => voiceStep(axis, key).name;
|
|
226
|
+
return `${voiceAxis(axis).name}: ${stepName(before)} → ${stepName(after)}`;
|
|
227
|
+
}
|
|
222
228
|
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
229
|
+
* What publishing the draft changes in the brand: each Voice Axis it moves, from Live's step to the
|
|
230
|
+
* draft's, then the memories it adds, changes and deletes, each with its text, and Live's before a
|
|
231
|
+
* change. Nothing when it changes neither.
|
|
225
232
|
*/
|
|
226
|
-
function
|
|
227
|
-
const $ = c(
|
|
228
|
-
const { changes } = t0;
|
|
229
|
-
if (changes.length === 0) return null;
|
|
233
|
+
function BrandChanges(t0) {
|
|
234
|
+
const $ = c(8);
|
|
235
|
+
const { voice, changes } = t0;
|
|
236
|
+
if (voice.length === 0 && changes.length === 0) return null;
|
|
230
237
|
let t1;
|
|
231
238
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
232
239
|
t1 = /* @__PURE__ */ jsx("h3", {
|
|
233
240
|
className: "text-sm font-medium",
|
|
234
|
-
children: "
|
|
241
|
+
children: "Brand"
|
|
235
242
|
});
|
|
236
243
|
$[0] = t1;
|
|
237
244
|
} else t1 = $[0];
|
|
238
245
|
let t2;
|
|
239
|
-
if ($[1] !==
|
|
240
|
-
t2 =
|
|
241
|
-
$[1] =
|
|
246
|
+
if ($[1] !== voice) {
|
|
247
|
+
t2 = voice.map(_temp4);
|
|
248
|
+
$[1] = voice;
|
|
242
249
|
$[2] = t2;
|
|
243
250
|
} else t2 = $[2];
|
|
244
251
|
let t3;
|
|
245
|
-
if ($[3] !==
|
|
246
|
-
t3 =
|
|
247
|
-
|
|
252
|
+
if ($[3] !== changes) {
|
|
253
|
+
t3 = changes.map(_temp5);
|
|
254
|
+
$[3] = changes;
|
|
255
|
+
$[4] = t3;
|
|
256
|
+
} else t3 = $[4];
|
|
257
|
+
let t4;
|
|
258
|
+
if ($[5] !== t2 || $[6] !== t3) {
|
|
259
|
+
t4 = /* @__PURE__ */ jsxs("section", {
|
|
260
|
+
"data-brand-changes": true,
|
|
248
261
|
className: "flex flex-col gap-2",
|
|
249
|
-
children: [t1, /* @__PURE__ */
|
|
262
|
+
children: [t1, /* @__PURE__ */ jsxs("ul", {
|
|
250
263
|
className: "flex flex-col gap-2",
|
|
251
|
-
children: t2
|
|
264
|
+
children: [t2, t3]
|
|
252
265
|
})]
|
|
253
266
|
});
|
|
254
|
-
$[
|
|
255
|
-
$[
|
|
256
|
-
|
|
257
|
-
|
|
267
|
+
$[5] = t2;
|
|
268
|
+
$[6] = t3;
|
|
269
|
+
$[7] = t4;
|
|
270
|
+
} else t4 = $[7];
|
|
271
|
+
return t4;
|
|
258
272
|
}
|
|
259
|
-
function
|
|
273
|
+
function _temp5(memory) {
|
|
260
274
|
return /* @__PURE__ */ jsxs("li", {
|
|
261
275
|
className: "flex flex-col gap-0.5 rounded-lg border p-3 text-sm",
|
|
262
276
|
children: [
|
|
@@ -275,6 +289,12 @@ function _temp4(memory) {
|
|
|
275
289
|
]
|
|
276
290
|
}, memory.id);
|
|
277
291
|
}
|
|
292
|
+
function _temp4(change) {
|
|
293
|
+
return /* @__PURE__ */ jsx("li", {
|
|
294
|
+
className: "rounded-lg border p-3 text-sm",
|
|
295
|
+
children: /* @__PURE__ */ jsx("p", { children: voiceLine(change) })
|
|
296
|
+
}, change.axis);
|
|
297
|
+
}
|
|
278
298
|
/**
|
|
279
299
|
* The draft's overview: the bullets AI wrote about its changes, in full, saying it's updating
|
|
280
300
|
* while they describe an older version of the draft. While
|
|
@@ -290,7 +310,7 @@ function DraftOverview(t0) {
|
|
|
290
310
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
291
311
|
t1 = {
|
|
292
312
|
...draftMutations.retryOverview(),
|
|
293
|
-
onError:
|
|
313
|
+
onError: _temp6
|
|
294
314
|
};
|
|
295
315
|
$[0] = t1;
|
|
296
316
|
} else t1 = $[0];
|
|
@@ -311,7 +331,7 @@ function DraftOverview(t0) {
|
|
|
311
331
|
if (overview.status === "ready") {
|
|
312
332
|
let t2;
|
|
313
333
|
if ($[3] !== overview.bullets) {
|
|
314
|
-
t2 = overview.bullets.map(
|
|
334
|
+
t2 = overview.bullets.map(_temp7);
|
|
315
335
|
$[3] = overview.bullets;
|
|
316
336
|
$[4] = t2;
|
|
317
337
|
} else t2 = $[4];
|
|
@@ -352,7 +372,7 @@ function DraftOverview(t0) {
|
|
|
352
372
|
if (overview.status === "failed") {
|
|
353
373
|
let t2;
|
|
354
374
|
if ($[12] !== changes) {
|
|
355
|
-
t2 = new Set(changes.map(
|
|
375
|
+
t2 = new Set(changes.map(_temp8));
|
|
356
376
|
$[12] = changes;
|
|
357
377
|
$[13] = t2;
|
|
358
378
|
} else t2 = $[13];
|
|
@@ -455,13 +475,13 @@ function DraftOverview(t0) {
|
|
|
455
475
|
} else t2 = $[34];
|
|
456
476
|
return t2;
|
|
457
477
|
}
|
|
458
|
-
function
|
|
478
|
+
function _temp8(change) {
|
|
459
479
|
return ownerName(change.owner, change.entity);
|
|
460
480
|
}
|
|
461
|
-
function
|
|
481
|
+
function _temp7(bullet, index) {
|
|
462
482
|
return /* @__PURE__ */ jsx("li", { children: bullet }, index);
|
|
463
483
|
}
|
|
464
|
-
function
|
|
484
|
+
function _temp6(error) {
|
|
465
485
|
return toast.error(error.message || "Could not summarize the draft");
|
|
466
486
|
}
|
|
467
487
|
/**
|
|
@@ -569,8 +589,11 @@ function DraftReview(t0) {
|
|
|
569
589
|
showInCanvas(target);
|
|
570
590
|
}
|
|
571
591
|
}),
|
|
572
|
-
lines.map(
|
|
573
|
-
/* @__PURE__ */ jsx(
|
|
592
|
+
lines.map(_temp9),
|
|
593
|
+
/* @__PURE__ */ jsx(BrandChanges, {
|
|
594
|
+
voice: data.voiceChanges,
|
|
595
|
+
changes: data.memoryChanges
|
|
596
|
+
})
|
|
574
597
|
]
|
|
575
598
|
}) : /* @__PURE__ */ jsx("div", {
|
|
576
599
|
className: cn("flex items-center justify-center", REVIEW_BODY_HEIGHT),
|
|
@@ -646,7 +669,7 @@ function DraftReview(t0) {
|
|
|
646
669
|
} else t16 = $[50];
|
|
647
670
|
return t16;
|
|
648
671
|
}
|
|
649
|
-
function
|
|
672
|
+
function _temp9(line) {
|
|
650
673
|
return /* @__PURE__ */ jsx("p", {
|
|
651
674
|
className: "text-sm text-amber-700 dark:text-amber-400",
|
|
652
675
|
"data-review-line": true,
|