camox 0.40.0 → 0.41.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 (97) hide show
  1. package/dist/components/PlatformLabel.js +62 -0
  2. package/dist/core/components/lexical/GradientNode.js +126 -0
  3. package/dist/core/components/lexical/InlineLexicalEditor.js +37 -56
  4. package/dist/core/components/lexical/InlineStylesPlugin.js +183 -0
  5. package/dist/core/components/lexical/SelectionBroadcaster.js +49 -46
  6. package/dist/core/components/lexical/SidebarLexicalEditor.js +197 -151
  7. package/dist/core/components/lexical/editorConfig.js +25 -7
  8. package/dist/core/components/lexical/selectTextLink.js +21 -0
  9. package/dist/core/createApp.d.ts +30 -17
  10. package/dist/core/createApp.js +14 -3
  11. package/dist/core/createBlock.d.ts +2 -2
  12. package/dist/core/createBlock.js +18 -15
  13. package/dist/core/createLayout.d.ts +48 -4
  14. package/dist/core/createLayout.js +51 -23
  15. package/dist/core/derivedRoutes.js +36 -0
  16. package/dist/core/editing/createEditableBlock.d.ts +9 -8
  17. package/dist/core/editing/createEditableBlock.js +167 -168
  18. package/dist/core/lib/lexicalReact.d.ts +1 -26
  19. package/dist/core/lib/lexicalReact.js +46 -60
  20. package/dist/core/lib/lexicalState.js +164 -81
  21. package/dist/core/lib/modifierFormats.js +4 -15
  22. package/dist/core/lib/textStyles.d.ts +27 -0
  23. package/dist/core/lib/textStyles.js +47 -0
  24. package/dist/features/navigation/navigation.d.ts +2 -0
  25. package/dist/features/navigation/navigation.js +51 -46
  26. package/dist/features/page/DerivedPageContent.js +61 -0
  27. package/dist/features/page/PublishedPageContent.js +52 -83
  28. package/dist/features/page/SharedLayoutContent.js +66 -0
  29. package/dist/features/preview/CamoxPreview.d.ts +16 -1
  30. package/dist/features/preview/CamoxPreview.js +242 -265
  31. package/dist/features/preview/DerivedPreview.js +137 -0
  32. package/dist/features/preview/EditablePageExperience.js +25 -8
  33. package/dist/features/preview/components/AddBlockSidebar.js +43 -33
  34. package/dist/features/preview/components/AssetLightbox.js +1 -1
  35. package/dist/features/preview/components/BlockThumbnail.js +192 -0
  36. package/dist/features/preview/components/CreatePageModal.js +123 -115
  37. package/dist/features/preview/components/DerivedLayoutDialog.js +100 -0
  38. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +5 -0
  39. package/dist/features/preview/components/DerivedLayoutSidebar.js +163 -0
  40. package/dist/features/preview/components/DerivedPageInfoSidebar.js +57 -0
  41. package/dist/features/preview/components/FieldToolbar.js +42 -20
  42. package/dist/features/preview/components/Frame.js +133 -140
  43. package/dist/features/preview/components/LeftSidebar.js +42 -38
  44. package/dist/features/preview/components/MobilePreviewDrawer.js +4 -4
  45. package/dist/features/preview/components/Overlays.js +57 -42
  46. package/dist/features/preview/components/PageInfoSidebar.js +63 -55
  47. package/dist/features/preview/components/PageNavigatorSidebar.js +34 -483
  48. package/dist/features/preview/components/PagePicker.js +205 -153
  49. package/dist/features/preview/components/PageTree.js +298 -206
  50. package/dist/features/preview/components/PreviewActivation.js +78 -0
  51. package/dist/features/preview/components/PreviewPanel.js +67 -35
  52. package/dist/features/preview/components/PreviewToolbar.js +218 -232
  53. package/dist/features/preview/components/PublicationRow.js +431 -0
  54. package/dist/features/preview/components/PublishDialog.js +180 -97
  55. package/dist/features/preview/components/RightSidebar.js +19 -34
  56. package/dist/features/preview/components/previewStyles.js +8 -0
  57. package/dist/features/preview/components/useUpdateBlockPosition.js +7 -13
  58. package/dist/features/preview/previewConstants.js +8 -0
  59. package/dist/features/preview/previewStore.js +0 -10
  60. package/dist/features/preview/publication.js +71 -0
  61. package/dist/features/provider/AuthenticatedCamoxProvider.js +2 -1
  62. package/dist/features/provider/CoreCamoxProvider.js +11 -2
  63. package/dist/features/provider/LocalhostPreviewProvider.js +6 -7
  64. package/dist/features/routes/pageRuntime.d.ts +10 -0
  65. package/dist/features/routes/pageRuntime.js +7 -9
  66. package/dist/features/runtime/PreviewDocumentContext.js +7 -0
  67. package/dist/features/runtime/RuntimeChrome.js +137 -0
  68. package/dist/features/runtime/SharedChromeContext.js +7 -0
  69. package/dist/features/runtime/navigationRequest.js +21 -0
  70. package/dist/features/runtime/navigationTarget.js +23 -0
  71. package/dist/features/runtime/pageApp.js +196 -121
  72. package/dist/features/runtime/pageNavigation.js +79 -130
  73. package/dist/features/runtime/runtime.d.ts +11 -0
  74. package/dist/features/runtime/runtime.js +183 -47
  75. package/dist/features/runtime/studioApp.d.ts +2 -11
  76. package/dist/features/runtime/studioApp.js +14 -177
  77. package/dist/features/studio/CamoxStudio.js +27 -13
  78. package/dist/features/studio/components/Navbar.js +80 -74
  79. package/dist/features/studio/studioTheme.js +25 -0
  80. package/dist/features/studio/useTheme.js +94 -109
  81. package/dist/features/vite/appGeneration.js +12 -3
  82. package/dist/features/vite/definitionsSync.js +4 -2
  83. package/dist/features/vite/layoutGeneration.js +38 -0
  84. package/dist/features/vite/runtimeDev.js +3 -1
  85. package/dist/lib/auth.js +15 -13
  86. package/dist/lib/normalized-data.js +4 -4
  87. package/dist/lib/platform.js +13 -0
  88. package/dist/lib/utils.js +90 -38
  89. package/dist/studio-overlays.css +5 -40
  90. package/dist/studio.css +1 -1
  91. package/package.json +8 -4
  92. package/skills/camox/SKILL.md +2 -0
  93. package/skills/camox/references/block-definitions.md +31 -12
  94. package/skills/camox/references/layout-definitions.md +2 -0
  95. package/dist/core/lib/modifiers.js +0 -24
  96. package/dist/features/preview/previewQueryFns.js +0 -23
  97. package/dist/features/runtime/studioNavigation.js +0 -120
