camox 0.77.0 → 0.79.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 (32) hide show
  1. package/dist/features/canvas/CamoxCanvas.js +187 -197
  2. package/dist/features/canvas/canvasCameraController.js +1 -1
  3. package/dist/features/canvas/canvasItems.js +40 -9
  4. package/dist/features/canvas/sidebar/ContentSidebarFrame.js +15 -11
  5. package/dist/features/canvas/useCanvasLayout.js +34 -0
  6. package/dist/features/memory/CamoxMemory.js +756 -0
  7. package/dist/features/memory/VoiceGroup.js +350 -0
  8. package/dist/features/preview/EditablePageExperience.js +2 -0
  9. package/dist/features/runtime/publicCache.d.ts +2 -0
  10. package/dist/features/runtime/publicCache.js +27 -4
  11. package/dist/features/runtime/runtime.d.ts +1 -1
  12. package/dist/features/runtime/runtime.js +34 -15
  13. package/dist/features/studio/FollowAgentProvider.js +1 -1
  14. package/dist/features/studio/components/AgentSidebar.js +22 -16
  15. package/dist/features/studio/components/DraftBar.js +167 -103
  16. package/dist/features/studio/components/{ReviewDraftDialog.js → DraftReview.js} +214 -218
  17. package/dist/features/studio/components/DraftsControl.js +174 -202
  18. package/dist/features/studio/components/DraftsSheet.js +102 -94
  19. package/dist/features/studio/components/Navbar.js +121 -84
  20. package/dist/features/studio/components/PublishedTab.js +21 -20
  21. package/dist/features/studio/routes.js +3 -1
  22. package/dist/features/studio/studioStore.js +12 -9
  23. package/dist/features/studio/usePublishDraft.js +116 -0
  24. package/dist/features/studio/viewSwitch.js +10 -7
  25. package/dist/features/vite/vite.js +1 -0
  26. package/dist/lib/queries.js +11 -1
  27. package/dist/lib/studio-reads.d.ts +1 -0
  28. package/dist/lib/studio-reads.js +12 -0
  29. package/dist/package.js +1 -1
  30. package/dist/studio.css +1 -1
  31. package/package.json +5 -5
  32. package/LICENSE.md +0 -21
