camox 0.40.0 → 0.41.1

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 (108) 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 +17 -28
  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/BlockEditingRuntime.js +4 -4
  17. package/dist/core/editing/CompleteBlockEditingRuntime.js +5 -5
  18. package/dist/core/editing/createEditableBlock.d.ts +9 -8
  19. package/dist/core/editing/createEditableBlock.js +265 -403
  20. package/dist/core/hooks/useIsEditable.js +4 -7
  21. package/dist/core/hooks/useRequireDraftSource.js +7 -11
  22. package/dist/core/lib/lexicalReact.d.ts +1 -26
  23. package/dist/core/lib/lexicalReact.js +46 -60
  24. package/dist/core/lib/lexicalState.js +164 -81
  25. package/dist/core/lib/modifierFormats.js +4 -15
  26. package/dist/core/lib/textStyles.d.ts +27 -0
  27. package/dist/core/lib/textStyles.js +47 -0
  28. package/dist/features/navigation/navigation.d.ts +2 -0
  29. package/dist/features/navigation/navigation.js +51 -46
  30. package/dist/features/page/DerivedPageContent.js +61 -0
  31. package/dist/features/page/PublishedPageContent.js +52 -83
  32. package/dist/features/page/SharedLayoutContent.js +66 -0
  33. package/dist/features/preview/CamoxPreview.d.ts +16 -1
  34. package/dist/features/preview/CamoxPreview.js +246 -290
  35. package/dist/features/preview/DerivedPreview.js +134 -0
  36. package/dist/features/preview/EditablePageExperience.js +25 -8
  37. package/dist/features/preview/components/AddBlockSidebar.js +43 -33
  38. package/dist/features/preview/components/AssetLightbox.js +1 -1
  39. package/dist/features/preview/components/BlockActionsPopover.js +8 -8
  40. package/dist/features/preview/components/BlockThumbnail.js +192 -0
  41. package/dist/features/preview/components/CreatePageModal.js +123 -115
  42. package/dist/features/preview/components/DerivedLayoutDialog.js +100 -0
  43. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +5 -0
  44. package/dist/features/preview/components/DerivedLayoutSidebar.js +160 -0
  45. package/dist/features/preview/components/DerivedPageInfoSidebar.js +57 -0
  46. package/dist/features/preview/components/FieldToolbar.js +42 -20
  47. package/dist/features/preview/components/Frame.js +133 -140
  48. package/dist/features/preview/components/LeftSidebar.js +42 -38
  49. package/dist/features/preview/components/LinkFieldEditor.js +3 -4
  50. package/dist/features/preview/components/MobilePreviewDrawer.js +4 -4
  51. package/dist/features/preview/components/Overlays.js +59 -47
  52. package/dist/features/preview/components/PageEditorSidebar.js +194 -203
  53. package/dist/features/preview/components/PageInfoSidebar.js +63 -55
  54. package/dist/features/preview/components/PageNavigatorSidebar.js +34 -483
  55. package/dist/features/preview/components/PagePicker.js +205 -153
  56. package/dist/features/preview/components/PageTree.js +366 -340
  57. package/dist/features/preview/components/PreviewActivation.js +78 -0
  58. package/dist/features/preview/components/PreviewPanel.js +98 -68
  59. package/dist/features/preview/components/PreviewToolbar.js +224 -241
  60. package/dist/features/preview/components/PreviewToolbarSpacer.js +43 -0
  61. package/dist/features/preview/components/PublicationRow.js +425 -0
  62. package/dist/features/preview/components/PublishDialog.js +180 -97
  63. package/dist/features/preview/components/RightSidebar.js +19 -34
  64. package/dist/features/preview/components/previewStyles.js +8 -0
  65. package/dist/features/preview/components/useUpdateBlockPosition.js +7 -13
  66. package/dist/features/preview/previewConstants.js +8 -0
  67. package/dist/features/preview/previewStore.js +18 -39
  68. package/dist/features/preview/publication.js +71 -0
  69. package/dist/features/provider/AuthenticatedCamoxProvider.js +2 -1
  70. package/dist/features/provider/CoreCamoxProvider.js +11 -2
  71. package/dist/features/provider/LocalhostPreviewProvider.js +6 -7
  72. package/dist/features/provider/useAdminShortcuts.js +5 -4
  73. package/dist/features/routes/pageRuntime.d.ts +10 -0
  74. package/dist/features/routes/pageRuntime.js +7 -9
  75. package/dist/features/runtime/PreviewDocumentContext.js +7 -0
  76. package/dist/features/runtime/RuntimeChrome.js +149 -0
  77. package/dist/features/runtime/SharedChromeContext.js +7 -0
  78. package/dist/features/runtime/navigationRequest.js +21 -0
  79. package/dist/features/runtime/navigationTarget.js +23 -0
  80. package/dist/features/runtime/pageApp.js +196 -121
  81. package/dist/features/runtime/pageNavigation.js +79 -130
  82. package/dist/features/runtime/runtime.d.ts +11 -0
  83. package/dist/features/runtime/runtime.js +183 -47
  84. package/dist/features/runtime/studioApp.d.ts +2 -11
  85. package/dist/features/runtime/studioApp.js +14 -177
  86. package/dist/features/studio/CamoxStudio.js +27 -13
  87. package/dist/features/studio/components/Navbar.js +80 -74
  88. package/dist/features/studio/components/UserButton.js +4 -4
  89. package/dist/features/studio/studioTheme.js +25 -0
  90. package/dist/features/studio/useTheme.js +94 -109
  91. package/dist/features/vite/appGeneration.js +12 -3
  92. package/dist/features/vite/definitionsSync.js +4 -2
  93. package/dist/features/vite/layoutGeneration.js +38 -0
  94. package/dist/features/vite/runtimeDev.js +3 -1
  95. package/dist/lib/auth.js +15 -13
  96. package/dist/lib/normalized-data.js +4 -4
  97. package/dist/lib/platform.js +13 -0
  98. package/dist/lib/utils.js +90 -38
  99. package/dist/studio-overlays.css +5 -40
  100. package/dist/studio.css +1 -1
  101. package/package.json +8 -4
  102. package/skills/camox/SKILL.md +2 -0
  103. package/skills/camox/references/block-definitions.md +31 -12
  104. package/skills/camox/references/layout-definitions.md +2 -0
  105. package/dist/core/lib/modifiers.js +0 -24
  106. package/dist/features/preview/components/DraftSwitchDialog.js +0 -66
  107. package/dist/features/preview/previewQueryFns.js +0 -23
  108. package/dist/features/runtime/studioNavigation.js +0 -120