@@ -1,110 +1,193 @@
1
- import { pageMutations } from "../../../lib/queries.js";
2
- import { trackClientEvent } from "../../../lib/telemetry-client.js";
3
- import { useMutation } from "@tanstack/react-query";
1
+ import { c } from "react/compiler-runtime";
4
2
  import * as React from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
6
4
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
7
- import { toast } from "@camox/ui/toaster";
8
5
  import { Label } from "@camox/ui/label";
9
6
  import { Switch } from "@camox/ui/switch";
10
7
 
11
8
  //#region src/features/preview/components/PublishDialog.tsx
12
- /**
13
- * Confirmation dialog for `pages.publish`. Shows the path that will go live
14
- * (= the current draft path; same as today's public URL unless the user
15
- * changed it while drafting), and a primary Publish button.
16
- *
17
- * Description copy branches on the page's current status: a `'draft'` page
18
- * has never been public so we frame it as "will go live"; a `'modified'`
19
- * page is already public and we're promoting the latest edits.
20
- *
21
- * When the page's layout is itself modified (`alsoPublishLayout` prop set),
22
- * a toggle appears below the primary action, pre-checked. Bundling is the
23
- * common case — the user is usually editing page + layout together and
24
- * expects "Publish" to mean "what I see goes live". Power users who edited
25
- * the layout independently can opt out before confirming.
26
- */
27
- function PublishDialog({ page, pageStatus, alsoPublishLayout, open, onOpenChange }) {
28
- const publishPage = useMutation(pageMutations.publish());
29
- const [bundleLayout, setBundleLayout] = React.useState(true);
30
- React.useEffect(() => {
31
- if (open) setBundleLayout(true);
32
- }, [open]);
33
- const handlePublish = async () => {
34
- if (!page) return;
35
- const shouldBundle = bundleLayout && alsoPublishLayout != null;
36
- try {
37
- await publishPage.mutateAsync({
38
- id: page.id,
39
- ...shouldBundle ? { alsoPublishLayout: true } : {}
40
- });
41
- trackClientEvent("page_published", {
42
- pageId: page.id,
43
- ...shouldBundle ? { alsoPublishedLayout: true } : {}
44
- });
45
- toast.success(shouldBundle ? `Published this page and layout ${alsoPublishLayout?.layoutHandle}` : "Published this page");
46
- onOpenChange(false);
47
- } catch (error) {
48
- console.error("Failed to publish page:", error);
49
- toast.error("Could not publish this page");
50
- }
51
- };
52
- const pathCode = /* @__PURE__ */ jsx("code", {
53
- className: "bg-muted rounded px-1 py-0.5 font-mono text-xs",
54
- children: page?.fullPath ?? ""
55
- });
56
- return /* @__PURE__ */ jsx(AlertDialog, {
57
- open,
58
- onOpenChange,
59
- children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
60
- /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: pageStatus === "modified" ? "Publish changes" : "Publish page" }), /* @__PURE__ */ jsx(AlertDialogDescription, { children: pageStatus === "modified" ? /* @__PURE__ */ jsxs(Fragment, { children: [
61
- "Visitors at ",
62
- pathCode,
63
- " will start seeing your latest changes."
64
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
65
- "This page will go live at ",
66
- pathCode,
67
- ". Visitors will see the current draft."
68
- ] }) })] }),
69
- alsoPublishLayout && /* @__PURE__ */ jsxs("div", {
9
+ /** Shared scope/impact review. Mount afresh for each publication attempt. */
10
+ function PublishDialog(t0) {
11
+ const $ = c(53);
12
+ if ($[0] !== "0ada477bb5fc69a9b0f1551db385228a0e85d0d93484a56040a42f62e6af3eb0") {
13
+ for (let $i = 0; $i < 53; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
14
+ $[0] = "0ada477bb5fc69a9b0f1551db385228a0e85d0d93484a56040a42f62e6af3eb0";
15
+ }
16
+ const { plan, pending, onPublish, onOpenChange } = t0;
17
+ const id = React.useId();
18
+ let t1;
19
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
20
+ t1 = [];
21
+ $[1] = t1;
22
+ } else t1 = $[1];
23
+ const [excludedKeys, setExcludedKeys] = React.useState(t1);
24
+ let T0;
25
+ let T1;
26
+ let includedKeys;
27
+ let t2;
28
+ let t3;
29
+ let t4;
30
+ let t5;
31
+ if ($[2] !== excludedKeys || $[3] !== id || $[4] !== onOpenChange || $[5] !== pending || $[6] !== plan.description || $[7] !== plan.items || $[8] !== plan.title) {
32
+ const sideEffects = plan.items.filter(_temp);
33
+ let t6;
34
+ if ($[16] !== excludedKeys || $[17] !== plan.items) {
35
+ let t7;
36
+ if ($[19] !== excludedKeys) {
37
+ t7 = (item_0) => !item_0.optional || !excludedKeys.includes(item_0.key);
38
+ $[19] = excludedKeys;
39
+ $[20] = t7;
40
+ } else t7 = $[20];
41
+ t6 = plan.items.filter(t7).map(_temp2);
42
+ $[16] = excludedKeys;
43
+ $[17] = plan.items;
44
+ $[18] = t6;
45
+ } else t6 = $[18];
46
+ includedKeys = t6;
47
+ T1 = AlertDialog;
48
+ t4 = true;
49
+ if ($[21] !== onOpenChange || $[22] !== pending) {
50
+ t5 = (open) => !pending && onOpenChange(open);
51
+ $[21] = onOpenChange;
52
+ $[22] = pending;
53
+ $[23] = t5;
54
+ } else t5 = $[23];
55
+ T0 = AlertDialogContent;
56
+ let t7;
57
+ if ($[24] !== plan.title) {
58
+ t7 = /* @__PURE__ */ jsx(AlertDialogTitle, { children: plan.title });
59
+ $[24] = plan.title;
60
+ $[25] = t7;
61
+ } else t7 = $[25];
62
+ let t8;
63
+ if ($[26] !== plan.description) {
64
+ t8 = /* @__PURE__ */ jsx(AlertDialogDescription, { children: plan.description });
65
+ $[26] = plan.description;
66
+ $[27] = t8;
67
+ } else t8 = $[27];
68
+ if ($[28] !== t7 || $[29] !== t8) {
69
+ t2 = /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [t7, t8] });
70
+ $[28] = t7;
71
+ $[29] = t8;
72
+ $[30] = t2;
73
+ } else t2 = $[30];
74
+ t3 = sideEffects.length > 0 && /* @__PURE__ */ jsx("div", {
75
+ className: "space-y-3",
76
+ children: sideEffects.map((item_2) => /* @__PURE__ */ jsxs("div", {
70
77
  className: "flex items-start gap-3",
71
78
  children: [/* @__PURE__ */ jsx(Switch, {
72
- id: "also-publish-layout",
73
- checked: bundleLayout,
74
- onCheckedChange: (checked) => setBundleLayout(checked),
75
- disabled: publishPage.isPending
76
- }), /* @__PURE__ */ jsxs(Label, {
77
- htmlFor: "also-publish-layout",
78
- className: "flex flex-col items-start gap-1",
79
- children: [/* @__PURE__ */ jsxs("span", { children: [
80
- "Also publish layout",
81
- " ",
82
- /* @__PURE__ */ jsx("code", {
83
- className: "bg-muted rounded px-1 py-0.5 font-mono text-xs",
84
- children: alsoPublishLayout.layoutHandle
85
- })
86
- ] }), /* @__PURE__ */ jsx("span", {
87
- className: "text-muted-foreground text-xs font-normal",
88
- children: (() => {
89
- const others = Math.max(0, alsoPublishLayout.affectedPagesCount - 1);
90
- if (others === 0) return "Affects no other pages";
91
- return `Affects ${others} other ${others === 1 ? "page" : "pages"}`;
92
- })()
79
+ id: `${id}-${item_2.key}`,
80
+ checked: !excludedKeys.includes(item_2.key),
81
+ onCheckedChange: (checked) => setExcludedKeys((keys) => checked ? keys.filter((key) => key !== item_2.key) : [...keys, item_2.key]),
82
+ disabled: pending
83
+ }), /* @__PURE__ */ jsxs("div", {
84
+ className: "min-w-0 space-y-1",
85
+ children: [/* @__PURE__ */ jsxs(Label, {
86
+ htmlFor: `${id}-${item_2.key}`,
87
+ children: ["Also publish ", item_2.label]
88
+ }), /* @__PURE__ */ jsx("p", {
89
+ className: "text-muted-foreground text-sm",
90
+ children: item_2.impact
93
91
  })]
94
92
  })]
95
- }),
96
- /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, {
97
- variant: "outline",
98
- size: "default",
99
- disabled: publishPage.isPending,
100
- children: "Cancel"
101
- }), /* @__PURE__ */ jsx(AlertDialogAction, {
102
- onClick: handlePublish,
103
- disabled: publishPage.isPending,
104
- children: publishPage.isPending ? "Publishing…" : "Publish"
105
- })] })
106
- ] })
107
- });
93
+ }, item_2.key))
94
+ });
95
+ $[2] = excludedKeys;
96
+ $[3] = id;
97
+ $[4] = onOpenChange;
98
+ $[5] = pending;
99
+ $[6] = plan.description;
100
+ $[7] = plan.items;
101
+ $[8] = plan.title;
102
+ $[9] = T0;
103
+ $[10] = T1;
104
+ $[11] = includedKeys;
105
+ $[12] = t2;
106
+ $[13] = t3;
107
+ $[14] = t4;
108
+ $[15] = t5;
109
+ } else {
110
+ T0 = $[9];
111
+ T1 = $[10];
112
+ includedKeys = $[11];
113
+ t2 = $[12];
114
+ t3 = $[13];
115
+ t4 = $[14];
116
+ t5 = $[15];
117
+ }
118
+ let t6;
119
+ if ($[31] !== pending) {
120
+ t6 = /* @__PURE__ */ jsx(AlertDialogCancel, {
121
+ variant: "outline",
122
+ disabled: pending,
123
+ children: "Cancel"
124
+ });
125
+ $[31] = pending;
126
+ $[32] = t6;
127
+ } else t6 = $[32];
128
+ let t7;
129
+ if ($[33] !== includedKeys || $[34] !== onPublish) {
130
+ t7 = (event) => {
131
+ event.preventDefault();
132
+ onPublish(includedKeys);
133
+ };
134
+ $[33] = includedKeys;
135
+ $[34] = onPublish;
136
+ $[35] = t7;
137
+ } else t7 = $[35];
138
+ const t8 = pending ? "Publishing…" : "Publish";
139
+ let t9;
140
+ if ($[36] !== pending || $[37] !== t7 || $[38] !== t8) {
141
+ t9 = /* @__PURE__ */ jsx(AlertDialogAction, {
142
+ disabled: pending,
143
+ onClick: t7,
144
+ children: t8
145
+ });
146
+ $[36] = pending;
147
+ $[37] = t7;
148
+ $[38] = t8;
149
+ $[39] = t9;
150
+ } else t9 = $[39];
151
+ let t10;
152
+ if ($[40] !== t6 || $[41] !== t9) {
153
+ t10 = /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [t6, t9] });
154
+ $[40] = t6;
155
+ $[41] = t9;
156
+ $[42] = t10;
157
+ } else t10 = $[42];
158
+ let t11;
159
+ if ($[43] !== T0 || $[44] !== t10 || $[45] !== t2 || $[46] !== t3) {
160
+ t11 = /* @__PURE__ */ jsxs(T0, { children: [
161
+ t2,
162
+ t3,
163
+ t10
164
+ ] });
165
+ $[43] = T0;
166
+ $[44] = t10;
167
+ $[45] = t2;
168
+ $[46] = t3;
169
+ $[47] = t11;
170
+ } else t11 = $[47];
171
+ let t12;
172
+ if ($[48] !== T1 || $[49] !== t11 || $[50] !== t4 || $[51] !== t5) {
173
+ t12 = /* @__PURE__ */ jsx(T1, {
174
+ open: t4,
175
+ onOpenChange: t5,
176
+ children: t11
177
+ });
178
+ $[48] = T1;
179
+ $[49] = t11;
180
+ $[50] = t4;
181
+ $[51] = t5;
182
+ $[52] = t12;
183
+ } else t12 = $[52];
184
+ return t12;
185
+ }
186
+ function _temp2(item_1) {
187
+ return item_1.key;
188
+ }
189
+ function _temp(item) {
190
+ return item.optional;
108
191
  }
