camox 0.48.0 → 0.51.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 (57) hide show
  1. package/dist/core/layoutContext.d.ts +1 -0
  2. package/dist/features/canvas/sidebar/CanvasInspector.js +1 -1
  3. package/dist/features/content/ContentAssets.js +84 -88
  4. package/dist/features/preview/CamoxPreview.js +1 -1
  5. package/dist/features/preview/EditablePageExperience.js +17 -9
  6. package/dist/features/preview/components/BlockActionsPopover.js +11 -35
  7. package/dist/features/preview/components/FieldToolbar.js +1 -1
  8. package/dist/features/preview/components/MobilePreviewDrawer.js +6 -9
  9. package/dist/features/preview/components/PreviewPanel.js +10 -64
  10. package/dist/features/preview/components/PreviewToolbar.js +1 -1
  11. package/dist/features/preview/previewConstants.js +2 -8
  12. package/dist/features/preview/usePreviewViewportActions.js +1 -1
  13. package/dist/features/provider/AuthenticatedCamoxProvider.js +1 -1
  14. package/dist/features/provider/components/CommandPalette.js +2 -5
  15. package/dist/features/provider/signInShortcut.js +1 -2
  16. package/dist/features/provider/useAdminShortcuts.js +47 -53
  17. package/dist/features/routes/pageRuntime.d.ts +2 -1
  18. package/dist/features/routes/pageRuntime.js +27 -20
  19. package/dist/features/runtime/publicCache.js +83 -0
  20. package/dist/features/runtime/runtime.d.ts +13 -3
  21. package/dist/features/runtime/runtime.js +173 -79
  22. package/dist/features/runtime/worker.d.ts +5 -1
  23. package/dist/features/runtime/worker.js +20 -3
  24. package/dist/features/settings/AnalyticsSettings.js +622 -0
  25. package/dist/features/settings/CamoxSettings.js +95 -0
  26. package/dist/features/settings/FaviconSettings.js +254 -0
  27. package/dist/features/settings/ProjectNameSettings.js +145 -0
  28. package/dist/features/settings/SettingsPart.js +73 -0
  29. package/dist/features/settings/SocialProfilesSettings.js +245 -0
  30. package/dist/features/settings/useProjectMutation.js +61 -0
  31. package/dist/features/studio/components/DraftsControl.js +136 -131
  32. package/dist/features/studio/components/LocalePicker.js +178 -174
  33. package/dist/features/studio/components/Navbar.js +107 -66
  34. package/dist/features/studio/components/ProjectMenu.js +118 -170
  35. package/dist/features/studio/components/ReviewDraftSheet.js +1046 -990
  36. package/dist/features/studio/components/UserButton.js +1 -1
  37. package/dist/features/studio/routes.js +3 -1
  38. package/dist/features/studio/showInCanvas.js +129 -0
  39. package/dist/features/vite/definitionsSync.js +4 -5
  40. package/dist/features/vite/runtimeDev.js +32 -17
  41. package/dist/features/vite/staticAssetHeaders.js +52 -0
  42. package/dist/features/vite/vite.d.ts +1 -1
  43. package/dist/features/vite/vite.js +16 -4
  44. package/dist/lib/api-client-server.d.ts +1 -1
  45. package/dist/lib/api-client.d.ts +2 -1
  46. package/dist/lib/draft-scope.d.ts +1 -0
  47. package/dist/lib/locale-path.d.ts +3 -11
  48. package/dist/lib/locale-path.js +3 -27
  49. package/dist/lib/queries.js +7 -1
  50. package/dist/lib/shortcuts.js +29 -0
  51. package/dist/lib/studio-reads.d.ts +1 -0
  52. package/dist/lib/utils.js +1 -104
  53. package/dist/package.js +1 -1
  54. package/dist/studio.css +1 -1
  55. package/package.json +5 -4
  56. package/dist/features/studio/components/FaviconDialog.js +0 -318
  57. package/dist/features/studio/components/SocialProfilesDialog.js +0 -342
@@ -1,3 +1,4 @@
1
+ import "../lib/locale-path.js";
1
2
  //#region src/core/layoutContext.d.ts
