camox 0.26.0 → 0.27.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 (29) 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/PreviewToolbar.js +1 -1
  17. package/dist/features/preview/components/PublishDialog.js +1 -1
  18. package/dist/features/preview/components/UnlinkAssetButton.js +1 -1
  19. package/dist/features/routes/pageRoute.d.ts +3 -1
  20. package/dist/features/routes/pageRoute.js +34 -11
  21. package/dist/features/studio/components/EnvironmentMenu.js +1 -1
  22. package/dist/features/studio/components/ProjectMenu.js +1 -1
  23. package/dist/features/studio/components/UserButton.js +1 -1
  24. package/dist/lib/auth.js +43 -15
  25. package/dist/lib/queries.js +1 -0
  26. package/dist/lib/utils.js +1 -11
  27. package/dist/studio.css +1 -1
  28. package/package.json +4 -4
  29. package/skills/camox-cli/SKILL.md +71 -12
@@ -0,0 +1,69 @@
1
+ import { c } from "react/compiler-runtime";
2
+ import { Input } from "@camox/ui/input";
3
+ import { Label } from "@camox/ui/label";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/features/preview/components/PageNicknameField.tsx
7
+ const PAGE_NICKNAME_MAX_LENGTH = 80;
8
+ const PageNicknameField = (t0) => {
9
+ const $ = c(11);
10
+ if ($[0] !== "162dc2b9b16d70dc0796d14680eaaca4d0c3fb2e642401f82c11c475d9d72a71") {
11
+ for (let $i = 0; $i < 11; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
12
+ $[0] = "162dc2b9b16d70dc0796d14680eaaca4d0c3fb2e642401f82c11c475d9d72a71";
13
+ }
14
+ const { value, onChange, autoFocus } = t0;
15
+ let t1;
16
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
17
+ t1 = /* @__PURE__ */ jsx(Label, {
18
+ htmlFor: "pageNickname",
19
+ children: "Nickname"
20
+ });
21
+ $[1] = t1;
22
+ } else t1 = $[1];
23
+ let t2;
24
+ if ($[2] !== onChange) {
25
+ t2 = (e) => onChange(e.target.value);
26
+ $[2] = onChange;
27
+ $[3] = t2;
28
+ } else t2 = $[3];
29
+ let t3;
30
+ if ($[4] !== autoFocus || $[5] !== t2 || $[6] !== value) {
31
+ t3 = /* @__PURE__ */ jsx(Input, {
32
+ id: "pageNickname",
33
+ value,
34
+ onChange: t2,
35
+ placeholder: "e.g. Home, Pricing, About",
36
+ maxLength: 80,
37
+ autoFocus
38
+ });
39
+ $[4] = autoFocus;
40
+ $[5] = t2;
41
+ $[6] = value;
42
+ $[7] = t3;
43
+ } else t3 = $[7];
44
+ let t4;
45
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
46
+ t4 = /* @__PURE__ */ jsx("p", {
47
+ className: "text-muted-foreground text-xs",
48
+ children: "A short internal name used in Camox Studio. It does not affect the public page or SEO."
49
+ });
50
+ $[8] = t4;
51
+ } else t4 = $[8];
52
+ let t5;
53
+ if ($[9] !== t3) {
54
+ t5 = /* @__PURE__ */ jsxs("div", {
55
+ className: "space-y-2",
56
+ children: [
57
+ t1,
58
+ t3,
59
+ t4
60
+ ]
61
+ });
62
+ $[9] = t3;
63
+ $[10] = t5;
64
+ } else t5 = $[10];
65
+ return t5;
66
+ };
67
+
68
+ //#endregion
69
+ export { PageNicknameField };
@@ -1,8 +1,8 @@
1
1
  import { previewStore } from "../previewStore.js";
2
2
  import { useProjectSlug } from "../../../lib/auth.js";
3
3
  import { pageMutations, pageQueries, projectQueries } from "../../../lib/queries.js";
4
- import { cn, formatPathSegment } from "../../../lib/utils.js";
5
- import { c } from "react/compiler-runtime";
4
+ import { cn } from "../../../lib/utils.js";
5
+ import { PageStatusBadge } from "./PageStatusBadge.js";
6
6
  import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
7
7
  import { toast } from "@camox/ui/toaster";