@@ -2,6 +2,7 @@ import { Link as Link$1, useLocation, useNavigate } from "../../navigation/navig
2
2
  import { actionsStore } from "../../provider/actionsStore.js";
3
3
  import { useProjectSlug } from "../../../lib/auth.js";
4
4
  import { pageQueries, projectQueries } from "../../../lib/queries.js";
5
+ import { PlatformLabel } from "../../../components/PlatformLabel.js";
5
6
  import { cn } from "../../../lib/utils.js";
6
7
  import { STUDIO_CONTENT_PATH } from "../routes.js";
7
8
  import { studioStore } from "../studioStore.js";
@@ -34,121 +35,126 @@ const links = [{
34
35
  "CMS"
35
36
  ]
36
37
  }];
37
- const Navbar = () => {
38
- const $ = c(19);
39
- if ($[0] !== "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2") {
40
- for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
41
- $[0] = "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2";
38
+ const Navbar = (t0) => {
39
+ const $ = c(20);
40
+ if ($[0] !== "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d") {
41
+ for (let $i = 0; $i < 20; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
42
+ $[0] = "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d";
42
43
  }
44
+ const { isPreview: t1 } = t0;
45
+ const isPreview = t1 === void 0 ? false : t1;
43
46
  const projectSlug = useProjectSlug();
44
- let t0;
47
+ let t2;
45
48
  if ($[1] !== projectSlug) {
46
- t0 = projectQueries.getBySlug(projectSlug);
49
+ t2 = projectQueries.getBySlug(projectSlug);
47
50
  $[1] = projectSlug;
48
- $[2] = t0;
49
- } else t0 = $[2];
50
- const { data: project } = useQuery(t0);
51
- let t1;
51
+ $[2] = t2;
52
+ } else t2 = $[2];
53
+ const { data: project } = useQuery(t2);
54
+ let t3;
52
55
  if ($[3] !== project?.id) {
53
- t1 = pageQueries.list(project?.id ?? 0);
56
+ t3 = pageQueries.list(project?.id ?? 0);
54
57
  $[3] = project?.id;
55
- $[4] = t1;
56
- } else t1 = $[4];
57
- const t2 = !!project;
58
- let t3;
59
- if ($[5] !== t1 || $[6] !== t2) {
60
- t3 = {
61
- ...t1,
62
- enabled: t2
58
+ $[4] = t3;
59
+ } else t3 = $[4];
60
+ const t4 = !!project;
61
+ let t5;
62
+ if ($[5] !== t3 || $[6] !== t4) {
63
+ t5 = {
64
+ ...t3,
65
+ enabled: t4
63
66
  };
64
- $[5] = t1;
65
- $[6] = t2;
66
- $[7] = t3;
67
- } else t3 = $[7];
68
- const { data: pages } = useQuery(t3);
69
- const isMac = navigator.userAgent.toUpperCase().indexOf("MAC") >= 0;
67
+ $[5] = t3;
68
+ $[6] = t4;
69
+ $[7] = t5;
70
+ } else t5 = $[7];
71
+ const { data: pages } = useQuery(t5);
70
72
  const { pathname } = useLocation();
71
- let t4;
73
+ let t6;
72
74
  if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
73
- t4 = /* @__PURE__ */ jsxs("div", {
75
+ t6 = /* @__PURE__ */ jsxs("div", {
74
76
  className: "flew-row flex gap-1",
75
77
  children: [/* @__PURE__ */ jsx(ProjectMenu, {}), /* @__PURE__ */ jsx(EnvironmentMenu, {})]
76
78
  });
77
- $[8] = t4;
78
- } else t4 = $[8];
79
- let t5;
80
- if ($[9] !== pages || $[10] !== pathname) {
81
- t5 = links.map((link, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link$1, {
79
+ $[8] = t6;
80
+ } else t6 = $[8];
81
+ let t7;
82
+ if ($[9] !== isPreview || $[10] !== pages || $[11] !== pathname) {
83
+ t7 = links.map((link, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link$1, {
82
84
  to: link.to,
83
- className: cn("flex gap-2 items-center rounded-md px-4 py-2 text-sm font-medium", "hover:bg-accent hover:text-accent-foreground outline-none transition-[color,box-shadow] focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-1", pages?.some((page) => page.fullPath === pathname) && index === 0 ? "bg-accent hover:bg-accent text-accent-foreground" : "text-muted-foreground"),
85
+ className: cn("flex gap-2 items-center rounded-md px-4 py-2 text-sm font-medium", "hover:bg-accent hover:text-accent-foreground outline-none transition-[color,box-shadow] focus-visible:ring-ring/50 focus-visible:ring-[3px] focus-visible:outline-1", (isPreview || pages?.some((page) => page.fullPath === pathname)) && index === 0 ? "bg-accent hover:bg-accent text-accent-foreground" : "text-muted-foreground"),
84
86
  activeProps: { className: "bg-accent hover:bg-accent text-accent-foreground!" },
85
87
  children: link.children
86
88
  }) }, link.to));
87
- $[9] = pages;
88
- $[10] = pathname;
89
- $[11] = t5;
90
- } else t5 = $[11];
91
- let t6;
92
- if ($[12] !== t5) {
93
- t6 = /* @__PURE__ */ jsxs("div", {
89
+ $[9] = isPreview;
90
+ $[10] = pages;
91
+ $[11] = pathname;
92
+ $[12] = t7;
93
+ } else t7 = $[12];
94
+ let t8;
95
+ if ($[13] !== t7) {
96
+ t8 = /* @__PURE__ */ jsxs("div", {
94
97
  className: "flex flex-row gap-2",
95
- children: [t4, /* @__PURE__ */ jsx("ul", {
98
+ children: [t6, /* @__PURE__ */ jsx("ul", {
96
99
  className: "flex items-center gap-1",
97
- children: t5
100
+ children: t7
98
101
  })]
99
102
  });
100
- $[12] = t5;
101
- $[13] = t6;
102
- } else t6 = $[13];
103
- let t7;
104
- let t8;
105
- if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
106
- t7 = /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4" });
107
- t8 = /* @__PURE__ */ jsx("span", {
103
+ $[13] = t7;
104
+ $[14] = t8;
105
+ } else t8 = $[14];
106
+ let t10;
107
+ let t9;
108
+ if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
109
+ t9 = /* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4" });
110
+ t10 = /* @__PURE__ */ jsx("span", {
108
111
  className: "text-muted-foreground",
109
112
  children: "Quick find"
110
113
  });
111
- $[14] = t7;
112
- $[15] = t8;
114
+ $[15] = t10;
115
+ $[16] = t9;
113
116
  } else {
114
- t7 = $[14];
115
- t8 = $[15];
117
+ t10 = $[15];
118
+ t9 = $[16];
116
119
  }
117
- let t9;
118
- if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
119
- t9 = /* @__PURE__ */ jsxs("div", {
120
+ let t11;
121
+ if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
122
+ t11 = /* @__PURE__ */ jsxs("div", {
120
123
  className: "flex items-center gap-2",
121
124
  children: [/* @__PURE__ */ jsxs(Button, {
122
125
  variant: "outline",
123
126
  onClick: _temp,
124
127
  children: [
125
- t7,
126
- t8,
127
- /* @__PURE__ */ jsxs(Kbd, {
128
+ t9,
129
+ t10,
130
+ /* @__PURE__ */ jsx(Kbd, {
128
131
  className: "ml-4",
129
- children: [isMac ? "⌘" : "Ctrl", " K"]
132
+ children: /* @__PURE__ */ jsx(PlatformLabel, {
133
+ mac: "⌘ K",
134
+ other: "Ctrl K"
135
+ })
130
136
  })
131
137
  ]
132
138
  }), /* @__PURE__ */ jsx(UserButton, {})]
133
139
  });
134
- $[16] = t9;
135
- } else t9 = $[16];
136
- let t10;
137
- if ($[17] !== t6) {
138
- t10 = /* @__PURE__ */ jsxs("nav", {
140
+ $[17] = t11;
141
+ } else t11 = $[17];
142
+ let t12;
143
+ if ($[18] !== t8) {
144
+ t12 = /* @__PURE__ */ jsxs("nav", {
139
145
  className: "bg-background relative flex items-center justify-between gap-4 border-b-2 px-2 py-2",
140
- children: [t6, t9]
146
+ children: [t8, t11]
141
147
  });
142
- $[17] = t6;
143
- $[18] = t10;
144
- } else t10 = $[18];
145
- return t10;
148
+ $[18] = t8;
149
+ $[19] = t12;
150
+ } else t12 = $[19];
151
+ return t12;
146
152
  };
147
153
  function useNavbarActions() {
148
154
  const $ = c(4);
149
- if ($[0] !== "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2") {
155
+ if ($[0] !== "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d") {
150
156
  for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
151
- $[0] = "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2";
157
+ $[0] = "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d";
152
158
  }
153
159
  const navigate = useNavigate();
154
160
  let t0;
@@ -1,7 +1,7 @@
1
1
  import { useLocation, useNavigate } from "../../navigation/navigation.js";
2
2
  import { useAuthContext, useAuthState } from "../../../lib/auth.js";
3
3
  import { STUDIO_BASE_PATH } from "../routes.js";
4
- import { previewStore } from "../../preview/previewStore.js";
4
+ import { previewStore, selectIsEditMode } from "../../preview/previewStore.js";
5
5
  import { useApplyTheme } from "../useTheme.js";
6
6
  import { c } from "react/compiler-runtime";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -14,9 +14,9 @@ import { Avatar, AvatarFallback, AvatarImage } from "@camox/ui/avatar";
14
14
  //#region src/features/studio/components/UserButton.tsx
15
15
  const UserButton = () => {
16
16
  const $ = c(5);
17
- if ($[0] !== "a09d40e74ed3a5b0583d8899c9b1d3c569af18304db9c6f598775bf2c16e2608") {
17
+ if ($[0] !== "b876c985f56cb759905567177978ca8597789d0818f006e69fd4bcfb86c58f94") {
18
18
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
19
- $[0] = "a09d40e74ed3a5b0583d8899c9b1d3c569af18304db9c6f598775bf2c16e2608";
19
+ $[0] = "b876c985f56cb759905567177978ca8597789d0818f006e69fd4bcfb86c58f94";
20
20
  }
21
21
  const { isAuthenticated, isLoading } = useAuthState();
22
22
  const { theme, setTheme } = useApplyTheme();
@@ -48,7 +48,7 @@ function AuthenticatedUserButton({ theme, setTheme }) {
48
48
  const authCtx = useAuthContext();
49
49
  const { data: session } = authCtx.authClient.useSession();
50
50
  const authenticationUrl = authCtx.authenticationUrl;
51
- const isEditMode = useSelector(previewStore, (state) => state.context.isEditMode);
51
+ const isEditMode = useSelector(previewStore, selectIsEditMode);
52
52
  const { pathname } = useLocation();
53
53
  const navigate = useNavigate();
54
54
  const isPreviewPage = pathname !== STUDIO_BASE_PATH && !pathname.startsWith(`${STUDIO_BASE_PATH}/`);
@@ -0,0 +1,25 @@
1
+ //#region src/features/studio/studioTheme.ts
2
+ const STUDIO_THEME_STORAGE_KEY = "camox:studio:theme";
3
+ function readStudioTheme() {
4
+ try {
5
+ const value = localStorage.getItem("camox:studio:theme") ?? localStorage.getItem("theme");
6
+ if (value === "light" || value === "dark") return value;
7
+ } catch {}
8
+ return "system";
9
+ }
10
+ function bootstrapStudioTheme(preference) {
11
+ let theme = preference ?? "system";
12
+ if (!preference) try {
13
+ theme = localStorage.getItem("camox:studio:theme") ?? localStorage.getItem("theme") ?? "system";
14
+ } catch {}
15
+ const resolved = theme === "dark" || theme !== "light" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
16
+ const root = document.documentElement;
17
+ root.classList.remove("light", "dark");
18
+ root.classList.add(resolved);
19
+ root.dataset.camoxStudioTheme = resolved;
20
+ root.style.colorScheme = resolved;
21
+ }
22
+ const STUDIO_THEME_SCRIPT = `(${bootstrapStudioTheme.toString()})();`;
23
+
24
+ //#endregion
25
+ export { STUDIO_THEME_SCRIPT, STUDIO_THEME_STORAGE_KEY, bootstrapStudioTheme, readStudioTheme };
@@ -1,127 +1,112 @@
1
1
  import { actionsStore } from "../provider/actionsStore.js";
2
+ import { STUDIO_THEME_STORAGE_KEY, bootstrapStudioTheme, readStudioTheme } from "./studioTheme.js";
2
3
  import { c } from "react/compiler-runtime";
3
4
  import * as React from "react";
4
5
 
5
6
  //#region src/features/studio/useTheme.ts
6
7
  let activeThemeOwnerCount = 0;
8
+ const listeners = /* @__PURE__ */ new Set();
9
+ let preference;
7
10
  function readStoredTheme() {
8
11
  if (typeof window === "undefined") return "system";
9
- return localStorage.getItem("theme") || "system";
12
+ return preference ?? readStudioTheme();
13
+ }
14
+ function subscribe(listener) {
15
+ listeners.add(listener);
16
+ return () => {
17
+ listeners.delete(listener);
18
+ };
19
+ }
20
+ function notify() {
21
+ listeners.forEach((listener) => listener());
22
+ }
23
+ function applyTheme(theme = readStoredTheme()) {
24
+ bootstrapStudioTheme(theme);
25
+ const resolved = document.documentElement.dataset.camoxStudioTheme;
26
+ document.body.classList.remove("light", "dark");
27
+ document.body.classList.add("camox-studio-theme", resolved);
28
+ document.body.dataset.camoxStudioTheme = resolved;
29
+ }
30
+ function setTheme(theme) {
31
+ preference = theme;
32
+ try {
33
+ localStorage.setItem(STUDIO_THEME_STORAGE_KEY, theme);
34
+ } catch {}
35
+ applyTheme(theme);
36
+ notify();
10
37
  }
11
- /**
12
- * Owns the studio theme: writes the active class onto `<html>` and persists
13
- * changes to localStorage. Should only be mounted from the studio chrome —
14
- * never from contexts that share `<html>` with the user's site.
15
- */
16
38
  function useApplyTheme() {
17
- const $ = c(10);
18
- if ($[0] !== "da3935c3f496f05138d1e012522f8d34be97b470fa5c8f37185df49127eab10f") {
19
- for (let $i = 0; $i < 10; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
20
- $[0] = "da3935c3f496f05138d1e012522f8d34be97b470fa5c8f37185df49127eab10f";
39
+ const $ = c(5);
40
+ if ($[0] !== "5860ae8e48f7dc27e488236def482df22f144de61ae2d86bfde82aeeea4f9874") {
41
+ for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
42
+ $[0] = "5860ae8e48f7dc27e488236def482df22f144de61ae2d86bfde82aeeea4f9874";
21
43
  }
22
- const [theme, setTheme] = React.useState(readStoredTheme);
23
- const [resolvedTheme, setResolvedTheme] = React.useState("light");
44
+ const theme = React.useSyncExternalStore(subscribe, readStoredTheme, _temp2);
45
+ const resolvedTheme = React.useSyncExternalStore(subscribe, _temp3, _temp4);
24
46
  let t0;
47
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
48
+ t0 = [];
49
+ $[1] = t0;
50
+ } else t0 = $[1];
51
+ React.useEffect(_temp7, t0);
25
52
  let t1;
26
- if ($[1] !== theme) {
27
- t0 = () => {
28
- const root = window.document.documentElement;
29
- const body = window.document.body;
30
- activeThemeOwnerCount = activeThemeOwnerCount + 1;
31
- const applyTheme = (themeToApply) => {
32
- setResolvedTheme(themeToApply);
33
- root.classList.remove("light", "dark");
34
- root.classList.add(themeToApply);
35
- root.dataset.camoxStudioTheme = themeToApply;
36
- body.classList.remove("light", "dark");
37
- body.classList.add("camox-studio-theme", themeToApply);
38
- body.dataset.camoxStudioTheme = themeToApply;
39
- };
40
- const resolveTheme = () => {
41
- if (theme !== "system") return theme;
42
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
43
- };
44
- applyTheme(resolveTheme());
45
- const observer = new MutationObserver(() => {
46
- const themeToApply_0 = resolveTheme();
47
- if (root.classList.contains(themeToApply_0) && body.classList.contains(themeToApply_0)) return;
48
- applyTheme(themeToApply_0);
49
- });
50
- observer.observe(root, {
51
- attributes: true,
52
- attributeFilter: ["class"]
53
- });
54
- observer.observe(body, {
55
- attributes: true,
56
- attributeFilter: ["class"]
57
- });
58
- if (theme === "system") {
59
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
60
- const updateSystemTheme = () => applyTheme(resolveTheme());
61
- mediaQuery.addEventListener("change", updateSystemTheme);
62
- return () => {
63
- mediaQuery.removeEventListener("change", updateSystemTheme);
64
- observer.disconnect();
65
- activeThemeOwnerCount = activeThemeOwnerCount - 1;
66
- if (activeThemeOwnerCount > 0) return;
67
- root.classList.remove("light", "dark");
68
- delete root.dataset.camoxStudioTheme;
69
- body.classList.remove("camox-studio-theme", "light", "dark");
70
- delete body.dataset.camoxStudioTheme;
71
- };
72
- }
73
- return () => {
74
- observer.disconnect();
75
- activeThemeOwnerCount = activeThemeOwnerCount - 1;
76
- if (activeThemeOwnerCount > 0) return;
77
- root.classList.remove("light", "dark");
78
- delete root.dataset.camoxStudioTheme;
79
- body.classList.remove("camox-studio-theme", "light", "dark");
80
- delete body.dataset.camoxStudioTheme;
81
- };
82
- };
83
- t1 = [theme];
84
- $[1] = theme;
85
- $[2] = t0;
86
- $[3] = t1;
87
- } else {
88
- t0 = $[2];
89
- t1 = $[3];
90
- }
91
- React.useEffect(t0, t1);
92
- let t2;
93
- let t3;
94
- if ($[4] !== theme) {
95
- t2 = () => {
96
- localStorage.setItem("theme", theme);
97
- };
98
- t3 = [theme];
99
- $[4] = theme;
100
- $[5] = t2;
101
- $[6] = t3;
102
- } else {
103
- t2 = $[5];
104
- t3 = $[6];
105
- }
106
- React.useEffect(t2, t3);
107
- let t4;
108
- if ($[7] !== resolvedTheme || $[8] !== theme) {
109
- t4 = {
53
+ if ($[2] !== resolvedTheme || $[3] !== theme) {
54
+ t1 = {
110
55
  theme,
111
56
  resolvedTheme,
112
57
  setTheme
113
58
  };
114
- $[7] = resolvedTheme;
115
- $[8] = theme;
116
- $[9] = t4;
117
- } else t4 = $[9];
118
- return t4;
59
+ $[2] = resolvedTheme;
60
+ $[3] = theme;
61
+ $[4] = t1;
62
+ } else t1 = $[4];
63
+ return t1;
64
+ }
65
+ function _temp7() {
66
+ const root = window.document.documentElement;
67
+ const body = window.document.body;
68
+ activeThemeOwnerCount = activeThemeOwnerCount + 1;
69
+ setTheme(readStoredTheme());
70
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
71
+ const updateSystemTheme = _temp5;
72
+ const onStorage = _temp6;
73
+ mediaQuery.addEventListener("change", updateSystemTheme);
74
+ window.addEventListener("storage", onStorage);
75
+ return () => {
76
+ mediaQuery.removeEventListener("change", updateSystemTheme);
77
+ window.removeEventListener("storage", onStorage);
78
+ activeThemeOwnerCount = activeThemeOwnerCount - 1;
79
+ if (activeThemeOwnerCount > 0) return;
80
+ root.classList.remove("light", "dark");
81
+ root.style.removeProperty("color-scheme");
82
+ delete root.dataset.camoxStudioTheme;
83
+ body.classList.remove("camox-studio-theme", "light", "dark");
84
+ delete body.dataset.camoxStudioTheme;
85
+ };
86
+ }
87
+ function _temp6(event) {
88
+ if (event.key !== "camox:studio:theme" && event.key !== null) return;
89
+ preference = void 0;
90
+ applyTheme();
91
+ notify();
92
+ }
93
+ function _temp5() {
94
+ if (readStoredTheme() === "system") setTheme("system");
95
+ }
96
+ function _temp4() {
97
+ return "light";
98
+ }
99
+ function _temp3() {
100
+ return document.documentElement.dataset.camoxStudioTheme === "dark" ? "dark" : "light";
101
+ }
102
+ function _temp2() {
103
+ return "system";
119
104
  }
120
105
  function useThemeActions() {
121
106
  const $ = c(5);
122
- if ($[0] !== "da3935c3f496f05138d1e012522f8d34be97b470fa5c8f37185df49127eab10f") {
107
+ if ($[0] !== "5860ae8e48f7dc27e488236def482df22f144de61ae2d86bfde82aeeea4f9874") {
123
108
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
124
- $[0] = "da3935c3f496f05138d1e012522f8d34be97b470fa5c8f37185df49127eab10f";
109
+ $[0] = "5860ae8e48f7dc27e488236def482df22f144de61ae2d86bfde82aeeea4f9874";
125
110
  }
126
111
  const { theme, setTheme } = useApplyTheme();
127
112
  let t0;
@@ -138,9 +123,9 @@ function useThemeActions() {
138
123
  "Color mode"
139
124
  ],
140
125
  groupLabel: "Studio",
141
- checkIfAvailable: _temp,
126
+ checkIfAvailable: _temp8,
142
127
  hasChildren: true,
143
- execute: _temp2
128
+ execute: _temp9
144
129
  },
145
130
  {
146
131
  id: "switch-to-light-theme",
@@ -177,7 +162,7 @@ function useThemeActions() {
177
162
  return () => {
178
163
  actionsStore.send({
179
164
  type: "unregisterManyActions",
180
- ids: actions.map(_temp3)
165
+ ids: actions.map(_temp0)
181
166
  });
182
167
  };
183
168
  };
@@ -192,11 +177,11 @@ function useThemeActions() {
192
177
  }
193
178
  React.useEffect(t0, t1);
194
179
  }
195
- function _temp3(a) {
180
+ function _temp0(a) {
196
181
  return a.id;
197
182
  }
198
- function _temp2() {}
199
- function _temp() {
183
+ function _temp9() {}
184
+ function _temp8() {
200
185
  return true;
201
186
  }
202
187
 
@@ -1,4 +1,5 @@
1
1
  import { writeIfChanged } from "./utils.js";
2
+ import { generateLayoutFiles } from "./layoutGeneration.js";
2
3
  import { readFileSync, writeFileSync } from "node:fs";
3
4
  import { relative, resolve } from "node:path";
4
5
 
@@ -28,16 +29,16 @@ const blocks = [...Object.values(blockModules), ...Object.values(legacyBlockModu
28
29
  );
29
30
 
30
31
  // Auto-import all layouts from the first-class layouts directory
31
- const layoutModules = import.meta.glob<{ layout: Layout }>(
32
+ const layoutModules = import.meta.glob<{ layout?: Layout; Layout?: Layout }>(
32
33
  '../layouts/*.{ts,tsx}',
33
34
  { eager: true },
34
35
  );
35
- const legacyLayoutModules = import.meta.glob<{ layout: Layout }>(
36
+ const legacyLayoutModules = import.meta.glob<{ layout?: Layout; Layout?: Layout }>(
36
37
  './layouts/*.{ts,tsx}',
37
38
  { eager: true },
38
39
  );
39
40
  const layouts = [...Object.values(layoutModules), ...Object.values(legacyLayoutModules)].map(
40
- (mod) => mod.layout,
41
+ (mod) => (mod.Layout ?? mod.layout)!,
41
42
  );
42
43
 
43
44
  export const camoxApp = createApp({
@@ -53,9 +54,17 @@ function getAppFileEntries(appRoot) {
53
54
  }];
54
55
  }
55
56
  function generateAppFile(appRoot) {
57
+ generateLayoutFiles(appRoot);
56
58
  for (const entry of getAppFileEntries(appRoot)) writeIfChanged(entry.path, entry.content);
57
59
  }
58
60
  function watchAppFile(server, appRoot) {
61
+ for (const event of [
62
+ "add",
63
+ "change",
64
+ "unlink"
65
+ ]) server.watcher.on(event, (path) => {
66
+ if (path.startsWith(resolve(appRoot, "src/layouts") + "/")) generateLayoutFiles(appRoot);
67
+ });
59
68
  const entries = getAppFileEntries(appRoot);
60
69
  const expectedByPath = new Map(entries.map((e) => [e.path, e.content]));
61
70
  server.watcher.on("change", (changedPath) => {
@@ -32,7 +32,8 @@ async function syncDefinitionsToApi(options) {
32
32
  settingsSchema: block._internal.settingsSchema,
33
33
  defaultContent: block._internal.getInitialContent(),
34
34
  defaultSettings: block._internal.getInitialSettings(),
35
- layoutOnly: block._internal.layoutOnly || void 0
35
+ layoutOnly: block._internal.layoutOnly || void 0,
36
+ synced: block._internal.synced
36
37
  }));
37
38
  let environmentCreated = false;
38
39
  let deletedDefinitionTypes = [];
@@ -186,7 +187,8 @@ async function syncDefinitions(server, options) {
186
187
  settingsSchema: block._internal.settingsSchema,
187
188
  defaultContent: block._internal.getInitialContent(),
188
189
  defaultSettings: block._internal.getInitialSettings(),
189
- layoutOnly: block._internal.layoutOnly || void 0
190
+ layoutOnly: block._internal.layoutOnly || void 0,
191
+ synced: block._internal.synced
190
192
  });
191
193
  } catch (error) {
192
194
  throwIfSyncAuthError(error);
@@ -0,0 +1,38 @@
1
+ import { writeIfChanged } from "./utils.js";
2
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
3
+ import { basename, resolve } from "node:path";
4
+
5
+ //#region src/features/vite/layoutGeneration.ts
6
+ /** Keep file identities stable across moves without executing application modules. */
7
+ function generateLayoutFiles(appRoot) {
8
+ const directory = resolve(appRoot, "src/layouts");
9
+ if (!existsSync(directory)) return;
10
+ const declarations = [];
11
+ for (const file of readdirSync(directory).sort()) {
12
+ if (!/\.(ts|tsx)$/.test(file) || file.endsWith(".d.ts")) continue;
13
+ const id = basename(file).replace(/\.(ts|tsx)$/, "");
14
+ const path = resolve(directory, file);
15
+ let source = readFileSync(path, "utf8");
16
+ if (!source.trim()) source = `import { createLayout } from "camox/createLayout";
17
+
18
+ export const Layout = createLayout(${JSON.stringify(id)})({
19
+ kind: "curated",
20
+ title: ${JSON.stringify(id)},
21
+ description: "",
22
+ blocks: { before: [], after: [] },
23
+ buildMetaTitle: ({ pageMetaTitle }) => pageMetaTitle,
24
+ component: ({ children }) => <main><Layout.BeforeBlocks />{children}<Layout.AfterBlocks /></main>,
25
+ });
26
+ `;
27
+ source = source.replace(/(createLayout\(\s*)["'][^"']+["'](\s*\)\s*\()/g, `$1${JSON.stringify(id)}$2`);
28
+ writeIfChanged(path, source);
29
+ const segments = id.split(".");
30
+ const route = "/" + segments.map((segment) => segment.startsWith("$") ? `:${segment.slice(1)}` : segment).join("/");
31
+ const params = segments.filter((segment) => segment.startsWith("$")).map((segment) => `${JSON.stringify(segment.slice(1))}: string`).join("; ");
32
+ declarations.push(`${JSON.stringify(id)}: { route: ${JSON.stringify(route)}; params: { ${params} } };`);
33
+ }
34
+ writeIfChanged(resolve(appRoot, "src/camox/layouts.gen.d.ts"), `// Generated by Camox.\nimport "camox/createLayout";\ndeclare module "camox/createLayout" {\n interface LayoutFileRoutes {\n ${declarations.join("\n ")}\n }\n}\n`);
35
+ }
36
+
37
+ //#endregion
38
+ export { generateLayoutFiles };
@@ -79,7 +79,7 @@ const blocks = [...Object.values(rootBlockModules), ...Object.values(legacyBlock
79
79
  .map((mod) => mod.block)
80
80
  .filter(Boolean);
81
81
  const layouts = [...Object.values(rootLayoutModules), ...Object.values(legacyLayoutModules)]
82
- .map((mod) => mod.layout)
82
+ .map((mod) => mod.Layout ?? mod.layout)
83
83
  .filter(Boolean);
84
84
 
85
85
  export const camoxApp = createApp({ blocks, layouts });
@@ -146,6 +146,7 @@ function generateVirtualCamoxServer(runtimeBasePath) {
146
146
  import { camoxApp } from "virtual:camox/app";
147
147
  import { camoxDocument } from "virtual:camox/document";
148
148
  import appStylesheetUrl from "virtual:camox/app-stylesheet-url";
149
+ import studioStylesheetUrl from "virtual:camox-studio-css";
149
150
  import pageClientEntryUrl from "virtual:camox/page-client-url";
150
151
  import studioClientEntryUrl from "virtual:camox/studio-client-url";
151
152
  import { renderPage } from "virtual:camox/page-server";
@@ -165,6 +166,7 @@ export async function handleCamoxRequest(request) {
165
166
  renderStudio,
166
167
  runtimeBasePath: ${JSON.stringify(runtimeBasePath)},
167
168
  stylesheetUrl: appStylesheetUrl,
169
+ studioStylesheetUrl,
168
170
  });
169
171
  }
170
172
  `;