@@ -0,0 +1,350 @@
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 { memoryMutations } from "../../lib/queries.js";
5
+ import { previewStore } from "../preview/previewStore.js";
6
+ import { useWebsiteHref } from "../studio/components/Navbar.js";
7
+ import { c } from "react/compiler-runtime";
8
+ import * as React from "react";
9
+ import { jsx, jsxs } from "react/jsx-runtime";
10
+ import { useMutation } from "@tanstack/react-query";
11
+ import { Button } from "@camox/ui/button";
12
+ import { Skeleton } from "@camox/ui/skeleton";
13
+ import { Spinner } from "@camox/ui/spinner";
14
+ import { VOICE_AXES } from "@camox/api-contract/voice";
15
+ import { Slider } from "@camox/ui/slider";
16
+
17
+ //#region src/features/memory/VoiceGroup.tsx
18
+ /**
19
+ * The Voice group, at the top of the Memory Section (ADR 0051): a slider per Voice Axis, showing
20
+ * its chosen step and that step's definition, as models read it. Moving sliders saves nothing:
21
+ * Save shows while they differ from the saved Voice, and saves every moved axis as one change.
22
+ * Like any memory edit, saving while viewing Live starts a draft. "Save and rewrite", beside it,
23
+ * also asks Camox's agent, as the member, to rewrite the site's copy in the new Voice, and shows
24
+ * the draft with its thread beside the Canvas, where the rewrite happens.
25
+ */
26
+ function VoiceGroup(t0) {
27
+ const $ = c(31);
28
+ const { projectId } = t0;
29
+ let t1;
30
+ if ($[0] !== projectId) {
31
+ t1 = reads.voice({ projectId });
32
+ $[0] = projectId;
33
+ $[1] = t1;
34
+ } else t1 = $[1];
35
+ const { data: saved } = useReadQuery(t1);
36
+ let t2;
37
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
38
+ t2 = {};
39
+ $[2] = t2;
40
+ } else t2 = $[2];
41
+ const [moved, setMoved] = React.useState(t2);
42
+ const cache = useStudioCache();
43
+ const draftScope = useDraftScope();
44
+ const navigate = useNavigate();
45
+ const websiteHref = useWebsiteHref();
46
+ let t3;
47
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
48
+ t3 = memoryMutations.saveVoice();
49
+ $[3] = t3;
50
+ } else t3 = $[3];
51
+ let t4;
52
+ if ($[4] !== cache || $[5] !== draftScope || $[6] !== navigate || $[7] !== projectId || $[8] !== websiteHref) {
53
+ t4 = {
54
+ ...t3,
55
+ onSuccess: (_voice, t5) => {
56
+ const { rewrite } = t5;
57
+ setMoved({});
58
+ cache.invalidate(reads.voice({ projectId }));
59
+ if (!rewrite) return;
60
+ previewStore.send({ type: "showCanvas" });
61
+ navigate({
62
+ to: websiteHref(),
63
+ draft: draftScope.current() ?? void 0
64
+ });
65
+ }
66
+ };
67
+ $[4] = cache;
68
+ $[5] = draftScope;
69
+ $[6] = navigate;
70
+ $[7] = projectId;
71
+ $[8] = websiteHref;
72
+ $[9] = t4;
73
+ } else t4 = $[9];
74
+ const save = useMutation(t4);
75
+ if (!saved) {
76
+ let t5;
77
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
78
+ t5 = /* @__PURE__ */ jsx(Skeleton, { className: "h-72" });
79
+ $[10] = t5;
80
+ } else t5 = $[10];
81
+ return t5;
82
+ }
83
+ let t5;
84
+ let voice;
85
+ if ($[11] !== moved || $[12] !== saved) {
86
+ voice = {
87
+ ...saved,
88
+ ...moved
89
+ };
90
+ t5 = changedAxes(saved, voice);
91
+ $[11] = moved;
92
+ $[12] = saved;
93
+ $[13] = t5;
94
+ $[14] = voice;
95
+ } else {
96
+ t5 = $[13];
97
+ voice = $[14];
98
+ }
99
+ const changed = t5;
100
+ let t6;
101
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
102
+ t6 = /* @__PURE__ */ jsxs("header", {
103
+ className: "flex flex-col gap-1",
104
+ children: [/* @__PURE__ */ jsx("h2", {
105
+ id: "camox-voice-title",
106
+ className: "text-sm font-semibold",
107
+ children: "Voice"
108
+ }), /* @__PURE__ */ jsx("p", {
109
+ className: "text-muted-foreground text-xs",
110
+ children: "How the website speaks. Every model that writes for visitors keeps to it."
111
+ })]
112
+ });
113
+ $[15] = t6;
114
+ } else t6 = $[15];
115
+ let t7;
116
+ if ($[16] !== save || $[17] !== voice) {
117
+ t7 = VOICE_AXES.map((axis) => /* @__PURE__ */ jsx(VoiceAxisRow, {
118
+ axis,
119
+ step: voice[axis.key],
120
+ disabled: save.isPending,
121
+ onStep: (step) => {
122
+ setMoved((current) => ({
123
+ ...current,
124
+ [axis.key]: step
125
+ }));
126
+ save.reset();
127
+ }
128
+ }, axis.key));
129
+ $[16] = save;
130
+ $[17] = voice;
131
+ $[18] = t7;
132
+ } else t7 = $[18];
133
+ let t8;
134
+ if ($[19] !== t7) {
135
+ t8 = /* @__PURE__ */ jsx("ul", {
136
+ className: "flex flex-col gap-5",
137
+ children: t7
138
+ });
139
+ $[19] = t7;
140
+ $[20] = t8;
141
+ } else t8 = $[20];
142
+ let t9;
143
+ if ($[21] !== save.error) {
144
+ t9 = save.error && /* @__PURE__ */ jsx("p", {
145
+ "data-voice-error": true,
146
+ className: "text-destructive text-xs",
147
+ children: save.error.message
148
+ });
149
+ $[21] = save.error;
150
+ $[22] = t9;
151
+ } else t9 = $[22];
152
+ let t10;
153
+ if ($[23] !== changed || $[24] !== projectId || $[25] !== save) {
154
+ t10 = Object.keys(changed).length > 0 && /* @__PURE__ */ jsxs("div", {
155
+ className: "flex flex-wrap gap-2",
156
+ children: [/* @__PURE__ */ jsxs(Button, {
157
+ disabled: save.isPending,
158
+ onClick: () => save.mutate({
159
+ projectId,
160
+ steps: changed
161
+ }),
162
+ children: [save.isPending && !save.variables?.rewrite && /* @__PURE__ */ jsx(Spinner, {}), "Save"]
163
+ }), /* @__PURE__ */ jsxs(Button, {
164
+ variant: "outline",
165
+ disabled: save.isPending,
166
+ onClick: () => save.mutate({
167
+ projectId,
168
+ steps: changed,
169
+ rewrite: true
170
+ }),
171
+ children: [save.isPending && save.variables?.rewrite && /* @__PURE__ */ jsx(Spinner, {}), "Save and rewrite"]
172
+ })]
173
+ });
174
+ $[23] = changed;
175
+ $[24] = projectId;
176
+ $[25] = save;
177
+ $[26] = t10;
178
+ } else t10 = $[26];
179
+ let t11;
180
+ if ($[27] !== t10 || $[28] !== t8 || $[29] !== t9) {
181
+ t11 = /* @__PURE__ */ jsxs("section", {
182
+ "data-voice": true,
183
+ "aria-labelledby": "camox-voice-title",
184
+ className: "flex flex-col gap-5 rounded-lg border p-4",
185
+ children: [
186
+ t6,
187
+ t8,
188
+ t9,
189
+ t10
190
+ ]
191
+ });
192
+ $[27] = t10;
193
+ $[28] = t8;
194
+ $[29] = t9;
195
+ $[30] = t11;
196
+ } else t11 = $[30];
197
+ return t11;
198
+ }
199
+ /** The axes `voice` moves away from `saved`, with their new steps. */
200
+ function changedAxes(saved, voice) {
201
+ const changed = {};
202
+ for (const { key } of VOICE_AXES) if (voice[key] !== saved[key]) changed[key] = voice[key];
203
+ return changed;
204
+ }
205
+ const sentenceCase = (text) => text.charAt(0).toUpperCase() + text.slice(1);
206
+ /** One Voice Axis: its name, its chosen step and that step's definition, over a slider. */
207
+ function VoiceAxisRow(t0) {
208
+ const $ = c(39);
209
+ const { axis, step, disabled, onStep } = t0;
210
+ const steps = axis.steps;
211
+ let t1;
212
+ if ($[0] !== step || $[1] !== steps) {
213
+ let t2;
214
+ if ($[3] !== step) {
215
+ t2 = (candidate) => candidate.key === step;
216
+ $[3] = step;
217
+ $[4] = t2;
218
+ } else t2 = $[4];
219
+ t1 = steps.findIndex(t2);
220
+ $[0] = step;
221
+ $[1] = steps;
222
+ $[2] = t1;
223
+ } else t1 = $[2];
224
+ const index = Math.max(t1, 0);
225
+ const chosen = steps[index];
226
+ let t2;
227
+ if ($[5] !== axis.name) {
228
+ t2 = /* @__PURE__ */ jsx("span", {
229
+ "data-voice-axis-name": true,
230
+ className: "font-medium",
231
+ children: axis.name
232
+ });
233
+ $[5] = axis.name;
234
+ $[6] = t2;
235
+ } else t2 = $[6];
236
+ let t3;
237
+ if ($[7] !== chosen.name) {
238
+ t3 = /* @__PURE__ */ jsx("span", {
239
+ "data-voice-step": true,
240
+ children: chosen.name
241
+ });
242
+ $[7] = chosen.name;
243
+ $[8] = t3;
244
+ } else t3 = $[8];
245
+ let t4;
246
+ if ($[9] !== t2 || $[10] !== t3) {
247
+ t4 = /* @__PURE__ */ jsxs("div", {
248
+ className: "flex items-baseline justify-between gap-3 text-sm",
249
+ children: [t2, t3]
250
+ });
251
+ $[9] = t2;
252
+ $[10] = t3;
253
+ $[11] = t4;
254
+ } else t4 = $[11];
255
+ const t5 = steps.length - 1;
256
+ let t6;
257
+ if ($[12] !== onStep || $[13] !== steps) {
258
+ t6 = (value) => {
259
+ const next = steps[value];
260
+ if (next) onStep(next.key);
261
+ };
262
+ $[12] = onStep;
263
+ $[13] = steps;
264
+ $[14] = t6;
265
+ } else t6 = $[14];
266
+ let t7;
267
+ if ($[15] !== axis.name || $[16] !== chosen.name || $[17] !== disabled || $[18] !== index || $[19] !== steps.length || $[20] !== t5 || $[21] !== t6) {
268
+ t7 = /* @__PURE__ */ jsx(Slider, {
269
+ label: axis.name,
270
+ valueText: chosen.name,
271
+ min: 0,
272
+ max: t5,
273
+ step: 1,
274
+ marks: steps.length,
275
+ value: index,
276
+ disabled,
277
+ onValueChange: t6
278
+ });
279
+ $[15] = axis.name;
280
+ $[16] = chosen.name;
281
+ $[17] = disabled;
282
+ $[18] = index;
283
+ $[19] = steps.length;
284
+ $[20] = t5;
285
+ $[21] = t6;
286
+ $[22] = t7;
287
+ } else t7 = $[22];
288
+ let t8;
289
+ if ($[23] !== steps[0].name) {
290
+ t8 = /* @__PURE__ */ jsx("span", { children: steps[0].name });
291
+ $[23] = steps[0].name;
292
+ $[24] = t8;
293
+ } else t8 = $[24];
294
+ const t9 = steps[steps.length - 1];
295
+ let t10;
296
+ if ($[25] !== t9.name) {
297
+ t10 = /* @__PURE__ */ jsx("span", { children: t9.name });
298
+ $[25] = t9.name;
299
+ $[26] = t10;
300
+ } else t10 = $[26];
301
+ let t11;
302
+ if ($[27] !== t10 || $[28] !== t8) {
303
+ t11 = /* @__PURE__ */ jsxs("div", {
304
+ "aria-hidden": true,
305
+ className: "text-muted-foreground flex justify-between text-[11px]",
306
+ children: [t8, t10]
307
+ });
308
+ $[27] = t10;
309
+ $[28] = t8;
310
+ $[29] = t11;
311
+ } else t11 = $[29];
312
+ let t12;
313
+ if ($[30] !== chosen.definition) {
314
+ t12 = sentenceCase(chosen.definition);
315
+ $[30] = chosen.definition;
316
+ $[31] = t12;
317
+ } else t12 = $[31];
318
+ let t13;
319
+ if ($[32] !== t12) {
320
+ t13 = /* @__PURE__ */ jsx("p", {
321
+ "data-voice-definition": true,
322
+ className: "text-muted-foreground text-xs",
323
+ children: t12
324
+ });
325
+ $[32] = t12;
326
+ $[33] = t13;
327
+ } else t13 = $[33];
328
+ let t14;
329
+ if ($[34] !== t11 || $[35] !== t13 || $[36] !== t4 || $[37] !== t7) {
330
+ t14 = /* @__PURE__ */ jsxs("li", {
331
+ "data-voice-axis": true,
332
+ className: "flex flex-col gap-2",
333
+ children: [
334
+ t4,
335
+ t7,
336
+ t11,
337
+ t13
338
+ ]
339
+ });
340
+ $[34] = t11;
341
+ $[35] = t13;
342
+ $[36] = t4;
343
+ $[37] = t7;
344
+ $[38] = t14;
345
+ } else t14 = $[38];
346
+ return t14;
347
+ }
348
+
349
+ //#endregion
350
+ export { VoiceGroup };
@@ -3,6 +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 { CamoxMemory } from "../memory/CamoxMemory.js";
6
7
  import { CamoxSettings } from "../settings/CamoxSettings.js";