2
3
  /**
3
4
  * The context every layout `to*` option receives. Each name always means the same thing and is
@@ -1,4 +1,4 @@
1
- import { formatShortcut } from "../../../lib/utils.js";
1
+ import { formatShortcut } from "../../../lib/shortcuts.js";
2
2
  import "../../preview/components/PageNicknameField.js";
3
3
  import { SHOW_WHOLE_CANVAS_SHORTCUT } from "../../preview/previewConstants.js";
4
4
  import { c } from "react/compiler-runtime";
@@ -1,6 +1,5 @@
1
1
  import { useProjectUuid } from "../../lib/auth.js";
2
2
  import { reads, useReadQuery } from "../../lib/studio-reads.js";
3
- import { checkIfInputFocused } from "../../lib/utils.js";
4
3
  import { UploadDropZone } from "./components/UploadDropZone.js";
5
4
  import { UploadProgressDrawer } from "./components/UploadProgressDrawer.js";
6
5
  import { useFileUpload } from "../../hooks/use-file-upload.js";
@@ -10,12 +9,13 @@ import { AssetCard } from "./components/AssetCard.js";
10
9
  import { useMarqueeSelection } from "../../hooks/use-marquee-selection.js";
11
10
  import { AssetCardSkeleton } from "./components/AssetCardSkeleton.js";
12
11
  import { c } from "react/compiler-runtime";
13
- import { useEffect, useRef, useState } from "react";
12
+ import { useRef, useState } from "react";
14
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
15
14
  import { useMutation } from "@tanstack/react-query";
16
15
  import { Button } from "@camox/ui/button";
17
16
  import { PanelContent } from "@camox/ui/panel";
18
17
  import { ImageIcon, UploadIcon } from "lucide-react";
18
+ import { useHotkey } from "@tanstack/react-hotkeys";
19
19
  import { FloatingToolbar } from "@camox/ui/floating-toolbar";
20
20
 
21
21
  //#region src/features/content/ContentAssets.tsx
@@ -74,31 +74,27 @@ const ContentAssets = () => {
74
74
  $[10] = t8;
75
75
  } else t8 = $[10];
76
76
  const { selectionRect, didDragRef, handlers } = useMarqueeSelection(containerRef, t8);
77
- let t10;
78
77
  let t9;
79
- if ($[11] !== lightboxFileId) {
80
- t9 = () => {
81
- const handleKeyDown = (event) => {
82
- if (event.key !== "Escape") return;
83
- if (lightboxFileId !== null) return;
84
- if (checkIfInputFocused()) return;
85
- setSelectedIds(_temp);
86
- };
87
- document.addEventListener("keydown", handleKeyDown);
88
- return () => document.removeEventListener("keydown", handleKeyDown);
78
+ if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
79
+ t9 = () => setSelectedIds(_temp);
80
+ $[11] = t9;
81
+ } else t9 = $[11];
82
+ const t10 = lightboxFileId === null;
83
+ let t11;
84
+ if ($[12] !== t10) {
85
+ t11 = {
86
+ enabled: t10,
87
+ ignoreInputs: true,
88
+ preventDefault: false,
89
+ stopPropagation: false
89
90
  };
90
- t10 = [lightboxFileId];
91
- $[11] = lightboxFileId;
92
91
  $[12] = t10;
93
- $[13] = t9;
94
- } else {
95
- t10 = $[12];
96
- t9 = $[13];
97
- }
98
- useEffect(t9, t10);
99
- let t11;
92
+ $[13] = t11;
93
+ } else t11 = $[13];
94
+ useHotkey("Escape", t9, t11);
95
+ let t12;
100
96
  if ($[14] !== didDragRef) {
101
- t11 = () => {
97
+ t12 = () => {
102
98
  if (didDragRef.current) {
103
99
  didDragRef.current = false;
104
100
  return;
@@ -106,20 +102,20 @@ const ContentAssets = () => {
106
102
  setSelectedIds(/* @__PURE__ */ new Set());
107
103
  };
108
104
  $[14] = didDragRef;
