camox 0.22.0 → 0.23.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.
- package/dist/core/createApp.d.ts +88 -60
- package/dist/core/createBlock.d.ts +54 -232
- package/dist/core/createBlock.js +46 -38
- package/dist/core/lib/contentType.d.ts +42 -24
- package/dist/core/lib/contentType.js +55 -29
- package/dist/core/lib/fieldTypes.js +28 -16
- package/dist/core/lib/imageTransform.js +73 -0
- package/dist/features/content/CamoxContent.js +3 -3
- package/dist/features/content/components/AssetCard.js +30 -25
- package/dist/features/preview/components/AssetFieldEditor.js +9 -5
- package/dist/features/preview/components/AssetLightbox.js +194 -12
- package/dist/features/preview/components/ItemFieldsEditor.js +8 -9
- package/dist/features/preview/components/MultipleAssetFieldEditor.js +47 -42
- package/dist/features/preview/components/PageContentSheet.js +2 -3
- package/dist/features/preview/components/PageTree.js +66 -68
- package/dist/features/preview/components/useRepeatableItemActions.js +5 -5
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox-block/SKILL.md +38 -30
- package/skills/camox-cli/SKILL.md +40 -4
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { cn } from "../../../lib/utils.js";
|
|
2
|
+
import { transformImageUrl } from "../../../core/lib/imageTransform.js";
|
|
2
3
|
import { c } from "react/compiler-runtime";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
import { FileIcon } from "lucide-react";
|
|
5
6
|
|
|
6
7
|
//#region src/features/content/components/AssetCard.tsx
|
|
7
8
|
const AssetCard = (t0) => {
|
|
8
|
-
const $ = c(
|
|
9
|
-
if ($[0] !== "
|
|
10
|
-
for (let $i = 0; $i <
|
|
11
|
-
$[0] = "
|
|
9
|
+
const $ = c(27);
|
|
10
|
+
if ($[0] !== "9ecd8015db2f9718029c6f2952f71322cd0d4bdec949a22375db105de6ada5db") {
|
|
11
|
+
for (let $i = 0; $i < 27; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
12
|
+
$[0] = "9ecd8015db2f9718029c6f2952f71322cd0d4bdec949a22375db105de6ada5db";
|
|
12
13
|
}
|
|
13
14
|
const { file, selected, onSelect, onOpen } = t0;
|
|
14
15
|
let t1;
|
|
@@ -52,14 +53,17 @@ const AssetCard = (t0) => {
|
|
|
52
53
|
$[10] = t7;
|
|
53
54
|
} else t7 = $[10];
|
|
54
55
|
let t8;
|
|
55
|
-
if ($[11] !== extension || $[12] !== file.alt || $[13] !== file.filename || $[14] !== file.
|
|
56
|
+
if ($[11] !== extension || $[12] !== file.alt || $[13] !== file.filename || $[14] !== file.mimeType || $[15] !== file.url || $[16] !== isImage) {
|
|
56
57
|
t8 = /* @__PURE__ */ jsx("div", {
|
|
57
|
-
className: "
|
|
58
|
+
className: "checkered flex aspect-4/3 w-full items-center justify-center overflow-hidden rounded-sm p-1.5",
|
|
58
59
|
children: isImage ? /* @__PURE__ */ jsx("img", {
|
|
59
|
-
src: file.url,
|
|
60
|
+
src: transformImageUrl(file.url, {
|
|
61
|
+
width: 480,
|
|
62
|
+
mimeType: file.mimeType
|
|
63
|
+
}),
|
|
60
64
|
alt: file.alt || file.filename,
|
|
61
65
|
draggable: false,
|
|
62
|
-
className: "pointer-events-none h-full w-full object-
|
|
66
|
+
className: "pointer-events-none h-full w-full object-contain"
|
|
63
67
|
}) : /* @__PURE__ */ jsxs("div", {
|
|
64
68
|
className: "text-muted-foreground flex flex-col items-center gap-1",
|
|
65
69
|
children: [/* @__PURE__ */ jsx(FileIcon, { className: "h-8 w-8" }), extension && /* @__PURE__ */ jsx("span", {
|
|
@@ -71,21 +75,22 @@ const AssetCard = (t0) => {
|
|
|
71
75
|
$[11] = extension;
|
|
72
76
|
$[12] = file.alt;
|
|
73
77
|
$[13] = file.filename;
|
|
74
|
-
$[14] = file.
|
|
75
|
-
$[15] =
|
|
76
|
-
$[16] =
|
|
77
|
-
|
|
78
|
+
$[14] = file.mimeType;
|
|
79
|
+
$[15] = file.url;
|
|
80
|
+
$[16] = isImage;
|
|
81
|
+
$[17] = t8;
|
|
82
|
+
} else t8 = $[17];
|
|
78
83
|
let t9;
|
|
79
|
-
if ($[
|
|
84
|
+
if ($[18] !== file.filename) {
|
|
80
85
|
t9 = /* @__PURE__ */ jsx("p", {
|
|
81
86
|
className: "line-clamp-2 px-0.5 text-xs break-all",
|
|
82
87
|
children: file.filename
|
|
83
88
|
});
|
|
84
|
-
$[
|
|
85
|
-
$[
|
|
86
|
-
} else t9 = $[
|
|
89
|
+
$[18] = file.filename;
|
|
90
|
+
$[19] = t9;
|
|
91
|
+
} else t9 = $[19];
|
|
87
92
|
let t10;
|
|
88
|
-
if ($[
|
|
93
|
+
if ($[20] !== file.id || $[21] !== t5 || $[22] !== t6 || $[23] !== t7 || $[24] !== t8 || $[25] !== t9) {
|
|
89
94
|
t10 = /* @__PURE__ */ jsxs("button", {
|
|
90
95
|
type: "button",
|
|
91
96
|
"data-asset-id": t3,
|
|
@@ -94,14 +99,14 @@ const AssetCard = (t0) => {
|
|
|
94
99
|
onDoubleClick: t7,
|
|
95
100
|
children: [t8, t9]
|
|
96
101
|
});
|
|
97
|
-
$[
|
|
98
|
-
$[
|
|
99
|
-
$[
|
|
100
|
-
$[
|
|
101
|
-
$[
|
|
102
|
-
$[
|
|
103
|
-
$[
|
|
104
|
-
} else t10 = $[
|
|
102
|
+
$[20] = file.id;
|
|
103
|
+
$[21] = t5;
|
|
104
|
+
$[22] = t6;
|
|
105
|
+
$[23] = t7;
|
|
106
|
+
$[24] = t8;
|
|
107
|
+
$[25] = t9;
|
|
108
|
+
$[26] = t10;
|
|
109
|
+
} else t10 = $[26];
|
|
105
110
|
return t10;
|
|
106
111
|
};
|
|
107
112
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useProjectSlug } from "../../../lib/auth.js";
|
|
2
2
|
import { projectQueries } from "../../../lib/queries.js";
|
|
3
|
+
import { transformImageUrl } from "../../../core/lib/imageTransform.js";
|
|
3
4
|
import { UploadDropZone } from "../../content/components/UploadDropZone.js";
|
|
4
5
|
import { UploadItemRow } from "../../content/components/UploadProgressDrawer.js";
|
|
5
6
|
import { useFileUpload } from "../../../hooks/use-file-upload.js";
|
|
@@ -20,9 +21,9 @@ function assetLabel(isImage, multiple) {
|
|
|
20
21
|
}
|
|
21
22
|
const AssetActionButtons = (t0) => {
|
|
22
23
|
const $ = c(27);
|
|
23
|
-
if ($[0] !== "
|
|
24
|
+
if ($[0] !== "44259d77bb7c7c167ba0def21aa6d5d7b7d1115e4b30d38a3c3d829d0a76df91") {
|
|
24
25
|
for (let $i = 0; $i < 27; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
25
|
-
$[0] = "
|
|
26
|
+
$[0] = "44259d77bb7c7c167ba0def21aa6d5d7b7d1115e4b30d38a3c3d829d0a76df91";
|
|
26
27
|
}
|
|
27
28
|
const { isImage, multiple, fileInputRef, onPickerOpen, onFilesSelected, uploads } = t0;
|
|
28
29
|
let t1;
|
|
@@ -125,9 +126,9 @@ const AssetActionButtons = (t0) => {
|
|
|
125
126
|
};
|
|
126
127
|
const SingleAssetFieldEditor = (t0) => {
|
|
127
128
|
const $ = c(29);
|
|
128
|
-
if ($[0] !== "
|
|
129
|
+
if ($[0] !== "44259d77bb7c7c167ba0def21aa6d5d7b7d1115e4b30d38a3c3d829d0a76df91") {
|
|
129
130
|
for (let $i = 0; $i < 29; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
130
|
-
$[0] = "
|
|
131
|
+
$[0] = "44259d77bb7c7c167ba0def21aa6d5d7b7d1115e4b30d38a3c3d829d0a76df91";
|
|
131
132
|
}
|
|
132
133
|
const { fieldName, assetType, currentData, onFieldChange } = t0;
|
|
133
134
|
const asset = currentData[fieldName];
|
|
@@ -207,7 +208,10 @@ const SingleAssetFieldEditor = (t0) => {
|
|
|
207
208
|
children: [isImage ? /* @__PURE__ */ jsx("div", {
|
|
208
209
|
className: "border-border h-10 w-10 shrink-0 overflow-hidden rounded border",
|
|
209
210
|
children: /* @__PURE__ */ jsx("img", {
|
|
210
|
-
src: asset.url,
|
|
211
|
+
src: transformImageUrl(asset.url, {
|
|
212
|
+
width: 128,
|
|
213
|
+
mimeType: asset.mimeType
|
|
214
|
+
}),
|
|
211
215
|
alt: asset.alt || asset.filename,
|
|
212
216
|
className: "h-full w-full object-cover"
|
|
213
217
|
})
|
|
@@ -2,6 +2,7 @@ import { trackClientEvent } from "../../../lib/telemetry-client.js";
|
|
|
2
2
|
import { getAuthCookieHeader } from "../../../lib/auth.js";
|
|
3
3
|
import { getApiUrl, getEnvironmentName } from "../../../lib/api-client.js";
|
|
4
4
|
import { fileMutations, fileQueries } from "../../../lib/queries.js";
|
|
5
|
+
import { isRasterImage, transformImageUrl } from "../../../core/lib/imageTransform.js";
|
|
5
6
|
import { DebouncedFieldEditor } from "./DebouncedFieldEditor.js";
|
|
6
7
|
import { UploadDropZone } from "../../content/components/UploadDropZone.js";
|
|
7
8
|
import { c } from "react/compiler-runtime";
|
|
@@ -9,10 +10,10 @@ import { Label } from "@camox/ui/label";
|
|
|
9
10
|
import { toast } from "@camox/ui/toaster";
|
|
10
11
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
11
12
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { Button } from "@camox/ui/button";
|
|
14
15
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@camox/ui/tooltip";
|
|
15
|
-
import { Check, Download, FileIcon, Link, Loader2, Trash2, X } from "lucide-react";
|
|
16
|
+
import { Check, Download, FileIcon, Info, Link, Loader2, Trash2, X } from "lucide-react";
|
|
16
17
|
import { Dialog, DialogContent, DialogTitle } from "@camox/ui/dialog";
|
|
17
18
|
import { Switch } from "@camox/ui/switch";
|
|
18
19
|
import { ButtonGroup } from "@camox/ui/button-group";
|
|
@@ -20,9 +21,9 @@ import { ButtonGroup } from "@camox/ui/button-group";
|
|
|
20
21
|
//#region src/features/preview/components/AssetLightbox.tsx
|
|
21
22
|
function MetadataRow(t0) {
|
|
22
23
|
const $ = c(9);
|
|
23
|
-
if ($[0] !== "
|
|
24
|
+
if ($[0] !== "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a") {
|
|
24
25
|
for (let $i = 0; $i < 9; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
25
|
-
$[0] = "
|
|
26
|
+
$[0] = "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a";
|
|
26
27
|
}
|
|
27
28
|
const { label, children } = t0;
|
|
28
29
|
let t1;
|
|
@@ -64,6 +65,116 @@ function MetadataRow(t0) {
|
|
|
64
65
|
} else t4 = $[8];
|
|
65
66
|
return t4;
|
|
66
67
|
}
|
|
68
|
+
function DeliveredSize(t0) {
|
|
69
|
+
const $ = c(16);
|
|
70
|
+
if ($[0] !== "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a") {
|
|
71
|
+
for (let $i = 0; $i < 16; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
72
|
+
$[0] = "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a";
|
|
73
|
+
}
|
|
74
|
+
const { bytes, raw } = t0;
|
|
75
|
+
if (bytes == null) {
|
|
76
|
+
let t1;
|
|
77
|
+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
|
78
|
+
t1 = /* @__PURE__ */ jsx(Fragment, { children: "…" });
|
|
79
|
+
$[1] = t1;
|
|
80
|
+
} else t1 = $[1];
|
|
81
|
+
return t1;
|
|
82
|
+
}
|
|
83
|
+
let t1;
|
|
84
|
+
if ($[2] !== bytes || $[3] !== raw) {
|
|
85
|
+
t1 = raw != null && raw > 0 ? Math.round((raw - bytes) / raw * 100) : null;
|
|
86
|
+
$[2] = bytes;
|
|
87
|
+
$[3] = raw;
|
|
88
|
+
$[4] = t1;
|
|
89
|
+
} else t1 = $[4];
|
|
90
|
+
const savingsPct = t1;
|
|
91
|
+
if (savingsPct == null || savingsPct <= 0) {
|
|
92
|
+
let t2;
|
|
93
|
+
if ($[5] !== bytes) {
|
|
94
|
+
t2 = formatFileSize(bytes);
|
|
95
|
+
$[5] = bytes;
|
|
96
|
+
$[6] = t2;
|
|
97
|
+
} else t2 = $[6];
|
|
98
|
+
let t3;
|
|
99
|
+
if ($[7] !== t2) {
|
|
100
|
+
t3 = /* @__PURE__ */ jsxs(Fragment, { children: ["≈", t2] });
|
|
101
|
+
$[7] = t2;
|
|
102
|
+
$[8] = t3;
|
|
103
|
+
} else t3 = $[8];
|
|
104
|
+
return t3;
|
|
105
|
+
}
|
|
106
|
+
let t2;
|
|
107
|
+
if ($[9] !== bytes) {
|
|
108
|
+
t2 = formatFileSize(bytes);
|
|
109
|
+
$[9] = bytes;
|
|
110
|
+
$[10] = t2;
|
|
111
|
+
} else t2 = $[10];
|
|
112
|
+
let t3;
|
|
113
|
+
if ($[11] !== savingsPct) {
|
|
114
|
+
t3 = /* @__PURE__ */ jsxs("span", {
|
|
115
|
+
className: "text-muted-foreground",
|
|
116
|
+
children: [
|
|
117
|
+
"(−",
|
|
118
|
+
savingsPct,
|
|
119
|
+
"%)"
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
$[11] = savingsPct;
|
|
123
|
+
$[12] = t3;
|
|
124
|
+
} else t3 = $[12];
|
|
125
|
+
let t4;
|
|
126
|
+
if ($[13] !== t2 || $[14] !== t3) {
|
|
127
|
+
t4 = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
128
|
+
"≈",
|
|
129
|
+
t2,
|
|
130
|
+
" ",
|
|
131
|
+
t3
|
|
132
|
+
] });
|
|
133
|
+
$[13] = t2;
|
|
134
|
+
$[14] = t3;
|
|
135
|
+
$[15] = t4;
|
|
136
|
+
} else t4 = $[15];
|
|
137
|
+
return t4;
|
|
138
|
+
}
|
|
139
|
+
function DeliveredLabel(t0) {
|
|
140
|
+
const $ = c(4);
|
|
141
|
+
if ($[0] !== "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a") {
|
|
142
|
+
for (let $i = 0; $i < 4; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
143
|
+
$[0] = "50fbf2f7ee8a6e8b4452e53c7c776c76cf2aa2926c13218fcc4664012d02ad9a";
|
|
144
|
+
}
|
|
145
|
+
const { children } = t0;
|
|
146
|
+
let t1;
|
|
147
|
+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
|
148
|
+
t1 = /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
149
|
+
render: /* @__PURE__ */ jsx("button", {
|
|
150
|
+
type: "button",
|
|
151
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
152
|
+
"aria-label": "About image optimization"
|
|
153
|
+
}),
|
|
154
|
+
children: /* @__PURE__ */ jsx(Info, { className: "h-3.5 w-3.5" })
|
|
155
|
+
}), /* @__PURE__ */ jsxs(TooltipContent, {
|
|
156
|
+
className: "max-w-xs",
|
|
157
|
+
children: [
|
|
158
|
+
"Visitors automatically receive a compressed WebP/AVIF version sized to their device. Estimates use ",
|
|
159
|
+
DELIVERED_PHONE_WIDTH,
|
|
160
|
+
"px (phone) and ",
|
|
161
|
+
DELIVERED_LAPTOP_WIDTH,
|
|
162
|
+
"px (laptop) — the original is preserved."
|
|
163
|
+
]
|
|
164
|
+
})] });
|
|
165
|
+
$[1] = t1;
|
|
166
|
+
} else t1 = $[1];
|
|
167
|
+
let t2;
|
|
168
|
+
if ($[2] !== children) {
|
|
169
|
+
t2 = /* @__PURE__ */ jsxs("span", {
|
|
170
|
+
className: "inline-flex items-center gap-1",
|
|
171
|
+
children: [children, t1]
|
|
172
|
+
});
|
|
173
|
+
$[2] = children;
|
|
174
|
+
$[3] = t2;
|
|
175
|
+
} else t2 = $[3];
|
|
176
|
+
return t2;
|
|
177
|
+
}
|
|
67
178
|
function formatRelativeTime(epochMs) {
|
|
68
179
|
const now = Temporal.Now.instant();
|
|
69
180
|
const then = Temporal.Instant.fromEpochMilliseconds(epochMs);
|
|
@@ -86,6 +197,20 @@ function formatFileSize(bytes) {
|
|
|
86
197
|
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
87
198
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
88
199
|
}
|
|
200
|
+
const DELIVERED_PHONE_WIDTH = 640;
|
|
201
|
+
const DELIVERED_LAPTOP_WIDTH = 1280;
|
|
202
|
+
async function measureContentLength(url, signal) {
|
|
203
|
+
try {
|
|
204
|
+
const res = await fetch(url, { signal });
|
|
205
|
+
res.body?.cancel();
|
|
206
|
+
const cl = res.headers.get("content-length");
|
|
207
|
+
if (!cl) return null;
|
|
208
|
+
const parsed = Number.parseInt(cl, 10);
|
|
209
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
210
|
+
} catch {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
89
214
|
const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
90
215
|
const replaceFile = useMutation(fileMutations.replace());
|
|
91
216
|
const deleteFile = useMutation(fileMutations.delete());
|
|
@@ -100,12 +225,55 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
100
225
|
const [zoomed, setZoomed] = useState(false);
|
|
101
226
|
const [zoomedWidth, setZoomedWidth] = useState(null);
|
|
102
227
|
const clickFractionRef = useRef(null);
|
|
228
|
+
const [deliveredSizes, setDeliveredSizes] = useState(null);
|
|
103
229
|
useEffect(() => {
|
|
104
230
|
if (!open) {
|
|
105
231
|
setZoomed(false);
|
|
106
232
|
setZoomedWidth(null);
|
|
107
233
|
}
|
|
108
234
|
}, [open]);
|
|
235
|
+
const isImage = file?.mimeType?.startsWith("image/") ?? false;
|
|
236
|
+
const canUseAiMetadata = isRasterImage(file?.mimeType);
|
|
237
|
+
const fileUrl = file?.url;
|
|
238
|
+
const fileMimeType = file?.mimeType;
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
if (!open || !isImage || !fileUrl) {
|
|
241
|
+
setDeliveredSizes(null);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const phoneUrl = transformImageUrl(fileUrl, {
|
|
245
|
+
width: DELIVERED_PHONE_WIDTH,
|
|
246
|
+
mimeType: fileMimeType
|
|
247
|
+
});
|
|
248
|
+
const laptopUrl = transformImageUrl(fileUrl, {
|
|
249
|
+
width: DELIVERED_LAPTOP_WIDTH,
|
|
250
|
+
mimeType: fileMimeType
|
|
251
|
+
});
|
|
252
|
+
if (phoneUrl === fileUrl && laptopUrl === fileUrl) {
|
|
253
|
+
setDeliveredSizes(null);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
setDeliveredSizes({
|
|
257
|
+
phone: null,
|
|
258
|
+
laptop: null,
|
|
259
|
+
measured: false
|
|
260
|
+
});
|
|
261
|
+
const controller = new AbortController();
|
|
262
|
+
Promise.all([measureContentLength(phoneUrl, controller.signal), measureContentLength(laptopUrl, controller.signal)]).then(([phone, laptop]) => {
|
|
263
|
+
if (controller.signal.aborted) return;
|
|
264
|
+
setDeliveredSizes({
|
|
265
|
+
phone,
|
|
266
|
+
laptop,
|
|
267
|
+
measured: true
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
return () => controller.abort();
|
|
271
|
+
}, [
|
|
272
|
+
open,
|
|
273
|
+
isImage,
|
|
274
|
+
fileUrl,
|
|
275
|
+
fileMimeType
|
|
276
|
+
]);
|
|
109
277
|
useEffect(() => {
|
|
110
278
|
if (!zoomed || !zoomedWidth || !containerRef.current || !clickFractionRef.current) return;
|
|
111
279
|
const container = containerRef.current;
|
|
@@ -194,7 +362,6 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
194
362
|
onOpenChange(false);
|
|
195
363
|
};
|
|
196
364
|
if (!file) return null;
|
|
197
|
-
const isImage = file.mimeType?.startsWith("image/");
|
|
198
365
|
return /* @__PURE__ */ jsx(Dialog, {
|
|
199
366
|
open,
|
|
200
367
|
onOpenChange,
|
|
@@ -340,11 +507,12 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
340
507
|
children: /* @__PURE__ */ jsx(Trash2, {})
|
|
341
508
|
}), /* @__PURE__ */ jsx(TooltipContent, { children: "Delete" })] })
|
|
342
509
|
] }),
|
|
343
|
-
/* @__PURE__ */ jsxs(
|
|
344
|
-
className:
|
|
510
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
|
|
511
|
+
render: /* @__PURE__ */ jsx("div", { className: `flex items-center gap-2 ${canUseAiMetadata ? "" : "cursor-not-allowed"}` }),
|
|
345
512
|
children: [/* @__PURE__ */ jsx(Switch, {
|
|
346
513
|
id: "ai-metadata",
|
|
347
|
-
|
|
514
|
+
disabled: !canUseAiMetadata,
|
|
515
|
+
checked: canUseAiMetadata && file.aiMetadataEnabled !== false,
|
|
348
516
|
onCheckedChange: (checked) => {
|
|
349
517
|
setAiMetadata.mutate({
|
|
350
518
|
id: fileId,
|
|
@@ -359,14 +527,15 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
359
527
|
}
|
|
360
528
|
}), /* @__PURE__ */ jsx(Label, {
|
|
361
529
|
htmlFor: "ai-metadata",
|
|
530
|
+
className: canUseAiMetadata ? "" : "text-muted-foreground",
|
|
362
531
|
children: "AI metadata"
|
|
363
532
|
})]
|
|
364
|
-
}),
|
|
533
|
+
}), !canUseAiMetadata && /* @__PURE__ */ jsx(TooltipContent, { children: "AI metadata is only available for raster images." })] }),
|
|
365
534
|
/* @__PURE__ */ jsx(DebouncedFieldEditor, {
|
|
366
535
|
label: "File name",
|
|
367
536
|
placeholder: "File name...",
|
|
368
537
|
initialValue: file.filename,
|
|
369
|
-
disabled: file.aiMetadataEnabled !== false,
|
|
538
|
+
disabled: canUseAiMetadata && file.aiMetadataEnabled !== false,
|
|
370
539
|
onSave: (value) => setFilename.mutate({
|
|
371
540
|
id: fileId,
|
|
372
541
|
filename: value
|
|
@@ -376,7 +545,7 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
376
545
|
label: "Alt text",
|
|
377
546
|
placeholder: "Describe this file...",
|
|
378
547
|
initialValue: file.alt,
|
|
379
|
-
disabled: file.aiMetadataEnabled !== false,
|
|
548
|
+
disabled: canUseAiMetadata && file.aiMetadataEnabled !== false,
|
|
380
549
|
rows: 2,
|
|
381
550
|
onSave: (value_0) => setAlt.mutate({
|
|
382
551
|
id: fileId,
|
|
@@ -391,9 +560,22 @@ const AssetLightbox = ({ open, onOpenChange, fileId }) => {
|
|
|
391
560
|
children: file.mimeType.split("/").pop()?.toUpperCase() ?? "Unknown"
|
|
392
561
|
}),
|
|
393
562
|
/* @__PURE__ */ jsx(MetadataRow, {
|
|
394
|
-
label: "
|
|
563
|
+
label: "Raw size",
|
|
395
564
|
children: file.size != null ? formatFileSize(file.size) : "Unknown"
|
|
396
565
|
}),
|
|
566
|
+
isImage && deliveredSizes && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MetadataRow, {
|
|
567
|
+
label: /* @__PURE__ */ jsx(DeliveredLabel, { children: "On phone" }),
|
|
568
|
+
children: /* @__PURE__ */ jsx(DeliveredSize, {
|
|
569
|
+
bytes: deliveredSizes.phone,
|
|
570
|
+
raw: file.size
|
|
571
|
+
})
|
|
572
|
+
}), /* @__PURE__ */ jsx(MetadataRow, {
|
|
573
|
+
label: /* @__PURE__ */ jsx(DeliveredLabel, { children: "On laptop" }),
|
|
574
|
+
children: /* @__PURE__ */ jsx(DeliveredSize, {
|
|
575
|
+
bytes: deliveredSizes.laptop,
|
|
576
|
+
raw: file.size
|
|
577
|
+
})
|
|
578
|
+
})] }),
|
|
397
579
|
/* @__PURE__ */ jsx(MetadataRow, {
|
|
398
580
|
label: "Created",
|
|
399
581
|
children: formatRelativeTime(file.createdAt)
|
|
@@ -24,16 +24,15 @@ const getSchemaFieldsInOrder = (schema) => {
|
|
|
24
24
|
fieldType: prop.fieldType,
|
|
25
25
|
label: prop.title,
|
|
26
26
|
minItems: prop.minItems,
|
|
27
|
-
maxItems: prop.maxItems
|
|
28
|
-
arrayItemType: prop.arrayItemType
|
|
27
|
+
maxItems: prop.maxItems
|
|
29
28
|
};
|
|
30
29
|
});
|
|
31
30
|
};
|
|
32
31
|
const DrillRow = (t0) => {
|
|
33
32
|
const $ = c(28);
|
|
34
|
-
if ($[0] !== "
|
|
33
|
+
if ($[0] !== "10d7e880adc2a5d4d4076a1bdf35fee064c2b42cd42d45b2991c5ebaf6c4d95d") {
|
|
35
34
|
for (let $i = 0; $i < 28; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
36
|
-
$[0] = "
|
|
35
|
+
$[0] = "10d7e880adc2a5d4d4076a1bdf35fee064c2b42cd42d45b2991c5ebaf6c4d95d";
|
|
37
36
|
}
|
|
38
37
|
const { label, preview, Icon, onClick, hover, postToIframe } = t0;
|
|
39
38
|
let t1;
|
|
@@ -136,9 +135,9 @@ const DrillRow = (t0) => {
|
|
|
136
135
|
};
|
|
137
136
|
const ItemFieldsEditor = (t0) => {
|
|
138
137
|
const $ = c(47);
|
|
139
|
-
if ($[0] !== "
|
|
138
|
+
if ($[0] !== "10d7e880adc2a5d4d4076a1bdf35fee064c2b42cd42d45b2991c5ebaf6c4d95d") {
|
|
140
139
|
for (let $i = 0; $i < 47; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
141
|
-
$[0] = "
|
|
140
|
+
$[0] = "10d7e880adc2a5d4d4076a1bdf35fee064c2b42cd42d45b2991c5ebaf6c4d95d";
|
|
142
141
|
}
|
|
143
142
|
const { schema, data, blockId, itemId, onFieldChange, postToIframe, filesMap, itemsMap, fieldIdPrefix, autoFocusFieldName } = t0;
|
|
144
143
|
let t1;
|
|
@@ -384,10 +383,10 @@ const ItemFieldsEditor = (t0) => {
|
|
|
384
383
|
postToIframe
|
|
385
384
|
}, field.name);
|
|
386
385
|
}
|
|
387
|
-
if (field.fieldType === "
|
|
386
|
+
if (field.fieldType === "ImageList" || field.fieldType === "FileList") {
|
|
388
387
|
const value_1 = data[field.name];
|
|
389
388
|
const count = Array.isArray(value_1) ? value_1.length : 0;
|
|
390
|
-
const isImage = field.
|
|
389
|
+
const isImage = field.fieldType === "ImageList";
|
|
391
390
|
const noun = isImage ? "image" : "file";
|
|
392
391
|
let preview_0;
|
|
393
392
|
if (count === 0) preview_0 = isImage ? "No images" : "No files";
|
|
@@ -434,7 +433,7 @@ const ItemFieldsEditor = (t0) => {
|
|
|
434
433
|
postToIframe
|
|
435
434
|
}, field.name);
|
|
436
435
|
}
|
|
437
|
-
if (field.fieldType === "
|
|
436
|
+
if (field.fieldType === "Repeater") {
|
|
438
437
|
const items = (data[field.name] ?? []).map((item) => {
|
|
439
438
|
if (isItemMarker(item)) return itemsMap.get(item._itemId) ?? null;
|
|
440
439
|
return item;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useProjectSlug } from "../../../lib/auth.js";
|
|
2
2
|
import { projectQueries } from "../../../lib/queries.js";
|
|
3
3
|
import { cn } from "../../../lib/utils.js";
|
|
4
|
+
import { transformImageUrl } from "../../../core/lib/imageTransform.js";
|
|
4
5
|
import { UploadDropZone } from "../../content/components/UploadDropZone.js";
|
|
5
6
|
import { useFileUpload } from "../../../hooks/use-file-upload.js";
|
|
6
7
|
import { AssetLightbox } from "./AssetLightbox.js";
|
|
@@ -20,10 +21,10 @@ import { CSS } from "@dnd-kit/utilities";
|
|
|
20
21
|
|
|
21
22
|
//#region src/features/preview/components/MultipleAssetFieldEditor.tsx
|
|
22
23
|
const SortableAssetItem = (t0) => {
|
|
23
|
-
const $ = c(
|
|
24
|
-
if ($[0] !== "
|
|
25
|
-
for (let $i = 0; $i <
|
|
26
|
-
$[0] = "
|
|
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";
|
|
27
28
|
}
|
|
28
29
|
const { asset, assetType, onRemove, onAssetOpen } = t0;
|
|
29
30
|
const t1 = String(asset._fileId);
|
|
@@ -89,11 +90,14 @@ const SortableAssetItem = (t0) => {
|
|
|
89
90
|
$[17] = t10;
|
|
90
91
|
} else t10 = $[17];
|
|
91
92
|
let t11;
|
|
92
|
-
if ($[18] !== asset.alt || $[19] !== asset.filename || $[20] !== asset.
|
|
93
|
+
if ($[18] !== asset.alt || $[19] !== asset.filename || $[20] !== asset.mimeType || $[21] !== asset.url || $[22] !== assetType) {
|
|
93
94
|
t11 = assetType === "Image" ? /* @__PURE__ */ jsx("div", {
|
|
94
95
|
className: "border-border h-12 w-12 shrink-0 overflow-hidden rounded border",
|
|
95
96
|
children: /* @__PURE__ */ jsx("img", {
|
|
96
|
-
src: asset.url,
|
|
97
|
+
src: transformImageUrl(asset.url, {
|
|
98
|
+
width: 128,
|
|
99
|
+
mimeType: asset.mimeType
|
|
100
|
+
}),
|
|
97
101
|
alt: asset.alt || asset.filename,
|
|
98
102
|
className: "h-full w-full object-cover"
|
|
99
103
|
})
|
|
@@ -103,55 +107,56 @@ const SortableAssetItem = (t0) => {
|
|
|
103
107
|
});
|
|
104
108
|
$[18] = asset.alt;
|
|
105
109
|
$[19] = asset.filename;
|
|
106
|
-
$[20] = asset.
|
|
107
|
-
$[21] =
|
|
108
|
-
$[22] =
|
|
109
|
-
|
|
110
|
+
$[20] = asset.mimeType;
|
|
111
|
+
$[21] = asset.url;
|
|
112
|
+
$[22] = assetType;
|
|
113
|
+
$[23] = t11;
|
|
114
|
+
} else t11 = $[23];
|
|
110
115
|
const t12 = asset.filename || "Untitled";
|
|
111
116
|
let t13;
|
|
112
|
-
if ($[
|
|
117
|
+
if ($[24] !== asset.filename || $[25] !== t12) {
|
|
113
118
|
t13 = /* @__PURE__ */ jsx("p", {
|
|
114
119
|
className: "flex-1 truncate text-left text-sm",
|
|
115
120
|
title: asset.filename,
|
|
116
121
|
children: t12
|
|
117
122
|
});
|
|
118
|
-
$[
|
|
119
|
-
$[
|
|
120
|
-
$[
|
|
121
|
-
} else t13 = $[
|
|
123
|
+
$[24] = asset.filename;
|
|
124
|
+
$[25] = t12;
|
|
125
|
+
$[26] = t13;
|
|
126
|
+
} else t13 = $[26];
|
|
122
127
|
let t14;
|
|
123
|
-
if ($[
|
|
128
|
+
if ($[27] !== t10 || $[28] !== t11 || $[29] !== t13) {
|
|
124
129
|
t14 = /* @__PURE__ */ jsxs("button", {
|
|
125
130
|
type: "button",
|
|
126
131
|
className: "flex min-w-0 flex-1 cursor-zoom-in items-center gap-2",
|
|
127
132
|
onClick: t10,
|
|
128
133
|
children: [t11, t13]
|
|
129
134
|
});
|
|
130
|
-
$[
|
|
131
|
-
$[
|
|
132
|
-
$[
|
|
133
|
-
$[
|
|
134
|
-
} else t14 = $[
|
|
135
|
+
$[27] = t10;
|
|
136
|
+
$[28] = t11;
|
|
137
|
+
$[29] = t13;
|
|
138
|
+
$[30] = t14;
|
|
139
|
+
} else t14 = $[30];
|
|
135
140
|
let t15;
|
|
136
|
-
if ($[
|
|
141
|
+
if ($[31] !== asset._fileId || $[32] !== onRemove) {
|
|
137
142
|
t15 = () => onRemove(asset._fileId);
|
|
138
|
-
$[
|
|
139
|
-
$[
|
|
140
|
-
$[
|
|
141
|
-
} else t15 = $[
|
|
143
|
+
$[31] = asset._fileId;
|
|
144
|
+
$[32] = onRemove;
|
|
145
|
+
$[33] = t15;
|
|
146
|
+
} else t15 = $[33];
|
|
142
147
|
let t16;
|
|
143
|
-
if ($[
|
|
148
|
+
if ($[34] !== asset._fileId || $[35] !== t15) {
|
|
144
149
|
t16 = /* @__PURE__ */ jsx(UnlinkAssetButton, {
|
|
145
150
|
fileId: asset._fileId,
|
|
146
151
|
onUnlink: t15,
|
|
147
152
|
className: "hidden group-focus-within:flex group-hover:flex"
|
|
148
153
|
});
|
|
149
|
-
$[
|
|
150
|
-
$[
|
|
151
|
-
$[
|
|
152
|
-
} else t16 = $[
|
|
154
|
+
$[34] = asset._fileId;
|
|
155
|
+
$[35] = t15;
|
|
156
|
+
$[36] = t16;
|
|
157
|
+
} else t16 = $[36];
|
|
153
158
|
let t17;
|
|
154
|
-
if ($[
|
|
159
|
+
if ($[37] !== setNodeRef || $[38] !== style || $[39] !== t14 || $[40] !== t16 || $[41] !== t7 || $[42] !== t9) {
|
|
155
160
|
t17 = /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", {
|
|
156
161
|
ref: setNodeRef,
|
|
157
162
|
style,
|
|
@@ -162,21 +167,21 @@ const SortableAssetItem = (t0) => {
|
|
|
162
167
|
t16
|
|
163
168
|
]
|
|
164
169
|
}) });
|
|
165
|
-
$[
|
|
166
|
-
$[
|
|
167
|
-
$[
|
|
168
|
-
$[
|
|
169
|
-
$[
|
|
170
|
-
$[
|
|
171
|
-
$[
|
|
172
|
-
} else t17 = $[
|
|
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];
|
|
173
178
|
return t17;
|
|
174
179
|
};
|
|
175
180
|
const MultipleAssetFieldEditor = (t0) => {
|
|
176
181
|
const $ = c(12);
|
|
177
|
-
if ($[0] !== "
|
|
182
|
+
if ($[0] !== "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d") {
|
|
178
183
|
for (let $i = 0; $i < 12; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
|
|
179
|
-
$[0] = "
|
|
184
|
+
$[0] = "2cded293bac7099a2f0d21d69f3d54cce48c76ba242f02366ecaa807b6b79b7d";
|
|
180
185
|
}
|
|
181
186
|
const { fieldName, assetType, currentData, onFieldChange } = t0;
|
|
182
187
|
const isImage = assetType === "Image";
|
|
@@ -35,8 +35,7 @@ const getSettingsFields = (schema) => {
|
|
|
35
35
|
fieldType: prop.fieldType,
|
|
36
36
|
label: prop.title,
|
|
37
37
|
enumLabels: prop.enumLabels,
|
|
38
|
-
enumValues: prop.enum
|
|
39
|
-
arrayItemType: prop.arrayItemType
|
|
38
|
+
enumValues: prop.enum
|
|
40
39
|
};
|
|
41
40
|
});
|
|
42
41
|
};
|
|
@@ -211,7 +210,7 @@ const PageContentSheet = () => {
|
|
|
211
210
|
const isMultipleAsset = React.useMemo(() => {
|
|
212
211
|
if (!isViewingAsset || !assetFieldName) return false;
|
|
213
212
|
const prop = currentSchema?.properties?.[assetFieldName];
|
|
214
|
-
return prop?.
|
|
213
|
+
return prop?.fieldType === "ImageList" || prop?.fieldType === "FileList";
|
|
215
214
|
}, [
|
|
216
215
|
isViewingAsset,
|
|
217
216
|
assetFieldName,
|