camox 0.23.0 → 0.24.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.
@@ -5,19 +5,20 @@ import { blockQueries, layoutQueries, pageMutations, pageQueries, projectQueries
5
5
  import { formatPathSegment } from "../../../lib/utils.js";
6
6
  import { useCamoxApp } from "../../provider/components/CamoxAppContext.js";
7
7
  import { PageLocationFieldset } from "./PageLocationFieldset.js";
8
+ import { UploadDropZone } from "../../content/components/UploadDropZone.js";
8
9
  import { DebouncedFieldEditor } from "./DebouncedFieldEditor.js";
9
10
  import { ShikiMarkdown } from "./ShikiMarkdown.js";
10
11
  import { c } from "react/compiler-runtime";
11
12
  import { Label } from "@camox/ui/label";
12
13
  import { toast } from "@camox/ui/toaster";
13
- import { useMutation, useQuery } from "@tanstack/react-query";
14
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
14
15
  import { useNavigate } from "@tanstack/react-router";
15
16
  import { useSelector } from "@xstate/store-react";
16
17
  import * as React from "react";
17
18
  import { jsx, jsxs } from "react/jsx-runtime";
18
19
  import { Button } from "@camox/ui/button";
19
20
  import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
20
- import { Globe, Info } from "lucide-react";
21
+ import { Globe, Info, Trash2, Upload } from "lucide-react";
21
22
  import { Spinner } from "@camox/ui/spinner";
22
23
  import { Alert, AlertDescription, AlertTitle } from "@camox/ui/alert";
23
24
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@camox/ui/dialog";
@@ -28,9 +29,9 @@ import { Switch } from "@camox/ui/switch";
28
29
  //#region src/features/preview/components/EditPageModal.tsx
29
30
  const EditPageModal = () => {
30
31
  const $ = c(3);
31
- if ($[0] !== "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2") {
32
+ if ($[0] !== "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c") {
32
33
  for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
33
- $[0] = "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2";
34
+ $[0] = "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c";
34
35
  }
35
36
  const editingPageId = useSelector(previewStore, _temp);
36
37
  let t0;
@@ -302,9 +303,9 @@ function truncateText(text, maxLen) {
302
303
  }
303
304
  const SearchEnginePreview = (t0) => {
304
305
  const $ = c(17);
305
- if ($[0] !== "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2") {
306
+ if ($[0] !== "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c") {
306
307
  for (let $i = 0; $i < 17; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
307
- $[0] = "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2";
308
+ $[0] = "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c";
308
309
  }
309
310
  const { page, metaTitle, metaDescription } = t0;
310
311
  const url = `${typeof window !== "undefined" ? window.location.origin : ""}${page.fullPath}`;
@@ -386,10 +387,10 @@ const SearchEnginePreview = (t0) => {
386
387
  return t10;
387
388
  };
388
389
  const SocialPreviewSection = (t0) => {
389
- const $ = c(23);
390
- if ($[0] !== "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2") {
391
- for (let $i = 0; $i < 23; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
392
- $[0] = "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2";
390
+ const $ = c(60);
391
+ if ($[0] !== "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c") {
392
+ for (let $i = 0; $i < 60; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
393
+ $[0] = "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c";
393
394
  }
394
395
  const { page, metaTitle, metaDescription, layoutId, projectName } = t0;
395
396
  const pageMetaTitle = page.metaTitle ?? page.pathSegment;
@@ -407,102 +408,265 @@ const SocialPreviewSection = (t0) => {
407
408
  $[4] = projectName;
408
409
  $[5] = t1;
409
410
  } else t1 = $[5];
410
- const ogImage = `/og?${t1}`;
411
+ const generatedOgImage = `/og?${t1}`;
412
+ const ogImage = page.customOgImageUrl ?? generatedOgImage;
411
413
  const url = `${typeof window !== "undefined" ? window.location.origin : ""}${page.fullPath}`;
414
+ const fileInputRef = React.useRef(null);
415
+ const queryClient = useQueryClient();
412
416
  let t2;
413
417
  if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
414
- t2 = /* @__PURE__ */ jsx(Label, { children: "Social preview" });
418
+ t2 = pageMutations.uploadCustomOgImage();
415
419
  $[6] = t2;
416
420
  } else t2 = $[6];
417
421
  let t3;
418
- if ($[7] !== ogImage) {
419
- t3 = ogImage ? /* @__PURE__ */ jsx("img", {
422
+ if ($[7] !== page.id || $[8] !== queryClient) {
423
+ t3 = {
424
+ ...t2,
425
+ onSuccess: () => {
426
+ queryClient.invalidateQueries({ queryKey: pageQueries.getById(page.id).queryKey });
427
+ trackClientEvent("page_custom_og_image_uploaded", { pageId: page.id });
428
+ },
429
+ onError: _temp5
430
+ };
431
+ $[7] = page.id;
432
+ $[8] = queryClient;
433
+ $[9] = t3;
434
+ } else t3 = $[9];
435
+ const uploadCustomOgImage = useMutation(t3);
436
+ let t4;
437
+ if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
438
+ t4 = pageMutations.deleteCustomOgImage();
439
+ $[10] = t4;
440
+ } else t4 = $[10];
441
+ let t5;
442
+ if ($[11] !== page.id || $[12] !== queryClient) {
443
+ t5 = {
444
+ ...t4,
445
+ onSuccess: () => {
446
+ queryClient.invalidateQueries({ queryKey: pageQueries.getById(page.id).queryKey });
447
+ trackClientEvent("page_custom_og_image_removed", { pageId: page.id });
448
+ },
449
+ onError: _temp6
450
+ };
451
+ $[11] = page.id;
452
+ $[12] = queryClient;
453
+ $[13] = t5;
454
+ } else t5 = $[13];
455
+ const deleteCustomOgImage = useMutation(t5);
456
+ const hasCustomImage = !!page.customOgImageUrl;
457
+ const isBusy = uploadCustomOgImage.isPending || deleteCustomOgImage.isPending;
458
+ let t6;
459
+ if ($[14] !== page.id || $[15] !== uploadCustomOgImage) {
460
+ t6 = (files) => {
461
+ const file = files[0];
462
+ if (file) uploadCustomOgImage.mutate({
463
+ pageId: page.id,
464
+ file
465
+ });
466
+ };
467
+ $[14] = page.id;
468
+ $[15] = uploadCustomOgImage;
469
+ $[16] = t6;
470
+ } else t6 = $[16];
471
+ const handleFiles = t6;
472
+ let t7;
473
+ if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
474
+ t7 = /* @__PURE__ */ jsx(Label, { children: "Social preview" });
475
+ $[17] = t7;
476
+ } else t7 = $[17];
477
+ let t8;
478
+ if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
479
+ t8 = () => fileInputRef.current?.click();
480
+ $[18] = t8;
481
+ } else t8 = $[18];
482
+ let t9;
483
+ if ($[19] !== uploadCustomOgImage.isPending) {
484
+ t9 = uploadCustomOgImage.isPending ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(Upload, { className: "size-3.5" });
485
+ $[19] = uploadCustomOgImage.isPending;
486
+ $[20] = t9;
487
+ } else t9 = $[20];
488
+ const t10 = hasCustomImage ? "Replace custom image" : "Upload custom image";
489
+ let t11;
490
+ if ($[21] !== isBusy || $[22] !== t10 || $[23] !== t9) {
491
+ t11 = /* @__PURE__ */ jsxs(Button, {
492
+ type: "button",
493
+ variant: "secondary",
494
+ size: "sm",
495
+ disabled: isBusy,
496
+ onClick: t8,
497
+ children: [t9, t10]
498
+ });
499
+ $[21] = isBusy;
500
+ $[22] = t10;
501
+ $[23] = t9;
502
+ $[24] = t11;
503
+ } else t11 = $[24];
504
+ let t12;
505
+ if ($[25] !== deleteCustomOgImage || $[26] !== hasCustomImage || $[27] !== isBusy || $[28] !== page.id) {
506
+ t12 = hasCustomImage && /* @__PURE__ */ jsxs(Button, {
507
+ type: "button",
508
+ variant: "ghost",
509
+ disabled: isBusy,
510
+ onClick: () => deleteCustomOgImage.mutate({ pageId: page.id }),
511
+ "aria-label": "Remove custom image",
512
+ children: [
513
+ deleteCustomOgImage.isPending ? /* @__PURE__ */ jsx(Spinner, { className: "text-muted-foreground" }) : /* @__PURE__ */ jsx(Trash2, { className: "text-muted-foreground" }),
514
+ " ",
515
+ "Clear"
516
+ ]
517
+ });
518
+ $[25] = deleteCustomOgImage;
519
+ $[26] = hasCustomImage;
520
+ $[27] = isBusy;
521
+ $[28] = page.id;
522
+ $[29] = t12;
523
+ } else t12 = $[29];
524
+ let t13;
525
+ if ($[30] !== handleFiles) {
526
+ t13 = /* @__PURE__ */ jsx("input", {
527
+ type: "file",
528
+ ref: fileInputRef,
529
+ className: "hidden",
530
+ accept: "image/jpeg,image/png,image/gif,image/webp",
531
+ onChange: (e) => {
532
+ if (e.target.files) handleFiles(e.target.files);
533
+ e.target.value = "";
534
+ }
535
+ });
536
+ $[30] = handleFiles;
537
+ $[31] = t13;
538
+ } else t13 = $[31];
539
+ let t14;
540
+ if ($[32] !== t11 || $[33] !== t12 || $[34] !== t13) {
541
+ t14 = /* @__PURE__ */ jsxs("div", {
542
+ className: "flex items-center gap-2",
543
+ children: [
544
+ t11,
545
+ t12,
546
+ t13
547
+ ]
548
+ });
549
+ $[32] = t11;
550
+ $[33] = t12;
551
+ $[34] = t13;
552
+ $[35] = t14;
553
+ } else t14 = $[35];
554
+ const t15 = hasCustomImage ? "Drop image to replace" : "Drop image to upload";
555
+ let t16;
556
+ if ($[36] === Symbol.for("react.memo_cache_sentinel")) {
557
+ t16 = { aspectRatio: "1200 / 630" };
558
+ $[36] = t16;
559
+ } else t16 = $[36];
560
+ let t17;
561
+ if ($[37] !== ogImage) {
562
+ t17 = /* @__PURE__ */ jsx("img", {
420
563
  src: ogImage,
421
564
  alt: "",
422
565
  className: "w-full object-cover",
423
- style: { aspectRatio: "1200 / 630" }
424
- }) : /* @__PURE__ */ jsx("div", {
425
- className: "bg-muted w-full",
426
- style: { aspectRatio: "1200 / 630" }
566
+ style: t16
427
567
  });
428
- $[7] = ogImage;
429
- $[8] = t3;
430
- } else t3 = $[8];
431
- const t4 = metaTitle || "Untitled";
432
- let t5;
433
- if ($[9] !== t4) {
434
- t5 = /* @__PURE__ */ jsx("p", {
568
+ $[37] = ogImage;
569
+ $[38] = t17;
570
+ } else t17 = $[38];
571
+ let t18;
572
+ if ($[39] !== handleFiles || $[40] !== t15 || $[41] !== t17) {
573
+ t18 = /* @__PURE__ */ jsx(UploadDropZone, {
574
+ onDrop: handleFiles,
575
+ label: t15,
576
+ className: "block",
577
+ children: t17
578
+ });
579
+ $[39] = handleFiles;
580
+ $[40] = t15;
581
+ $[41] = t17;
582
+ $[42] = t18;
583
+ } else t18 = $[42];
584
+ const t19 = metaTitle || "Untitled";
585
+ let t20;
586
+ if ($[43] !== t19) {
587
+ t20 = /* @__PURE__ */ jsx("p", {
435
588
  className: "text-foreground truncate text-sm font-semibold",
436
- children: t4
589
+ children: t19
437
590
  });
438
- $[9] = t4;
439
- $[10] = t5;
440
- } else t5 = $[10];
441
- const t6 = metaDescription || "No description";
442
- let t7;
443
- if ($[11] !== t6) {
444
- t7 = /* @__PURE__ */ jsx("p", {
591
+ $[43] = t19;
592
+ $[44] = t20;
593
+ } else t20 = $[44];
594
+ const t21 = metaDescription || "No description";
595
+ let t22;
596
+ if ($[45] !== t21) {
597
+ t22 = /* @__PURE__ */ jsx("p", {
445
598
  className: "text-muted-foreground line-clamp-2 text-xs",
446
- children: t6
599
+ children: t21
447
600
  });
448
- $[11] = t6;
449
- $[12] = t7;
450
- } else t7 = $[12];
451
- let t8;
452
- if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
453
- t8 = /* @__PURE__ */ jsx(Globe, { className: "size-3 shrink-0" });
454
- $[13] = t8;
455
- } else t8 = $[13];
456
- let t9;
457
- if ($[14] !== url) {
458
- t9 = /* @__PURE__ */ jsx("div", {
601
+ $[45] = t21;
602
+ $[46] = t22;
603
+ } else t22 = $[46];
604
+ let t23;
605
+ if ($[47] === Symbol.for("react.memo_cache_sentinel")) {
606
+ t23 = /* @__PURE__ */ jsx(Globe, { className: "size-3 shrink-0" });
607
+ $[47] = t23;
608
+ } else t23 = $[47];
609
+ let t24;
610
+ if ($[48] !== url) {
611
+ t24 = /* @__PURE__ */ jsx("div", {
459
612
  className: "pt-1.5",
460
613
  children: /* @__PURE__ */ jsxs("p", {
461
614
  className: "text-muted-foreground flex items-center gap-1 text-xs",
462
- children: [t8, /* @__PURE__ */ jsx("span", {
615
+ children: [t23, /* @__PURE__ */ jsx("span", {
463
616
  className: "truncate",
464
617
  children: url
465
618
  })]
466
619
  })
467
620
  });
468
- $[14] = url;
469
- $[15] = t9;
470
- } else t9 = $[15];
471
- let t10;
472
- if ($[16] !== t5 || $[17] !== t7 || $[18] !== t9) {
473
- t10 = /* @__PURE__ */ jsxs("div", {
621
+ $[48] = url;
622
+ $[49] = t24;
623
+ } else t24 = $[49];
624
+ let t25;
625
+ if ($[50] !== t20 || $[51] !== t22 || $[52] !== t24) {
626
+ t25 = /* @__PURE__ */ jsxs("div", {
474
627
  className: "space-y-1.5 border-t px-3 py-2.5",
475
628
  children: [
476
- t5,
477
- t7,
478
- t9
629
+ t20,
630
+ t22,
631
+ t24
479
632
  ]
480
633
  });
481
- $[16] = t5;
482
- $[17] = t7;
483
- $[18] = t9;
484
- $[19] = t10;
485
- } else t10 = $[19];
486
- let t11;
487
- if ($[20] !== t10 || $[21] !== t3) {
488
- t11 = /* @__PURE__ */ jsxs("div", {
634
+ $[50] = t20;
635
+ $[51] = t22;
636
+ $[52] = t24;
637
+ $[53] = t25;
638
+ } else t25 = $[53];
639
+ let t26;
640
+ if ($[54] !== t18 || $[55] !== t25) {
641
+ t26 = /* @__PURE__ */ jsxs("div", {
642
+ className: "border-border max-w-xl overflow-hidden rounded-lg border",
643
+ children: [t18, t25]
644
+ });
645
+ $[54] = t18;
646
+ $[55] = t25;
647
+ $[56] = t26;
648
+ } else t26 = $[56];
649
+ let t27;
650
+ if ($[57] !== t14 || $[58] !== t26) {
651
+ t27 = /* @__PURE__ */ jsxs("div", {
489
652
  className: "space-y-2 pt-2",
490
- children: [t2, /* @__PURE__ */ jsxs("div", {
491
- className: "border-border max-w-xl overflow-hidden rounded-lg border",
492
- children: [t3, t10]
493
- })]
653
+ children: [
654
+ t7,
655
+ t14,
656
+ t26
657
+ ]
494
658
  });
495
- $[20] = t10;
496
- $[21] = t3;
497
- $[22] = t11;
498
- } else t11 = $[22];
499
- return t11;
659
+ $[57] = t14;
660
+ $[58] = t26;
661
+ $[59] = t27;
662
+ } else t27 = $[59];
663
+ return t27;
500
664
  };
501
665
  const PageMarkdownPreview = (t0) => {
502
666
  const $ = c(9);
503
- if ($[0] !== "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2") {
667
+ if ($[0] !== "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c") {
504
668
  for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
505
- $[0] = "1a44c3b62f2d464ddc4fb38012b10897c6c1b331adf4c50075c0f78c681dcec2";
669
+ $[0] = "4cfb98531a98e3bc48da0686012d88a786d8b22d52089109158bebc149d3d37c";
506
670
  }
507
671
  const { pageId, metaTitle, metaDescription } = t0;
508
672
  let t1;
@@ -549,6 +713,12 @@ const PageMarkdownPreview = (t0) => {
549
713
  function _temp(state) {
550
714
  return state.context.editingPageId;
551
715
  }
716
+ function _temp5(error) {
717
+ toast.error(error.message || "Could not upload image");
718
+ }
719
+ function _temp6() {
720
+ toast.error("Could not remove image");
721
+ }
552
722
 
553
723
  //#endregion
554
724
  export { EditPageModal };
@@ -21,10 +21,10 @@ import { CSS } from "@dnd-kit/utilities";
21
21
 
22
22
  //#region src/features/preview/components/MultipleAssetFieldEditor.tsx
23
23
  const SortableAssetItem = (t0) => {
24
- const $ = c(44);
25
- if ($[0] !== "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d") {
26
- for (let $i = 0; $i < 44; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
27
- $[0] = "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d";
24
+ const $ = c(45);
25
+ if ($[0] !== "3cf038d410d44a905bb67a7755e19d8146b75274d1d91e2ad5f7a0d2580bec80") {
26
+ for (let $i = 0; $i < 45; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
27
+ $[0] = "3cf038d410d44a905bb67a7755e19d8146b75274d1d91e2ad5f7a0d2580bec80";
28
28
  }
29
29
  const { asset, assetType, onRemove, onAssetOpen } = t0;
30
30
  const t1 = String(asset._fileId);
@@ -90,13 +90,14 @@ const SortableAssetItem = (t0) => {
90
90
  $[17] = t10;
91
91
  } else t10 = $[17];
92
92
  let t11;
93
- if ($[18] !== asset.alt || $[19] !== asset.filename || $[20] !== asset.mimeType || $[21] !== asset.url || $[22] !== assetType) {
93
+ if ($[18] !== asset.alt || $[19] !== asset.filename || $[20] !== asset.mimeType || $[21] !== asset.size || $[22] !== asset.url || $[23] !== assetType) {
94
94
  t11 = assetType === "Image" ? /* @__PURE__ */ jsx("div", {
95
95
  className: "border-border h-12 w-12 shrink-0 overflow-hidden rounded border",
96
96
  children: /* @__PURE__ */ jsx("img", {
97
97
  src: transformImageUrl(asset.url, {
98
98
  width: 128,
99
- mimeType: asset.mimeType
99
+ mimeType: asset.mimeType,
100
+ size: asset.size
100
101
  }),
101
102
  alt: asset.alt || asset.filename,
102
103
  className: "h-full w-full object-cover"
@@ -108,55 +109,56 @@ const SortableAssetItem = (t0) => {
108
109
  $[18] = asset.alt;
109
110
  $[19] = asset.filename;
110
111
  $[20] = asset.mimeType;
111
- $[21] = asset.url;
112
- $[22] = assetType;
113
- $[23] = t11;
114
- } else t11 = $[23];
112
+ $[21] = asset.size;
113
+ $[22] = asset.url;
114
+ $[23] = assetType;
115
+ $[24] = t11;
116
+ } else t11 = $[24];
115
117
  const t12 = asset.filename || "Untitled";
116
118
  let t13;
117
- if ($[24] !== asset.filename || $[25] !== t12) {
119
+ if ($[25] !== asset.filename || $[26] !== t12) {
118
120
  t13 = /* @__PURE__ */ jsx("p", {
119
121
  className: "flex-1 truncate text-left text-sm",
120
122
  title: asset.filename,
121
123
  children: t12
122
124
  });
123
- $[24] = asset.filename;
124
- $[25] = t12;
125
- $[26] = t13;
126
- } else t13 = $[26];
125
+ $[25] = asset.filename;
126
+ $[26] = t12;
127
+ $[27] = t13;
128
+ } else t13 = $[27];
127
129
  let t14;
128
- if ($[27] !== t10 || $[28] !== t11 || $[29] !== t13) {
130
+ if ($[28] !== t10 || $[29] !== t11 || $[30] !== t13) {
129
131
  t14 = /* @__PURE__ */ jsxs("button", {
130
132
  type: "button",
131
133
  className: "flex min-w-0 flex-1 cursor-zoom-in items-center gap-2",
132
134
  onClick: t10,
133
135
  children: [t11, t13]
134
136
  });
135
- $[27] = t10;
136
- $[28] = t11;
137
- $[29] = t13;
138
- $[30] = t14;
139
- } else t14 = $[30];
137
+ $[28] = t10;
138
+ $[29] = t11;
139
+ $[30] = t13;
140
+ $[31] = t14;
141
+ } else t14 = $[31];
140
142
  let t15;
141
- if ($[31] !== asset._fileId || $[32] !== onRemove) {
143
+ if ($[32] !== asset._fileId || $[33] !== onRemove) {
142
144
  t15 = () => onRemove(asset._fileId);
143
- $[31] = asset._fileId;
144
- $[32] = onRemove;
145
- $[33] = t15;
146
- } else t15 = $[33];
145
+ $[32] = asset._fileId;
146
+ $[33] = onRemove;
147
+ $[34] = t15;
148
+ } else t15 = $[34];
147
149
  let t16;
148
- if ($[34] !== asset._fileId || $[35] !== t15) {
150
+ if ($[35] !== asset._fileId || $[36] !== t15) {
149
151
  t16 = /* @__PURE__ */ jsx(UnlinkAssetButton, {
150
152
  fileId: asset._fileId,
151
153
  onUnlink: t15,
152
154
  className: "hidden group-focus-within:flex group-hover:flex"
153
155
  });
154
- $[34] = asset._fileId;
155
- $[35] = t15;
156
- $[36] = t16;
157
- } else t16 = $[36];
156
+ $[35] = asset._fileId;
157
+ $[36] = t15;
158
+ $[37] = t16;
159
+ } else t16 = $[37];
158
160
  let t17;
159
- if ($[37] !== setNodeRef || $[38] !== style || $[39] !== t14 || $[40] !== t16 || $[41] !== t7 || $[42] !== t9) {
161
+ if ($[38] !== setNodeRef || $[39] !== style || $[40] !== t14 || $[41] !== t16 || $[42] !== t7 || $[43] !== t9) {
160
162
  t17 = /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", {
161
163
  ref: setNodeRef,
162
164
  style,
@@ -167,21 +169,21 @@ const SortableAssetItem = (t0) => {
167
169
  t16
168
170
  ]
169
171
  }) });
170
- $[37] = setNodeRef;
171
- $[38] = style;
172
- $[39] = t14;
173
- $[40] = t16;
174
- $[41] = t7;
175
- $[42] = t9;
176
- $[43] = t17;
177
- } else t17 = $[43];
172
+ $[38] = setNodeRef;
173
+ $[39] = style;
174
+ $[40] = t14;
175
+ $[41] = t16;
176
+ $[42] = t7;
177
+ $[43] = t9;
178
+ $[44] = t17;
179
+ } else t17 = $[44];
178
180
  return t17;
179
181
  };
180
182
  const MultipleAssetFieldEditor = (t0) => {
181
183
  const $ = c(12);
182
- if ($[0] !== "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d") {
184
+ if ($[0] !== "3cf038d410d44a905bb67a7755e19d8146b75274d1d91e2ad5f7a0d2580bec80") {
183
185
  for (let $i = 0; $i < 12; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
184
- $[0] = "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d";
186
+ $[0] = "3cf038d410d44a905bb67a7755e19d8146b75274d1d91e2ad5f7a0d2580bec80";
185
187
  }
186
188
  const { fieldName, assetType, currentData, onFieldChange } = t0;
187
189
  const isImage = assetType === "Image";
@@ -33,6 +33,8 @@ declare function createPageLoader(apiUrl: string, projectSlug: string, environme
33
33
  metaTitle: string | null;
34
34
  metaDescription: string | null;
35
35
  aiSeoEnabled: boolean | null;
36
+ customOgImageBlobId: string | null;
37
+ customOgImageUrl: string | null;
36
38
  createdAt: number;
37
39
  updatedAt: number;
38
40
  };
@@ -117,12 +117,13 @@ function createPageHead(camoxApp) {
117
117
  name: "description",
118
118
  content: page.page.metaDescription
119
119
  });
120
- const ogImageUrl = `${origin}/og?${new URLSearchParams({
120
+ const ogImageParams = new URLSearchParams({
121
121
  ...page.layout && { layoutId: page.layout.layoutId },
122
122
  title: pageMetaTitle,
123
123
  ...page.page.metaDescription && { description: page.page.metaDescription },
124
124
  ...page.projectName && { projectName: page.projectName }
125
- }).toString()}`;
125
+ });
126
+ const ogImageUrl = page.page.customOgImageUrl ?? `${origin}/og?${ogImageParams.toString()}`;
126
127
  meta.push({
127
128
  property: "og:title",
128
129
  content: title
@@ -151,9 +152,9 @@ function createPageHead(camoxApp) {
151
152
  }
152
153
  const PageRouteComponent = () => {
153
154
  const $ = c(2);
154
- if ($[0] !== "4272e30e312eefa840c2b2498844aebeea94a415ce1efe0a8248fab111737756") {
155
+ if ($[0] !== "858ec55c5e5a82d2aa9c37c6b5b7f1c0a2d1cff69ec415b2b53199f5bf1faa69") {
155
156
  for (let $i = 0; $i < 2; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
156
- $[0] = "4272e30e312eefa840c2b2498844aebeea94a415ce1efe0a8248fab111737756";
157
+ $[0] = "858ec55c5e5a82d2aa9c37c6b5b7f1c0a2d1cff69ec415b2b53199f5bf1faa69";
157
158
  }
158
159
  let t0;
159
160
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
@@ -11,9 +11,9 @@ const NormalizedDataContext = React.createContext({
11
11
  });
12
12
  const NormalizedDataProvider = (t0) => {
13
13
  const $ = c(13);
14
- if ($[0] !== "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8") {
14
+ if ($[0] !== "c508cbfbcda1ea1312463b6cbebcd66332f3346f51c02c09c35687c430a19469") {
15
15
  for (let $i = 0; $i < 13; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
16
- $[0] = "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8";
16
+ $[0] = "c508cbfbcda1ea1312463b6cbebcd66332f3346f51c02c09c35687c430a19469";
17
17
  }
18
18
  const { files, repeatableItems, children } = t0;
19
19
  let t1;
@@ -74,9 +74,9 @@ function useStableArray(next) {
74
74
  }
75
75
  function usePageBlocks(pageStructure) {
76
76
  const $ = c(36);
77
- if ($[0] !== "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8") {
77
+ if ($[0] !== "c508cbfbcda1ea1312463b6cbebcd66332f3346f51c02c09c35687c430a19469") {
78
78
  for (let $i = 0; $i < 36; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
79
- $[0] = "a0f4a795dddd4ca9e7a329b1dc44ac458e5aff0b7cf376e05306b8ccf0cac2a8";
79
+ $[0] = "c508cbfbcda1ea1312463b6cbebcd66332f3346f51c02c09c35687c430a19469";
80
80
  }
81
81
  const blockIds = pageStructure.page.blockIds;
82
82
  const beforeIds = pageStructure.layout?.beforeBlockIds ?? EMPTY_IDS;
@@ -239,6 +239,7 @@ function resolveFileMarker(marker, filesMap) {
239
239
  alt: file.alt,
240
240
  filename: file.filename,
241
241
  mimeType: file.mimeType,
242
+ size: file.size,
242
243
  _fileId: marker._fileId
243
244
  };
244
245
  return {
@@ -246,6 +247,7 @@ function resolveFileMarker(marker, filesMap) {
246
247
  alt: "",
247
248
  filename: "",
248
249
  mimeType: "",
250
+ size: 0,
249
251
  _fileId: marker._fileId
250
252
  };
251
253
  }
@@ -1,4 +1,5 @@
1
- import { getOrpc } from "./api-client.js";
1
+ import { getAuthCookieHeader } from "./auth.js";
2
+ import { getApiUrl, getEnvironmentName, getOrpc } from "./api-client.js";
2
3
  import { queryKeys } from "@camox/api-contract/query-keys";
3
4
 
4
5
  //#region src/lib/queries.ts
@@ -124,6 +125,12 @@ const repeatableItemMutations = {
124
125
  updateSettings: () => getOrpc().repeatableItems.updateSettings.mutationOptions(),
125
126
  updatePosition: () => getOrpc().repeatableItems.updatePosition.mutationOptions()
126
127
  };
128
+ function ogImageHeaders() {
129
+ const headers = { "Better-Auth-Cookie": getAuthCookieHeader() };
130
+ const envName = getEnvironmentName();
131
+ if (envName) headers["x-environment-name"] = envName;
132
+ return headers;
133
+ }
127
134
  const pageMutations = {
128
135
  create: () => getOrpc().pages.create.mutationOptions(),
129
136
  delete: () => getOrpc().pages.delete.mutationOptions(),
@@ -131,7 +138,31 @@ const pageMutations = {
131
138
  setLayout: () => getOrpc().pages.setLayout.mutationOptions(),
132
139
  setAiSeo: () => getOrpc().pages.setAiSeo.mutationOptions(),
133
140
  setMetaTitle: () => getOrpc().pages.setMetaTitle.mutationOptions(),
134
- setMetaDescription: () => getOrpc().pages.setMetaDescription.mutationOptions()
141
+ setMetaDescription: () => getOrpc().pages.setMetaDescription.mutationOptions(),
142
+ uploadCustomOgImage: () => ({ mutationFn: async ({ pageId, file }) => {
143
+ const formData = new FormData();
144
+ formData.append("file", file);
145
+ const res = await fetch(`${getApiUrl()}/pages/${pageId}/og-image`, {
146
+ method: "POST",
147
+ body: formData,
148
+ headers: ogImageHeaders(),
149
+ credentials: "omit"
150
+ });
151
+ if (!res.ok) {
152
+ const body = await res.json().catch(() => null);
153
+ throw new Error(body?.error ?? `Upload failed: ${res.status}`);
154
+ }
155
+ return res.json();
156
+ } }),
157
+ deleteCustomOgImage: () => ({ mutationFn: async ({ pageId }) => {
158
+ const res = await fetch(`${getApiUrl()}/pages/${pageId}/og-image`, {
159
+ method: "DELETE",
160
+ headers: ogImageHeaders(),
161
+ credentials: "omit"
162
+ });
163
+ if (!res.ok) throw new Error(`Delete failed: ${res.status}`);
164
+ return res.json();
165
+ } })
135
166
  };
136
167
  const fileMutations = {
137
168
  delete: () => getOrpc().files.delete.mutationOptions(),