8
8
  import { useMutation, useQuery } from "@tanstack/react-query";
@@ -10,87 +10,13 @@ import { useLocation, useNavigate } from "@tanstack/react-router";
10
10
  import { useSelector } from "@xstate/store-react";
11
11
  import * as React from "react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
13
14
  import { Button } from "@camox/ui/button";
14
- import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
15
15
  import { Check, ChevronsUpDown, Pencil, Plus, Trash2 } from "lucide-react";
16
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
17
- import { Badge } from "@camox/ui/badge";
18
16
  import { Skeleton } from "@camox/ui/skeleton";
19
17
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "@camox/ui/command";
20
18
 
21
19
  //#region src/features/preview/components/PagePicker.tsx
22
- const StatusBadge = (t0) => {
23
- const $ = c(9);
24
- if ($[0] !== "b1d62d8bb85e72a5793231a5fd9f232264a809f20c92f8c88b773f51f15623ec") {
25
- for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
26
- $[0] = "b1d62d8bb85e72a5793231a5fd9f232264a809f20c92f8c88b773f51f15623ec";
27
- }
28
- const { page } = t0;
29
- if (page.status === "draft") {
30
- let t1;
31
- if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
32
- t1 = /* @__PURE__ */ jsx(Badge, {
33
- variant: "outline",
34
- className: "h-4 shrink-0 px-1.5",
35
- children: "Draft"
36
- });
37
- $[1] = t1;
38
- } else t1 = $[1];
39
- return t1;
40
- }
41
- if (page.status === "published") {
42
- let t1;
43
- if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
44
- t1 = /* @__PURE__ */ jsx(Badge, {
45
- variant: "secondary",
46
- className: "h-4 shrink-0 px-1.5",
47
- children: "Published"
48
- });
49
- $[2] = t1;
50
- } else t1 = $[2];
51
- return t1;
52
- }
53
- const reason = page.modifiedReason;
54
- let t1;
55
- if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
56
- t1 = /* @__PURE__ */ jsx(Badge, {
57
- variant: "default",
58
- className: "h-4 shrink-0 px-1.5",
59
- children: "Modified"
60
- });
61
- $[3] = t1;
62
- } else t1 = $[3];
63
- const badge = t1;
64
- if (reason && (reason.reason === "layout" || reason.reason === "both")) {
65
- const pluralized = reason.affectedPagesCount === 1 ? "page" : "pages";
66
- const headline = reason.reason === "layout" ? `Layout ${reason.layoutHandle} has unpublished changes` : `This page and layout ${reason.layoutHandle} both have unpublished changes`;
67
- let t2;
68
- if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
69
- t2 = /* @__PURE__ */ jsx(TooltipTrigger, {
70
- render: /* @__PURE__ */ jsx("span", { className: "shrink-0" }),
71
- children: badge
72
- });
73
- $[4] = t2;
74
- } else t2 = $[4];
75
- let t3;
76
- if ($[5] !== headline || $[6] !== pluralized || $[7] !== reason.affectedPagesCount) {
77
- t3 = /* @__PURE__ */ jsxs(Tooltip, { children: [t2, /* @__PURE__ */ jsxs(TooltipContent, { children: [
78
- headline,
79
- " (affects ",
80
- reason.affectedPagesCount,
81
- " ",
82
- pluralized,
83
- ")"
84
- ] })] });
85
- $[5] = headline;
86
- $[6] = pluralized;
87
- $[7] = reason.affectedPagesCount;
88
- $[8] = t3;
89
- } else t3 = $[8];
90
- return t3;
91
- }
92
- return badge;
93
- };
94
20
  const CREATE_PAGE_VALUE = "__create_page__";