109
192
 
110
193
  //#endregion
@@ -1,18 +1,20 @@
1
1
  import { previewStore, selectionBlockId } from "../previewStore.js";
2
+ import { AddBlockSidebar } from "./AddBlockSidebar.js";
2
3
  import { PageEditorSidebar } from "./PageEditorSidebar.js";
3
4
  import { PageInfoSidebar } from "./PageInfoSidebar.js";
5
+ import { DerivedPageInfoSidebar } from "./DerivedPageInfoSidebar.js";
4
6
  import { c } from "react/compiler-runtime";
5
- import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { jsx } from "react/jsx-runtime";
6
8
  import { useSelector } from "@xstate/store-react";
7
9
 
8
10
  //#region src/features/preview/components/RightSidebar.tsx
9
11
  const RightSidebar = (t0) => {
10
- const $ = c(12);
11
- if ($[0] !== "b9589c1fea2c3efabdd9fae80871373106138216610ee3b8e2f4045e3b45670f") {
12
- for (let $i = 0; $i < 12; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
13
- $[0] = "b9589c1fea2c3efabdd9fae80871373106138216610ee3b8e2f4045e3b45670f";
12
+ const $ = c(9);
13
+ if ($[0] !== "d71e54add8a0890703a029e4ca30b5b8fa7b144767455a2e03c5c0c217ac64a0") {
14
+ for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
+ $[0] = "d71e54add8a0890703a029e4ca30b5b8fa7b144767455a2e03c5c0c217ac64a0";
14
16
  }
15
- const { pageId } = t0;
17
+ const { pageId, derivedLayoutId } = t0;
16
18
  const selection = useSelector(previewStore, _temp);
17
19
  const isAddBlockSidebarOpen = useSelector(previewStore, _temp2);
18
20
  let t1;
@@ -22,40 +24,26 @@ const RightSidebar = (t0) => {
22
24
  $[2] = t1;
23
25
  } else t1 = $[2];
24
26
  const selectedBlockId = t1;
27
+ if (pageId == null && derivedLayoutId == null && selectedBlockId == null) return null;
25
28
  let t2;
26
29
  if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
27
30
  t2 = { width: 250 };
28
31
  $[3] = t2;
29
32
  } else t2 = $[3];
30
33
  let t3;
31
- if ($[4] !== pageId || $[5] !== selectedBlockId) {
32
- t3 = selectedBlockId == null ? /* @__PURE__ */ jsx(PageInfoSidebar, { pageId }) : /* @__PURE__ */ jsx(PageEditorSidebar, {});
33
- $[4] = pageId;
34
- $[5] = selectedBlockId;
35
- $[6] = t3;
36
- } else t3 = $[6];
37
- let t4;
38
- if ($[7] !== isAddBlockSidebarOpen) {
39
- t4 = isAddBlockSidebarOpen && /* @__PURE__ */ jsx("div", {
40
- className: "absolute inset-0 z-20",
41
- style: { background: "rgba(0, 0, 0, 0.66)" },
42
- onClick: _temp3
43
- });
44
- $[7] = isAddBlockSidebarOpen;
45
- $[8] = t4;
46
- } else t4 = $[8];
47
- let t5;
48
- if ($[9] !== t3 || $[10] !== t4) {
49
- t5 = /* @__PURE__ */ jsxs("aside", {
34
+ if ($[4] !== derivedLayoutId || $[5] !== isAddBlockSidebarOpen || $[6] !== pageId || $[7] !== selectedBlockId) {
35
+ t3 = /* @__PURE__ */ jsx("aside", {
50
36
  className: "bg-background relative flex shrink-0 flex-col border-l-2",
51
37
  style: t2,
52
- children: [t3, t4]
38
+ children: pageId != null && isAddBlockSidebarOpen ? /* @__PURE__ */ jsx(AddBlockSidebar, {}) : selectedBlockId != null ? /* @__PURE__ */ jsx(PageEditorSidebar, {}) : pageId != null ? /* @__PURE__ */ jsx(PageInfoSidebar, { pageId }) : /* @__PURE__ */ jsx(DerivedPageInfoSidebar, { layoutId: derivedLayoutId })
53
39
  });
54
- $[9] = t3;
55
- $[10] = t4;
56
- $[11] = t5;
57
- } else t5 = $[11];
58
- return t5;
40
+ $[4] = derivedLayoutId;
41
+ $[5] = isAddBlockSidebarOpen;
42
+ $[6] = pageId;
43
+ $[7] = selectedBlockId;
44
+ $[8] = t3;
45
+ } else t3 = $[8];
46
+ return t3;
59
47
  };
60
48
  function _temp(state) {
61
49
  return state.context.selection;
@@ -63,9 +51,6 @@ function _temp(state) {
63
51
  function _temp2(state_0) {
64
52
  return state_0.context.isAddBlockSidebarOpen;
65
53
  }
66
- function _temp3() {
67
- return previewStore.send({ type: "closeAddBlockSidebar" });
68
- }
69
54
 
70
55
  //#endregion
71
56
  export { RightSidebar };
@@ -0,0 +1,8 @@
1
+ //#region src/features/preview/components/previewStyles.ts
2
+ function isSiteStyle(element) {
3
+ return !element.hasAttribute("data-camox-studio");
4
+ }
5
+ const EMPTY_PREVIEW_DOCUMENT = "<!doctype html><html><head></head><body><div id=\"root\" data-camox-preview-root></div></body></html>";
6
+
7
+ //#endregion
8
+ export { EMPTY_PREVIEW_DOCUMENT, isSiteStyle };
@@ -1,33 +1,30 @@
1
1
  import { useLocation } from "../../navigation/navigation.js";
2
2
  import { blockMutations } from "../../../lib/queries.js";
3
- import { previewStore } from "../previewStore.js";
4
3
  import { c } from "react/compiler-runtime";
5
4
  import { useMutation, useQueryClient } from "@tanstack/react-query";
6
5
  import { generateKeyBetween } from "fractional-indexing";
7
- import { useSelector } from "@xstate/store-react";
8
6
  import { queryKeys } from "@camox/api-contract/query-keys";
9
7
 
10
8
  //#region src/features/preview/components/useUpdateBlockPosition.ts
11
9
  function useUpdateBlockPosition() {
12
10
  const $ = c(5);
13
- if ($[0] !== "c7adf6cdbf960268a5bfbb85b92c76404d7abbe36258eed14eb7062e652330a8") {
11
+ if ($[0] !== "fed14916f5b55681b4a767606bad92ee9c3a074376e24f2981a9af1a6551ad97") {
14
12
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
- $[0] = "c7adf6cdbf960268a5bfbb85b92c76404d7abbe36258eed14eb7062e652330a8";
13
+ $[0] = "fed14916f5b55681b4a767606bad92ee9c3a074376e24f2981a9af1a6551ad97";
16
14
  }
17
15
  const queryClient = useQueryClient();
18
16
  const { pathname } = useLocation();
19
- const pagePathname = useSelector(previewStore, _temp) ?? pathname;
20
17
  let t0;
21
18
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
22
19
  t0 = blockMutations.updatePosition();
23
20
  $[1] = t0;
24
21
  } else t0 = $[1];
25
22
  let t1;
26
- if ($[2] !== pagePathname || $[3] !== queryClient) {
23
+ if ($[2] !== pathname || $[3] !== queryClient) {
27
24
  t1 = {
28
25
  ...t0,
29
26
  onMutate: (variables) => {
30
- const pageQueryKey = queryKeys.pages.getByPath(pagePathname, "draft");
27
+ const pageQueryKey = queryKeys.pages.getByPath(pathname, "draft");
31
28
  const previousPage = queryClient.getQueryData(pageQueryKey);
32
29
  if (!previousPage) return {};
33
30
  const newPosition = generateKeyBetween(variables.afterPosition ?? null, variables.beforePosition ?? null);
@@ -66,22 +63,19 @@ function useUpdateBlockPosition() {
66
63
  };
67
64
  },
68
65
  onError: (_error, variables_0, context) => {
69
- if (context?.previousPage) queryClient.setQueryData(queryKeys.pages.getByPath(pagePathname, "draft"), context.previousPage);
66
+ if (context?.previousPage) queryClient.setQueryData(queryKeys.pages.getByPath(pathname, "draft"), context.previousPage);
70
67
  if (context?.prevBundle) queryClient.setQueryData(queryKeys.blocks.get(variables_0.id, "draft"), context.prevBundle);
71
68
  },
72
69
  onSettled: () => {
73
- queryClient.invalidateQueries({ queryKey: queryKeys.pages.getByPath(pagePathname, "draft") });
70
+ queryClient.invalidateQueries({ queryKey: queryKeys.pages.getByPath(pathname, "draft") });
74
71
  }
75
72
  };
76
- $[2] = pagePathname;
73
+ $[2] = pathname;
77
74
  $[3] = queryClient;
78
75
  $[4] = t1;
79
76
  } else t1 = $[4];
80
77
  return useMutation(t1);
81
78
  }
82
- function _temp(state) {
83
- return state.context.peekedPagePathname;
84
- }
85
79
 
86
80
  //#endregion
87
81
  export { useUpdateBlockPosition };
@@ -0,0 +1,8 @@
1
+ //#region src/features/preview/previewConstants.ts
2
+ const EDIT_MODE_SHORTCUT = {
3
+ key: "Enter",
4
+ withMeta: true
5
+ };
6
+
7
+ //#endregion
8
+ export { EDIT_MODE_SHORTCUT };
@@ -35,7 +35,6 @@ const previewStore = createStore({
35
35
  viewportMode: "full",
36
36
  peekedBlock: null,
37
37
  peekedBlockPosition: null,
38
- peekedPagePathname: null,
39
38
  skipPeekedBlockExitAnimation: false,
40
39
  selection: null,
41
40
  iframeElement: null,
@@ -184,15 +183,6 @@ const previewStore = createStore({
184
183
  ...context,
185
184
  selection: null
186
185
  }),
187
- setPeekedPage: (context, event) => ({
188
- ...context,
189
- selection: null,
190
- peekedPagePathname: event.pathname
191
- }),
192
- clearPeekedPage: (context) => ({
193
- ...context,
194
- peekedPagePathname: null
195
- }),
196
186
  openAddBlockSidebar: (context, event) => ({
197
187
  ...context,
198
188
  isAddBlockSidebarOpen: true,
@@ -0,0 +1,71 @@
1
+ //#region src/features/preview/publication.ts
2
+ /** Scope and impact are shared by all publishing entry points. Only expose choices
3
+ * the backend can execute: a page and its optional layout, or a whole layout.
4
+ * Synced-block dependencies will need backend planning/execution support here,
5
+ * not a separate publishing UI or a sequence of independent block requests.
6
+ */
7
+ function buildPublicationPlan(target) {
8
+ if (target.kind === "layout") return {
9
+ title: target.layout.status === "modified" ? "Publish changes" : "Publish layout",
10
+ description: `Publishes ${target.name} on every page using this layout. Generated content and external data are not published by this action.`,
11
+ items: [{
12
+ key: `layout:${target.layout.id}`,
13
+ label: `${target.name} layout blocks`,
14
+ impact: "Updates the before and after blocks on every page using this layout, including all derived URLs.",
15
+ optional: false
16
+ }]
17
+ };
18
+ const { page } = target;
19
+ const items = [{
20
+ key: `page:${page.id}`,
21
+ label: page.fullPath,
22
+ impact: page.status === "draft" ? "Makes this page live with its current draft content." : "Updates this page with its latest draft content.",
23
+ optional: false
24
+ }];
25
+ const reason = page.modifiedReason;
26
+ if (reason && (reason.reason === "layout" || reason.reason === "both")) {
27
+ const others = Math.max(0, reason.affectedPagesCount - 1);
28
+ items.push({
29
+ key: `layout:${reason.layoutId}`,
30
+ label: `${reason.layoutHandle} layout blocks`,
31
+ impact: others === 0 ? "Updates shared blocks on this page. Affects no other pages." : `Updates shared blocks on this page and ${others} other ${others === 1 ? "page" : "pages"}.`,
32
+ optional: true
33
+ });
34
+ }
35
+ return {
36
+ title: page.status === "modified" ? "Publish changes" : "Publish page",
37
+ description: page.status === "draft" ? `This page will go live at ${page.fullPath}.` : `Visitors at ${page.fullPath} will see your latest changes.`,
38
+ items
39
+ };
40
+ }
41
+ function getPublicationCapabilities(target, source) {
42
+ if (!target) return {
43
+ publish: false,
44
+ unpublish: false,
45
+ discard: false
46
+ };
47
+ const record = target.kind === "page" ? target.page : target.layout;
48
+ return {
49
+ publish: record.status !== "published" && source === "draft",
50
+ unpublish: record.livePublishedCheckpointId != null && !(target.kind === "page" && target.page.fullPath === "/"),
51
+ discard: target.kind === "page" && record.status === "modified"
52
+ };
53
+ }
54
+ function getPublicationRequest(target, includedKeys) {
55
+ if (target.kind === "layout") return {
56
+ kind: "layout",
57
+ input: { id: target.layout.id }
58
+ };
59
+ const optionalLayout = buildPublicationPlan(target).items.find((item) => item.optional);
60
+ const alsoPublishLayout = optionalLayout && includedKeys.includes(optionalLayout.key);
61
+ return {
62
+ kind: "page",
63
+ input: {
64
+ id: target.page.id,
65
+ ...alsoPublishLayout ? { alsoPublishLayout: true } : {}
66
+ }
67
+ };
68
+ }
69
+
70
+ //#endregion
71
+ export { buildPublicationPlan, getPublicationCapabilities, getPublicationRequest };
@@ -51,7 +51,8 @@ function AuthenticatedCamoxProvider({ children }) {
51
51
  return /* @__PURE__ */ jsxs(Fragment, { children: [
52
52
  /* @__PURE__ */ jsx("link", {
53
53
  rel: "stylesheet",
54
- href: studioCssUrl
54
+ href: studioCssUrl,
55
+ "data-camox-studio": true
55
56
  }),
56
57
  children,
57
58
  /* @__PURE__ */ jsx(Toaster, { theme: resolvedTheme }),
@@ -1,6 +1,8 @@
1
1
  import { AuthContext, createCamoxAuthClient, useProcessOtt } from "../../lib/auth.js";
2
2
  import { initApiClient } from "../../lib/api-client.js";
3
+ import { projectQueries } from "../../lib/queries.js";
3
4
  import { CamoxAppProvider } from "./components/CamoxAppContext.js";
5
+ import { useQueryClient } from "@tanstack/react-query";
4
6
  import * as React from "react";
5
7
  import { jsx } from "react/jsx-runtime";
6
8
 
@@ -10,13 +12,19 @@ function isLocalhostPreview() {
10
12
  const { hostname } = window.location;
11
13
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
12
14
  }
13
- function CoreCamoxProvider({ apiUrl, authenticationUrl, camoxApp, children, environmentName, projectSlug }) {
15
+ function CoreCamoxProvider({ apiUrl, authenticationUrl, camoxApp, children, environmentName, projectSlug, initialAuthenticated = false, initialProject }) {
16
+ const queryClient = useQueryClient();
14
17
  const authClient = React.useMemo(() => createCamoxAuthClient(apiUrl), [apiUrl]);
15
18
  const initializedApiUrl = React.useRef(null);
16
19
  if (initializedApiUrl.current !== apiUrl) {
17
20
  initApiClient(apiUrl, environmentName);
18
21
  initializedApiUrl.current = apiUrl;
19
22
  }
23
+ const seededProject = React.useRef(void 0);
24
+ if (initialProject && seededProject.current !== initialProject) {
25
+ queryClient.setQueryData(projectQueries.getBySlug(projectSlug).queryKey, initialProject);
26
+ seededProject.current = initialProject;
27
+ }
20
28
  useProcessOtt(authClient);
21
29
  return /* @__PURE__ */ jsx(AuthContext.Provider, {
22
30
  value: {
@@ -24,7 +32,8 @@ function CoreCamoxProvider({ apiUrl, authenticationUrl, camoxApp, children, envi
24
32
  authenticationUrl,
25
33
  apiUrl,
26
34
  projectSlug,
27
- environmentName
35
+ environmentName,
36
+ initialAuthenticated
28
37
  },
29
38
  children: /* @__PURE__ */ jsx(CamoxAppProvider, {
30
39
  app: camoxApp,
@@ -1,6 +1,7 @@
1
1
  import { actionsStore } from "./actionsStore.js";
2
2
  import { useSignInRedirect } from "../../lib/auth.js";
3
3
  import { useApplyTheme } from "../studio/useTheme.js";
4
+ import { EDIT_MODE_SHORTCUT } from "../preview/previewConstants.js";
4
5
  import { PreviewPanel } from "../preview/components/PreviewPanel.js";
5
6
  import { useAdminShortcuts } from "./useAdminShortcuts.js";
6
7
  import { c } from "react/compiler-runtime";
@@ -13,9 +14,9 @@ import studioCssUrl from "virtual:camox-studio-css";
13
14
  //#region src/features/provider/LocalhostPreviewProvider.tsx
14
15
  function LocalhostPreviewProvider(t0) {
15
16
  const $ = c(21);
16
- if ($[0] !== "bd39c46de8381d98c4a04fb8c92df87704f578a9561f6fbfafaa182175dd58dd") {
17
+ if ($[0] !== "fddfba0cee29e4fb721206c13e16fbe407ae9aeb750c2a716d0ba1347b862953") {
17
18
  for (let $i = 0; $i < 21; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
18
- $[0] = "bd39c46de8381d98c4a04fb8c92df87704f578a9561f6fbfafaa182175dd58dd";
19
+ $[0] = "fddfba0cee29e4fb721206c13e16fbe407ae9aeb750c2a716d0ba1347b862953";
19
20
  }
20
21
  const { children } = t0;
21
22
  const signInRedirect = useSignInRedirect();
@@ -33,10 +34,7 @@ function LocalhostPreviewProvider(t0) {
33
34
  groupLabel: "Preview",
34
35
  checkIfAvailable: _temp,
35
36
  execute: () => setIsSignInDialogOpen(true),
36
- shortcut: {
37
- key: "Enter",
38
- withMeta: true
39
- }
37
+ shortcut: EDIT_MODE_SHORTCUT
40
38
  }];
41
39
  actionsStore.send({
42
40
  type: "registerManyActions",
@@ -61,7 +59,8 @@ function LocalhostPreviewProvider(t0) {
61
59
  if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
62
60
  t3 = /* @__PURE__ */ jsx("link", {
63
61
  rel: "stylesheet",
64
- href: studioCssUrl
62
+ href: studioCssUrl,
63
+ "data-camox-studio": true
65
64
  });
66
65
  $[3] = t3;
67
66
  } else t3 = $[3];