camox 0.35.0 → 0.36.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/features/preview/CamoxPreview.js +127 -95
- package/dist/features/preview/components/AssetLightbox.js +1 -1
- package/dist/features/preview/components/PageEditorSidebar.js +35 -25
- package/dist/features/preview/components/PageInfoSidebar.js +1 -1
- package/dist/features/preview/components/PageNavigatorSidebar.js +241 -313
- package/dist/features/preview/components/PreviewToolbar.js +85 -55
- package/dist/features/preview/previewStore.js +13 -6
- package/dist/features/routes/pageRuntime.js +240 -2
- package/dist/features/runtime/runtime.d.ts +30 -1
- package/dist/features/runtime/runtime.js +343 -4
- package/dist/features/runtime/studioNavigation.js +7 -2
- package/dist/features/studio/components/Navbar.js +5 -5
- package/dist/features/studio/components/ProjectMenu.js +5 -5
- package/dist/features/vite/vite.d.ts +1 -1
- package/dist/features/vite/vite.js +2 -2
- package/dist/lib/telemetry-client.js +2 -2
- package/dist/lib/telemetry.js +24 -1
- package/dist/studio.css +1 -1
- package/package.json +4 -4
|
@@ -1,8 +1,142 @@
|
|
|
1
|
-
import "../routes/pageRuntime.js";
|
|
2
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
3
|
-
import "@unhead/react/server";
|
|
1
|
+
import { buildCamoxPageHead, createMarkdownResponse, createServerApiClient, isNotFoundError, loadCamoxPageForRequest } from "../routes/pageRuntime.js";
|
|
2
|
+
import { QueryClient, dehydrate } from "@tanstack/react-query";
|
|
3
|
+
import { createHead, renderSSRHead } from "@unhead/react/server";
|
|
4
4
|
|
|
5
5
|
//#region src/features/runtime/runtime.ts
|
|
6
|
+
const DEFAULT_RUNTIME_BASE_PATH = "";
|
|
7
|
+
const RUNTIME_HEALTH_PATH = "/_camox/health";
|
|
8
|
+
const RUNTIME_REGISTRY_PATH = "/_camox/registry";
|
|
9
|
+
const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
|
|
10
|
+
function buildClearServerAuthCookieHeader() {
|
|
11
|
+
return `${SERVER_AUTH_COOKIE_NAME}=; Path=/; SameSite=Lax; Max-Age=0`;
|
|
12
|
+
}
|
|
13
|
+
function getServerAuthCookieHeader(headers) {
|
|
14
|
+
const authCookie = (headers.get("Cookie") ?? headers.get("cookie") ?? "").split(";").map((part) => part.trim()).find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
|
|
15
|
+
if (!authCookie) return "";
|
|
16
|
+
const value = authCookie.slice(18);
|
|
17
|
+
try {
|
|
18
|
+
return decodeURIComponent(value);
|
|
19
|
+
} catch {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function normalizeRuntimeBasePath(basePath) {
|
|
24
|
+
if (basePath === void 0) return DEFAULT_RUNTIME_BASE_PATH;
|
|
25
|
+
if (!basePath || basePath === "/") return "";
|
|
26
|
+
return basePath.startsWith("/") ? basePath.replace(/\/+$/, "") : `/${basePath.replace(/\/+$/, "")}`;
|
|
27
|
+
}
|
|
28
|
+
function getRuntimePathname(url, runtimeBasePath = DEFAULT_RUNTIME_BASE_PATH) {
|
|
29
|
+
const { pathname } = new URL(url);
|
|
30
|
+
const basePath = normalizeRuntimeBasePath(runtimeBasePath);
|
|
31
|
+
if (!basePath) return pathname || "/";
|
|
32
|
+
if (pathname === basePath) return "/";
|
|
33
|
+
if (!pathname.startsWith(`${basePath}/`)) return null;
|
|
34
|
+
return pathname.slice(basePath.length) || "/";
|
|
35
|
+
}
|
|
36
|
+
function escapeXml(value) {
|
|
37
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
38
|
+
}
|
|
39
|
+
function normalizePagePath(path) {
|
|
40
|
+
if (!path) return "/";
|
|
41
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
42
|
+
}
|
|
43
|
+
function getLayoutIdentity(data, source) {
|
|
44
|
+
const layout = data.page.layout;
|
|
45
|
+
if (!layout) return null;
|
|
46
|
+
const version = source === "live" ? String(layout.livePublishedCheckpointId ?? "none") : String(layout.contentUpdatedAt ?? layout.updatedAt);
|
|
47
|
+
return {
|
|
48
|
+
blockIds: [...layout.beforeBlockIds, ...layout.afterBlockIds],
|
|
49
|
+
id: layout.id,
|
|
50
|
+
layoutId: layout.layoutId,
|
|
51
|
+
version
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function matchRuntimeRoute(pathname) {
|
|
55
|
+
if (pathname === RUNTIME_HEALTH_PATH) return {
|
|
56
|
+
kind: "health",
|
|
57
|
+
pathname
|
|
58
|
+
};
|
|
59
|
+
if (pathname === RUNTIME_REGISTRY_PATH) return {
|
|
60
|
+
kind: "registry",
|
|
61
|
+
pathname
|
|
62
|
+
};
|
|
63
|
+
if (pathname === "/_camox/data") return {
|
|
64
|
+
kind: "data",
|
|
65
|
+
pathname
|
|
66
|
+
};
|
|
67
|
+
if (pathname === "/camox") return {
|
|
68
|
+
kind: "studio",
|
|
69
|
+
pathname
|
|
70
|
+
};
|
|
71
|
+
if (pathname === "/camox/content") return {
|
|
72
|
+
kind: "studio-content",
|
|
73
|
+
pathname
|
|
74
|
+
};
|
|
75
|
+
if (pathname.startsWith("/camox/")) return {
|
|
76
|
+
kind: "studio-nested",
|
|
77
|
+
pathname
|
|
78
|
+
};
|
|
79
|
+
if (pathname === "/og") return {
|
|
80
|
+
kind: "og",
|
|
81
|
+
pathname
|
|
82
|
+
};
|
|
83
|
+
if (pathname === "/sitemap.xml") return {
|
|
84
|
+
kind: "sitemap",
|
|
85
|
+
pathname
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
kind: "page",
|
|
89
|
+
pathname
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function withRuntimeBasePath(pathname, runtimeBasePath) {
|
|
93
|
+
const basePath = normalizeRuntimeBasePath(runtimeBasePath);
|
|
94
|
+
if (!basePath) return pathname;
|
|
95
|
+
if (pathname === "/") return `${basePath}/`;
|
|
96
|
+
return `${basePath}${pathname}`;
|
|
97
|
+
}
|
|
98
|
+
async function createSitemapResponse(request, options) {
|
|
99
|
+
if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox sitemap is not configured." }, { status: 500 });
|
|
100
|
+
const api = createServerApiClient(options.apiUrl, options.environmentName);
|
|
101
|
+
const origin = new URL(request.url).origin;
|
|
102
|
+
const entries = (await api.pages.listBySlug({ projectSlug: options.projectSlug })).map((page) => ` <url>
|
|
103
|
+
<loc>${escapeXml(`${origin}${withRuntimeBasePath(page.fullPath, options.runtimeBasePath)}`)}</loc>
|
|
104
|
+
<lastmod>${escapeXml(new Date(page.updatedAt).toISOString())}</lastmod>
|
|
105
|
+
</url>`).join("\n");
|
|
106
|
+
return new Response(`<?xml version="1.0" encoding="UTF-8"?>
|
|
107
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
108
|
+
${entries}
|
|
109
|
+
</urlset>`, { headers: { "Content-Type": "application/xml; charset=utf-8" } });
|
|
110
|
+
}
|
|
111
|
+
async function createOgResponse(request, options) {
|
|
112
|
+
const camoxApp = await options.getCamoxApp?.();
|
|
113
|
+
if (!camoxApp) return Response.json({ message: "Camox app registry is not available." }, { status: 500 });
|
|
114
|
+
const url = new URL(request.url);
|
|
115
|
+
const layoutId = url.searchParams.get("layoutId") || "";
|
|
116
|
+
const title = url.searchParams.get("title") || "";
|
|
117
|
+
const description = url.searchParams.get("description") || "";
|
|
118
|
+
const projectName = url.searchParams.get("projectName") || "";
|
|
119
|
+
const layout = camoxApp.getLayoutById(layoutId);
|
|
120
|
+
if (!layout?._internal.buildOgImage) return new Response("Not found", { status: 404 });
|
|
121
|
+
return layout._internal.buildOgImage({
|
|
122
|
+
title,
|
|
123
|
+
description,
|
|
124
|
+
projectName
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function createDefaultHeadInput(stylesheetUrl) {
|
|
128
|
+
return {
|
|
129
|
+
htmlAttrs: { lang: "en" },
|
|
130
|
+
meta: [{ charset: "utf-8" }, {
|
|
131
|
+
name: "viewport",
|
|
132
|
+
content: "width=device-width, initial-scale=1"
|
|
133
|
+
}],
|
|
134
|
+
link: stylesheetUrl ? [{
|
|
135
|
+
rel: "stylesheet",
|
|
136
|
+
href: stylesheetUrl
|
|
137
|
+
}] : []
|
|
138
|
+
};
|
|
139
|
+
}
|
|
6
140
|
function createPageHeadInput(head) {
|
|
7
141
|
const meta = head.meta ?? [];
|
|
8
142
|
const links = head.links ?? [];
|
|
@@ -39,6 +173,211 @@ function createPageHeadInput(head) {
|
|
|
39
173
|
link: pageLinks
|
|
40
174
|
};
|
|
41
175
|
}
|
|
176
|
+
function renderHead(document, pageHead, stylesheetUrl) {
|
|
177
|
+
const head = createHead();
|
|
178
|
+
head.push(createDefaultHeadInput(stylesheetUrl));
|
|
179
|
+
head.push(document);
|
|
180
|
+
head.push(pageHead);
|
|
181
|
+
return renderSSRHead(head);
|
|
182
|
+
}
|
|
183
|
+
function serializeJsonForHtml(value) {
|
|
184
|
+
return JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
185
|
+
}
|
|
186
|
+
function renderClientEntryScript(src) {
|
|
187
|
+
if (!src) return "";
|
|
188
|
+
return `<script type="module" src="${escapeXml(src)}"><\/script>`;
|
|
189
|
+
}
|
|
190
|
+
async function createPageHtmlResponse({ options, pathname, request }) {
|
|
191
|
+
if (!options.apiUrl || !options.authenticationUrl || !options.projectSlug || !options.renderPage) return createPageDataResponse({
|
|
192
|
+
options,
|
|
193
|
+
pathname,
|
|
194
|
+
request
|
|
195
|
+
});
|
|
196
|
+
const queryClient = new QueryClient();
|
|
197
|
+
try {
|
|
198
|
+
const result = await loadCamoxPageForRequest({
|
|
199
|
+
apiUrl: options.apiUrl,
|
|
200
|
+
authCookieHeader: getServerAuthCookieHeader(request.headers),
|
|
201
|
+
environmentName: options.environmentName,
|
|
202
|
+
origin: new URL(request.url).origin,
|
|
203
|
+
pathname,
|
|
204
|
+
projectSlug: options.projectSlug,
|
|
205
|
+
queryClient
|
|
206
|
+
});
|
|
207
|
+
const headers = new Headers({ "Content-Type": "text/html; charset=utf-8" });
|
|
208
|
+
if (result.shouldClearAuthCookie) headers.append("Set-Cookie", buildClearServerAuthCookieHeader());
|
|
209
|
+
const camoxApp = await options.getCamoxApp?.();
|
|
210
|
+
const document = await options.getDocument?.() ?? {};
|
|
211
|
+
const head = camoxApp ? buildCamoxPageHead(camoxApp, result.data) : {};
|
|
212
|
+
const dehydratedState = dehydrate(queryClient);
|
|
213
|
+
const pageRenderInput = {
|
|
214
|
+
apiUrl: options.apiUrl,
|
|
215
|
+
authenticationUrl: options.authenticationUrl,
|
|
216
|
+
dehydratedState,
|
|
217
|
+
environmentName: options.environmentName,
|
|
218
|
+
head,
|
|
219
|
+
href: new URL(request.url).href,
|
|
220
|
+
layoutIdentity: getLayoutIdentity(result.data, result.source),
|
|
221
|
+
loaderData: result.data,
|
|
222
|
+
pathname,
|
|
223
|
+
projectSlug: options.projectSlug,
|
|
224
|
+
runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath),
|
|
225
|
+
source: result.source
|
|
226
|
+
};
|
|
227
|
+
const appHtml = await options.renderPage(pageRenderInput);
|
|
228
|
+
const renderedHead = renderHead(document, createPageHeadInput(head), options.stylesheetUrl);
|
|
229
|
+
return new Response(`<!doctype html>
|
|
230
|
+
<html${renderedHead.htmlAttrs}>
|
|
231
|
+
<head>
|
|
232
|
+
${renderedHead.headTags}
|
|
233
|
+
<script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(pageRenderInput)}<\/script>
|
|
234
|
+
</head>
|
|
235
|
+
<body${renderedHead.bodyAttrs}>
|
|
236
|
+
${renderedHead.bodyTagsOpen}
|
|
237
|
+
<div id="root">${appHtml}</div>
|
|
238
|
+
${renderedHead.bodyTags}
|
|
239
|
+
${renderClientEntryScript(options.pageClientEntryUrl)}
|
|
240
|
+
</body>
|
|
241
|
+
</html>`, { headers });
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (isNotFoundError(error)) return new Response("No Camox page on this path", { status: 404 });
|
|
244
|
+
throw error;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function createStudioHtmlResponse({ match, options, request }) {
|
|
248
|
+
if (!options.apiUrl || !options.authenticationUrl || !options.projectSlug || !options.renderStudio) return Response.json({
|
|
249
|
+
message: "Camox Studio renderer is not configured.",
|
|
250
|
+
...match
|
|
251
|
+
}, { status: 500 });
|
|
252
|
+
const queryClient = new QueryClient();
|
|
253
|
+
const document = await options.getDocument?.() ?? {};
|
|
254
|
+
const studioRenderInput = {
|
|
255
|
+
apiUrl: options.apiUrl,
|
|
256
|
+
authenticationUrl: options.authenticationUrl,
|
|
257
|
+
dehydratedState: dehydrate(queryClient),
|
|
258
|
+
environmentName: options.environmentName,
|
|
259
|
+
href: new URL(request.url).href,
|
|
260
|
+
pathname: match.pathname,
|
|
261
|
+
projectSlug: options.projectSlug,
|
|
262
|
+
routeKind: match.kind,
|
|
263
|
+
runtimeBasePath: normalizeRuntimeBasePath(options.runtimeBasePath)
|
|
264
|
+
};
|
|
265
|
+
const appHtml = await options.renderStudio(studioRenderInput);
|
|
266
|
+
const renderedHead = renderHead(document, {}, options.stylesheetUrl);
|
|
267
|
+
return new Response(`<!doctype html>
|
|
268
|
+
<html${renderedHead.htmlAttrs}>
|
|
269
|
+
<head>
|
|
270
|
+
${renderedHead.headTags}
|
|
271
|
+
<script id="__CAMOX_DATA__" type="application/json">${serializeJsonForHtml(studioRenderInput)}<\/script>
|
|
272
|
+
</head>
|
|
273
|
+
<body${renderedHead.bodyAttrs}>
|
|
274
|
+
${renderedHead.bodyTagsOpen}
|
|
275
|
+
<div id="root">${appHtml}</div>
|
|
276
|
+
${renderedHead.bodyTags}
|
|
277
|
+
${renderClientEntryScript(options.studioClientEntryUrl)}
|
|
278
|
+
</body>
|
|
279
|
+
</html>`, { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
280
|
+
}
|
|
281
|
+
async function createPageDataResponse({ options, pathname, request }) {
|
|
282
|
+
if (!options.apiUrl || !options.projectSlug) return Response.json({ message: "Camox page loader is not configured." }, { status: 500 });
|
|
283
|
+
const queryClient = new QueryClient();
|
|
284
|
+
try {
|
|
285
|
+
const result = await loadCamoxPageForRequest({
|
|
286
|
+
apiUrl: options.apiUrl,
|
|
287
|
+
authCookieHeader: getServerAuthCookieHeader(request.headers),
|
|
288
|
+
environmentName: options.environmentName,
|
|
289
|
+
origin: new URL(request.url).origin,
|
|
290
|
+
pathname,
|
|
291
|
+
projectSlug: options.projectSlug,
|
|
292
|
+
queryClient
|
|
293
|
+
});
|
|
294
|
+
const headers = new Headers({ "Content-Type": "application/json; charset=utf-8" });
|
|
295
|
+
if (result.shouldClearAuthCookie) headers.append("Set-Cookie", buildClearServerAuthCookieHeader());
|
|
296
|
+
const camoxApp = await options.getCamoxApp?.();
|
|
297
|
+
const head = camoxApp ? buildCamoxPageHead(camoxApp, result.data) : null;
|
|
298
|
+
const dehydratedState = dehydrate(queryClient);
|
|
299
|
+
const { page, layout, projectName, project } = result.data.page;
|
|
300
|
+
return new Response(`${JSON.stringify({
|
|
301
|
+
kind: "page",
|
|
302
|
+
pathname,
|
|
303
|
+
page: {
|
|
304
|
+
id: page.id,
|
|
305
|
+
fullPath: page.fullPath,
|
|
306
|
+
metaTitle: page.metaTitle,
|
|
307
|
+
metaDescription: page.metaDescription
|
|
308
|
+
},
|
|
309
|
+
layout: layout ? {
|
|
310
|
+
id: layout.id,
|
|
311
|
+
layoutId: layout.layoutId
|
|
312
|
+
} : null,
|
|
313
|
+
layoutIdentity: getLayoutIdentity(result.data, result.source),
|
|
314
|
+
project: {
|
|
315
|
+
id: project.id,
|
|
316
|
+
name: projectName
|
|
317
|
+
},
|
|
318
|
+
loaderData: {
|
|
319
|
+
faviconUrl: result.data.faviconUrl,
|
|
320
|
+
origin: result.data.origin
|
|
321
|
+
},
|
|
322
|
+
head,
|
|
323
|
+
dehydratedState
|
|
324
|
+
}, null, 2)}\n`, { headers });
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if (isNotFoundError(error)) return Response.json({
|
|
327
|
+
kind: "page",
|
|
328
|
+
message: "No Camox page on this path",
|
|
329
|
+
pathname
|
|
330
|
+
}, { status: 404 });
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async function handleCamoxRequest(request, options = {}) {
|
|
335
|
+
const pathname = getRuntimePathname(request.url, options.runtimeBasePath);
|
|
336
|
+
if (!pathname) return null;
|
|
337
|
+
const match = matchRuntimeRoute(pathname);
|
|
338
|
+
if (match.kind === "health") return new Response("ok\n", { headers: { "Content-Type": "text/plain; charset=utf-8" } });
|
|
339
|
+
if (match.kind === "registry") {
|
|
340
|
+
const camoxApp = await options.getCamoxApp?.();
|
|
341
|
+
if (!camoxApp) return Response.json({ message: "Camox app registry is not available." }, { status: 500 });
|
|
342
|
+
return Response.json({
|
|
343
|
+
blocks: camoxApp.getBlocks().map((block) => block._internal.id),
|
|
344
|
+
layouts: camoxApp.getLayouts().map((layout) => layout._internal.id)
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (match.kind === "og") return createOgResponse(request, options);
|
|
348
|
+
if (match.kind === "sitemap") return createSitemapResponse(request, options);
|
|
349
|
+
if (match.kind === "data") return createPageDataResponse({
|
|
350
|
+
options,
|
|
351
|
+
pathname: normalizePagePath(new URL(request.url).searchParams.get("path")),
|
|
352
|
+
request
|
|
353
|
+
});
|
|
354
|
+
if (match.kind === "studio" || match.kind === "studio-content" || match.kind === "studio-nested") return createStudioHtmlResponse({
|
|
355
|
+
match,
|
|
356
|
+
options,
|
|
357
|
+
request
|
|
358
|
+
});
|
|
359
|
+
if (match.kind === "page") {
|
|
360
|
+
if (options.apiUrl && options.projectSlug) {
|
|
361
|
+
const markdownResponse = await createMarkdownResponse({
|
|
362
|
+
apiUrl: options.apiUrl,
|
|
363
|
+
environmentName: options.environmentName,
|
|
364
|
+
pathname: match.pathname,
|
|
365
|
+
projectSlug: options.projectSlug,
|
|
366
|
+
request
|
|
367
|
+
});
|
|
368
|
+
if (markdownResponse) return markdownResponse;
|
|
369
|
+
}
|
|
370
|
+
return createPageHtmlResponse({
|
|
371
|
+
options,
|
|
372
|
+
pathname: match.pathname,
|
|
373
|
+
request
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return Response.json({
|
|
377
|
+
message: "Camox runtime route matched.",
|
|
378
|
+
...match
|
|
379
|
+
}, { status: 501 });
|
|
380
|
+
}
|
|
42
381
|
|
|
43
382
|
//#endregion
|
|
44
|
-
export { createPageHeadInput };
|
|
383
|
+
export { createPageHeadInput, getRuntimePathname, handleCamoxRequest, matchRuntimeRoute };
|
|
@@ -39,9 +39,9 @@ function getStudioTarget(to, runtimeBasePath) {
|
|
|
39
39
|
}
|
|
40
40
|
function StudioNavigationProvider(t0) {
|
|
41
41
|
const $ = c(19);
|
|
42
|
-
if ($[0] !== "
|
|
42
|
+
if ($[0] !== "8131144acb7b9faccf5f5924d863b9186349bfc7c842e7feba7a3ac1e01ed910") {
|
|
43
43
|
for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
44
|
-
$[0] = "
|
|
44
|
+
$[0] = "8131144acb7b9faccf5f5924d863b9186349bfc7c842e7feba7a3ac1e01ed910";
|
|
45
45
|
}
|
|
46
46
|
const { children, href, pathname, runtimeBasePath } = t0;
|
|
47
47
|
let t1;
|
|
@@ -53,6 +53,11 @@ function StudioNavigationProvider(t0) {
|
|
|
53
53
|
window.location.assign(to);
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
|
+
const currentPathname = normalizeRuntimePathname(window.location.pathname, runtimeBasePath);
|
|
57
|
+
if (normalizeRuntimePathname(target.pathname, runtimeBasePath) !== currentPathname) {
|
|
58
|
+
window.location.assign(target.href);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
56
61
|
if (replace) window.history.replaceState(null, "", target.href);
|
|
57
62
|
else window.history.pushState(null, "", target.href);
|
|
58
63
|
window.dispatchEvent(new Event("camox:navigation"));
|
|
@@ -36,9 +36,9 @@ const links = [{
|
|
|
36
36
|
}];
|
|
37
37
|
const Navbar = () => {
|
|
38
38
|
const $ = c(19);
|
|
39
|
-
if ($[0] !== "
|
|
39
|
+
if ($[0] !== "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2") {
|
|
40
40
|
for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
41
|
-
$[0] = "
|
|
41
|
+
$[0] = "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2";
|
|
42
42
|
}
|
|
43
43
|
const projectSlug = useProjectSlug();
|
|
44
44
|
let t0;
|
|
@@ -136,7 +136,7 @@ const Navbar = () => {
|
|
|
136
136
|
let t10;
|
|
137
137
|
if ($[17] !== t6) {
|
|
138
138
|
t10 = /* @__PURE__ */ jsxs("nav", {
|
|
139
|
-
className: "relative flex items-center justify-between gap-4 border-b-2
|
|
139
|
+
className: "bg-background relative flex items-center justify-between gap-4 border-b-2 px-2 py-2",
|
|
140
140
|
children: [t6, t9]
|
|
141
141
|
});
|
|
142
142
|
$[17] = t6;
|
|
@@ -146,9 +146,9 @@ const Navbar = () => {
|
|
|
146
146
|
};
|
|
147
147
|
function useNavbarActions() {
|
|
148
148
|
const $ = c(4);
|
|
149
|
-
if ($[0] !== "
|
|
149
|
+
if ($[0] !== "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2") {
|
|
150
150
|
for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
151
|
-
$[0] = "
|
|
151
|
+
$[0] = "4fd5828728ac76dc7c63a1b38dbea3991a6750be46540e90d91c5dccb46797e2";
|
|
152
152
|
}
|
|
153
153
|
const navigate = useNavigate();
|
|
154
154
|
let t0;
|
|
@@ -13,9 +13,9 @@ import { Skeleton } from "@camox/ui/skeleton";
|
|
|
13
13
|
//#region src/features/studio/components/ProjectMenu.tsx
|
|
14
14
|
const Favicon = (t0) => {
|
|
15
15
|
const $ = c(19);
|
|
16
|
-
if ($[0] !== "
|
|
16
|
+
if ($[0] !== "d995cbe0a8e25c463e5078777395b1432b84ff5ecc4a4a3cc0e7e416badd244d") {
|
|
17
17
|
for (let $i = 0; $i < 19; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
18
|
-
$[0] = "
|
|
18
|
+
$[0] = "d995cbe0a8e25c463e5078777395b1432b84ff5ecc4a4a3cc0e7e416badd244d";
|
|
19
19
|
}
|
|
20
20
|
const { size: t1 } = t0;
|
|
21
21
|
const size = t1 === void 0 ? 16 : t1;
|
|
@@ -113,9 +113,9 @@ const Favicon = (t0) => {
|
|
|
113
113
|
};
|
|
114
114
|
const ProjectMenu = () => {
|
|
115
115
|
const $ = c(41);
|
|
116
|
-
if ($[0] !== "
|
|
116
|
+
if ($[0] !== "d995cbe0a8e25c463e5078777395b1432b84ff5ecc4a4a3cc0e7e416badd244d") {
|
|
117
117
|
for (let $i = 0; $i < 41; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
118
|
-
$[0] = "
|
|
118
|
+
$[0] = "d995cbe0a8e25c463e5078777395b1432b84ff5ecc4a4a3cc0e7e416badd244d";
|
|
119
119
|
}
|
|
120
120
|
const authCtx = React.useContext(AuthContext);
|
|
121
121
|
let t0;
|
|
@@ -282,7 +282,7 @@ const ProjectMenu = () => {
|
|
|
282
282
|
if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
|
|
283
283
|
t22 = /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
284
284
|
render: /* @__PURE__ */ jsx(Link$1, {
|
|
285
|
-
to: "https://camox.
|
|
285
|
+
to: "https://camox.dev",
|
|
286
286
|
target: "_blank"
|
|
287
287
|
}),
|
|
288
288
|
children: [/* @__PURE__ */ jsx(Info, { className: "h-4 w-4" }), "Powered by Camox"]
|
|
@@ -28,7 +28,7 @@ interface CamoxPluginOptions {
|
|
|
28
28
|
disableTelemetry?: boolean;
|
|
29
29
|
/** Internal options (intended for Camox contributors in development, not for public use) */
|
|
30
30
|
_internal?: {
|
|
31
|
-
/** URL of the Camox API backend, used for data fetching */apiUrl?: string; /** URL of the Camox authentication backend (default: https://app.camox.
|
|
31
|
+
/** URL of the Camox API backend, used for data fetching */apiUrl?: string; /** URL of the Camox authentication backend (default: https://app.camox.dev) */
|
|
32
32
|
authenticationUrl?: string; /** Show Tanstack query devtools (default: false) */
|
|
33
33
|
enableTanstackDevtools?: boolean; /** Disable automatic code generation (route files, app file, skill files) (default: false) */
|
|
34
34
|
disableCodeGen?: boolean; /** Mount the Camox-owned dev runtime at this base path. Use "/" for root mode. */
|
|
@@ -21,9 +21,9 @@ const VIRTUAL_PAGE_CLIENT = "virtual:camox/page-client";
|
|
|
21
21
|
const RESOLVED_VIRTUAL_PAGE_CLIENT_URL = "\0virtual:camox/page-client-url";
|
|
22
22
|
const VIRTUAL_STUDIO_CLIENT = "virtual:camox/studio-client";
|
|
23
23
|
const RESOLVED_VIRTUAL_STUDIO_CLIENT_URL = "\0virtual:camox/studio-client-url";
|
|
24
|
-
const PRODUCTION_API_URL = "https://api.camox.
|
|
24
|
+
const PRODUCTION_API_URL = "https://api.camox.dev";
|
|
25
25
|
/** Authentication URL to use for Camox authentication (production Camox Dashboard) */
|
|
26
|
-
const DEFAULT_AUTHENTICATION_URL = "https://app.camox.
|
|
26
|
+
const DEFAULT_AUTHENTICATION_URL = "https://app.camox.dev";
|
|
27
27
|
const authTokenSchema = z.object({
|
|
28
28
|
token: z.string(),
|
|
29
29
|
name: z.string(),
|
|
@@ -8,7 +8,7 @@ function ensureInitialized() {
|
|
|
8
8
|
if (typeof window === "undefined") return false;
|
|
9
9
|
if (initialized) return true;
|
|
10
10
|
posthog.init(POSTHOG_PUBLIC_KEY, {
|
|
11
|
-
api_host: "https://t.camox.
|
|
11
|
+
api_host: "https://t.camox.dev",
|
|
12
12
|
ui_host: "https://us.posthog.com",
|
|
13
13
|
defaults: "2026-01-30",
|
|
14
14
|
autocapture: false,
|
|
@@ -56,7 +56,7 @@ function identifyProject(options) {
|
|
|
56
56
|
slug: options.projectSlug
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
-
/** Client-side tracking for CMS actions. Events route through t.camox.
|
|
59
|
+
/** Client-side tracking for CMS actions. Events route through t.camox.dev reverse proxy. */
|
|
60
60
|
function trackClientEvent(event, properties) {
|
|
61
61
|
if (!ensureInitialized()) return;
|
|
62
62
|
posthog.capture(event, properties);
|
package/dist/lib/telemetry.js
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
1
|
import { POSTHOG_PUBLIC_KEY } from "@camox/api-contract";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
//#region src/lib/telemetry.ts
|
|
4
|
+
let client = null;
|
|
5
|
+
async function getClient() {
|
|
6
|
+
if (__CAMOX_TELEMETRY_DISABLED__) return null;
|
|
7
|
+
if (!client) {
|
|
8
|
+
const { PostHog } = await import("posthog-node");
|
|
9
|
+
client = new PostHog(POSTHOG_PUBLIC_KEY, { host: "https://us.i.posthog.com" });
|
|
10
|
+
}
|
|
11
|
+
return client;
|
|
12
|
+
}
|
|
13
|
+
/** Server-side tracking for page views (used in pageRoute.tsx) */
|
|
14
|
+
async function trackEvent(event, properties) {
|
|
15
|
+
const posthog = await getClient();
|
|
16
|
+
if (!posthog) return;
|
|
17
|
+
const distinctId = properties.projectId ?? "unknown";
|
|
18
|
+
posthog.capture({
|
|
19
|
+
distinctId,
|
|
20
|
+
event,
|
|
21
|
+
properties
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { trackEvent };
|