95
21
  const PagePicker = () => {
96
22
  const [open, setOpen] = React.useState(false);
@@ -110,7 +36,7 @@ const PagePicker = () => {
110
36
  setOpen(false);
111
37
  };
112
38
  const handleDeletePage = async (page) => {
113
- const displayName = page.metaTitle ?? formatPathSegment(page.pathSegment);
39
+ const displayName = page.nickname;
114
40
  try {
115
41
  await deletePage.mutateAsync({ id: page.id });
116
42
  toast.success(`Deleted ${displayName} page`);
@@ -145,9 +71,16 @@ const PagePicker = () => {
145
71
  role: "combobox",
146
72
  className: "min-w-0 flex-1 justify-between"
147
73
  }),
148
- children: [/* @__PURE__ */ jsx("span", {
149
- className: "truncate",
150
- children: currentPage.metaTitle ?? currentPage.fullPath
74
+ children: [/* @__PURE__ */ jsxs("div", {
75
+ className: "flex min-w-0 flex-1 items-center gap-2",
76
+ children: [/* @__PURE__ */ jsx("span", {
77
+ className: "truncate",
78
+ children: currentPage.nickname
79
+ }), /* @__PURE__ */ jsx(PageStatusBadge, {
80
+ size: "sm",
81
+ status: currentPage.status,
82
+ modifiedReason: currentPage.modifiedReason
83
+ })]
151
84
  }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })]
152
85
  }), /* @__PURE__ */ jsx(PopoverContent, {
153
86
  className: "flex h-[300px] w-[400px] flex-col p-0",
@@ -187,13 +120,17 @@ const PagePicker = () => {
187
120
  className: "flex min-w-0 flex-col",
188
121
  children: [/* @__PURE__ */ jsx("p", {
189
122
  className: "truncate",
190
- children: page_1.metaTitle ?? formatPathSegment(page_1.pathSegment)
123
+ children: page_1.nickname
191
124
  }), /* @__PURE__ */ jsxs("div", {
192
125
  className: "flex min-w-0 items-center gap-1.5",
193
126
  children: [/* @__PURE__ */ jsx("p", {
194
127
  className: "text-muted-foreground truncate font-mono text-xs",
195
128
  children: page_1.fullPath
196
- }), /* @__PURE__ */ jsx(StatusBadge, { page: page_1 })]
129
+ }), /* @__PURE__ */ jsx(PageStatusBadge, {
130
+ size: "sm",
131
+ status: page_1.status,
132
+ modifiedReason: page_1.modifiedReason
133
+ })]
197
134
  })]
198
135
  })]
