camox 0.54.0 → 0.57.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/features/canvas/CamoxCanvas.js +14 -11
- package/dist/features/canvas/CanvasLoadingScreen.js +1 -1
- package/dist/features/canvas/CanvasPageHeader.js +1 -1
- package/dist/features/canvas/canvasFrameDocument.js +13 -6
- package/dist/features/canvas/sidebar/CanvasViewHeader.js +1 -1
- package/dist/features/canvas/sidebar/{CanvasSidebar.js → ContentSidebar.js} +5 -5
- package/dist/features/canvas/sidebar/{CanvasSidebarFrame.js → ContentSidebarFrame.js} +4 -4
- package/dist/features/canvas/sidebar/{CanvasSidebarSkeleton.js → ContentSidebarSkeleton.js} +7 -7
- package/dist/features/preview/CamoxPreview.js +2 -2
- package/dist/features/preview/components/Frame.js +1 -18
- package/dist/features/preview/components/PreviewToolbar.js +1 -1
- package/dist/features/preview/components/SelectionEditor.js +2 -2
- package/dist/features/preview/components/SidebarStack.js +201 -116
- package/dist/features/preview/components/StudioEditingFallback.js +3 -3
- package/dist/features/preview/components/previewStyles.js +25 -1
- package/dist/features/renderer/RendererApp.js +55 -37
- package/dist/features/routes/pageRuntime.d.ts +1 -1
- package/dist/features/runtime/RuntimeChrome.js +4 -4
- package/dist/features/studio/components/AgentSidebar.js +56 -0
- package/dist/features/studio/components/ChatWidget.js +219 -0
- package/dist/features/studio/components/DraftsControl.js +17 -17
- package/dist/features/studio/components/Patrol.js +676 -0
- package/dist/features/studio/components/ReviewDraftSheet.js +38 -38
- package/dist/features/studio/components/Squareling.js +171 -84
- package/dist/features/studio/components/ThreadMessages.js +256 -136
- package/dist/features/studio/localeOptions.js +7 -3
- package/dist/features/studio/reportSentence.js +75 -0
- package/dist/features/studio/routes.js +2 -1
- package/dist/features/studio/showInCanvas.js +9 -2
- package/dist/features/studio/studioStore.js +5 -5
- package/dist/features/studio/workingSentence.js +55 -0
- package/dist/lib/queries.js +1 -1
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/studio-reads.js +5 -0
- package/dist/lib/use-thread.js +156 -190
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox/SKILL.md +3 -10
- package/skills/camox/references/artifacts.md +2 -2
- package/skills/camox/references/block-definitions.md +1 -1
- package/skills/camox/references/layout-definitions.md +1 -1
- package/skills/camox/references/routed-layouts.md +2 -2
- package/skills/camox-cli/SKILL.md +15 -0
- package/skills/{camox → camox-cli}/references/cli-common.md +1 -1
- package/skills/{camox → camox-cli}/references/cli-content.md +2 -2
- package/skills/{camox → camox-cli}/references/cli-publishing.md +4 -4
- package/skills/camox-expand/SKILL.md +1 -1
- package/skills/camoxify/SKILL.md +1 -1
- package/dist/features/studio/components/ChatSidebar.js +0 -346
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { cn } from "../../../lib/utils.js";
|
|
1
2
|
import { Squareling } from "./Squareling.js";
|
|
2
3
|
import { ThreadMarkdown } from "./ThreadMarkdown.js";
|
|
3
4
|
import { c } from "react/compiler-runtime";
|
|
5
|
+
import * as React from "react";
|
|
4
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
7
|
import { Avatar, AvatarFallback, AvatarImage } from "@camox/ui/avatar";
|
|
6
8
|
import { Bubble, BubbleContent } from "@camox/ui/bubble";
|
|
@@ -9,38 +11,43 @@ import { MessageScroller, MessageScrollerButton, MessageScrollerContent, Message
|
|
|
9
11
|
|
|
10
12
|
//#region src/features/studio/components/ThreadMessages.tsx
|
|
11
13
|
/**
|
|
12
|
-
* A thread's messages, and the agent's squareling
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* A thread's messages, and the agent's squareling, which always lives in it. An empty thread has
|
|
15
|
+
* it greet the editor; when the first message arrives, it moves to the end of the thread, and
|
|
16
|
+
* stays there after each message. While a run is going or a message is still `sending` (on its
|
|
17
|
+
* way to the thread, which hasn't accepted it yet), its face says whether the agent thinks or
|
|
18
|
+
* acts (uses its tools, or builds its code). While the end of a turn builds the agent's code, or
|
|
19
|
+
* the agent fixes a build error, it says so in words too. Each editor's message shows its
|
|
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.
|
|
18
22
|
*/
|
|
19
23
|
function ThreadMessages(t0) {
|
|
20
24
|
const $ = c(20);
|
|
21
|
-
const { view, sending: t1,
|
|
25
|
+
const { view, sending: t1, onOpenDraft } = t0;
|
|
22
26
|
const sending = t1 === void 0 ? false : t1;
|
|
23
27
|
const working = view.running || sending;
|
|
24
|
-
|
|
28
|
+
const empty = view.messages.length === 0 && !working;
|
|
29
|
+
const squareling = useSquarelingMorph(empty);
|
|
30
|
+
if (empty) {
|
|
25
31
|
let t2;
|
|
26
|
-
if ($[0] !==
|
|
27
|
-
t2 =
|
|
28
|
-
|
|
32
|
+
if ($[0] !== squareling) {
|
|
33
|
+
t2 = /* @__PURE__ */ jsx(Squareling, {
|
|
34
|
+
ref: squareling,
|
|
35
|
+
status: "idle",
|
|
36
|
+
className: "size-10"
|
|
37
|
+
});
|
|
38
|
+
$[0] = squareling;
|
|
29
39
|
$[1] = t2;
|
|
30
40
|
} else t2 = $[1];
|
|
31
41
|
let t3;
|
|
32
42
|
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
|
33
|
-
t3 = /* @__PURE__ */ jsx("p", {
|
|
34
|
-
"data-thread-empty": true,
|
|
35
|
-
className: "text-muted-foreground flex min-h-0 flex-1 items-center justify-center p-4 text-center text-sm",
|
|
36
|
-
children: "Ask Camox about your website’s content."
|
|
37
|
-
});
|
|
43
|
+
t3 = /* @__PURE__ */ jsx("p", { children: "How can I help?" });
|
|
38
44
|
$[2] = t3;
|
|
39
45
|
} else t3 = $[2];
|
|
40
46
|
let t4;
|
|
41
47
|
if ($[3] !== t2) {
|
|
42
48
|
t4 = /* @__PURE__ */ jsxs("div", {
|
|
43
|
-
|
|
49
|
+
"data-thread-empty": true,
|
|
50
|
+
className: "text-muted-foreground flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-4 text-center text-sm",
|
|
44
51
|
children: [t2, t3]
|
|
45
52
|
});
|
|
46
53
|
$[3] = t2;
|
|
@@ -49,108 +56,155 @@ function ThreadMessages(t0) {
|
|
|
49
56
|
return t4;
|
|
50
57
|
}
|
|
51
58
|
let t2;
|
|
52
|
-
if ($[5] !==
|
|
53
|
-
t2 =
|
|
54
|
-
$[5] = continuedIn;
|
|
55
|
-
$[6] = t2;
|
|
56
|
-
} else t2 = $[6];
|
|
57
|
-
let t3;
|
|
58
|
-
if ($[7] !== view || $[8] !== working) {
|
|
59
|
-
t3 = view.messages.map((message, index) => /* @__PURE__ */ jsxs(MessageScrollerItem, {
|
|
59
|
+
if ($[5] !== onOpenDraft || $[6] !== squareling || $[7] !== view || $[8] !== working) {
|
|
60
|
+
t2 = view.messages.map((message, index) => /* @__PURE__ */ jsxs(MessageScrollerItem, {
|
|
60
61
|
messageId: message.id,
|
|
61
62
|
scrollAnchor: message.from === "editor",
|
|
62
63
|
className: "flex flex-col gap-4",
|
|
63
|
-
children: [/* @__PURE__ */ jsx(ThreadMessageRow, {
|
|
64
|
+
children: [/* @__PURE__ */ jsx(ThreadMessageRow, {
|
|
65
|
+
message,
|
|
66
|
+
onOpenDraft
|
|
67
|
+
}), index === view.messages.length - 1 && /* @__PURE__ */ jsx(AgentPresence, {
|
|
68
|
+
view,
|
|
69
|
+
working,
|
|
70
|
+
squareling
|
|
71
|
+
})]
|
|
64
72
|
}, message.id));
|
|
73
|
+
$[5] = onOpenDraft;
|
|
74
|
+
$[6] = squareling;
|
|
65
75
|
$[7] = view;
|
|
66
76
|
$[8] = working;
|
|
67
|
-
$[9] =
|
|
68
|
-
} else
|
|
77
|
+
$[9] = t2;
|
|
78
|
+
} else t2 = $[9];
|
|
79
|
+
let t3;
|
|
80
|
+
if ($[10] !== squareling || $[11] !== view || $[12] !== working) {
|
|
81
|
+
t3 = view.messages.length === 0 && /* @__PURE__ */ jsx(AgentPresence, {
|
|
82
|
+
view,
|
|
83
|
+
working,
|
|
84
|
+
squareling
|
|
85
|
+
});
|
|
86
|
+
$[10] = squareling;
|
|
87
|
+
$[11] = view;
|
|
88
|
+
$[12] = working;
|
|
89
|
+
$[13] = t3;
|
|
90
|
+
} else t3 = $[13];
|
|
69
91
|
let t4;
|
|
70
|
-
if ($[
|
|
71
|
-
t4 =
|
|
72
|
-
$[10] = view;
|
|
73
|
-
$[11] = working;
|
|
74
|
-
$[12] = t4;
|
|
75
|
-
} else t4 = $[12];
|
|
76
|
-
let t5;
|
|
77
|
-
if ($[13] !== t2 || $[14] !== t3 || $[15] !== t4) {
|
|
78
|
-
t5 = /* @__PURE__ */ jsx(MessageScrollerViewport, { children: /* @__PURE__ */ jsxs(MessageScrollerContent, {
|
|
92
|
+
if ($[14] !== t2 || $[15] !== t3) {
|
|
93
|
+
t4 = /* @__PURE__ */ jsx(MessageScrollerViewport, { children: /* @__PURE__ */ jsxs(MessageScrollerContent, {
|
|
79
94
|
className: "flex flex-col gap-4 px-2 py-3",
|
|
80
|
-
children: [
|
|
81
|
-
t2,
|
|
82
|
-
t3,
|
|
83
|
-
t4
|
|
84
|
-
]
|
|
95
|
+
children: [t2, t3]
|
|
85
96
|
}) });
|
|
86
|
-
$[
|
|
87
|
-
$[
|
|
88
|
-
$[
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
let t6;
|
|
97
|
+
$[14] = t2;
|
|
98
|
+
$[15] = t3;
|
|
99
|
+
$[16] = t4;
|
|
100
|
+
} else t4 = $[16];
|
|
101
|
+
let t5;
|
|
92
102
|
if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
|
|
93
|
-
|
|
94
|
-
$[17] =
|
|
95
|
-
} else
|
|
96
|
-
let
|
|
97
|
-
if ($[18] !==
|
|
98
|
-
|
|
103
|
+
t5 = /* @__PURE__ */ jsx(MessageScrollerButton, {});
|
|
104
|
+
$[17] = t5;
|
|
105
|
+
} else t5 = $[17];
|
|
106
|
+
let t6;
|
|
107
|
+
if ($[18] !== t4) {
|
|
108
|
+
t6 = /* @__PURE__ */ jsx(MessageScrollerProvider, { children: /* @__PURE__ */ jsxs(MessageScroller, {
|
|
99
109
|
className: "min-h-0 flex-1",
|
|
100
|
-
children: [
|
|
110
|
+
children: [t4, t5]
|
|
101
111
|
}) });
|
|
102
|
-
$[18] =
|
|
103
|
-
$[19] =
|
|
104
|
-
} else
|
|
105
|
-
return
|
|
112
|
+
$[18] = t4;
|
|
113
|
+
$[19] = t6;
|
|
114
|
+
} else t6 = $[19];
|
|
115
|
+
return t6;
|
|
106
116
|
}
|
|
117
|
+
/** How long the greeting's squareling takes to reach the end of the thread, in ms. */
|
|
118
|
+
const MORPH_DURATION = 350;
|
|
119
|
+
/** A strong ease-in-out, for moving across the screen. */
|
|
120
|
+
const MORPH_EASING = "cubic-bezier(0.77, 0, 0.175, 1)";
|
|
107
121
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
122
|
+
* Moves the squareling from where the greeting showed it to the end of the thread, as the first
|
|
123
|
+
* message arrives, so it reads as the same one. Without motion for editors who reduce it.
|
|
110
124
|
*/
|
|
111
|
-
function
|
|
112
|
-
const $ = c(
|
|
113
|
-
const
|
|
125
|
+
function useSquarelingMorph(empty) {
|
|
126
|
+
const $ = c(2);
|
|
127
|
+
const node = React.useRef(null);
|
|
128
|
+
const greeting = React.useRef(null);
|
|
129
|
+
let t0;
|
|
130
|
+
if ($[0] !== empty) {
|
|
131
|
+
t0 = () => {
|
|
132
|
+
const element = node.current;
|
|
133
|
+
if (!element) return;
|
|
134
|
+
if (empty) {
|
|
135
|
+
greeting.current = element.getBoundingClientRect();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const from = greeting.current;
|
|
139
|
+
greeting.current = null;
|
|
140
|
+
if (!from || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
141
|
+
const to = element.getBoundingClientRect();
|
|
142
|
+
if (to.width === 0) return;
|
|
143
|
+
const dx = from.left + from.width / 2 - (to.left + to.width / 2);
|
|
144
|
+
const dy = from.top + from.height / 2 - (to.top + to.height / 2);
|
|
145
|
+
element.animate([{ transform: `translate(${dx}px, ${dy}px) scale(${from.width / to.width})` }, { transform: "none" }], {
|
|
146
|
+
duration: MORPH_DURATION,
|
|
147
|
+
easing: MORPH_EASING
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
$[0] = empty;
|
|
151
|
+
$[1] = t0;
|
|
152
|
+
} else t0 = $[1];
|
|
153
|
+
React.useLayoutEffect(t0);
|
|
154
|
+
return node;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The squareling at the end of the thread: idle between turns, at work during one. Shown in the
|
|
158
|
+
* last message, not after it: the scroller only brings a message the editor just sent into view
|
|
159
|
+
* when it's appended last, and it arrives while the agent is already working.
|
|
160
|
+
*/
|
|
161
|
+
function AgentPresence(t0) {
|
|
162
|
+
const $ = c(13);
|
|
163
|
+
const { view: t1, working, squareling } = t0;
|
|
114
164
|
const { status, usingTools } = t1;
|
|
115
165
|
let t2;
|
|
116
|
-
if ($[0] !== status || $[1] !== usingTools) {
|
|
117
|
-
t2 = squarelingStatus(status, usingTools);
|
|
166
|
+
if ($[0] !== status || $[1] !== usingTools || $[2] !== working) {
|
|
167
|
+
t2 = working ? squarelingStatus(status, usingTools) : "idle";
|
|
118
168
|
$[0] = status;
|
|
119
169
|
$[1] = usingTools;
|
|
120
|
-
$[2] =
|
|
121
|
-
|
|
170
|
+
$[2] = working;
|
|
171
|
+
$[3] = t2;
|
|
172
|
+
} else t2 = $[3];
|
|
122
173
|
let t3;
|
|
123
|
-
if ($[
|
|
174
|
+
if ($[4] !== squareling || $[5] !== t2) {
|
|
124
175
|
t3 = /* @__PURE__ */ jsx(Squareling, {
|
|
176
|
+
ref: squareling,
|
|
125
177
|
status: t2,
|
|
126
178
|
className: "size-6"
|
|
127
179
|
});
|
|
128
|
-
$[
|
|
129
|
-
$[
|
|
130
|
-
|
|
180
|
+
$[4] = squareling;
|
|
181
|
+
$[5] = t2;
|
|
182
|
+
$[6] = t3;
|
|
183
|
+
} else t3 = $[6];
|
|
131
184
|
let t4;
|
|
132
|
-
if ($[
|
|
133
|
-
t4 = status ? /* @__PURE__ */ jsx("span", {
|
|
185
|
+
if ($[7] !== status || $[8] !== working) {
|
|
186
|
+
t4 = working && (status ? /* @__PURE__ */ jsx("span", {
|
|
134
187
|
className: "shimmer",
|
|
135
188
|
children: STATUS_LINES[status]
|
|
136
189
|
}) : /* @__PURE__ */ jsx("span", {
|
|
137
190
|
className: "sr-only",
|
|
138
191
|
children: "Working…"
|
|
139
|
-
});
|
|
140
|
-
$[
|
|
141
|
-
$[
|
|
142
|
-
|
|
192
|
+
}));
|
|
193
|
+
$[7] = status;
|
|
194
|
+
$[8] = working;
|
|
195
|
+
$[9] = t4;
|
|
196
|
+
} else t4 = $[9];
|
|
143
197
|
let t5;
|
|
144
|
-
if ($[
|
|
198
|
+
if ($[10] !== t3 || $[11] !== t4) {
|
|
145
199
|
t5 = /* @__PURE__ */ jsxs("div", {
|
|
146
200
|
role: "status",
|
|
147
201
|
className: "text-muted-foreground flex items-center gap-2 text-sm",
|
|
148
202
|
children: [t3, t4]
|
|
149
203
|
});
|
|
150
|
-
$[
|
|
151
|
-
$[
|
|
152
|
-
$[
|
|
153
|
-
} else t5 = $[
|
|
204
|
+
$[10] = t3;
|
|
205
|
+
$[11] = t4;
|
|
206
|
+
$[12] = t5;
|
|
207
|
+
} else t5 = $[12];
|
|
154
208
|
return t5;
|
|
155
209
|
}
|
|
156
210
|
function squarelingStatus(status, usingTools) {
|
|
@@ -162,41 +216,54 @@ const STATUS_LINES = {
|
|
|
162
216
|
fixing: "Fixing a build error…"
|
|
163
217
|
};
|
|
164
218
|
function ThreadMessageRow(t0) {
|
|
165
|
-
const $ = c(
|
|
166
|
-
const { message } = t0;
|
|
219
|
+
const $ = c(17);
|
|
220
|
+
const { message, onOpenDraft } = t0;
|
|
221
|
+
if (message.from === "draft") {
|
|
222
|
+
let t1;
|
|
223
|
+
if ($[0] !== message || $[1] !== onOpenDraft) {
|
|
224
|
+
t1 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(DraftCard, {
|
|
225
|
+
message,
|
|
226
|
+
onOpenDraft
|
|
227
|
+
}) }) });
|
|
228
|
+
$[0] = message;
|
|
229
|
+
$[1] = onOpenDraft;
|
|
230
|
+
$[2] = t1;
|
|
231
|
+
} else t1 = $[2];
|
|
232
|
+
return t1;
|
|
233
|
+
}
|
|
167
234
|
if (message.from === "stopped" || message.from === "failed") {
|
|
168
235
|
const t1 = message.from === "stopped" ? `Stopped by ${message.sender.name}` : "Camox couldn’t finish this. Send your message again to retry.";
|
|
169
236
|
let t2;
|
|
170
|
-
if ($[
|
|
237
|
+
if ($[3] !== t1) {
|
|
171
238
|
t2 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx("p", {
|
|
172
239
|
className: "text-muted-foreground text-sm",
|
|
173
240
|
children: t1
|
|
174
241
|
}) }) });
|
|
175
|
-
$[
|
|
176
|
-
$[
|
|
177
|
-
} else t2 = $[
|
|
242
|
+
$[3] = t1;
|
|
243
|
+
$[4] = t2;
|
|
244
|
+
} else t2 = $[4];
|
|
178
245
|
return t2;
|
|
179
246
|
}
|
|
180
247
|
if (message.from === "agent") {
|
|
181
248
|
let t1;
|
|
182
|
-
if ($[
|
|
249
|
+
if ($[5] !== message.text) {
|
|
183
250
|
t1 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(ThreadMarkdown, { text: message.text }) }) });
|
|
184
|
-
$[
|
|
185
|
-
$[
|
|
186
|
-
} else t1 = $[
|
|
251
|
+
$[5] = message.text;
|
|
252
|
+
$[6] = t1;
|
|
253
|
+
} else t1 = $[6];
|
|
187
254
|
return t1;
|
|
188
255
|
}
|
|
189
256
|
if (message.from === "camox") {
|
|
190
257
|
let t1;
|
|
191
|
-
if ($[
|
|
258
|
+
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
|
192
259
|
t1 = /* @__PURE__ */ jsx(MessageHeader, {
|
|
193
260
|
className: "text-muted-foreground text-xs",
|
|
194
261
|
children: "Camox"
|
|
195
262
|
});
|
|
196
|
-
$[
|
|
197
|
-
} else t1 = $[
|
|
263
|
+
$[7] = t1;
|
|
264
|
+
} else t1 = $[7];
|
|
198
265
|
let t2;
|
|
199
|
-
if ($[
|
|
266
|
+
if ($[8] !== message.text) {
|
|
200
267
|
t2 = /* @__PURE__ */ jsx(Message, { children: /* @__PURE__ */ jsxs(MessageContent, { children: [t1, /* @__PURE__ */ jsx(Bubble, {
|
|
201
268
|
variant: "muted",
|
|
202
269
|
className: "max-w-full",
|
|
@@ -205,19 +272,19 @@ function ThreadMessageRow(t0) {
|
|
|
205
272
|
children: message.text
|
|
206
273
|
})
|
|
207
274
|
})] }) });
|
|
208
|
-
$[
|
|
209
|
-
$[
|
|
210
|
-
} else t2 = $[
|
|
275
|
+
$[8] = message.text;
|
|
276
|
+
$[9] = t2;
|
|
277
|
+
} else t2 = $[9];
|
|
211
278
|
return t2;
|
|
212
279
|
}
|
|
213
280
|
let t1;
|
|
214
|
-
if ($[
|
|
281
|
+
if ($[10] !== message.sender) {
|
|
215
282
|
t1 = /* @__PURE__ */ jsx(MessageAvatar, { children: /* @__PURE__ */ jsx(SenderAvatar, { sender: message.sender }) });
|
|
216
|
-
$[
|
|
217
|
-
$[
|
|
218
|
-
} else t1 = $[
|
|
283
|
+
$[10] = message.sender;
|
|
284
|
+
$[11] = t1;
|
|
285
|
+
} else t1 = $[11];
|
|
219
286
|
let t2;
|
|
220
|
-
if ($[
|
|
287
|
+
if ($[12] !== message.text) {
|
|
221
288
|
t2 = /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Bubble, {
|
|
222
289
|
align: "end",
|
|
223
290
|
variant: "muted",
|
|
@@ -227,47 +294,100 @@ function ThreadMessageRow(t0) {
|
|
|
227
294
|
children: message.text
|
|
228
295
|
})
|
|
229
296
|
}) });
|
|
230
|
-
$[
|
|
231
|
-
$[
|
|
232
|
-
} else t2 = $[
|
|
297
|
+
$[12] = message.text;
|
|
298
|
+
$[13] = t2;
|
|
299
|
+
} else t2 = $[13];
|
|
233
300
|
let t3;
|
|
234
|
-
if ($[
|
|
301
|
+
if ($[14] !== t1 || $[15] !== t2) {
|
|
235
302
|
t3 = /* @__PURE__ */ jsxs(Message, {
|
|
236
303
|
align: "end",
|
|
237
304
|
children: [t1, t2]
|
|
238
305
|
});
|
|
239
|
-
$[
|
|
240
|
-
$[
|
|
241
|
-
$[
|
|
242
|
-
} else t3 = $[
|
|
306
|
+
$[14] = t1;
|
|
307
|
+
$[15] = t2;
|
|
308
|
+
$[16] = t3;
|
|
309
|
+
} else t3 = $[16];
|
|
243
310
|
return t3;
|
|
244
311
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
312
|
+
const initials = (name) => name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
|
|
313
|
+
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";
|
|
314
|
+
/**
|
|
315
|
+
* The card of the draft the agent on Live handed the request off to, where its agent does the
|
|
316
|
+
* work: it opens the draft. Once the draft ended, there's nothing to open: it says how it ended.
|
|
317
|
+
*/
|
|
318
|
+
function DraftCard(t0) {
|
|
319
|
+
const $ = c(15);
|
|
320
|
+
const { message, onOpenDraft } = t0;
|
|
248
321
|
let t1;
|
|
249
|
-
if ($[0] !==
|
|
250
|
-
t1 = /* @__PURE__ */
|
|
251
|
-
className: "
|
|
252
|
-
children:
|
|
253
|
-
"Continued in",
|
|
254
|
-
" ",
|
|
255
|
-
/* @__PURE__ */ jsx("a", {
|
|
256
|
-
href: link.href,
|
|
257
|
-
onClick: link.onOpen,
|
|
258
|
-
className: "text-foreground underline",
|
|
259
|
-
children: link.label
|
|
260
|
-
})
|
|
261
|
-
]
|
|
322
|
+
if ($[0] !== message.name) {
|
|
323
|
+
t1 = /* @__PURE__ */ jsx("span", {
|
|
324
|
+
className: "font-medium",
|
|
325
|
+
children: message.name
|
|
262
326
|
});
|
|
263
|
-
$[0] =
|
|
264
|
-
$[1] =
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
327
|
+
$[0] = message.name;
|
|
328
|
+
$[1] = t1;
|
|
329
|
+
} else t1 = $[1];
|
|
330
|
+
const name = t1;
|
|
331
|
+
if (message.ended) {
|
|
332
|
+
const t2 = message.ended === "published" ? "Published" : "Deleted";
|
|
333
|
+
let t3;
|
|
334
|
+
if ($[2] !== t2) {
|
|
335
|
+
t3 = /* @__PURE__ */ jsx("span", {
|
|
336
|
+
className: "text-muted-foreground text-xs",
|
|
337
|
+
children: t2
|
|
338
|
+
});
|
|
339
|
+
$[2] = t2;
|
|
340
|
+
$[3] = t3;
|
|
341
|
+
} else t3 = $[3];
|
|
342
|
+
let t4;
|
|
343
|
+
if ($[4] !== name || $[5] !== t3) {
|
|
344
|
+
t4 = /* @__PURE__ */ jsxs("div", {
|
|
345
|
+
"data-thread-draft-card": true,
|
|
346
|
+
className: DRAFT_CARD_CLASSES,
|
|
347
|
+
children: [t3, name]
|
|
348
|
+
});
|
|
349
|
+
$[4] = name;
|
|
350
|
+
$[5] = t3;
|
|
351
|
+
$[6] = t4;
|
|
352
|
+
} else t4 = $[6];
|
|
353
|
+
return t4;
|
|
354
|
+
}
|
|
355
|
+
let t2;
|
|
356
|
+
if ($[7] !== message.draftId || $[8] !== onOpenDraft) {
|
|
357
|
+
t2 = () => onOpenDraft?.(message.draftId);
|
|
358
|
+
$[7] = message.draftId;
|
|
359
|
+
$[8] = onOpenDraft;
|
|
360
|
+
$[9] = t2;
|
|
361
|
+
} else t2 = $[9];
|
|
362
|
+
let t3;
|
|
363
|
+
let t4;
|
|
364
|
+
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
|
|
365
|
+
t3 = cn(DRAFT_CARD_CLASSES, "hover:bg-muted focus-visible:ring-ring transition-colors focus-visible:ring-2 focus-visible:outline-none");
|
|
366
|
+
t4 = /* @__PURE__ */ jsx("span", {
|
|
367
|
+
className: "text-muted-foreground text-xs",
|
|
368
|
+
children: "Started a draft"
|
|
369
|
+
});
|
|
370
|
+
$[10] = t3;
|
|
371
|
+
$[11] = t4;
|
|
372
|
+
} else {
|
|
373
|
+
t3 = $[10];
|
|
374
|
+
t4 = $[11];
|
|
375
|
+
}
|
|
376
|
+
let t5;
|
|
377
|
+
if ($[12] !== name || $[13] !== t2) {
|
|
378
|
+
t5 = /* @__PURE__ */ jsxs("button", {
|
|
379
|
+
type: "button",
|
|
380
|
+
"data-thread-draft-card": true,
|
|
381
|
+
onClick: t2,
|
|
382
|
+
className: t3,
|
|
383
|
+
children: [t4, name]
|
|
384
|
+
});
|
|
385
|
+
$[12] = name;
|
|
386
|
+
$[13] = t2;
|
|
387
|
+
$[14] = t5;
|
|
388
|
+
} else t5 = $[14];
|
|
389
|
+
return t5;
|
|
269
390
|
}
|
|
270
|
-
const initials = (name) => name.split(" ").map((part) => part[0]).join("").toUpperCase().slice(0, 2);
|
|
271
391
|
function SenderAvatar(t0) {
|
|
272
392
|
const $ = c(10);
|
|
273
393
|
const { sender } = t0;
|
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
const localeNames = new Intl.DisplayNames(["en"], { type: "language" });
|
|
3
3
|
/** A locale's name as the studio shows it, "German (de)", or its tag alone when unnamed. */
|
|
4
4
|
function localeLabel(tag) {
|
|
5
|
+
const name = localeName(tag);
|
|
6
|
+
return name !== tag ? `${name} (${tag})` : tag;
|
|
7
|
+
}
|
|
8
|
+
/** A locale's name in the studio's language, "German", or its tag alone when unnamed. */
|
|
9
|
+
function localeName(tag) {
|
|
5
10
|
try {
|
|
6
|
-
|
|
7
|
-
return name && name !== tag ? `${name} (${tag})` : tag;
|
|
11
|
+
return localeNames.of(tag) ?? tag;
|
|
8
12
|
} catch {
|
|
9
13
|
return tag;
|
|
10
14
|
}
|
|
@@ -115,4 +119,4 @@ function localeOptions(existing) {
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
//#endregion
|
|
118
|
-
export { localeLabel, localeOptions };
|
|
122
|
+
export { localeLabel, localeName, localeOptions };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { localeName } from "./localeOptions.js";
|
|
2
|
+
import { reportLocale } from "@camox/api-contract/patrol";
|
|
3
|
+
|
|
4
|
+
//#region src/features/studio/reportSentence.ts
|
|
5
|
+
const PHRASES = {
|
|
6
|
+
translated: {
|
|
7
|
+
done: "Translated",
|
|
8
|
+
failed: "Couldn't translate",
|
|
9
|
+
locale: "to"
|
|
10
|
+
},
|
|
11
|
+
mergedWithLive: {
|
|
12
|
+
done: "Translated",
|
|
13
|
+
failed: "Couldn't translate",
|
|
14
|
+
locale: "to"
|
|
15
|
+
},
|
|
16
|
+
wroteSeo: {
|
|
17
|
+
done: "Wrote SEO for",
|
|
18
|
+
failed: "Couldn't write SEO for",
|
|
19
|
+
locale: "in"
|
|
20
|
+
},
|
|
21
|
+
describedFile: {
|
|
22
|
+
done: "Described",
|
|
23
|
+
failed: "Couldn't describe",
|
|
24
|
+
locale: "in"
|
|
25
|
+
},
|
|
26
|
+
publishedCatchUp: {
|
|
27
|
+
done: "Published the caught-up translations",
|
|
28
|
+
failed: "Couldn't publish the caught-up translations"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** A report as a sentence: "Wrote SEO for Pricing in German", or the text an AI wrote. */
|
|
32
|
+
function reportSentence(summary, outcome) {
|
|
33
|
+
if (summary.kind === "text") return summary.text;
|
|
34
|
+
const phrase = PHRASES[summary.verb];
|
|
35
|
+
const parts = [phrase[outcome]];
|
|
36
|
+
if (summary.subject) parts.push(summary.subject.name);
|
|
37
|
+
if (summary.locale && phrase.locale) parts.push(phrase.locale, localeName(summary.locale));
|
|
38
|
+
return parts.join(" ");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Where clicking a report leads, as its accessible name ends: "show it on the Canvas in German",
|
|
42
|
+
* "open the file on Live". Null for a report that leads nowhere: one without a place.
|
|
43
|
+
*/
|
|
44
|
+
function whereReportLeads({ summary, place, live }, inDraft) {
|
|
45
|
+
if (!place) return null;
|
|
46
|
+
const locale = reportLocale(summary);
|
|
47
|
+
const inLocale = locale ? ` in ${localeName(locale)}` : "";
|
|
48
|
+
const onLive = inDraft && live;
|
|
49
|
+
if (place.kind === "file") return `open the file${onLive ? " on Live" : ""}`;
|
|
50
|
+
if (place.kind === "record") return `open it${inLocale}${onLive ? " on Live" : ""}`;
|
|
51
|
+
return `show it on ${onLive ? "Live's" : "the"} Canvas${inLocale}`;
|
|
52
|
+
}
|
|
53
|
+
/** The reports not shown, by locale: "…and 212 more in German, 3 in French". */
|
|
54
|
+
function moreSentence(more) {
|
|
55
|
+
return `…and ${more.map(({ locale, count }, index) => {
|
|
56
|
+
const where = locale === null ? "" : ` in ${localeName(locale)}`;
|
|
57
|
+
return `${count}${index === 0 ? " more" : ""}${where}`;
|
|
58
|
+
}).join(", ")}`;
|
|
59
|
+
}
|
|
60
|
+
const SHORT_UNITS = [
|
|
61
|
+
["y", 31536e6],
|
|
62
|
+
["w", 6048e5],
|
|
63
|
+
["d", 864e5],
|
|
64
|
+
["h", 36e5],
|
|
65
|
+
["m", 6e4]
|
|
66
|
+
];
|
|
67
|
+
/** How long ago a moment was, briefly: "now", "2m", "1h". */
|
|
68
|
+
function shortTimeAgo(at, now = Date.now()) {
|
|
69
|
+
const elapsed = now - at;
|
|
70
|
+
for (const [unit, size] of SHORT_UNITS) if (elapsed >= size) return `${Math.floor(elapsed / size)}${unit}`;
|
|
71
|
+
return "now";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { moreSentence, reportSentence, shortTimeAgo, whereReportLeads };
|
|
@@ -6,6 +6,7 @@ const STUDIO_ASSETS_PATH = `${STUDIO_CONTENT_PATH}/assets`;
|
|
|
6
6
|
/** The Settings Section, where members change Project Settings. */
|
|
7
7
|
const STUDIO_SETTINGS_PATH = `${STUDIO_BASE_PATH}/settings`;
|
|
8
8
|
const collectionContentPath = (collectionId) => `${STUDIO_CONTENT_PATH}/collections/${encodeURIComponent(collectionId)}`;
|
|
9
|
+
const editCollectionItemPath = (collectionId, id) => `${collectionContentPath(collectionId)}/${encodeURIComponent(id)}/edit`;
|
|
9
10
|
function matchCollectionContentPath(pathname) {
|
|
10
11
|
const match = /^\/camox\/content\/collections\/([^/]+)(?:(\/new)|\/([^/]+)\/edit)?$/.exec(pathname);
|
|
11
12
|
if (!match) return null;
|
|
@@ -31,4 +32,4 @@ function matchCollectionContentPath(pathname) {
|
|
|
31
32
|
const STUDIO_CANVAS_PATH = `${STUDIO_BASE_PATH}/canvas`;
|
|
32
33
|
|
|
33
34
|
//#endregion
|
|
34
|
-
export { STUDIO_ASSETS_PATH, STUDIO_BASE_PATH, STUDIO_CANVAS_PATH, STUDIO_CONTENT_PATH, STUDIO_ROUTE_SEGMENT, STUDIO_SETTINGS_PATH, collectionContentPath, matchCollectionContentPath };
|
|
35
|
+
export { STUDIO_ASSETS_PATH, STUDIO_BASE_PATH, STUDIO_CANVAS_PATH, STUDIO_CONTENT_PATH, STUDIO_ROUTE_SEGMENT, STUDIO_SETTINGS_PATH, collectionContentPath, editCollectionItemPath, matchCollectionContentPath };
|
|
@@ -97,7 +97,11 @@ function useShowInCanvas() {
|
|
|
97
97
|
if ($[1] !== cache || $[2] !== navigate || $[3] !== projectUuid || $[4] !== search || $[5] !== view.locale || $[6] !== view.locales) {
|
|
98
98
|
t1 = async (target) => {
|
|
99
99
|
const locale = view.locales.find((each) => each.tag === target.locale) ?? view.locale;
|
|
100
|
-
const
|
|
100
|
+
const inDraft = target.draft === void 0 ? {} : { in: target.draft };
|
|
101
|
+
const page = (await cache.ensure(reads.pageList({ projectUuid }), {
|
|
102
|
+
locale,
|
|
103
|
+
...inDraft
|
|
104
|
+
}).catch(_temp2)).find((each_0) => each_0.id === target.pageId);
|
|
101
105
|
if (!page) return;
|
|
102
106
|
const selection = selectionOf(target);
|
|
103
107
|
if (selection) selectOnceShown({
|
|
@@ -106,7 +110,10 @@ function useShowInCanvas() {
|
|
|
106
110
|
}, selection);
|
|
107
111
|
else stopSelecting?.();
|
|
108
112
|
previewStore.send({ type: "showCanvas" });
|
|
109
|
-
await navigate({
|
|
113
|
+
await navigate({
|
|
114
|
+
to: `${localizedPath(page.fullPath, locale)}${search}`,
|
|
115
|
+
...target.draft === void 0 ? {} : { draft: target.draft }
|
|
116
|
+
});
|
|
110
117
|
};
|
|
111
118
|
$[1] = cache;
|
|
112
119
|
$[2] = navigate;
|