camox 0.56.0 → 0.58.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 (38) hide show
  1. package/dist/core/createApp.d.ts +2 -2
  2. package/dist/core/createBlock.js +6 -2
  3. package/dist/core/createLayout.d.ts +1 -1
  4. package/dist/core/editing/createEditableBlock.d.ts +1 -1
  5. package/dist/core/lib/artifact.d.ts +14 -4
  6. package/dist/core/lib/artifact.js +57 -16
  7. package/dist/core/lib/artifactRuntime.js +4 -2
  8. package/dist/features/canvas/CanvasPageHeader.js +1 -1
  9. package/dist/features/canvas/canvasFrameDocument.js +13 -6
  10. package/dist/features/preview/components/Frame.js +1 -18
  11. package/dist/features/preview/components/SidebarStack.js +193 -106
  12. package/dist/features/preview/components/previewStyles.js +25 -1
  13. package/dist/features/renderer/RendererApp.js +55 -37
  14. package/dist/features/runtime/artifactDocuments.js +58 -0
  15. package/dist/features/runtime/artifactSources.js +36 -2
  16. package/dist/features/runtime/runtime.js +30 -12
  17. package/dist/features/runtime/runtimeRoutes.d.ts +6 -5
  18. package/dist/features/runtime/runtimeRoutes.js +12 -5
  19. package/dist/features/studio/components/AgentSidebar.js +4 -4
  20. package/dist/features/studio/components/ChatWidget.js +62 -80
  21. package/dist/features/studio/components/DraftsControl.js +60 -134
  22. package/dist/features/studio/components/Patrol.js +676 -0
  23. package/dist/features/studio/components/ReviewDraftSheet.js +52 -4
  24. package/dist/features/studio/components/Squareling.js +169 -84
  25. package/dist/features/studio/components/ThreadMessages.js +155 -58
  26. package/dist/features/studio/localeOptions.js +7 -3
  27. package/dist/features/studio/reportSentence.js +75 -0
  28. package/dist/features/studio/routes.js +2 -1
  29. package/dist/features/studio/showInCanvas.js +9 -2
  30. package/dist/features/studio/workingSentence.js +55 -0
  31. package/dist/lib/studio-reads.d.ts +1 -0
  32. package/dist/lib/studio-reads.js +5 -0
  33. package/dist/lib/use-thread.js +156 -190
  34. package/dist/package.js +1 -1
  35. package/dist/studio.css +1 -1
  36. package/package.json +4 -4
  37. package/dist/features/studio/components/RenameDraftDialog.js +0 -206
  38. package/dist/features/studio/components/WorkersWidget.js +0 -52
@@ -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
- const name = localeNames.of(tag);
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 page = (await cache.ensure(reads.pageList({ projectUuid }), { locale }).catch(_temp2)).find((each_0) => each_0.id === target.pageId);
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({ to: `${localizedPath(page.fullPath, locale)}${search}` });
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;
@@ -0,0 +1,55 @@
1
+ import { localeName } from "./localeOptions.js";
2
+
3
+ //#region src/features/studio/workingSentence.ts
4
+ const PHRASES = {
5
+ translator: {
6
+ verb: "Translating",
7
+ locale: "into",
8
+ singleLocale: "Add a language to translate into"
9
+ },
10
+ seoManager: {
11
+ verb: "Writing SEO for",
12
+ verbFor: { file: "Describing" },
13
+ locale: "in"
14
+ }
15
+ };
16
+ /** The most locales a burst names; past it, the first are named and the rest counted. */
17
+ const NAMED_LOCALES = 3;
18
+ /** Joins parts as a list: "a", "a and b", "a, b and c". */
19
+ const listed = (parts) => parts.length < 2 ? parts.join("") : `${parts.slice(0, -1).join(", ")} and ${parts.at(-1)}`;
20
+ /**
21
+ * Each locale of a burst with its count, the first counted by the caller: "into German and 12
22
+ * into Spanish", or with the rest counted together, "…and 9 into 2 other languages".
23
+ */
24
+ function inLocales(locales, preposition) {
25
+ const named = locales.length > NAMED_LOCALES ? locales.slice(0, 2) : locales;
26
+ const rest = locales.slice(named.length);
27
+ const parts = named.map(({ locale, count }, index) => `${index === 0 ? "" : `${count} `}${preposition} ${localeName(locale)}`);
28
+ if (rest.length > 0) {
29
+ const count = rest.reduce((sum, each) => sum + each.count, 0);
30
+ parts.push(`${count} ${preposition} ${rest.length} other languages`);
31
+ }
32
+ return listed(parts);
33
+ }
34
+ /**
35
+ * What a patroller is working on: "Translating hero into Spanish" for one subject, "Translating
36
+ * 38 items into German and 12 into Spanish" for a burst, counted in each locale.
37
+ */
38
+ function workingSentence(id, working) {
39
+ const phrase = PHRASES[id];
40
+ const { subject, locales } = working;
41
+ const verb = (subject && phrase.verbFor?.[subject.entity]) ?? phrase.verb;
42
+ const first = locales[0]?.count ?? 0;
43
+ return [
44
+ verb,
45
+ subject?.name ?? `${first} ${first === 1 ? "item" : "items"}`,
46
+ inLocales(locales, phrase.locale)
47
+ ].filter(Boolean).join(" ");
48
+ }
49
+ /** What a patroller says while idle in a project with a single locale, if anything. */
50
+ function singleLocaleSentence(id) {
51
+ return PHRASES[id].singleLocale;
52
+ }
53
+
54
+ //#endregion
55
+ export { singleLocaleSentence, workingSentence };
@@ -7,6 +7,7 @@ import "./queries.js";
7
7
  import "react";