199
136
  }), /* @__PURE__ */ jsxs("div", {
@@ -258,7 +195,7 @@ const PagePicker = () => {
258
195
  children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: "Delete page" }), /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
259
196
  "Are you sure you want to delete",
260
197
  " ",
261
- /* @__PURE__ */ jsx("strong", { children: pageToDelete ? pageToDelete.metaTitle ?? formatPathSegment(pageToDelete.pathSegment) : "" }),
198
+ /* @__PURE__ */ jsx("strong", { children: pageToDelete ? pageToDelete.nickname : "" }),
262
199
  "? This action cannot be undone."
263
200
  ] })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(AlertDialogCancel, {
264
201
  variant: "outline",
@@ -0,0 +1,97 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { c } from "react/compiler-runtime";
3
+ import "react";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
6
+ import { Badge } from "@camox/ui/badge";
7
+
8
+ //#region src/features/preview/components/PageStatusBadge.tsx
9
+ const statusStyles = {
10
+ draft: {
11
+ className: "bg-blue-500/10 text-blue-700 dark:bg-blue-500/20 dark:text-blue-400",
12
+ label: "Draft"
13
+ },
14
+ published: {
15
+ className: "bg-primary/10 text-primary dark:bg-primary/20",
16
+ label: "Published"
17
+ },
18
+ modified: {
19
+ className: "bg-purple-500/10 text-purple-700 dark:bg-purple-500/20 dark:text-purple-400",
20
+ label: "Modified"
21
+ }
22
+ };
23
+ const PageStatusBadge = (t0) => {
24
+ const $ = c(18);
25
+ if ($[0] !== "9168a870b9514df0e89a2bb3f4bf1b341d85dcccf45743f1d6f9b68bbb30a7d4") {
26
+ for (let $i = 0; $i < 18; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
27
+ $[0] = "9168a870b9514df0e89a2bb3f4bf1b341d85dcccf45743f1d6f9b68bbb30a7d4";
28
+ }
29
+ const { status, modifiedReason, size: t1, className } = t0;
30
+ const size = t1 === void 0 ? "default" : t1;
31
+ const { className: statusClassName, label } = statusStyles[status];
32
+ let t2;
33
+ if ($[1] !== className || $[2] !== statusClassName) {
34
+ t2 = cn(statusClassName, className);
35
+ $[1] = className;
36
+ $[2] = statusClassName;
37
+ $[3] = t2;
38
+ } else t2 = $[3];
39
+ let t3;
40
+ if ($[4] !== label || $[5] !== size || $[6] !== t2) {
41
+ t3 = /* @__PURE__ */ jsx(Badge, {
42
+ size,
43
+ className: t2,
44
+ children: label
45
+ });
46
+ $[4] = label;
47
+ $[5] = size;
48
+ $[6] = t2;
49
+ $[7] = t3;
50
+ } else t3 = $[7];
51
+ const badge = t3;
52
+ if (status === "modified" && modifiedReason && (modifiedReason.reason === "layout" || modifiedReason.reason === "both")) {
53
+ const pluralized = modifiedReason.affectedPagesCount === 1 ? "page" : "pages";
54
+ const headline = modifiedReason.reason === "layout" ? `Layout ${modifiedReason.layoutHandle} has unpublished changes` : `This page and layout ${modifiedReason.layoutHandle} both have unpublished changes`;
55
+ let t4;
56
+ if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
57
+ t4 = /* @__PURE__ */ jsx("span", { className: "shrink-0" });
58
+ $[8] = t4;
59
+ } else t4 = $[8];
60
+ let t5;
61
+ if ($[9] !== badge) {
62
+ t5 = /* @__PURE__ */ jsx(TooltipTrigger, {
63
+ render: t4,
64
+ children: badge
65
+ });
66
+ $[9] = badge;
67
+ $[10] = t5;
68
+ } else t5 = $[10];
69
+ let t6;
70
+ if ($[11] !== headline || $[12] !== modifiedReason.affectedPagesCount || $[13] !== pluralized) {
71
+ t6 = /* @__PURE__ */ jsxs(TooltipContent, { children: [
72
+ headline,
73
+ " (affects ",
74
+ modifiedReason.affectedPagesCount,
75
+ " ",
76
+ pluralized,
77
+ ")"
78
+ ] });
79
+ $[11] = headline;
80
+ $[12] = modifiedReason.affectedPagesCount;
81
+ $[13] = pluralized;
82
+ $[14] = t6;
83
+ } else t6 = $[14];
84
+ let t7;
85
+ if ($[15] !== t5 || $[16] !== t6) {
86
+ t7 = /* @__PURE__ */ jsxs(Tooltip, { children: [t5, t6] });
87
+ $[15] = t5;
88
+ $[16] = t6;
89
+ $[17] = t7;
90
+ } else t7 = $[17];
91
+ return t7;
92
+ }
93
+ return badge;
94
+ };
95
+
96
+ //#endregion
97
+ export { PageStatusBadge };
@@ -6,9 +6,9 @@ import { Kbd } from "@camox/ui/kbd";
6
6
  import { useSelector } from "@xstate/store-react";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Button } from "@camox/ui/button";
9
+ import { ButtonGroup } from "@camox/ui/button-group";
9
10
  import * as Tooltip$1 from "@camox/ui/tooltip";
10
11
  import { Lock, MonitorPlay, PanelRight, TabletSmartphone } from "lucide-react";
11
- import { ButtonGroup } from "@camox/ui/button-group";
12
12
  import { FloatingToolbar } from "@camox/ui/floating-toolbar";
13
13
  import { Toggle } from "@camox/ui/toggle";
14
14
 
@@ -5,8 +5,8 @@ import { toast } from "@camox/ui/toaster";
5
5
  import { useMutation } from "@tanstack/react-query";
6
6
  import * as React from "react";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { Switch } from "@camox/ui/switch";
9
8
  import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
9
+ import { Switch } from "@camox/ui/switch";
10
10
 
11
11
  //#region src/features/preview/components/PublishDialog.tsx
12
12
  /**
@@ -4,10 +4,10 @@ import { c } from "react/compiler-runtime";
4
4
  import { useMutation, useQuery } from "@tanstack/react-query";
5
5
  import { useState } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
7
8
  import { Button } from "@camox/ui/button";
8
9
  import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
9
10
  import { X } from "lucide-react";
10
- import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
11
11
 
12
12
  //#region src/features/preview/components/UnlinkAssetButton.tsx
13
13
  const UnlinkAssetButton = (t0) => {
@@ -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);