camox 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/core/components/lexical/InlineLexicalEditor.js +14 -11
  2. package/dist/core/components/lexical/SidebarLexicalEditor.js +10 -7
  3. package/dist/features/preview/CamoxPreview.d.ts +1 -0
  4. package/dist/features/preview/CamoxPreview.js +507 -147
  5. package/dist/features/preview/components/AssetLightbox.js +1 -1
  6. package/dist/features/preview/components/BlockActionsPopover.js +1 -1
  7. package/dist/features/preview/components/CreatePageModal.js +23 -8
  8. package/dist/features/preview/components/FieldToolbar.js +1 -1
  9. package/dist/features/preview/components/LinkFieldEditor.js +192 -204
  10. package/dist/features/preview/components/PageContentSheet.js +1 -1
  11. package/dist/features/preview/components/PageLocationFieldset.js +3 -4
  12. package/dist/features/preview/components/{EditPageModal.js → PageMetadataModal.js} +413 -229
  13. package/dist/features/preview/components/PageNicknameField.js +69 -0
  14. package/dist/features/preview/components/PagePicker.js +21 -84
  15. package/dist/features/preview/components/PageStatusBadge.js +97 -0
  16. package/dist/features/preview/components/PreviewPanel.js +111 -117
  17. package/dist/features/preview/components/PreviewToolbar.js +4 -4
  18. package/dist/features/preview/components/PublishDialog.js +1 -1
  19. package/dist/features/preview/components/UnlinkAssetButton.js +1 -1
  20. package/dist/features/routes/pageRoute.d.ts +3 -1
  21. package/dist/features/routes/pageRoute.js +34 -11
  22. package/dist/features/studio/components/EnvironmentMenu.js +1 -1
  23. package/dist/features/studio/components/ProjectMenu.js +1 -1
  24. package/dist/features/studio/components/UserButton.js +1 -1
  25. package/dist/lib/auth.js +43 -15
  26. package/dist/lib/queries.js +1 -0
  27. package/dist/lib/utils.js +1 -11
  28. package/dist/studio.css +1 -1
  29. package/package.json +4 -4
  30. package/skills/camox-cli/SKILL.md +79 -12
@@ -8,6 +8,7 @@ import * as _$_tanstack_react_start0 from "@tanstack/react-start";
8
8
  declare function parseQuality(part: string): number;
9
9
  declare function prefersMarkdown(accept: string): boolean;
10
10
  declare const getOrigin: _$_tanstack_react_start0.OptionalFetcher<undefined, undefined, Promise<string>>;
11
+ declare const getServerLoaderAuthCookieHeader: _$_tanstack_react_start0.OptionalFetcher<undefined, undefined, Promise<string>>;
11
12
  declare function createMarkdownMiddleware(apiUrl: string, projectSlug: string, environmentName?: string): _$_tanstack_react_start0.RequestMiddlewareAfterServer<{}, undefined, undefined>;