7
8
  import { c } from "react/compiler-runtime";
8
9
  import { jsx } from "react/jsx-runtime";
@@ -11,6 +12,7 @@ import { jsx } from "react/jsx-runtime";
11
12
  /** The studio section each studio route shows. */
12
13
  const STUDIO_SECTIONS = {
13
14
  "studio-content": CamoxContent,
15
+ "studio-memory": CamoxMemory,
14
16
  "studio-settings": CamoxSettings
15
17
  };
16
18
  function EditablePageExperience(t0) {
@@ -16,6 +16,8 @@ export declare function readLiveCacheKey(apiUrl: string, projectUuid: string): P
16
16
  /**
17
17
  * Answers a public request from the cache, or renders it and keeps the render when it may be
18
18
  * shared. Without a Live cache key, it renders without the cache: nothing tells when Live changed.
19
+ * A key slower than `RENDER_HEAD_START_MS` starts the render before it arrives; should the cache
20
+ * have the page after all, that render is dropped.
19
21
  */
20
22
  export declare function respondThroughPublicCache(request: Request, publicCache: PublicCache, render: () => Promise<Response | null>): Promise<Response | null>;
21
23
  //#endregion
@@ -9,6 +9,13 @@ const EDGE_MAX_AGE = 86400;
9
9
  const VISITOR_CACHE_CONTROL = "public, max-age=0, must-revalidate";
10
10
  /** The search parameter the cache key adds to a request's URL. */
11
11
  const CACHE_KEY_PARAM = "__camox_cache";
12
+ /**
13
+ * How long a render waits for the Live cache key before it starts anyway. The edge answers a key
14
+ * it keeps within a few milliseconds. One it doesn't keep, the API mints, which often takes a
15
+ * second while an API isolate starts, and no render is kept under a key just minted, so the page
16
+ * renders either way: it starts while the key is on its way.
17
+ */
18
+ const RENDER_HEAD_START_MS = 50;
12
19
  /** The project's Live cache key, as the API serves it from its edge cache. */
13
20
  async function readLiveCacheKey(apiUrl, projectUuid) {
14
21
  try {
@@ -21,10 +28,26 @@ async function readLiveCacheKey(apiUrl, projectUuid) {
21
28
  /**
22
29
  * Answers a public request from the cache, or renders it and keeps the render when it may be
23
30
  * shared. Without a Live cache key, it renders without the cache: nothing tells when Live changed.
31
+ * A key slower than `RENDER_HEAD_START_MS` starts the render before it arrives; should the cache
32
+ * have the page after all, that render is dropped.
24
33
  */
25
34
  async function respondThroughPublicCache(request, publicCache, render) {
26
- const liveCacheKey = await publicCache.liveCacheKey();
27
- if (!liveCacheKey) return render();
35
+ let rendering;
36
+ const renderOnce = () => {
37
+ if (!rendering) {
38
+ rendering = render();
39
+ rendering.catch(() => void 0);
40
+ }
41
+ return rendering;
42
+ };
43
+ const headStart = setTimeout(renderOnce, 50);
44
+ let liveCacheKey;
45
+ try {
46
+ liveCacheKey = await publicCache.liveCacheKey();
47
+ } finally {
48
+ clearTimeout(headStart);
49
+ }
50
+ if (!liveCacheKey) return renderOnce();
28
51
  const key = cacheKeyOf(request, `${liveCacheKey}.${publicCache.build}`);
29
52
  let cache;
30
53
  let cached;
@@ -33,10 +56,10 @@ async function respondThroughPublicCache(request, publicCache, render) {
33
56
  cached = await cache.match(withValidators(key, request));
34
57
  } catch (error) {
35
58
  console.error("The edge cache couldn't be read: the page renders without it", error);
36
- return render();
59
+ return renderOnce();
37
60
  }
38
61
  if (cached) return forVisitors(cached, request);
39
- const response = await render();
62
+ const response = await renderOnce();
40
63
  if (!response || !mayBeShared(response)) return response;
41
64
  publicCache.waitUntil((async () => cache.put(key, forEdge(response.clone())))().catch((error) => {
42
65
  console.error("The edge cache couldn't keep a render", error);
@@ -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-settings", "studio-nested"];
14
+ declare const STUDIO_ROUTE_KINDS: readonly ["studio", "studio-content", "studio-memory", "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, STUDIO_SETTINGS_PATH, matchCollectionContentPath } from "../studio/routes.js";
11
+ import { STUDIO_ASSETS_PATH, STUDIO_CANVAS_PATH, STUDIO_CONTENT_PATH, STUDIO_MEMORY_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,6 +39,7 @@ const DEFAULT_RUNTIME_BASE_PATH = "";
39
39
  const STUDIO_ROUTE_KINDS = [
40
40
  "studio",
41
41
  "studio-content",
42
+ "studio-memory",
42
43
  "studio-settings",
43
44
  "studio-nested"
44
45
  ];
@@ -122,6 +123,10 @@ function matchRuntimeRoute(pathname) {
122
123
  kind: "studio-content",
123
124
  pathname
124
125
  };
126
+ if (pathname === "/camox/memory" || pathname === `${"/camox/memory"}/`) return {
127
+ kind: "studio-memory",
128
+ pathname
129
+ };
125
130
  if (pathname === "/camox/settings" || pathname === `${"/camox/settings"}/`) return {
126
131
  kind: "studio-settings",
127
132
  pathname
@@ -1687,21 +1692,35 @@ async function handleCamoxRequest(request, options = {}) {
1687
1692
  if (signedIn) return signedIn;
1688
1693
  }
1689
1694
  const { apiUrl, projectUuid } = options;
1695
+ if (!apiUrl || !projectUuid) return respondInLocale(options, request, pathname);
1690
1696
  const markdownPathname = parseMarkdownPath(pathname);
1691
- if (apiUrl && projectUuid && (markdownPathname || prefersMarkdown(request.headers.get("Accept") ?? ""))) {
1692
- const markdownResponse = await createPageMarkdownResponse({
1693
- ...options,
1694
- apiUrl,
1695
- projectUuid
1696
- }, {
1697
- pathname: markdownPathname ?? pathname,
1698
- request,
1699
- pagesOnly: !markdownPathname
1700
- });
1701
- if (markdownResponse) return markdownResponse;
1702
- if (markdownPathname) return new Response("Page not found", { status: 404 });
1703
- }
1704
- return respondInLocale(options, request, pathname);
1697
+ if (markdownPathname) return await createPageMarkdownResponse({
1698
+ ...options,
1699
+ apiUrl,
1700
+ projectUuid
1701
+ }, {
1702
+ pathname: markdownPathname,
1703
+ request,
1704
+ pagesOnly: false
1705
+ }) ?? new Response("Page not found", { status: 404 });
1706
+ return varyingByAccept((prefersMarkdown(request.headers.get("Accept") ?? "") ? await createPageMarkdownResponse({
1707
+ ...options,
1708
+ apiUrl,
1709
+ projectUuid
1710
+ }, {
1711
+ pathname,
1712
+ request,
1713
+ pagesOnly: true
1714
+ }) : null) ?? await respondInLocale(options, request, pathname));
1715
+ }
1716
+ /**
1717
+ * A response its URL answers in HTML or Markdown, by what the request accepts: it says so, so no
1718
+ * cache answers one with the other.
1719
+ */
1720
+ function varyingByAccept(response) {
1721
+ const varying = new Response(response.body, response);
1722
+ varying.headers.append("Vary", "Accept");
1723
+ return varying;
1705
1724
  }
1706
1725
  /**
1707
1726
  * Answers a path under its locale's prefix: the Canvas, a studio route or a page, in that locale.
@@ -4,9 +4,9 @@ import { useLocation, useNavigate } from "../navigation/navigation.js";
4
4
  import { useProjectUuid } from "../../lib/auth.js";
5
5
  import { reads, useReadQueries, useReadQuery } from "../../lib/studio-reads.js";
6
6
  import { useRoutableLayouts } from "../../lib/studio-definitions.js";
7
+ import { canvasPageId, getCanvasPages } from "../canvas/canvasPages.js";
7
8
  import { STUDIO_CANVAS_PATH } from "./routes.js";
8
9
  import { FollowAgentContext } from "./followAgentContext.js";
9
- import { canvasPageId, getCanvasPages } from "../canvas/canvasPages.js";
10
10
  import { selectedCanvasPage } from "../canvas/canvasSelection.js";
11
11
  import { useDiscoveredPreviewPaths } from "../canvas/useDiscoveredPreviewPaths.js";
12
12
  import { EditorWhereContext } from "./editorWhereContext.js";
@@ -1,5 +1,6 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { SIDEBAR_GUTTER } from "../../preview/components/SidebarStack.js";
3
+ import { useCanvasLayout } from "../../canvas/useCanvasLayout.js";
3
4
  import { ChatWidget } from "./ChatWidget.js";
4
5
  import { Specialists } from "./Specialists.js";
5
6
  import { c } from "react/compiler-runtime";
@@ -14,30 +15,34 @@ import { jsx, jsxs } from "react/jsx-runtime";
14
15
  * and the Preview and Studio pages follow the editor's runs too.
15
16
  */
16
17
  function AgentSidebar(t0) {
17
- const $ = c(6);
18
+ const $ = c(8);
18
19
  const { hidden: t1 } = t0;
19
20
  const hidden = t1 === void 0 ? false : t1;
21
+ const { sidebarWidth } = useCanvasLayout();
20
22
  let t2;
23
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24
+ t2 = cn("absolute inset-y-0 right-0 z-10 flex flex-col gap-2 py-2 max-md:hidden", SIDEBAR_GUTTER);
25
+ $[0] = t2;
26
+ } else t2 = $[0];
21
27
  let t3;
28
+ if ($[1] !== sidebarWidth) {
29
+ t3 = { width: sidebarWidth };
30
+ $[1] = sidebarWidth;
31
+ $[2] = t3;
32
+ } else t3 = $[2];
22
33
  let t4;
23
34
  let t5;
24
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25
- t2 = cn("absolute inset-y-0 right-0 z-10 flex flex-col gap-2 py-2 max-md:hidden", SIDEBAR_GUTTER);
26
- t3 = { width: 265 };
35
+ if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
27
36
  t4 = /* @__PURE__ */ jsx(Specialists, { className: "min-h-0 flex-[0_1_auto]" });
28
37
  t5 = /* @__PURE__ */ jsx(ChatWidget, { className: "min-h-0 flex-[7_1_0]" });
29
- $[0] = t2;
30
- $[1] = t3;
31
- $[2] = t4;
32
- $[3] = t5;
38
+ $[3] = t4;
39
+ $[4] = t5;
33
40
  } else {
34
- t2 = $[0];
35
- t3 = $[1];
36
- t4 = $[2];
37
- t5 = $[3];
41
+ t4 = $[3];
42
+ t5 = $[4];
38
43
  }
39
44
  let t6;
40
- if ($[4] !== hidden) {
45
+ if ($[5] !== hidden || $[6] !== t3) {
41
46
  t6 = /* @__PURE__ */ jsxs("aside", {
42
47
  "aria-label": "Agent",
43
48
  "data-agent-sidebar": true,
@@ -46,9 +51,10 @@ function AgentSidebar(t0) {
46
51
  style: t3,
47
52
  children: [t4, t5]
48
53
  });
49
- $[4] = hidden;
50
- $[5] = t6;
51
- } else t6 = $[5];
54
+ $[5] = hidden;
55
+ $[6] = t3;
56
+ $[7] = t6;
57
+ } else t6 = $[7];
52
58
  return t6;
53
59
  }
54
60