camox 0.18.2 → 0.20.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.
@@ -18,9 +18,9 @@ import * as React from "react";
18
18
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
19
19
  import { Button } from "@camox/ui/button";
20
20
  import { CircleMinus, CirclePlus, CornerLeftUp } from "lucide-react";
21
+ import { Spinner } from "@camox/ui/spinner";
21
22
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@camox/ui/dropdown-menu";
22
23
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@camox/ui/select";
23
- import { Spinner } from "@camox/ui/spinner";
24
24
  import { Switch } from "@camox/ui/switch";
25
25
  import { Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@camox/ui/breadcrumb";
26
26
 
@@ -11,9 +11,9 @@ import * as React from "react";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { Button } from "@camox/ui/button";
13
13
  import { Check, ChevronsUpDown, Pencil, Plus, Trash2 } from "lucide-react";
14
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
14
15
  import { Skeleton } from "@camox/ui/skeleton";
15
16
  import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator } from "@camox/ui/command";
16
- import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
17
17
 
18
18
  //#region src/features/preview/components/PagePicker.tsx
19
19
  const CREATE_PAGE_VALUE = "__create_page__";
@@ -1,7 +1,8 @@
1
1
  import { c } from "react/compiler-runtime";
2
2
  import * as React from "react";
3
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
- import { codeToHtml } from "shiki";
4
+ import { createHighlighterCore } from "@shikijs/core";
5
+ import { createJavaScriptRegexEngine } from "@shikijs/engine-javascript";
5
6
 
6
7
  //#region src/features/preview/components/ShikiMarkdown.tsx
7
8
  const css = `
@@ -11,11 +12,20 @@ const css = `
11
12
  background-color: var(--shiki-dark-bg) !important;
12
13
  }