109
- $[15] = t11;
110
- } else t11 = $[15];
111
- let t12;
105
+ $[15] = t12;
106
+ } else t12 = $[15];
107
+ let t13;
112
108
  if ($[16] !== files) {
113
- t12 = files === void 0 && /* @__PURE__ */ jsx("div", {
109
+ t13 = files === void 0 && /* @__PURE__ */ jsx("div", {
114
110
  className: "grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-4",
115
111
  children: Array.from({ length: 12 }, _temp2)
116
112
  });
117
113
  $[16] = files;
118
- $[17] = t12;
119
- } else t12 = $[17];
120
- let t13;
114
+ $[17] = t13;
115
+ } else t13 = $[17];
116
+ let t14;
121
117
  if ($[18] !== files?.length || $[19] !== uploadFiles) {
122
- t13 = files?.length === 0 && /* @__PURE__ */ jsx("div", {
118
+ t14 = files?.length === 0 && /* @__PURE__ */ jsx("div", {
123
119
  className: "flex h-full items-center justify-center",
124
120
  children: /* @__PURE__ */ jsxs("div", {
125
121
  className: "flex max-w-sm flex-col items-center gap-2 text-center",
@@ -150,9 +146,9 @@ const ContentAssets = () => {
150
146
  type: "file",
151
147
  className: "sr-only",
152
148
  multiple: true,
153
- onChange: (event_0) => {
154
- if (event_0.target.files) uploadFiles(event_0.target.files);
155
- event_0.target.value = "";
149
+ onChange: (event) => {
150
+ if (event.target.files) uploadFiles(event.target.files);
151
+ event.target.value = "";
156
152
  }
157
153
  })
158
154
  ]
@@ -162,11 +158,11 @@ const ContentAssets = () => {
162
158
  });
163
159
  $[18] = files?.length;
164
160
  $[19] = uploadFiles;
165
- $[20] = t13;
166
- } else t13 = $[20];
167
- let t14;
161
+ $[20] = t14;
162
+ } else t14 = $[20];
163
+ let t15;
168
164
  if ($[21] !== files || $[22] !== selectedIds) {
169
- t14 = files && files.length > 0 && /* @__PURE__ */ jsx("div", {
165
+ t15 = files && files.length > 0 && /* @__PURE__ */ jsx("div", {
170
166
  className: "grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-4",
171
167
  children: files.map((file) => /* @__PURE__ */ jsx(AssetCard, {
172
168
  file,
@@ -184,11 +180,11 @@ const ContentAssets = () => {
184
180
  });
185
181
  $[21] = files;
186
182
  $[22] = selectedIds;
187
- $[23] = t14;
188
- } else t14 = $[23];
189
- let t15;
183
+ $[23] = t15;
184
+ } else t15 = $[23];
185
+ let t16;
190
186
  if ($[24] !== selectionRect) {
191
- t15 = selectionRect && /* @__PURE__ */ jsx("div", {
187
+ t16 = selectionRect && /* @__PURE__ */ jsx("div", {
192
188
  className: "border-primary bg-primary/10 pointer-events-none absolute z-50 border",
193
189
  style: {
194
190
  left: selectionRect.left,
@@ -198,61 +194,61 @@ const ContentAssets = () => {
198
194
  }
199
195
  });
200
196
  $[24] = selectionRect;
201
- $[25] = t15;
202
- } else t15 = $[25];
203
- let t16;
204
- if ($[26] !== handlers.onPointerDown || $[27] !== handlers.onPointerMove || $[28] !== handlers.onPointerUp || $[29] !== t11 || $[30] !== t12 || $[31] !== t13 || $[32] !== t14 || $[33] !== t15) {
205
- t16 = /* @__PURE__ */ jsxs(PanelContent, {
197
+ $[25] = t16;
198
+ } else t16 = $[25];
199
+ let t17;
200
+ if ($[26] !== handlers.onPointerDown || $[27] !== handlers.onPointerMove || $[28] !== handlers.onPointerUp || $[29] !== t12 || $[30] !== t13 || $[31] !== t14 || $[32] !== t15 || $[33] !== t16) {
201
+ t17 = /* @__PURE__ */ jsxs(PanelContent, {
206
202
  ref: containerRef,
207
203
  className: "relative p-4 select-none",
208
- onClick: t11,
204
+ onClick: t12,
209
205
  onPointerDown: handlers.onPointerDown,
210
206
  onPointerMove: handlers.onPointerMove,
211
207
  onPointerUp: handlers.onPointerUp,
212
208
  children: [
213
- t12,
214
209
  t13,
215
210
  t14,
216
- t15
211
+ t15,
212
+ t16
217
213
  ]
218
214
  });
219
215
  $[26] = handlers.onPointerDown;
220
216
  $[27] = handlers.onPointerMove;
221
217
  $[28] = handlers.onPointerUp;
222
- $[29] = t11;
223
- $[30] = t12;
224
- $[31] = t13;
225
- $[32] = t14;
226
- $[33] = t15;
227
- $[34] = t16;
228
- } else t16 = $[34];
229
- let t17;
230
- if ($[35] !== t16 || $[36] !== uploadFiles) {
231
- t17 = /* @__PURE__ */ jsx("div", {
218
+ $[29] = t12;
219
+ $[30] = t13;
220
+ $[31] = t14;
221
+ $[32] = t15;
222
+ $[33] = t16;
223
+ $[34] = t17;
224
+ } else t17 = $[34];
225
+ let t18;
226
+ if ($[35] !== t17 || $[36] !== uploadFiles) {
227
+ t18 = /* @__PURE__ */ jsx("div", {
232
228
  className: "flex min-h-0 min-w-0 flex-1 flex-col",
233
229
  children: /* @__PURE__ */ jsx(UploadDropZone, {
234
230
  onDrop: uploadFiles,
235
231
  className: "flex min-h-0 flex-1 flex-col",
236
- children: t16
232
+ children: t17
237
233
  })
238
234
  });
239
- $[35] = t16;
235
+ $[35] = t17;
240
236
  $[36] = uploadFiles;
241
- $[37] = t17;
242
- } else t17 = $[37];
243
- let t18;
237
+ $[37] = t18;
238
+ } else t18 = $[37];
239
+ let t19;
244
240
  if ($[38] !== clearAll || $[39] !== uploads) {
245
- t18 = /* @__PURE__ */ jsx(UploadProgressDrawer, {
241
+ t19 = /* @__PURE__ */ jsx(UploadProgressDrawer, {
246
242
  uploads,
247
243
  onClose: clearAll
248
244
  });
249
245
  $[38] = clearAll;
250
246
  $[39] = uploads;
251
- $[40] = t18;
252
- } else t18 = $[40];
253
- let t19;
247
+ $[40] = t19;
248
+ } else t19 = $[40];
249
+ let t20;
254
250
  if ($[41] !== deleteFiles || $[42] !== selectedIds) {
255
- t19 = selectedIds.size > 0 && /* @__PURE__ */ jsxs(FloatingToolbar, {
251
+ t20 = selectedIds.size > 0 && /* @__PURE__ */ jsxs(FloatingToolbar, {
256
252
  className: "bottom-4 min-w-xs justify-between gap-4 pl-3",
257
253
  children: [/* @__PURE__ */ jsxs("span", {
258
254
  className: "text-muted-foreground",
@@ -274,11 +270,11 @@ const ContentAssets = () => {
274
270
  });
275
271
  $[41] = deleteFiles;
276
272
  $[42] = selectedIds;
277
- $[43] = t19;
278
- } else t19 = $[43];
279
- let t20;
273
+ $[43] = t20;
274
+ } else t20 = $[43];
275
+ let t21;
280
276
  if ($[44] !== lightboxFileId) {
281
- t20 = lightboxFileId && /* @__PURE__ */ jsx(AssetLightbox, {
277
+ t21 = lightboxFileId && /* @__PURE__ */ jsx(AssetLightbox, {
282
278
  open: true,
283
279
  onOpenChange: (open) => {
284
280
  if (!open) setLightboxFileId(null);
@@ -286,23 +282,23 @@ const ContentAssets = () => {
286
282
  fileId: lightboxFileId
287
283
  });
288
284
  $[44] = lightboxFileId;
289
- $[45] = t20;
290
- } else t20 = $[45];
291
- let t21;
292
- if ($[46] !== t17 || $[47] !== t18 || $[48] !== t19 || $[49] !== t20) {
293
- t21 = /* @__PURE__ */ jsxs(Fragment, { children: [
294
- t17,
285
+ $[45] = t21;
286
+ } else t21 = $[45];
287
+ let t22;
288
+ if ($[46] !== t18 || $[47] !== t19 || $[48] !== t20 || $[49] !== t21) {
289
+ t22 = /* @__PURE__ */ jsxs(Fragment, { children: [
295
290
  t18,
296
291
  t19,
297
- t20
292
+ t20,
293
+ t21
298
294
  ] });
299
- $[46] = t17;
300
- $[47] = t18;
301
- $[48] = t19;
302
- $[49] = t20;
303
- $[50] = t21;
304
- } else t21 = $[50];
305
- return t21;
295
+ $[46] = t18;
296
+ $[47] = t19;
297
+ $[48] = t20;
298
+ $[49] = t21;
299
+ $[50] = t22;
300
+ } else t22 = $[50];
301
+ return t22;
306
302
  };
307
303
  function _temp(prev) {
308
304
  return prev.size === 0 ? prev : /* @__PURE__ */ new Set();
@@ -749,7 +749,7 @@ const PreviewShell = (t0) => {
749
749
  groupLabel: "Website",
750
750
  checkIfAvailable: _temp6,
751
751
  execute: _temp7,
752
- shortcut: { key: "Escape" }
752
+ shortcut: "Escape"
753
753
  }
754
754
  ];
755
755
  actionsStore.send({
@@ -4,16 +4,23 @@ import { AuthenticatedCamoxProvider } from "../provider/AuthenticatedCamoxProvid
4
4
  import { CamoxStudio } from "../studio/CamoxStudio.js";
5
5
  import "./EditablePageContent.js";
6
6
  import { CompleteBlockEditingRuntimeProvider } from "../../core/editing/CompleteBlockEditingRuntime.js";
7
+ import { CamoxSettings } from "../settings/CamoxSettings.js";
7
8
  import { c } from "react/compiler-runtime";
8
9
  import { jsx } from "react/jsx-runtime";
9
10
 
10
11
  //#region src/features/preview/EditablePageExperience.tsx
12
+ /** The studio section each studio route shows. */
13
+ const STUDIO_SECTIONS = {
14
+ "studio-content": CamoxContent,
15
+ "studio-settings": CamoxSettings
16
+ };
11
17
  function EditablePageExperience(t0) {
12
- const $ = c(5);
18
+ const $ = c(6);
13
19
  const { input } = t0;
20
+ const StudioSection = input.routeKind && STUDIO_SECTIONS[input.routeKind];
14
21
  let t1;
15
- if ($[0] !== input.canvasOnly || $[1] !== input.derived || $[2] !== input.routeKind || $[3] !== input.runtimeBasePath) {
16
- t1 = /* @__PURE__ */ jsx(AuthenticatedCamoxProvider, { children: /* @__PURE__ */ jsx(CompleteBlockEditingRuntimeProvider, { children: input.routeKind ? /* @__PURE__ */ jsx(CamoxStudio, { children: input.routeKind === "studio-content" ? /* @__PURE__ */ jsx(CamoxContent, {}) : /* @__PURE__ */ jsx("div", {
22
+ if ($[0] !== StudioSection || $[1] !== input.canvasOnly || $[2] !== input.derived || $[3] !== input.routeKind || $[4] !== input.runtimeBasePath) {
23
+ t1 = /* @__PURE__ */ jsx(AuthenticatedCamoxProvider, { children: /* @__PURE__ */ jsx(CompleteBlockEditingRuntimeProvider, { children: input.routeKind ? /* @__PURE__ */ jsx(CamoxStudio, { children: StudioSection ? /* @__PURE__ */ jsx(StudioSection, {}) : /* @__PURE__ */ jsx("div", {
17
24
  className: "text-muted-foreground p-6 text-sm",
18
25
  children: "Studio page not found"
19
26
  }) }) : /* @__PURE__ */ jsx(CamoxPreview, {
@@ -22,12 +29,13 @@ function EditablePageExperience(t0) {
22
29
  runtimeBasePath: input.runtimeBasePath,
23
30
  children: /* @__PURE__ */ jsx(PageContent, {})
24
31
  }) }) });
25
- $[0] = input.canvasOnly;
26
- $[1] = input.derived;
27
- $[2] = input.routeKind;
28
- $[3] = input.runtimeBasePath;
29
- $[4] = t1;
30
- } else t1 = $[4];
32
+ $[0] = StudioSection;
33
+ $[1] = input.canvasOnly;
34
+ $[2] = input.derived;
35
+ $[3] = input.routeKind;
36
+ $[4] = input.runtimeBasePath;
37
+ $[5] = t1;
38
+ } else t1 = $[5];
31
39
  return t1;
32
40
  }
33
41
 
@@ -1,8 +1,8 @@
1
1
  import { actionsStore } from "../../provider/actionsStore.js";
2
2
  import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
3
3
  import { usePageBlocks } from "../../../lib/normalized-data.js";
4
- import { formatShortcut } from "../../../lib/utils.js";
5
4
  import { blockMutations, repeatableItemMutations } from "../../../lib/queries.js";
5
+ import { formatShortcut } from "../../../lib/shortcuts.js";
6
6
  import { isBlockSelection, previewStore, selectionForPage, selectionItemId } from "../previewStore.js";
7
7
  import { entityName } from "../entityName.js";
8
8
  import { useUpdateBlockPosition } from "./useUpdateBlockPosition.js";
@@ -139,7 +139,7 @@ const BlockActionsPopover = ({ block, children, open, onOpenChange, align = "sta
139
139
  children: [/* @__PURE__ */ jsxs("div", {
140
140
  className: "flex items-center gap-2",
141
141
  children: [/* @__PURE__ */ jsx("span", { className: "w-4" }), "Add block below"]
142
- }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut({ key: "o" }) })]
142
+ }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut("O") })]
143
143
  }), /* @__PURE__ */ jsxs(CommandItem, {
144
144
  className: "justify-between",
145
145
  onSelect: () => {
@@ -149,10 +149,7 @@ const BlockActionsPopover = ({ block, children, open, onOpenChange, align = "sta
149
149
  children: [/* @__PURE__ */ jsxs("div", {
150
150
  className: "flex items-center gap-2",
151
151
  children: [/* @__PURE__ */ jsx("span", { className: "w-4" }), "Add block above"]
152
- }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut({
153
- key: "o",
154
- withShift: true
155
- }) })]
152
+ }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut("Shift+O") })]
156
153
  })] }),
157
154
  /* @__PURE__ */ jsx(CommandSeparator, {}),
158
155
  /* @__PURE__ */ jsx(CommandGroup, { children: /* @__PURE__ */ jsxs(CommandItem, {
@@ -164,10 +161,7 @@ const BlockActionsPopover = ({ block, children, open, onOpenChange, align = "sta
164
161
  children: [/* @__PURE__ */ jsxs("div", {
165
162
  className: "flex items-center gap-2",
166
163
  children: [/* @__PURE__ */ jsx(Copy, { className: "h-4 w-4" }), "Duplicate block"]
167
- }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut({
168
- key: "d",
169
- withMeta: true
170
- }) })]
164
+ }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut("Mod+D") })]
171
165
  }) }),
172
166
  /* @__PURE__ */ jsx(CommandSeparator, {}),
173
167
  /* @__PURE__ */ jsxs(CommandGroup, { children: [
@@ -196,10 +190,7 @@ const BlockActionsPopover = ({ block, children, open, onOpenChange, align = "sta
196
190
  children: [/* @__PURE__ */ jsxs("div", {
197
191
  className: "flex items-center gap-2",
198
192
  children: [/* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" }), "Delete block"]
199
- }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut({
200
- key: "Backspace",
201
- withMeta: true
202
- }) })]
193
+ }), /* @__PURE__ */ jsx(CommandShortcut, { children: formatShortcut("Mod+Backspace") })]
203
194
  })
204
195
  ] })
205
196
  ] })
@@ -279,10 +270,7 @@ function useBlockActionsShortcuts() {
279
270
  id: "delete-selected",
280
271
  label: "Delete selected",
281
272
  groupLabel: "Website",
282
- shortcut: {
283
- key: "Backspace",
284
- withMeta: true
285
- },
273
+ shortcut: "Mod+Backspace",
286
274
  checkIfAvailable: () => {
287
275
  const ctx = previewStore.getSnapshot().context;
288
276
  if (ctx.view !== "canvas") return false;
@@ -320,10 +308,7 @@ function useBlockActionsShortcuts() {
320
308
  id: "duplicate-selected",
321
309
  label: "Duplicate selected",
322
310
  groupLabel: "Website",
323
- shortcut: {
324
- key: "d",
325
- withMeta: true
326
- },
311
+ shortcut: "Mod+D",
327
312
  checkIfAvailable: () => {
328
313
  const ctx_0 = previewStore.getSnapshot().context;
329
314
  if (ctx_0.view !== "canvas") return false;
@@ -348,10 +333,7 @@ function useBlockActionsShortcuts() {
348
333
  id: "move-block-up",
349
334
  label: "Move block up",
350
335
  groupLabel: "Website",
351
- shortcut: {
352
- key: "ArrowUp",
353
- withAlt: true
354
- },
336
+ shortcut: "Alt+ArrowUp",
355
337
  checkIfAvailable: () => {
356
338
  const ctx_1 = previewStore.getSnapshot().context;
357
339
  if (ctx_1.view !== "canvas") return false;
@@ -378,10 +360,7 @@ function useBlockActionsShortcuts() {
378
360
  id: "move-block-down",
379
361
  label: "Move block down",
380
362
  groupLabel: "Website",
381
- shortcut: {
382
- key: "ArrowDown",
383
- withAlt: true
384
- },
363
+ shortcut: "Alt+ArrowDown",
385
364
  checkIfAvailable: () => {
386
365
  const ctx_2 = previewStore.getSnapshot().context;
387
366
  if (ctx_2.view !== "canvas") return false;
@@ -409,7 +388,7 @@ function useBlockActionsShortcuts() {
409
388
  id: "add-block-below",
410
389
  label: "Add block below",
411
390
  groupLabel: "Website",
412
- shortcut: { key: "o" },
391
+ shortcut: "O",
413
392
  checkIfAvailable: () => {
414
393
  const ctx_3 = previewStore.getSnapshot().context;
415
394
  if (ctx_3.view !== "canvas") return false;
@@ -430,10 +409,7 @@ function useBlockActionsShortcuts() {
430
409
  id: "add-block-above",
431
410
  label: "Add block above",
432
411
  groupLabel: "Website",
433
- shortcut: {
434
- key: "o",
435
- withShift: true
436
- },
412
+ shortcut: "Shift+O",
437
413
  checkIfAvailable: () => {
438
414
  const ctx_4 = previewStore.getSnapshot().context;
439
415
  if (ctx_4.view !== "canvas") return false;
@@ -7,9 +7,9 @@ import * as React from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Button } from "@camox/ui/button";
9
9
  import { Bold, Highlighter, Italic, Strikethrough } from "lucide-react";
10
- import { Kbd } from "@camox/ui/kbd";
11
10
  import { ButtonGroup } from "@camox/ui/button-group";
12
11
  import * as Tooltip$1 from "@camox/ui/tooltip";
12
+ import { Kbd } from "@camox/ui/kbd";
13
13
  import { FloatingToolbar } from "@camox/ui/floating-toolbar";
14
14
  import { Toggle } from "@camox/ui/toggle";
15
15
 
@@ -1,6 +1,7 @@
1
- import { useAuthContext } from "../../../lib/auth.js";
2
- import { reads, useReadQuery } from "../../../lib/studio-reads.js";
1
+ import { useNavigate } from "../../navigation/navigation.js";
3
2
  import { cn } from "../../../lib/utils.js";
3
+ import { useLocalizedPath } from "../../../lib/view-locale.js";
4
+ import { STUDIO_SETTINGS_PATH } from "../../studio/routes.js";
4
5
  import { previewStore, selectionBlockId, selectionForPage } from "../previewStore.js";
5
6
  import { UserButton } from "../../studio/components/UserButton.js";
6
7
  import { PageInfo, PageMarkdownContent, PageSeoContent } from "./PageInfo.js";
@@ -28,8 +29,8 @@ function MobilePreviewDrawer({ page, projectName }) {
28
29
  const [isExpanded, setIsExpanded] = React.useState(false);
29
30
  const [isDragging, setIsDragging] = React.useState(false);
30
31
  const [screen, setScreen] = React.useState("root");
31
- const authCtx = useAuthContext();
32
- const { data: project } = useReadQuery(reads.project({ projectUuid: authCtx.projectUuid }));
32
+ const navigate = useNavigate();
33
+ const localized = useLocalizedPath();
33
34
  const dragStartRef = React.useRef({
34
35
  y: 0,
35
36
  height: COLLAPSED_HEIGHT
@@ -200,11 +201,7 @@ function MobilePreviewDrawer({ page, projectName }) {
200
201
  /* @__PURE__ */ jsx(DrawerTile, {
201
202
  title: "Project settings",
202
203
  icon: Settings,
203
- onClick: () => {
204
- if (!project) return;
205
- window.open(`${authCtx.authenticationUrl}/${project.organizationSlug}/${project.slug}/overview`, "_blank", "noopener,noreferrer");
206
- },
207
- disabled: !project
204
+ onClick: () => void navigate({ to: localized("/camox/settings") })
208
205
  })
209
206
  ]
210
207
  })]
@@ -1,7 +1,6 @@
1
- import { actionsStore } from "../../provider/actionsStore.js";
2
1
  import { NavigationDisabled, NavigationLocation } from "../../navigation/navigation.js";
3
2
  import { ArtifactStudioContext } from "../../../core/hooks/useArtifactHost.js";
4
- import { checkIfInputFocused, cn } from "../../../lib/utils.js";
3
+ import { cn } from "../../../lib/utils.js";
5
4
  import { previewStore } from "../previewStore.js";
6
5
  import { useEditingFrameClaim, useEditingOwner } from "../editingFrame.js";
7
6
  import { Frame, useFrame } from "./Frame.js";
@@ -10,6 +9,7 @@ import { EditableSurfaceContext } from "../../../core/hooks/useIsEditable.js";
10
9
  import { adoptCanvasDocument, pauseCanvasDocument } from "../../canvas/canvasFrameDocument.js";
11
10
  import { CanvasWorkspaceContext } from "../../canvas/canvasZoom.js";
12
11
  import { forwardCanvasWheel, useCanvasFrameInput } from "../../canvas/useCanvasFrameInput.js";
12
+ import { useActionShortcuts } from "../../provider/useAdminShortcuts.js";
13
13
  import { PreviewEditingOwnerContext } from "../previewSelection.js";
14
14
  import { useBlockActionsShortcuts } from "./BlockActionsPopover.js";
15
15
  import { FieldOverlayStyles } from "./FieldOverlayStyles.js";
@@ -67,60 +67,9 @@ const PreviewFrameEffects = () => {
67
67
  return t0;
68
68
  };
69
69
  const KeyDownForwarder = () => {
70
- const $ = c(4);
71
70
  const { window: iframeWindow } = useFrame();
72
- const actions = useSelector(actionsStore, _temp);
73
- let t0;
74
- let t1;
75
- if ($[0] !== actions || $[1] !== iframeWindow) {
76
- t0 = () => {
77
- if (!iframeWindow || !iframeWindow.parent || iframeWindow.parent === iframeWindow) return;
78
- const handleKeyDown = (e) => {
79
- const matchingAction = actions.find((action) => {
80
- if (!action.shortcut) return false;
81
- if (!action.checkIfAvailable()) return false;
82
- if (checkIfInputFocused(iframeWindow.document)) {
83
- if (action.shortcut.key !== "Escape" && !action.shortcut.withMeta && !action.shortcut.withAlt) return false;
84
- if (action.shortcut.key === "Backspace") return false;
85
- }
86
- const { key, withMeta, withAlt, withShift } = action.shortcut;
87
- return (withAlt && key.length === 1 && /[a-z]/i.test(key) ? e.code === `Key${key.toUpperCase()}` : key.toLowerCase() === e.key.toLowerCase()) && !!withMeta === (e.metaKey || e.ctrlKey) && !!withAlt === e.altKey && !!withShift === e.shiftKey;
88
- });
89
- if (matchingAction) {
90
- e.preventDefault();
91
- e.stopPropagation();
92
- if (e.key === "Escape" && checkIfInputFocused(iframeWindow.document)) iframeWindow.document.activeElement.blur();
93
- iframeWindow.parent.postMessage({
94
- type: "executeAction",
95
- actionId: matchingAction.id
96
- }, "*");
97
- }
98
- };
99
- const handleCapturedKeyDown = (event) => {
100
- if (event.key === "Escape") return;
101
- handleKeyDown(event);
102
- };
103
- const handleEscape = (event_0) => {
104
- if (event_0.key !== "Escape" || event_0.defaultPrevented) return;
105
- handleKeyDown(event_0);
106
- };
107
- iframeWindow.addEventListener("keydown", handleCapturedKeyDown, true);
108
- iframeWindow.addEventListener("keydown", handleEscape);
109
- return () => {
110
- iframeWindow.removeEventListener("keydown", handleCapturedKeyDown, true);
111
- iframeWindow.removeEventListener("keydown", handleEscape);
112
- };
113
- };
114
- t1 = [iframeWindow, actions];
115
- $[0] = actions;
116
- $[1] = iframeWindow;
117
- $[2] = t0;
118
- $[3] = t1;
119
- } else {
120
- t0 = $[2];
121
- t1 = $[3];
122
- }
123
- React.useEffect(t0, t1);
71
+ const inIframe = !!iframeWindow?.parent && iframeWindow.parent !== iframeWindow;
72
+ useActionShortcuts(inIframe ? iframeWindow : null);
124
73
  return null;
125
74
  };
126
75
  /** The preview's read-only tree is never editable, whether or not the Canvas shows. */
@@ -227,7 +176,7 @@ function PreviewPageCanvasEffects(t0) {
227
176
  t7 = () => {
228
177
  const doc = root?.ownerDocument;
229
178
  if (!doc || !onInteract) return;
230
- const preventLinkNavigation = _temp2;
179
+ const preventLinkNavigation = _temp;
231
180
  for (const type of [
232
181
  "pointerdown",
233
182
  "focusin",
@@ -304,7 +253,7 @@ function PreviewPageCanvasEffects(t0) {
304
253
  } else t14 = $[29];
305
254
  return t14;
306
255
  }
307
- function _temp2(event) {
256
+ function _temp(event) {
308
257
  if (event.target?.closest?.("a")) event.preventDefault();
309
258
  }
310
259
  const PreviewPanel = (t0) => {
@@ -349,8 +298,8 @@ const PreviewPanel = (t0) => {
349
298
  $[9] = t7;
350
299
  } else t7 = $[9];
351
300
  React.useLayoutEffect(t6, t7);
352
- const viewportMode = useSelector(previewStore, _temp3);
353
- const isToolbarHidden = useSelector(previewStore, _temp4);
301
+ const viewportMode = useSelector(previewStore, _temp2);
302
+ const isToolbarHidden = useSelector(previewStore, _temp3);
354
303
  const { frame, slot, inCanvas, revealed } = usePreviewPageFrame();
355
304
  const placed = !isMobileExperience && inCanvas ? slot : null;
356
305
  const underCanvas = revealed && !isMobileExperience;
@@ -709,13 +658,10 @@ function PreviewPageClip(t0) {
709
658
  } else t10 = $[18];
710
659
  return t10;
711
660
  }
712
- function _temp(state) {
713
- return state.context.actions;
714
- }
715
- function _temp3(state) {
661
+ function _temp2(state) {
716
662
  return state.context.viewportMode;
717
663
  }
718
- function _temp4(state_0) {
664
+ function _temp3(state_0) {
719
665
  return state_0.context.isToolbarHidden;
720
666
  }
721
667
 
@@ -1,4 +1,4 @@
1
- import { formatShortcut } from "../../../lib/utils.js";
1
+ import { formatShortcut } from "../../../lib/shortcuts.js";
2
2
  import { TOGGLE_CANVAS_SHORTCUT } from "../previewConstants.js";
3
3
  import { previewStore, selectIsCanvasShown } from "../previewStore.js";
4
4
  import { prefetchCanvas } from "./CanvasPreview.js";