12
13
  declare function createPageLoader(apiUrl: string, projectSlug: string, environmentName?: string): ({
13
14
  location,
@@ -36,6 +37,7 @@ declare function createPageLoader(apiUrl: string, projectSlug: string, environme
36
37
  layoutId: number;
37
38
  livePublishedCheckpointId: number | null;
38
39
  contentUpdatedAt: number;
40
+ nickname: string;
39
41
  metaTitle: string | null;
40
42
  metaDescription: string | null;
41
43
  aiSeoEnabled: boolean | null;
@@ -77,4 +79,4 @@ declare function createPageHead(camoxApp: CamoxApp): ({
77
79
  };
78
80
  declare const PageRouteComponent: () => _$react_jsx_runtime0.JSX.Element;
79
81
  //#endregion
80
- export { PageRouteComponent, createMarkdownMiddleware, createPageHead, createPageLoader, getOrigin, parseQuality, prefersMarkdown };
82
+ export { PageRouteComponent, createMarkdownMiddleware, createPageHead, createPageLoader, getOrigin, getServerLoaderAuthCookieHeader, parseQuality, prefersMarkdown };
@@ -1,3 +1,4 @@
1
+ import { getAuthCookieHeader, getServerAuthCookieHeader } from "../../lib/auth.js";
1
2
  import { seedBlockCaches } from "../../lib/normalized-data.js";
2
3
  import { CamoxPreview, PageContent } from "../preview/CamoxPreview.js";
3
4
  import { trackEvent } from "../../lib/telemetry.js";
@@ -25,18 +26,25 @@ function prefersMarkdown(accept) {
25
26
  }
26
27
  return markdownQ > 0 && markdownQ >= htmlQ;
27
28
  }
28
- function createServerApiClient(apiUrl, environmentName) {
29
+ function createServerApiClient(apiUrl, environmentName, options) {
29
30
  const headers = {};
30
31
  if (environmentName) headers["x-environment-name"] = environmentName;
31
32
  return createORPCClient(new RPCLink({
32
33
  url: `${apiUrl}/rpc`,
33
- headers
34
+ headers,
35
+ fetch: (request, init) => {
36
+ if (options?.authCookieHeader && request instanceof Request) request.headers.set("Better-Auth-Cookie", options.authCookieHeader);
37
+ return fetch(request, init);
38
+ }
34
39
  }));
35
40
  }
36
41
  const getOrigin = createServerFn({ method: "GET" }).handler(async () => {
37
42
  const request = getRequest();
38
43
  return new URL(request.url).origin;
39
44
  });
45
+ const getServerLoaderAuthCookieHeader = createServerFn({ method: "GET" }).handler(async () => {
46
+ return getServerAuthCookieHeader(getRequest().headers);
47
+ });
40
48
  function createMarkdownMiddleware(apiUrl, projectSlug, environmentName) {
41
49
  const api = createServerApiClient(apiUrl, environmentName);
42
50
  return createMiddleware().server(async ({ next, request }) => {
@@ -65,20 +73,35 @@ function createMarkdownMiddleware(apiUrl, projectSlug, environmentName) {
65
73
  });
66
74
  }
67
75
  function createPageLoader(apiUrl, projectSlug, environmentName) {
68
- const serverApi = createServerApiClient(apiUrl, environmentName);
69
76
  return async ({ location, context }) => {
70
77
  try {
78
+ const authCookieHeader = typeof window !== "undefined" ? getAuthCookieHeader() : await getServerLoaderAuthCookieHeader();
79
+ const source = authCookieHeader ? "draft" : "live";
80
+ const serverApi = createServerApiClient(apiUrl, environmentName, { authCookieHeader });
71
81
  const [page, origin] = await Promise.all([context.queryClient.ensureQueryData({
72
- queryKey: queryKeys.pages.getByPath(location.pathname, "live"),
82
+ queryKey: queryKeys.pages.getByPath(location.pathname, source),
73
83
  queryFn: async () => {
74
84
  const [data, pagesList] = await Promise.all([serverApi.pages.getByPath({
75
85
  path: location.pathname,
76
86
  projectSlug,
77
- source: "live"
87
+ source
78
88
  }), serverApi.pages.listBySlug({ projectSlug }).catch(() => null)]);
79
- seedBlockCaches(context.queryClient, data, "live");
80
- seedBlockCaches(context.queryClient, data, "draft");
81
- context.queryClient.setQueryData(queryKeys.pages.getByPath(location.pathname, "draft"), data);
89
+ seedBlockCaches(context.queryClient, data, source);
90
+ context.queryClient.setQueryData(queryKeys.pages.getByPath(location.pathname, source), {
91
+ page: data.page,
92
+ layout: data.layout,
93
+ projectName: data.projectName,
94
+ project: data.project
95
+ });
96
+ if (source === "live") {
97
+ seedBlockCaches(context.queryClient, data, "draft");
98
+ context.queryClient.setQueryData(queryKeys.pages.getByPath(location.pathname, "draft"), {
99
+ page: data.page,
100
+ layout: data.layout,
101
+ projectName: data.projectName,
102
+ project: data.project
103
+ });
104
+ }
82
105
  if (pagesList) context.queryClient.setQueryData(queryKeys.pages.list, pagesList);
83
106
  return {
84
107
  page: data.page,
@@ -156,9 +179,9 @@ function createPageHead(camoxApp) {
156
179
  }
157
180
  const PageRouteComponent = () => {
158
181
  const $ = c(2);
159
- if ($[0] !== "70af43df8dd9dfc07ba240c4b5ef667ad871db6e9cbe22eb8e3647d7a89af86f") {
182
+ if ($[0] !== "c7f5f058711714d2b4ce2a6ee4fb3259e56b272fafb972520b82049c0f52d9df") {
160
183
  for (let $i = 0; $i < 2; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
161
- $[0] = "70af43df8dd9dfc07ba240c4b5ef667ad871db6e9cbe22eb8e3647d7a89af86f";
184
+ $[0] = "c7f5f058711714d2b4ce2a6ee4fb3259e56b272fafb972520b82049c0f52d9df";
162
185
  }
163
186
  let t0;
164
187
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
@@ -169,4 +192,4 @@ const PageRouteComponent = () => {
169
192
  };
170
193
 
171
194
  //#endregion
172
- export { PageRouteComponent, createMarkdownMiddleware, createPageHead, createPageLoader, getOrigin, parseQuality, prefersMarkdown };
195
+ export { PageRouteComponent, createMarkdownMiddleware, createPageHead, createPageLoader, getOrigin, getServerLoaderAuthCookieHeader, parseQuality, prefersMarkdown };
@@ -6,9 +6,9 @@ import { toast } from "@camox/ui/toaster";
6
6
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
7
7
  import * as React from "react";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
9
10
  import { Button } from "@camox/ui/button";
10
11
  import { ChevronDown, Download, Upload } from "lucide-react";
11
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
12
12
  import { Badge } from "@camox/ui/badge";
13
13
  import { Separator } from "@camox/ui/separator";
14
14
  import { Spinner } from "@camox/ui/spinner";
@@ -6,8 +6,8 @@ import { Link } from "@tanstack/react-router";
6
6
  import * as React from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Button } from "@camox/ui/button";
9
- import { ChevronDown, Globe, Info, Settings, Users } from "lucide-react";
10
9
  import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@camox/ui/dropdown-menu";
10
+ import { ChevronDown, Globe, Info, Settings, Users } from "lucide-react";
11
11
  import { Skeleton } from "@camox/ui/skeleton";
12
12
 
13
13
  //#region src/features/studio/components/ProjectMenu.tsx
@@ -3,8 +3,8 @@ import { useApplyTheme } from "../useTheme.js";
3
3
  import { c } from "react/compiler-runtime";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { Button } from "@camox/ui/button";
6
- import { Check, LogOut, Monitor, Moon, Settings, Sun, User } from "lucide-react";
7
6
  import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "@camox/ui/dropdown-menu";
7
+ import { Check, LogOut, Monitor, Moon, Settings, Sun, User } from "lucide-react";
8
8
  import { Avatar, AvatarFallback, AvatarImage } from "@camox/ui/avatar";
9
9
 
10
10
  //#region src/features/studio/components/UserButton.tsx
package/dist/lib/auth.js CHANGED
@@ -5,6 +5,26 @@ import { oneTimeTokenClient, organizationClient } from "better-auth/client/plugi
5
5
  import { createAuthClient } from "better-auth/react";
6
6
 
7
7
  //#region src/lib/auth.ts
8
+ const SERVER_AUTH_COOKIE_NAME = "camox_auth_cookie";
9
+ function writeServerAuthCookie(cookie) {
10
+ if (typeof document === "undefined") return;
11
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
12
+ if (!cookie) {
13
+ document.cookie = `${SERVER_AUTH_COOKIE_NAME}=; Path=/; SameSite=Lax; Max-Age=0${secure}`;
14
+ return;
15
+ }
16
+ document.cookie = `${SERVER_AUTH_COOKIE_NAME}=${encodeURIComponent(cookie)}; Path=/; SameSite=Lax; Max-Age=2592000${secure}`;
17
+ }
18
+ function getServerAuthCookieHeader(headers) {
19
+ const authCookie = (headers.get("Cookie") ?? headers.get("cookie") ?? "").split(";").map((part) => part.trim()).find((part) => part.startsWith(`${SERVER_AUTH_COOKIE_NAME}=`));
20
+ if (!authCookie) return "";
21
+ const value = authCookie.slice(18);
22
+ try {
23
+ return decodeURIComponent(value);
24
+ } catch {
25
+ return "";
26
+ }
27
+ }
8
28
  function parseSetCookieHeader(header) {
9
29
  const cookieMap = /* @__PURE__ */ new Map();
10
30
  header.split(", ").forEach((cookie) => {
@@ -59,7 +79,9 @@ function getCookie(cookie) {
59
79
  */
60
80
  function getAuthCookieHeader() {
61
81
  if (typeof window === "undefined") return "";
62
- return getCookie(localStorage.getItem("better-auth_cookie") || "{}");
82
+ const cookie = getCookie(localStorage.getItem("better-auth_cookie") || "{}");
83
+ writeServerAuthCookie(cookie);
84
+ return cookie;
63
85
  }
64
86
  function crossDomainClient(opts = {}) {
65
87
  let store = null;
@@ -101,6 +123,7 @@ function crossDomainClient(opts = {}) {
101
123
  const prevCookie = storage.getItem(cookieName);
102
124
  const toSetCookie = getSetCookie(setCookie || "", prevCookie ?? void 0);
103
125
  await storage.setItem(cookieName, toSetCookie);
126
+ writeServerAuthCookie(getCookie(toSetCookie));
104
127
  if (setCookie.includes(".session_token=")) {
105
128
  const parsed = parseSetCookieHeader(setCookie);
106
129
  let prevParsed = {};
@@ -114,7 +137,10 @@ function crossDomainClient(opts = {}) {
114
137
  if (context.request.url.toString().includes("/get-session") && !opts?.disableCache) {
115
138
  const data = context.data;
116
139
  storage.setItem(localCacheName, JSON.stringify(data));
117
- if (data === null) storage.setItem(cookieName, "{}");
140
+ if (data === null) {
141
+ storage.setItem(cookieName, "{}");
142
+ writeServerAuthCookie("");
143
+ } else writeServerAuthCookie(getCookie(storage.getItem(cookieName) || "{}"));
118
144
  }
119
145
  } },
120
146
  async init(url, options) {
@@ -124,6 +150,7 @@ function crossDomainClient(opts = {}) {
124
150
  };
125
151
  options = options || {};
126
152
  const cookie = getCookie(storage.getItem(cookieName) || "{}");
153
+ writeServerAuthCookie(cookie);
127
154
  options.credentials = "omit";
128
155
  options.headers = {
129
156
  ...options.headers,
@@ -131,6 +158,7 @@ function crossDomainClient(opts = {}) {
131
158
  };
132
159
  if (url.includes("/sign-out")) {
133
160
  await storage.setItem(cookieName, "{}");
161
+ writeServerAuthCookie("");
134
162
  store?.atoms.session?.set({
135
163
  data: null,
136
164
  error: null,
@@ -164,9 +192,9 @@ function createCamoxAuthClient(apiUrl) {
164
192
  */
165
193
  function useProcessOtt(authClient) {
166
194
  const $ = c(5);
167
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
195
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
168
196
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
169
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
197
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
170
198
  }
171
199
  const [ready, setReady] = React.useState(_temp);
172
200
  let t0;
@@ -209,9 +237,9 @@ function _temp() {
209
237
  const AuthContext = React.createContext(null);
210
238
  function useAuthContext() {
211
239
  const $ = c(1);
212
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
240
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
213
241
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
214
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
242
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
215
243
  }
216
244
  const ctx = React.useContext(AuthContext);
217
245
  if (!ctx) throw new Error("Missing CamoxProvider");
@@ -219,9 +247,9 @@ function useAuthContext() {
219
247
  }
220
248
  function useProjectSlug() {
221
249
  const $ = c(1);
222
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
250
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
223
251
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
224
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
252
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
225
253
  }
226
254
  return useAuthContext().projectSlug;
227
255
  }
@@ -235,18 +263,18 @@ function useAuthState() {
235
263
  }
236
264
  function useIsAuthenticated() {
237
265
  const $ = c(1);
238
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
266
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
239
267
  for (let $i = 0; $i < 1; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
240
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
268
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
241
269
  }
242
270
  const { isAuthenticated } = useAuthState();
243
271
  return isAuthenticated;
244
272
  }
245
273
  function useSignInRedirect() {
246
274
  const $ = c(3);
247
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
275
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
248
276
  for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
249
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
277
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
250
278
  }
251
279
  const { authenticationUrl } = useAuthContext();
252
280
  let t0;
@@ -267,9 +295,9 @@ function useSignInRedirect() {
267
295
  */
268
296
  function useAuthActions() {
269
297
  const $ = c(5);
270
- if ($[0] !== "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f") {
298
+ if ($[0] !== "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a") {
271
299
  for (let $i = 0; $i < 5; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
272
- $[0] = "e81e7f3a7847debd5cb6569770cedb214617cac7132fef9c515454bbdf62997f";
300
+ $[0] = "576010e4eb39afcae6f9e5e7fcb1b2de7558bf9039ed93e955c51d3fe8a38f1a";
273
301
  }
274
302
  const { authClient, authenticationUrl } = useAuthContext();
275
303
  let t0;
@@ -321,4 +349,4 @@ function _temp2() {
321
349
  }
322
350
 
323
351
  //#endregion
324
- export { AuthContext, createCamoxAuthClient, getAuthCookieHeader, useAuthActions, useAuthContext, useAuthState, useIsAuthenticated, useProcessOtt, useProjectSlug, useSignInRedirect };
352
+ export { AuthContext, createCamoxAuthClient, getAuthCookieHeader, getServerAuthCookieHeader, useAuthActions, useAuthContext, useAuthState, useIsAuthenticated, useProcessOtt, useProjectSlug, useSignInRedirect };
@@ -145,6 +145,7 @@ const pageMutations = {
145
145
  setMetaDescription: () => getOrpc().pages.setMetaDescription.mutationOptions(),
146
146
  publish: () => getOrpc().pages.publish.mutationOptions(),
147
147
  unpublish: () => getOrpc().pages.unpublish.mutationOptions(),
148
+ discardChanges: () => getOrpc().pages.discardChanges.mutationOptions(),
148
149
  uploadCustomOgImage: () => ({ mutationFn: async ({ pageId, file }) => {
149
150
  const formData = new FormData();
150
151
  formData.append("file", file);
package/dist/lib/utils.js CHANGED
@@ -59,16 +59,6 @@ function getActionShortcut(actions, actionId) {
59
59
  const action = actions.find((a) => a.id === actionId);
60
60
  return action?.shortcut ? formatShortcut(action.shortcut) : null;
61
61
  }
62
- /**
63
- * Converts a URL path segment into a human-readable title.
64
- * Replaces dashes and underscores with spaces and capitalizes each word.
65
- *
66
- * @example formatPathSegment("about-us") // "About Us"
67
- * @example formatPathSegment("studio_ui") // "Studio Ui"
68
- */
69
- function formatPathSegment(segment) {
70
- return segment.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
71
- }
72
62
 
73
63
  //#endregion
74
- export { INPUT_BASE_STYLES, INPUT_FOCUS_STYLES, checkIfInputFocused, cn, formatPathSegment, formatShortcut, getActionShortcut };
64
+ export { INPUT_BASE_STYLES, INPUT_FOCUS_STYLES, checkIfInputFocused, cn, formatShortcut, getActionShortcut };