13
14
  `;
15
+ let highlighterPromise = null;
16
+ const getHighlighter = () => {
17
+ if (!highlighterPromise) highlighterPromise = createHighlighterCore({
18
+ themes: [import("@shikijs/themes/github-light"), import("@shikijs/themes/github-dark-high-contrast")],
19
+ langs: [import("@shikijs/langs/markdown")],
20
+ engine: createJavaScriptRegexEngine()
21
+ });
22
+ return highlighterPromise;
23
+ };
14
24
  const ShikiMarkdown = (t0) => {
15
25
  const $ = c(7);
16
- if ($[0] !== "b223838fb3a1b4481de41582ca6cb14c501b277b861d32801b02a34152f069d4") {
26
+ if ($[0] !== "51a753852d99d64ce6db6e4ded83a948d584276c60158342567a84f8fad92985") {
17
27
  for (let $i = 0; $i < 7; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
18
- $[0] = "b223838fb3a1b4481de41582ca6cb14c501b277b861d32801b02a34152f069d4";
28
+ $[0] = "51a753852d99d64ce6db6e4ded83a948d584276c60158342567a84f8fad92985";
19
29
  }
20
30
  const { code } = t0;
21
31
  const [html, setHtml] = React.useState("");
@@ -24,15 +34,16 @@ const ShikiMarkdown = (t0) => {
24
34
  if ($[1] !== code) {
25
35
  t1 = () => {
26
36
  let cancelled = false;
27
- codeToHtml(code, {
28
- lang: "markdown",
29
- themes: {
30
- light: "github-light",
31
- dark: "github-dark-high-contrast"
32
- },
33
- defaultColor: false
34
- }).then((result) => {
35
- if (!cancelled) setHtml(result);
37
+ getHighlighter().then((highlighter) => {
38
+ if (cancelled) return;
39
+ setHtml(highlighter.codeToHtml(code, {
40
+ lang: "markdown",
41
+ themes: {
42
+ light: "github-light",
43
+ dark: "github-dark-high-contrast"
44
+ },
45
+ defaultColor: false
46
+ }));
36
47
  });
37
48
  return () => {
38
49
  cancelled = true;
@@ -1,94 +1,398 @@
1
1
  import { AuthContext } from "../../../lib/auth.js";
2
+ import { environmentMutations, environmentQueries, projectQueries } from "../../../lib/queries.js";
2
3
  import { c } from "react/compiler-runtime";
3
4
  import { Popover, PopoverContent, PopoverTrigger } from "@camox/ui/popover";
5
+ import { toast } from "@camox/ui/toaster";
6
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
4
7
  import * as React from "react";
5
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
9
  import { Button } from "@camox/ui/button";
7
- import { ChevronDown } from "lucide-react";
10
+ import { ChevronDown, Download, Upload } from "lucide-react";
11
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@camox/ui/alert-dialog";
8
12
  import { Badge } from "@camox/ui/badge";
13
+ import { Separator } from "@camox/ui/separator";
14
+ import { Spinner } from "@camox/ui/spinner";
9
15
 
10
16
  //#region src/features/studio/components/EnvironmentMenu.tsx
17
+ const PRODUCTION_ENV = "production";
18
+ /**
19
+ * Renders the first incompatibility reason as a single-sentence explanation.
20
+ * Mirrors the framings used in the plan: "Cannot push because block type X
21
+ * exists in dev but not in production. Deploy your code to production…".
22
+ */
23
+ function formatReason(reason, direction) {
24
+ const isPush = direction === "push";
25
+ const sourceLabel = isPush ? "your dev environment" : "production";
26
+ const targetLabel = isPush ? "production" : "your dev environment";
27
+ const remedy = isPush ? "Deploy your code to production to register it, then try again." : "Run your dev project locally to register it, then try again.";
28
+ switch (reason.kind) {
29
+ case "block-definition-missing-in-source": return `Block type "${reason.blockId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
30
+ case "block-definition-missing-in-target": return `Block type "${reason.blockId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
31
+ case "block-definition-schema-mismatch": return `Block type "${reason.blockId}" has a different "${reason.field}" between envs. Sync block definitions and try again.`;
32
+ case "layout-missing-in-source": return `Layout "${reason.layoutId}" exists in ${targetLabel} but not in ${sourceLabel}.`;
33
+ case "layout-missing-in-target": return `Layout "${reason.layoutId}" exists in ${sourceLabel} but not in ${targetLabel}. ${remedy}`;
34
+ }
35
+ }
11
36
  const EnvironmentMenu = () => {
12
- const $ = c(14);
13
- if ($[0] !== "b4c87e8e94e837c7c310a7417eb826babd26658f7e32a825af55ee5b28e4b94c") {
14
- for (let $i = 0; $i < 14; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
15
- $[0] = "b4c87e8e94e837c7c310a7417eb826babd26658f7e32a825af55ee5b28e4b94c";
37
+ const $ = c(80);
38
+ if ($[0] !== "1f0c4a40f8c8f7e8cc223d91ecc208bcc158a215f699a477b4952e9fe211174f") {
39
+ for (let $i = 0; $i < 80; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
40
+ $[0] = "1f0c4a40f8c8f7e8cc223d91ecc208bcc158a215f699a477b4952e9fe211174f";
16
41
  }
17
42
  const [open, setOpen] = React.useState(false);
43
+ const [pendingDirection, setPendingDirection] = React.useState(null);
18
44
  const authCtx = React.useContext(AuthContext);
45
+ const queryClient = useQueryClient();
46
+ let t0;
47
+ if ($[1] !== authCtx?.projectSlug) {
48
+ t0 = projectQueries.getBySlug(authCtx?.projectSlug ?? "");
49
+ $[1] = authCtx?.projectSlug;
50
+ $[2] = t0;
51
+ } else t0 = $[2];
52
+ const t1 = !!authCtx?.projectSlug;
53
+ let t2;
54
+ if ($[3] !== t0 || $[4] !== t1) {
55
+ t2 = {
56
+ ...t0,
57
+ enabled: t1
58
+ };
59
+ $[3] = t0;
60
+ $[4] = t1;
61
+ $[5] = t2;
62
+ } else t2 = $[5];
63
+ const projectId = useQuery(t2).data?.id;
64
+ const environmentName = authCtx?.environmentName;
65
+ const isProduction = environmentName === PRODUCTION_ENV;
66
+ const canReplicate = !!projectId && !!environmentName && !isProduction;
67
+ let t3;
68
+ if ($[6] !== environmentName || $[7] !== projectId) {
69
+ t3 = environmentQueries.checkCompatibility(projectId ?? 0, environmentName ?? "", PRODUCTION_ENV);
70
+ $[6] = environmentName;
71
+ $[7] = projectId;
72
+ $[8] = t3;
73
+ } else t3 = $[8];
74
+ const t4 = open && canReplicate;
75
+ let t5;
76
+ if ($[9] !== t3 || $[10] !== t4) {
77
+ t5 = {
78
+ ...t3,
79
+ enabled: t4
80
+ };
81
+ $[9] = t3;
82
+ $[10] = t4;
83
+ $[11] = t5;
84
+ } else t5 = $[11];
85
+ const pushCheck = useQuery(t5);
86
+ let t6;
87
+ if ($[12] !== environmentName || $[13] !== projectId) {
88
+ t6 = environmentQueries.checkCompatibility(projectId ?? 0, PRODUCTION_ENV, environmentName ?? "");
89
+ $[12] = environmentName;
90
+ $[13] = projectId;
91
+ $[14] = t6;
92
+ } else t6 = $[14];
93
+ const t7 = open && canReplicate;
94
+ let t8;
95
+ if ($[15] !== t6 || $[16] !== t7) {
96
+ t8 = {
97
+ ...t6,
98
+ enabled: t7
99
+ };
100
+ $[15] = t6;
101
+ $[16] = t7;
102
+ $[17] = t8;
103
+ } else t8 = $[17];
104
+ const pullCheck = useQuery(t8);
105
+ let t9;
106
+ if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
107
+ t9 = environmentMutations.replicate();
108
+ $[18] = t9;
109
+ } else t9 = $[18];
110
+ let t10;
111
+ if ($[19] !== queryClient) {
112
+ t10 = (data, variables) => {
113
+ const verb = variables.targetEnvName === PRODUCTION_ENV ? "Pushed to production" : "Pulled from production";
114
+ const { pages, blocks, files } = data.copied;
115
+ toast.success(`${verb} — ${pages} pages, ${blocks} blocks, ${files} files`);
116
+ queryClient.invalidateQueries({ queryKey: ["camox"] });
117
+ setPendingDirection(null);
118
+ setOpen(false);
119
+ };
120
+ $[19] = queryClient;
121
+ $[20] = t10;
122
+ } else t10 = $[20];
123
+ let t11;
124
+ if ($[21] === Symbol.for("react.memo_cache_sentinel")) {
125
+ t11 = (error) => {
126
+ console.error("Failed to replicate environment:", error);
127
+ toast.error("Could not replicate environment");
128
+ setPendingDirection(null);
129
+ };
130
+ $[21] = t11;
131
+ } else t11 = $[21];
132
+ let t12;
133
+ if ($[22] !== t10) {
134
+ t12 = {
135
+ ...t9,
136
+ onSuccess: t10,
137
+ onError: t11
138
+ };
139
+ $[22] = t10;
140
+ $[23] = t12;
141
+ } else t12 = $[23];
142
+ const replicate = useMutation(t12);
19
143
  if (!authCtx?.environmentName) return null;
20
- const isProduction = authCtx.environmentName === "production";
21
144
  const label = isProduction ? "PROD" : "DEV";
22
145
  const badgeClassName = isProduction ? "bg-green-100 text-green-800 border border-green-300 hover:bg-green-100 dark:bg-green-900 dark:text-green-300 dark:border-green-700 dark:hover:bg-green-900 font-mono text-xs" : "bg-yellow-100 text-yellow-800 border border-yellow-300 hover:bg-yellow-100 dark:bg-yellow-900 dark:text-yellow-300 dark:border-yellow-700 dark:hover:bg-yellow-900 font-mono text-xs";
23
- let t0;
24
- if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
25
- t0 = /* @__PURE__ */ jsx(Button, {
146
+ let t13;
147
+ if ($[24] !== canReplicate || $[25] !== environmentName || $[26] !== pendingDirection || $[27] !== projectId || $[28] !== replicate) {
148
+ t13 = () => {
149
+ if (!pendingDirection || !canReplicate || !environmentName || !projectId) return;
150
+ const sourceEnvName = pendingDirection === "push" ? environmentName : PRODUCTION_ENV;
151
+ const targetEnvName = pendingDirection === "push" ? PRODUCTION_ENV : environmentName;
152
+ replicate.mutate({
153
+ projectId,
154
+ sourceEnvName,
155
+ targetEnvName
156
+ });
157
+ };
158
+ $[24] = canReplicate;
159
+ $[25] = environmentName;
160
+ $[26] = pendingDirection;
161
+ $[27] = projectId;
162
+ $[28] = replicate;
163
+ $[29] = t13;
164
+ } else t13 = $[29];
165
+ const handleConfirm = t13;
166
+ let t14;
167
+ if ($[30] !== pullCheck || $[31] !== pushCheck || $[32] !== replicate.isPending) {
168
+ t14 = (direction) => {
169
+ const check = direction === "push" ? pushCheck : pullCheck;
170
+ const checking = check.isLoading;
171
+ const compatible = check.data?.compatible ?? false;
172
+ const reason = check.data && !check.data.compatible ? check.data.reasons[0] : null;
173
+ const Icon = direction === "push" ? Upload : Download;
174
+ const actionLabel = direction === "push" ? "Push to production" : "Pull from production";
175
+ return /* @__PURE__ */ jsxs("div", {
176
+ className: "flex flex-col gap-1",
177
+ children: [/* @__PURE__ */ jsxs(Button, {
178
+ variant: "outline",
179
+ size: "sm",
180
+ className: "w-full justify-start gap-2",
181
+ disabled: checking || !compatible || replicate.isPending,
182
+ onClick: () => setPendingDirection(direction),
183
+ children: [checking ? /* @__PURE__ */ jsx(Spinner, { className: "text-muted-foreground" }) : /* @__PURE__ */ jsx(Icon, { className: "text-muted-foreground size-4" }), /* @__PURE__ */ jsx("span", { children: actionLabel })]
184
+ }), reason ? /* @__PURE__ */ jsx("p", {
185
+ className: "text-destructive text-xs leading-tight",
186
+ children: formatReason(reason, direction)
187
+ }) : null]
188
+ });
189
+ };
190
+ $[30] = pullCheck;
191
+ $[31] = pushCheck;
192
+ $[32] = replicate.isPending;
193
+ $[33] = t14;
194
+ } else t14 = $[33];
195
+ const renderActionButton = t14;
196
+ let t15;
197
+ if ($[34] === Symbol.for("react.memo_cache_sentinel")) {
198
+ t15 = /* @__PURE__ */ jsx(Button, {
26
199
  variant: "ghost",
27
200
  className: "gap-2"
28
201
  });
29
- $[1] = t0;
30
- } else t0 = $[1];
31
- let t1;
32
- if ($[2] !== badgeClassName || $[3] !== label) {
33
- t1 = /* @__PURE__ */ jsx(Badge, {
202
+ $[34] = t15;
203
+ } else t15 = $[34];
204
+ let t16;
205
+ if ($[35] !== badgeClassName || $[36] !== label) {
206
+ t16 = /* @__PURE__ */ jsx(Badge, {
34
207
  variant: "secondary",
35
208
  className: badgeClassName,
36
209
  children: label
37
210
  });
38
- $[2] = badgeClassName;
39
- $[3] = label;
40
- $[4] = t1;
41
- } else t1 = $[4];
42
- let t2;
43
- if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
44
- t2 = /* @__PURE__ */ jsx(ChevronDown, { className: "shrink-0 opacity-50" });
45
- $[5] = t2;
46
- } else t2 = $[5];
47
- let t3;
48
- if ($[6] !== t1) {
49
- t3 = /* @__PURE__ */ jsxs(PopoverTrigger, {
50
- render: t0,
51
- children: [t1, t2]
211
+ $[35] = badgeClassName;
212
+ $[36] = label;
213
+ $[37] = t16;
214
+ } else t16 = $[37];
215
+ let t17;
216
+ if ($[38] === Symbol.for("react.memo_cache_sentinel")) {
217
+ t17 = /* @__PURE__ */ jsx(ChevronDown, { className: "shrink-0 opacity-50" });
218
+ $[38] = t17;
219
+ } else t17 = $[38];
220
+ let t18;
221
+ if ($[39] !== t16) {
222
+ t18 = /* @__PURE__ */ jsxs(PopoverTrigger, {
223
+ render: t15,
224
+ children: [t16, t17]
52
225
  });
53
- $[6] = t1;
54
- $[7] = t3;
55
- } else t3 = $[7];
56
- let t4;
57
- if ($[8] !== isProduction) {
58
- t4 = /* @__PURE__ */ jsx(PopoverContent, {
59
- className: "w-96 p-4",
226
+ $[39] = t16;
227
+ $[40] = t18;
228
+ } else t18 = $[40];
229
+ let t19;
230
+ if ($[41] !== isProduction || $[42] !== renderActionButton) {
231
+ t19 = /* @__PURE__ */ jsx(PopoverContent, {
232
+ className: "w-96 p-0",
60
233
  align: "start",
61
234
  side: "bottom",
62
235
  children: /* @__PURE__ */ jsx("div", {
63
- className: "flex flex-col gap-3",
236
+ className: "flex flex-col gap-3 py-4",
64
237
  children: isProduction ? /* @__PURE__ */ jsx("p", {
65
- className: "text-sm",
238
+ className: "px-4 text-sm",
66
239
  children: "You are viewing the production environment."
67
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
68
- className: "text-sm",
69
- children: "This environment is your personal space to iterate on content and data structures. It doesn't affect production data."
70
- }), /* @__PURE__ */ jsx("p", {
71
- className: "text-muted-foreground text-xs",
72
- children: "You will be able to pull and push data to the production env from here."
73
- })] })
240
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
241
+ /* @__PURE__ */ jsx("p", {
242
+ className: "px-4 text-sm",
243
+ children: "This environment is your personal space to iterate on content and data structures. It doesn't affect production data."
244
+ }),
245
+ /* @__PURE__ */ jsx(Separator, {}),
246
+ /* @__PURE__ */ jsx("p", {
247
+ className: "text-muted-foreground px-4 text-xs font-medium",
248
+ children: "Transfer data across environments"
249
+ }),
250
+ /* @__PURE__ */ jsxs("div", {
251
+ className: "flex flex-col gap-2 px-4",
252
+ children: [renderActionButton("push"), renderActionButton("pull")]
253
+ })
254
+ ] })
74
255
  })
75
256
  });
76
- $[8] = isProduction;
77
- $[9] = t4;
78
- } else t4 = $[9];
79
- let t5;
80
- if ($[10] !== open || $[11] !== t3 || $[12] !== t4) {
81
- t5 = /* @__PURE__ */ jsxs(Popover, {
257
+ $[41] = isProduction;
258
+ $[42] = renderActionButton;
259
+ $[43] = t19;
260
+ } else t19 = $[43];
261
+ let t20;
262
+ if ($[44] !== open || $[45] !== t18 || $[46] !== t19) {
263
+ t20 = /* @__PURE__ */ jsxs(Popover, {
82
264
  open,
83
265
  onOpenChange: setOpen,
84
- children: [t3, t4]
266
+ children: [t18, t19]
267
+ });
268
+ $[44] = open;
269
+ $[45] = t18;
270
+ $[46] = t19;
271
+ $[47] = t20;
272
+ } else t20 = $[47];
273
+ const t21 = !!pendingDirection;
274
+ let t22;
275
+ if ($[48] !== replicate.isPending) {
276
+ t22 = (o) => {
277
+ if (!o && !replicate.isPending) setPendingDirection(null);
278
+ };
279
+ $[48] = replicate.isPending;
280
+ $[49] = t22;
281
+ } else t22 = $[49];
282
+ const t23 = pendingDirection === "push" ? "Push to production?" : "Pull from production?";
283
+ let t24;
284
+ if ($[50] !== t23) {
285
+ t24 = /* @__PURE__ */ jsx(AlertDialogTitle, { children: t23 });
286
+ $[50] = t23;
287
+ $[51] = t24;
288
+ } else t24 = $[51];
289
+ let t25;
290
+ if ($[52] !== environmentName || $[53] !== pendingDirection) {
291
+ t25 = /* @__PURE__ */ jsx(AlertDialogDescription, { children: pendingDirection === "push" ? /* @__PURE__ */ jsxs(Fragment, { children: [
292
+ "This will replace all content in",
293
+ " ",
294
+ /* @__PURE__ */ jsx("span", {
295
+ className: "font-mono font-semibold",
296
+ children: "production"
297
+ }),
298
+ " with the current",
299
+ " ",
300
+ /* @__PURE__ */ jsx("span", {
301
+ className: "font-mono font-semibold",
302
+ children: environmentName
303
+ }),
304
+ " environment. This action cannot be undone."
305
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
306
+ "This will replace all content in your",
307
+ " ",
308
+ /* @__PURE__ */ jsx("span", {
309
+ className: "font-mono font-semibold",
310
+ children: environmentName
311
+ }),
312
+ " environment with ",
313
+ /* @__PURE__ */ jsx("span", {
314
+ className: "font-mono font-semibold",
315
+ children: "production"
316
+ }),
317
+ ". This action cannot be undone."
318
+ ] }) });
319
+ $[52] = environmentName;
320
+ $[53] = pendingDirection;
321
+ $[54] = t25;
322
+ } else t25 = $[54];
323
+ let t26;
324
+ if ($[55] !== t24 || $[56] !== t25) {
325
+ t26 = /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [t24, t25] });
326
+ $[55] = t24;
327
+ $[56] = t25;
328
+ $[57] = t26;
329
+ } else t26 = $[57];
330
+ let t27;
331
+ if ($[58] !== replicate.isPending) {
332
+ t27 = /* @__PURE__ */ jsx(AlertDialogCancel, {
333
+ variant: "outline",
334
+ size: "default",
335
+ disabled: replicate.isPending,
336
+ children: "Cancel"
337
+ });
338
+ $[58] = replicate.isPending;
339
+ $[59] = t27;
340
+ } else t27 = $[59];
341
+ let t28;
342
+ if ($[60] !== replicate.isPending) {
343
+ t28 = replicate.isPending ? /* @__PURE__ */ jsx(Spinner, {}) : null;
344
+ $[60] = replicate.isPending;
345
+ $[61] = t28;
346
+ } else t28 = $[61];
347
+ const t29 = pendingDirection === "push" ? "Push" : "Pull";
348
+ let t30;
349
+ if ($[62] !== handleConfirm || $[63] !== replicate.isPending || $[64] !== t28 || $[65] !== t29) {
350
+ t30 = /* @__PURE__ */ jsxs(AlertDialogAction, {
351
+ variant: "destructive",
352
+ onClick: handleConfirm,
353
+ disabled: replicate.isPending,
354
+ children: [t28, t29]
355
+ });
356
+ $[62] = handleConfirm;
357
+ $[63] = replicate.isPending;
358
+ $[64] = t28;
359
+ $[65] = t29;
360
+ $[66] = t30;
361
+ } else t30 = $[66];
362
+ let t31;
363
+ if ($[67] !== t27 || $[68] !== t30) {
364
+ t31 = /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [t27, t30] });
365
+ $[67] = t27;
366
+ $[68] = t30;
367
+ $[69] = t31;
368
+ } else t31 = $[69];
369
+ let t32;
370
+ if ($[70] !== t26 || $[71] !== t31) {
371
+ t32 = /* @__PURE__ */ jsxs(AlertDialogContent, { children: [t26, t31] });
372
+ $[70] = t26;
373
+ $[71] = t31;
374
+ $[72] = t32;
375
+ } else t32 = $[72];
376
+ let t33;
377
+ if ($[73] !== t21 || $[74] !== t22 || $[75] !== t32) {
378
+ t33 = /* @__PURE__ */ jsx(AlertDialog, {
379
+ open: t21,
380
+ onOpenChange: t22,
381
+ children: t32
85
382
  });
86
- $[10] = open;
87
- $[11] = t3;
88
- $[12] = t4;
89
- $[13] = t5;
90
- } else t5 = $[13];
91
- return t5;
383
+ $[73] = t21;
384
+ $[74] = t22;
385
+ $[75] = t32;
386
+ $[76] = t33;
387
+ } else t33 = $[76];
388
+ let t34;
389
+ if ($[77] !== t20 || $[78] !== t33) {
390
+ t34 = /* @__PURE__ */ jsxs(Fragment, { children: [t20, t33] });
391
+ $[77] = t20;
392
+ $[78] = t33;
393
+ $[79] = t34;
394
+ } else t34 = $[79];
395
+ return t34;
92
396
  };
93
397
 
94
398
  //#endregion
@@ -102,6 +102,10 @@ function camox(options) {
102
102
  __CAMOX_API_URL__: JSON.stringify(apiUrl),
103
103
  __CAMOX_PROJECT_SLUG__: JSON.stringify(options.projectSlug)
104
104
  },
105
+ resolve: { alias: [{
106
+ find: /^tslib$/,
107
+ replacement: "tslib/tslib.es6.mjs"
108
+ }] },
105
109
  optimizeDeps: { include: [
106
110
  "react",
107
111
  "react-dom",
@@ -136,6 +140,11 @@ function camox(options) {
136
140
  "camox > @orpc/client",
137
141
  "camox > @orpc/client/fetch",
138
142
  "camox > @orpc/tanstack-query",
143
+ "camox > @shikijs/core",
144
+ "camox > @shikijs/engine-javascript",
145
+ "camox > @shikijs/langs/markdown",
146
+ "camox > @shikijs/themes/github-dark-high-contrast",
147
+ "camox > @shikijs/themes/github-light",
139
148
  "camox > @sinclair/typebox",
140
149
  "camox > @takumi-rs/image-response",
141
150
  "camox > @tanstack/react-form",
@@ -144,7 +153,6 @@ function camox(options) {
144
153
  "camox > fractional-indexing",
145
154
  "camox > lexical",
146
155
  "camox > posthog-js",
147
- "camox > shiki",
148
156
  "camox > @camox/ui > sonner",
149
157
  "camox > @camox/ui > lucide-react",
150
158
  "camox > lucide-react",
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useRef, useState } from "react";
4
+
5
+ //#region src/hooks/use-debounced-field.ts
6
+ /**
7
+ * Local state for a field that autosaves to a server value, debouncing the
8
+ * save and refusing to overwrite the user's in-progress edit when the server
9
+ * response lands.
10
+ *
11
+ * Without focus tracking, a fast typist can lose characters: their first
12
+ * keystroke triggers a debounced save → the mutation roundtrips → react-query
13
+ * pushes the (now stale) server value back through props → the input is reset
14
+ * to the just-saved value, dropping whatever the user typed in the meantime.
15
+ * While `isFocused` is true we ignore `serverValue` changes; when the user
16
+ * leaves the input we resync.
17
+ */
18
+ function useDebouncedField(serverValue, onSave, delay = 500) {
19
+ const [value, setLocal] = useState(serverValue);
20
+ const [isFocused, setIsFocused] = useState(false);
21
+ const saveRef = useRef(onSave);
22
+ saveRef.current = onSave;
23
+ const timerRef = useRef(null);
24
+ const pendingRef = useRef(null);
25
+ useEffect(() => {
26
+ if (!isFocused) setLocal(serverValue);
27
+ }, [serverValue, isFocused]);
28
+ const flush = useCallback(() => {
29
+ if (timerRef.current) {
30
+ clearTimeout(timerRef.current);
31
+ timerRef.current = null;
32
+ }
33
+ if (pendingRef.current) {
34
+ const { value: v } = pendingRef.current;
35
+ pendingRef.current = null;
36
+ saveRef.current(v);
37
+ }
38
+ }, []);
39
+ const cancel = useCallback(() => {
40
+ if (timerRef.current) clearTimeout(timerRef.current);
41
+ timerRef.current = null;
42
+ pendingRef.current = null;
43
+ }, []);
44
+ const setValue = useCallback((v_0) => {
45
+ setLocal(v_0);
46
+ pendingRef.current = { value: v_0 };
47
+ if (timerRef.current) clearTimeout(timerRef.current);
48
+ timerRef.current = window.setTimeout(() => {
49
+ timerRef.current = null;
50
+ const p = pendingRef.current;
51
+ pendingRef.current = null;
52
+ if (p) saveRef.current(p.value);
53
+ }, delay);
54
+ }, [delay]);
55
+ const onFocus = useCallback(() => setIsFocused(true), []);
56
+ const onBlur = useCallback(() => {
57
+ setIsFocused(false);
58
+ flush();
59
+ }, [flush]);
60
+ useEffect(() => () => flush(), [flush]);
61
+ return {
62
+ value,
63
+ setValue,
64
+ isFocused,
65
+ onFocus,
66
+ onBlur,
67
+ flush,
68
+ cancel
69
+ };
70
+ }
71
+
72
+ //#endregion
73
+ export { useDebouncedField };