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.
- package/dist/components/PlatformLabel.js +62 -0
- package/dist/core/components/lexical/GradientNode.js +126 -0
- package/dist/core/components/lexical/InlineLexicalEditor.js +37 -56
- package/dist/core/components/lexical/InlineStylesPlugin.js +183 -0
- package/dist/core/components/lexical/SelectionBroadcaster.js +49 -46
- package/dist/core/components/lexical/SidebarLexicalEditor.js +197 -151
- package/dist/core/components/lexical/editorConfig.js +25 -7
- package/dist/core/components/lexical/selectTextLink.js +21 -0
- package/dist/core/createApp.d.ts +30 -17
- package/dist/core/createApp.js +14 -3
- package/dist/core/createBlock.d.ts +2 -2
- package/dist/core/createBlock.js +17 -28
- package/dist/core/createLayout.d.ts +48 -4
- package/dist/core/createLayout.js +51 -23
- package/dist/core/derivedRoutes.js +36 -0
- package/dist/core/editing/BlockEditingRuntime.js +4 -4
- package/dist/core/editing/CompleteBlockEditingRuntime.js +5 -5
- package/dist/core/editing/createEditableBlock.d.ts +9 -8
- package/dist/core/editing/createEditableBlock.js +265 -403
- package/dist/core/hooks/useIsEditable.js +4 -7
- package/dist/core/hooks/useRequireDraftSource.js +7 -11
- package/dist/core/lib/lexicalReact.d.ts +1 -26
- package/dist/core/lib/lexicalReact.js +46 -60
- package/dist/core/lib/lexicalState.js +164 -81
- package/dist/core/lib/modifierFormats.js +4 -15
- package/dist/core/lib/textStyles.d.ts +27 -0
- package/dist/core/lib/textStyles.js +47 -0
- package/dist/features/navigation/navigation.d.ts +2 -0
- package/dist/features/navigation/navigation.js +51 -46
- package/dist/features/page/DerivedPageContent.js +61 -0
- package/dist/features/page/PublishedPageContent.js +52 -83
- package/dist/features/page/SharedLayoutContent.js +66 -0
- package/dist/features/preview/CamoxPreview.d.ts +16 -1
- package/dist/features/preview/CamoxPreview.js +246 -290
- package/dist/features/preview/DerivedPreview.js +134 -0
- package/dist/features/preview/EditablePageExperience.js +25 -8
- package/dist/features/preview/components/AddBlockSidebar.js +43 -33
- package/dist/features/preview/components/AssetLightbox.js +1 -1
- package/dist/features/preview/components/BlockActionsPopover.js +8 -8
- package/dist/features/preview/components/BlockThumbnail.js +192 -0
- package/dist/features/preview/components/CreatePageModal.js +123 -115
- package/dist/features/preview/components/DerivedLayoutDialog.js +100 -0
- package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +5 -0
- package/dist/features/preview/components/DerivedLayoutSidebar.js +160 -0
- package/dist/features/preview/components/DerivedPageInfoSidebar.js +57 -0
- package/dist/features/preview/components/FieldToolbar.js +42 -20
- package/dist/features/preview/components/Frame.js +133 -140
- package/dist/features/preview/components/LeftSidebar.js +42 -38
- package/dist/features/preview/components/LinkFieldEditor.js +3 -4
- package/dist/features/preview/components/MobilePreviewDrawer.js +4 -4
- package/dist/features/preview/components/Overlays.js +59 -47
- package/dist/features/preview/components/PageEditorSidebar.js +194 -203
- package/dist/features/preview/components/PageInfoSidebar.js +63 -55
- package/dist/features/preview/components/PageNavigatorSidebar.js +34 -483
- package/dist/features/preview/components/PagePicker.js +205 -153
- package/dist/features/preview/components/PageTree.js +366 -340
- package/dist/features/preview/components/PreviewActivation.js +78 -0
- package/dist/features/preview/components/PreviewPanel.js +98 -68
- package/dist/features/preview/components/PreviewToolbar.js +224 -241
- package/dist/features/preview/components/PreviewToolbarSpacer.js +43 -0
- package/dist/features/preview/components/PublicationRow.js +425 -0
- package/dist/features/preview/components/PublishDialog.js +180 -97
- package/dist/features/preview/components/RightSidebar.js +19 -34
- package/dist/features/preview/components/previewStyles.js +8 -0
- package/dist/features/preview/components/useUpdateBlockPosition.js +7 -13
- package/dist/features/preview/previewConstants.js +8 -0
- package/dist/features/preview/previewStore.js +18 -39
- package/dist/features/preview/publication.js +71 -0
- package/dist/features/provider/AuthenticatedCamoxProvider.js +2 -1
- package/dist/features/provider/CoreCamoxProvider.js +11 -2
- package/dist/features/provider/LocalhostPreviewProvider.js +6 -7
- package/dist/features/provider/useAdminShortcuts.js +5 -4
- package/dist/features/routes/pageRuntime.d.ts +10 -0
- package/dist/features/routes/pageRuntime.js +7 -9
- package/dist/features/runtime/PreviewDocumentContext.js +7 -0
- package/dist/features/runtime/RuntimeChrome.js +149 -0
- package/dist/features/runtime/SharedChromeContext.js +7 -0
- package/dist/features/runtime/navigationRequest.js +21 -0
- package/dist/features/runtime/navigationTarget.js +23 -0
- package/dist/features/runtime/pageApp.js +196 -121
- package/dist/features/runtime/pageNavigation.js +79 -130
- package/dist/features/runtime/runtime.d.ts +11 -0
- package/dist/features/runtime/runtime.js +183 -47
- package/dist/features/runtime/studioApp.d.ts +2 -11
- package/dist/features/runtime/studioApp.js +14 -177
- package/dist/features/studio/CamoxStudio.js +27 -13
- package/dist/features/studio/components/Navbar.js +80 -74
- package/dist/features/studio/components/UserButton.js +4 -4
- package/dist/features/studio/studioTheme.js +25 -0
- package/dist/features/studio/useTheme.js +94 -109
- package/dist/features/vite/appGeneration.js +12 -3
- package/dist/features/vite/definitionsSync.js +4 -2
- package/dist/features/vite/layoutGeneration.js +38 -0
- package/dist/features/vite/runtimeDev.js +3 -1
- package/dist/lib/auth.js +15 -13
- package/dist/lib/normalized-data.js +4 -4
- package/dist/lib/platform.js +13 -0
- package/dist/lib/utils.js +90 -38
- package/dist/studio-overlays.css +5 -40
- package/dist/studio.css +1 -1
- package/package.json +8 -4
- package/skills/camox/SKILL.md +2 -0
- package/skills/camox/references/block-definitions.md +31 -12
- package/skills/camox/references/layout-definitions.md +2 -0
- package/dist/core/lib/modifiers.js +0 -24
- package/dist/features/preview/components/DraftSwitchDialog.js +0 -66
- package/dist/features/preview/previewQueryFns.js +0 -23
- 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(
|
|
39
|
-
if ($[0] !== "
|
|
40
|
-
for (let $i = 0; $i <
|
|
41
|
-
$[0] = "
|
|
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
|
|
47
|
+
let t2;
|
|
45
48
|
if ($[1] !== projectSlug) {
|
|
46
|
-
|
|
49
|
+
t2 = projectQueries.getBySlug(projectSlug);
|
|
47
50
|
$[1] = projectSlug;
|
|
48
|
-
$[2] =
|
|
49
|
-
} else
|
|
50
|
-
const { data: project } = useQuery(
|
|
51
|
-
let
|
|
51
|
+
$[2] = t2;
|
|
52
|
+
} else t2 = $[2];
|
|
53
|
+
const { data: project } = useQuery(t2);
|
|
54
|
+
let t3;
|
|
52
55
|
if ($[3] !== project?.id) {
|
|
53
|
-
|
|
56
|
+
t3 = pageQueries.list(project?.id ?? 0);
|
|
54
57
|
$[3] = project?.id;
|
|
55
|
-
$[4] =
|
|
56
|
-
} else
|
|
57
|
-
const
|
|
58
|
-
let
|
|
59
|
-
if ($[5] !==
|
|
60
|
-
|
|
61
|
-
...
|
|
62
|
-
enabled:
|
|
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] =
|
|
65
|
-
$[6] =
|
|
66
|
-
$[7] =
|
|
67
|
-
} else
|
|
68
|
-
const { data: pages } = useQuery(
|
|
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
|
|
73
|
+
let t6;
|
|
72
74
|
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
|
73
|
-
|
|
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] =
|
|
78
|
-
} else
|
|
79
|
-
let
|
|
80
|
-
if ($[9] !==
|
|
81
|
-
|
|
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] =
|
|
88
|
-
$[10] =
|
|
89
|
-
$[11] =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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: [
|
|
98
|
+
children: [t6, /* @__PURE__ */ jsx("ul", {
|
|
96
99
|
className: "flex items-center gap-1",
|
|
97
|
-
children:
|
|
100
|
+
children: t7
|
|
98
101
|
})]
|
|
99
102
|
});
|
|
100
|
-
$[
|
|
101
|
-
$[
|
|
102
|
-
} else
|
|
103
|
-
let
|
|
104
|
-
let
|
|
105
|
-
if ($[
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
$[
|
|
112
|
-
$[
|
|
114
|
+
$[15] = t10;
|
|
115
|
+
$[16] = t9;
|
|
113
116
|
} else {
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
t10 = $[15];
|
|
118
|
+
t9 = $[16];
|
|
116
119
|
}
|
|
117
|
-
let
|
|
118
|
-
if ($[
|
|
119
|
-
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
t9,
|
|
129
|
+
t10,
|
|
130
|
+
/* @__PURE__ */ jsx(Kbd, {
|
|
128
131
|
className: "ml-4",
|
|
129
|
-
children:
|
|
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
|
-
$[
|
|
135
|
-
} else
|
|
136
|
-
let
|
|
137
|
-
if ($[
|
|
138
|
-
|
|
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: [
|
|
146
|
+
children: [t8, t11]
|
|
141
147
|
});
|
|
142
|
-
$[
|
|
143
|
-
$[
|
|
144
|
-
} else
|
|
145
|
-
return
|
|
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] !== "
|
|
155
|
+
if ($[0] !== "4608f306773df1a594409c84a68bc5a0c72c93c4fbda9136da94d4900e84546d") {
|
|
150
156
|
for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
151
|
-
$[0] = "
|
|
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] !== "
|
|
17
|
+
if ($[0] !== "b876c985f56cb759905567177978ca8597789d0818f006e69fd4bcfb86c58f94") {
|
|
18
18
|
for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
19
|
-
$[0] = "
|
|
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,
|
|
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
|
|
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(
|
|
18
|
-
if ($[0] !== "
|
|
19
|
-
for (let $i = 0; $i <
|
|
20
|
-
$[0] = "
|
|
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
|
|
23
|
-
const
|
|
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 ($[
|
|
27
|
-
|
|
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
|
-
$[
|
|
115
|
-
$[
|
|
116
|
-
$[
|
|
117
|
-
} else
|
|
118
|
-
return
|
|
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] !== "
|
|
107
|
+
if ($[0] !== "5860ae8e48f7dc27e488236def482df22f144de61ae2d86bfde82aeeea4f9874") {
|
|
123
108
|
for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
124
|
-
$[0] = "
|
|
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:
|
|
126
|
+
checkIfAvailable: _temp8,
|
|
142
127
|
hasChildren: true,
|
|
143
|
-
execute:
|
|
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(
|
|
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
|
|
180
|
+
function _temp0(a) {
|
|
196
181
|
return a.id;
|
|
197
182
|
}
|
|
198
|
-
function
|
|
199
|
-
function
|
|
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
|
|
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
|
|
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
|
`;
|