8
8
  import "@camox/api-contract/query-keys";
9
9
  import { QueryClient } from "@tanstack/react-query";
10
+ import "@camox/api-contract/patrol";
10
11
  //#region src/lib/studio-reads.d.ts
11
12
  type LayoutRead = Awaited<ReturnType<ApiClient["layouts"]["get"]>>;
12
13
  /** A layout's structure: its blocks before and after the content of its pages. */
@@ -206,6 +206,11 @@ const reads = {
206
206
  fetch: ({ apiIn, signal }, { projectId, draftId }) => apiIn(draftId).drafts.get({ projectId }, { signal }),
207
207
  staleTime: Infinity
208
208
  }),
209
+ patrol: entry({
210
+ key: ({ projectId }) => [...queryKeys.patrol.get, projectId],
211
+ everyLocale: true,
212
+ fetch: ({ api, signal }, { projectId }) => api.patrol.get({ projectId }, { signal })
213
+ }),
209
214
  publishPreview: entry({
210
215
  key: ({ draftId }) => [
211
216
  ...queryKeys.drafts.all,
@@ -13,19 +13,16 @@ const OPEN = 1;
13
13
  * signed-in editor's Live thread when `draftId` is null, or the draft's thread, which `draftId`
14
14
  * names as the draft header does (`development` in dev mode). A message is resent with its
15
15
  * operation id each time the thread shows itself after the socket opens, until it accepts it,
16
- * which records it once, or turns it down. A thread that ended with its draft isn't reconnected
17
- * to, but for a development draft's or the editor's Live thread, which move on to a new thread.
18
- * Mount it once per project and draft.
16
+ * which records it once, or turns it down. A draft's thread that ended with its draft isn't
17
+ * reconnected to, but for a development draft's, which moves on to a new thread. Mount it once
18
+ * per project and draft.
19
19
  *
20
- * When the Live thread starts a draft, a studio that sent a message of the run that wrote enters
21
- * the draft with it (`onEnterDraft`), even if it only hears so on reconnecting, from the editor's
22
- * fresh Live thread; its messages still on their way go with it. Any other studio moves on to
23
- * that fresh Live thread, continued in the draft.
20
+ * When the agent on Live hands a request off to a draft it started, the studio that sent a
21
+ * message of that run hears it (`onEnterDraft`), to enter the draft. The editor's Live thread
22
+ * stays as it is, ending that turn on the draft's card, which every studio on Live shows.
24
23
  *
25
24
  * It reports what each writing script touched (`touched`), for runs one of its messages was part
26
- * of only, so that the studio follows the agent; teammates' runs leave it where it is. A studio
27
- * entering the draft its Live thread started keeps following that run there, starting with what
28
- * the write touched.
25
+ * of only, so that the studio follows the agent; teammates' runs leave it where it is.
29
26
  */
30
27
  function useThread(apiUrl, projectId, draftId, t0) {
31
28
  const $ = c(47);
@@ -35,8 +32,8 @@ function useThread(apiUrl, projectId, draftId, t0) {
35
32
  $[0] = t0;
36
33
  $[1] = t1;
37
34
  } else t1 = $[1];
38
- const { carried, onEnterDraft } = t1;
39
- const [state, setState] = React.useState(carried?.state ?? EMPTY_THREAD_STATE);
35
+ const { onEnterDraft } = t1;
36
+ const [state, setState] = React.useState(EMPTY_THREAD_STATE);
40
37
  let t2;
41
38
  if ($[2] !== state) {
42
39
  t2 = threadViewOf(state);
@@ -45,50 +42,37 @@ function useThread(apiUrl, projectId, draftId, t0) {
45
42
  } else t2 = $[3];
46
43
  const view = t2;
47
44
  let t3;
48
- if ($[4] !== carried?.unaccepted) {
49
- t3 = carried?.unaccepted ?? /* @__PURE__ */ new Map();
50
- $[4] = carried?.unaccepted;
51
- $[5] = t3;
52
- } else t3 = $[5];
45
+ if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
46
+ t3 = /* @__PURE__ */ new Map();
47
+ $[4] = t3;
48
+ } else t3 = $[4];
53
49
  const [unaccepted, setUnaccepted] = React.useState(t3);
54
50
  const [error, setError] = React.useState();
55
- const t4 = carried?.sent;
56
- let t5;
57
- if ($[6] !== t4) {
58
- t5 = new Set(t4);
59
- $[6] = t4;
60
- $[7] = t5;
61
- } else t5 = $[7];
62
- const sent = React.useRef(t5);
51
+ let t4;
52
+ if ($[5] !== apiUrl || $[6] !== draftId || $[7] !== projectId) {
53
+ t4 = () => new Set(handedOffTo(apiUrl, projectId, draftId));
54
+ $[5] = apiUrl;
55
+ $[6] = draftId;
56
+ $[7] = projectId;
57
+ $[8] = t4;
58
+ } else t4 = $[8];
59
+ const [sent] = React.useState(t4);
63
60
  const shown = React.useRef(false);
64
61
  const [shows, setShows] = React.useState(0);
65
- const [entering, setEntering] = React.useState();
66
- const leaving = React.useRef(false);
67
- let t6;
68
- if ($[8] !== carried) {
69
- t6 = () => carried?.touched ? {
70
- sequence: 1,
71
- batch: carried.touched
72
- } : void 0;
73
- $[8] = carried;
74
- $[9] = t6;
75
- } else t6 = $[9];
76
- const [touched, setTouched] = React.useState(t6);
77
- const touchedWhileLeaving = React.useRef(void 0);
78
- let t7;
79
- if ($[10] !== apiUrl) {
80
- t7 = apiSocketOptions(apiUrl);
81
- $[10] = apiUrl;
82
- $[11] = t7;
83
- } else t7 = $[11];
62
+ const [touched, setTouched] = React.useState();
63
+ let t5;
64
+ if ($[9] !== apiUrl) {
65
+ t5 = apiSocketOptions(apiUrl);
66
+ $[9] = apiUrl;
67
+ $[10] = t5;
68
+ } else t5 = $[10];
84
69
  const socket = usePartySocket({
85
- ...t7,
70
+ ...t5,
86
71
  basePath: draftId ? `threads/draft/${projectId}/${draftId}` : `threads/live/${projectId}`,
87
72
  maxEnqueuedMessages: 0,
88
73
  shouldReconnectOnClose: _temp,
89
74
  onClose(event_0) {
90
- const movesOn = draftId === null || draftId === DEVELOPMENT_DRAFT;
91
- if (event_0.code === THREAD_ENDED_CLOSE_CODE && movesOn) socket.reconnect();
75
+ if (event_0.code === THREAD_ENDED_CLOSE_CODE && draftId === DEVELOPMENT_DRAFT) socket.reconnect();
92
76
  },
93
77
  onOpen() {
94
78
  shown.current = false;
@@ -104,153 +88,125 @@ function useThread(apiUrl, projectId, draftId, t0) {
104
88
  onFrame(frame);
105
89
  }
106
90
  });
91
+ let t6;
92
+ if ($[11] !== draftId || $[12] !== sent) {
93
+ t6 = (started) => draftId === null && started.operationIds.some((operationId) => sent.has(operationId));
94
+ $[11] = draftId;
95
+ $[12] = sent;
96
+ $[13] = t6;
97
+ } else t6 = $[13];
98
+ const asked = t6;
99
+ let t7;
100
+ if ($[14] !== sent) {
101
+ t7 = (batch) => batch.operationIds.some((operationId_0) => sent.has(operationId_0));
102
+ $[14] = sent;
103
+ $[15] = t7;
104
+ } else t7 = $[15];
105
+ const ours = t7;
107
106
  let t8;
108
- if ($[12] !== draftId) {
109
- t8 = (started) => draftId === null && started.operationIds.some((operationId) => sent.current.has(operationId));
110
- $[12] = draftId;
111
- $[13] = t8;
112
- } else t8 = $[13];
113
- const wrote = t8;
114
- let t9;
115
- if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
116
- t9 = (batch) => batch.operationIds.some((operationId_0) => sent.current.has(operationId_0));
117
- $[14] = t9;
118
- } else t9 = $[14];
119
- const ours = t9;
120
- const onFrame = React.useEffectEvent((frame_0) => {
121
- if (leaving.current) {
122
- if (frame_0.type === "touched" && ours(frame_0.touched)) touchedWhileLeaving.current = frame_0.touched;
123
- return;
124
- }
125
- switch (frame_0.type) {
126
- case "started-draft":
127
- case "continued-in": {
128
- const started_0 = frame_0.type === "started-draft" ? frame_0.startedDraft : frame_0.continuedIn;
129
- if (wrote(started_0)) {
130
- leaving.current = true;
131
- setEntering(started_0.draftId);
107
+ if ($[16] !== apiUrl || $[17] !== asked || $[18] !== onEnterDraft || $[19] !== ours || $[20] !== projectId || $[21] !== sent) {
108
+ t8 = (frame_0) => {
109
+ switch (frame_0.type) {
110
+ case "started-draft":
111
+ if (asked(frame_0.startedDraft)) {
112
+ handedOff.set(threadKey(apiUrl, projectId, frame_0.startedDraft.draftId), sent);
113
+ onEnterDraft?.(frame_0.startedDraft.draftId);
114
+ }
132
115
  return;
133
- }
134
- if (frame_0.type === "started-draft" && draftId === null) {
135
- setState(EMPTY_THREAD_STATE);
136
- socket.reconnect();
116
+ case "events":
117
+ setState((current_2) => reduceThreadFrame(current_2, frame_0));
118
+ if (!shown.current) {
119
+ shown.current = true;
120
+ setShows(_temp2);
121
+ }
122
+ return;
123
+ case "drafts":
124
+ setState((current_1) => reduceThreadFrame(current_1, frame_0));
125
+ return;
126
+ case "touched":
127
+ if (ours(frame_0.touched)) {
128
+ const batch_0 = frame_0.touched;
129
+ setTouched((last) => ({
130
+ sequence: (last?.sequence ?? 0) + 1,
131
+ batch: batch_0
132
+ }));
133
+ }
134
+ return;
135
+ case "accepted":
136
+ setUnaccepted((current_0) => without(current_0, frame_0.operationId));
137
+ return;
138
+ case "error": {
139
+ console.error("[useThread]", frame_0.message);
140
+ setError(frame_0.message);
141
+ const { operationId: operationId_1 } = frame_0;
142
+ if (operationId_1) setUnaccepted((current) => without(current, operationId_1));
137
143
  return;
138
144
  }
139
- setState((current_2) => reduceThreadFrame(current_2, frame_0));
140
- return;
141
- }
142
- case "events":
143
- setState((current_1) => reduceThreadFrame(current_1, frame_0));
144
- if (!shown.current) {
145
- shown.current = true;
146
- setShows(_temp2);
147
- }
148
- return;
149
- case "touched":
150
- if (ours(frame_0.touched)) {
151
- const batch_0 = frame_0.touched;
152
- setTouched((last) => ({
153
- sequence: (last?.sequence ?? 0) + 1,
154
- batch: batch_0
155
- }));
156
- }
157
- return;
158
- case "accepted":
159
- setUnaccepted((current_0) => without(current_0, frame_0.operationId));
160
- return;
161
- case "error": {
162
- console.error("[useThread]", frame_0.message);
163
- setError(frame_0.message);
164
- const { operationId: operationId_1 } = frame_0;
165
- if (operationId_1) setUnaccepted((current) => without(current, operationId_1));
166
- return;
167
145
  }
168
- }
169
- });
170
- let t10;
171
- if ($[15] !== socket || $[16] !== unaccepted) {
172
- t10 = () => {
146
+ };
147
+ $[16] = apiUrl;
148
+ $[17] = asked;
149
+ $[18] = onEnterDraft;
150
+ $[19] = ours;
151
+ $[20] = projectId;
152
+ $[21] = sent;
153
+ $[22] = t8;
154
+ } else t8 = $[22];
155
+ const onFrame = React.useEffectEvent(t8);
156
+ let t9;
157
+ if ($[23] !== socket || $[24] !== unaccepted) {
158
+ t9 = () => {
173
159
  for (const [operationId_2, text] of unaccepted) transmit(socket, {
174
160
  type: "send",
175
161
  operationId: operationId_2,
176
162
  text
177
163
  });
178
164
  };
179
- $[15] = socket;
180
- $[16] = unaccepted;
181
- $[17] = t10;
182
- } else t10 = $[17];
183
- const resendUnaccepted = React.useEffectEvent(t10);
184
- let t11;
185
- if ($[18] !== resendUnaccepted || $[19] !== shows) {
186
- t11 = () => {
165
+ $[23] = socket;
166
+ $[24] = unaccepted;
167
+ $[25] = t9;
168
+ } else t9 = $[25];
169
+ const resendUnaccepted = React.useEffectEvent(t9);
170
+ let t10;
171
+ if ($[26] !== resendUnaccepted || $[27] !== shows) {
172
+ t10 = () => {
187
173
  if (shows > 0) resendUnaccepted();
188
174
  };
189
- $[18] = resendUnaccepted;
190
- $[19] = shows;
191
- $[20] = t11;
192
- } else t11 = $[20];
175
+ $[26] = resendUnaccepted;
176
+ $[27] = shows;
177
+ $[28] = t10;
178
+ } else t10 = $[28];
179
+ let t11;
180
+ if ($[29] !== shows) {
181
+ t11 = [shows];
182
+ $[29] = shows;
183
+ $[30] = t11;
184
+ } else t11 = $[30];
185
+ React.useEffect(t10, t11);
193
186
  let t12;
194
- if ($[21] !== shows) {
195
- t12 = [shows];
196
- $[21] = shows;
197
- $[22] = t12;
198
- } else t12 = $[22];
199
- React.useEffect(t11, t12);
200
- let t13;
201
- if ($[23] !== onEnterDraft || $[24] !== state || $[25] !== unaccepted) {
202
- t13 = (started_1) => onEnterDraft?.(started_1, {
203
- state,
204
- unaccepted,
205
- sent: sent.current,
206
- touched: touchedWhileLeaving.current
207
- });
208
- $[23] = onEnterDraft;
209
- $[24] = state;
210
- $[25] = unaccepted;
211
- $[26] = t13;
212
- } else t13 = $[26];
213
- const enterDraft = React.useEffectEvent(t13);
214
- let t14;
215
- if ($[27] !== enterDraft || $[28] !== entering) {
216
- t14 = () => {
217
- if (entering) enterDraft(entering);
218
- };
219
- $[27] = enterDraft;
220
- $[28] = entering;
221
- $[29] = t14;
222
- } else t14 = $[29];
223
- let t15;
224
- if ($[30] !== entering) {
225
- t15 = [entering];
226
- $[30] = entering;
227
- $[31] = t15;
228
- } else t15 = $[31];
229
- React.useEffect(t14, t15);
230
- let t16;
231
- if ($[32] !== socket) {
232
- t16 = () => {
187
+ if ($[31] !== socket) {
188
+ t12 = () => {
233
189
  const resync = () => {
234
190
  if (document.visibilityState === "visible") transmit(socket, { type: "resync" });
235
191
  };
236
192
  document.addEventListener("visibilitychange", resync);
237
193
  return () => document.removeEventListener("visibilitychange", resync);
238
194
  };
239
- $[32] = socket;
240
- $[33] = t16;
241
- } else t16 = $[33];
242
- let t17;
243
- if ($[34] !== socket) {
244
- t17 = [socket];
245
- $[34] = socket;
246
- $[35] = t17;
247
- } else t17 = $[35];
248
- React.useEffect(t16, t17);
249
- let t18;
250
- if ($[36] !== socket) {
251
- t18 = (text_0) => {
195
+ $[31] = socket;
196
+ $[32] = t12;
197
+ } else t12 = $[32];
198
+ let t13;
199
+ if ($[33] !== socket) {
200
+ t13 = [socket];
201
+ $[33] = socket;
202
+ $[34] = t13;
203
+ } else t13 = $[34];
204
+ React.useEffect(t12, t13);
205
+ let t14;
206
+ if ($[35] !== sent || $[36] !== socket) {
207
+ t14 = (text_0) => {
252
208
  const operationId_3 = crypto.randomUUID();
253
- sent.current.add(operationId_3);
209
+ sent.add(operationId_3);
254
210
  setError(void 0);
255
211
  setUnaccepted((current_3) => new Map(current_3).set(operationId_3, text_0));
256
212
  transmit(socket, {
@@ -259,23 +215,24 @@ function useThread(apiUrl, projectId, draftId, t0) {
259
215
  text: text_0
260
216
  });
261
217
  };
218
+ $[35] = sent;
262
219
  $[36] = socket;
263
- $[37] = t18;
264
- } else t18 = $[37];
265
- const send = t18;
266
- let t19;
220
+ $[37] = t14;
221
+ } else t14 = $[37];
222
+ const send = t14;
223
+ let t15;
267
224
  if ($[38] !== socket) {
268
- t19 = () => transmit(socket, { type: "stop" });
225
+ t15 = () => transmit(socket, { type: "stop" });
269
226
  $[38] = socket;
270
- $[39] = t19;
271
- } else t19 = $[39];
272
- const stop = t19;
273
- const t20 = unaccepted.size > 0;
274
- let t21;
275
- if ($[40] !== error || $[41] !== send || $[42] !== stop || $[43] !== t20 || $[44] !== touched || $[45] !== view) {
276
- t21 = {
227
+ $[39] = t15;
228
+ } else t15 = $[39];
229
+ const stop = t15;
230
+ const t16 = unaccepted.size > 0;
231
+ let t17;
232
+ if ($[40] !== error || $[41] !== send || $[42] !== stop || $[43] !== t16 || $[44] !== touched || $[45] !== view) {
233
+ t17 = {
277
234
  view,
278
- sending: t20,
235
+ sending: t16,
279
236
  send,
280
237
  stop,
281
238
  error,
@@ -284,12 +241,12 @@ function useThread(apiUrl, projectId, draftId, t0) {
284
241
  $[40] = error;
285
242
  $[41] = send;
286
243
  $[42] = stop;
287
- $[43] = t20;
244
+ $[43] = t16;
288
245
  $[44] = touched;
289
246
  $[45] = view;
290
- $[46] = t21;
291
- } else t21 = $[46];
292
- return t21;
247
+ $[46] = t17;
248
+ } else t17 = $[46];
249
+ return t17;
293
250
  }
294
251
  function _temp2(count) {
295
252
  return count + 1;
@@ -297,6 +254,15 @@ function _temp2(count) {
297
254
  function _temp(event) {
298
255
  return event.code !== THREAD_ENDED_CLOSE_CODE;
299
256
  }
257
+ /**
258
+ * The operation ids of the messages a studio sent on Live, by the draft the agent on Live handed
259
+ * its request off to: the draft's agent runs on them, so once the studio follows that draft's
260
+ * thread, it follows what they touch.
261
+ */
262
+ const handedOff = /* @__PURE__ */ new Map();
263
+ const threadKey = (apiUrl, projectId, draftId) => `${apiUrl}|${projectId}|${draftId}`;
264
+ /** The operation ids a studio carries into the draft `draftId`: none for Live. */
265
+ const handedOffTo = (apiUrl, projectId, draftId) => draftId !== null && handedOff.get(threadKey(apiUrl, projectId, draftId)) || [];
300
266
  function transmit(socket, message) {
301
267
  if (socket.readyState === OPEN) socket.send(JSON.stringify(message));
302
268
  }
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.56.0";
2
+ var version = "0.58.0";
3
3
 
4
4
  //#endregion
5
5
  export { version };