sanity-plugin-smart-asset-manager 1.0.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/index.js ADDED
@@ -0,0 +1,1356 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ smartAssetManager: () => smartAssetManager
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+ var import_sanity2 = require("sanity");
37
+
38
+ // src/components/SmartAssetManagerTool.tsx
39
+ var import_react6 = require("react");
40
+ var import_ui6 = require("@sanity/ui");
41
+ var import_sanity = require("sanity");
42
+
43
+ // src/hooks/useAssets.ts
44
+ var import_react = require("react");
45
+ function useAssets(sanityClient, searchQuery, sortBy, type, offset, limit) {
46
+ const [assets, setAssets] = (0, import_react.useState)([]);
47
+ const [loading, setLoading] = (0, import_react.useState)(true);
48
+ const [total, setTotal] = (0, import_react.useState)(0);
49
+ const [refreshSeed, setRefreshSeed] = (0, import_react.useState)(0);
50
+ const refreshAssets = () => setRefreshSeed((s) => s + 1);
51
+ (0, import_react.useEffect)(() => {
52
+ async function fetchAssets() {
53
+ setLoading(true);
54
+ try {
55
+ let filterParts = ['_type in ["sanity.imageAsset", "sanity.fileAsset"]'];
56
+ if (type === "image") {
57
+ filterParts.push('_type == "sanity.imageAsset"');
58
+ } else if (type === "file") {
59
+ filterParts.push(
60
+ '_type == "sanity.fileAsset" && !(mimeType match "video/*") && !(mimeType match "audio/*")'
61
+ );
62
+ } else if (type === "video") {
63
+ filterParts.push('_type == "sanity.fileAsset" && mimeType match "video/*"');
64
+ } else if (type === "audio") {
65
+ filterParts.push('_type == "sanity.fileAsset" && mimeType match "audio/*"');
66
+ }
67
+ if (searchQuery) {
68
+ filterParts.push("(originalFilename match $searchQuery || _id match $searchQuery)");
69
+ }
70
+ const filter = filterParts.join(" && ");
71
+ const countQuery = `count(*[${filter}])`;
72
+ const query = `*[${filter}] | order(${sortBy} desc) [${offset}...${offset + limit}] {
73
+ _id,
74
+ _type,
75
+ url,
76
+ extension,
77
+ size,
78
+ mimeType,
79
+ originalFilename,
80
+ metadata {
81
+ dimensions
82
+ }
83
+ }`;
84
+ const [totalCount, results] = await Promise.all([
85
+ sanityClient.fetch(countQuery, { searchQuery: `*${searchQuery}*` }),
86
+ sanityClient.fetch(query, { searchQuery: `*${searchQuery}*` })
87
+ ]);
88
+ setTotal(totalCount);
89
+ setAssets(results);
90
+ } catch (error) {
91
+ console.error("Error fetching assets:", error);
92
+ } finally {
93
+ setLoading(false);
94
+ }
95
+ }
96
+ fetchAssets();
97
+ }, [sanityClient, searchQuery, sortBy, type, refreshSeed, offset, limit]);
98
+ return { assets, loading, total, refreshAssets };
99
+ }
100
+
101
+ // src/utils/assetQueries.ts
102
+ async function getAssetUsage(sanityClient, assetId) {
103
+ const query = `*[references($assetId)] {
104
+ _id,
105
+ _type,
106
+ "title": coalesce(title, name, label, _id)
107
+ }`;
108
+ return await sanityClient.fetch(query, { assetId });
109
+ }
110
+ async function findUnusedAssets(sanityClient) {
111
+ const query = `*[
112
+ _type in ["sanity.imageAsset", "sanity.fileAsset"] &&
113
+ count(*[references(^._id)]) == 0
114
+ ] {
115
+ _id,
116
+ _type,
117
+ url,
118
+ size,
119
+ extension,
120
+ mimeType,
121
+ originalFilename,
122
+ metadata { dimensions }
123
+ }`;
124
+ return await sanityClient.fetch(query);
125
+ }
126
+ async function deleteAssets(sanityClient, assetIds) {
127
+ const transaction = sanityClient.transaction();
128
+ assetIds.forEach((id) => transaction.delete(id));
129
+ return await transaction.commit();
130
+ }
131
+ async function updateAssetFilename(sanityClient, assetId, filename) {
132
+ return await sanityClient.patch(assetId).set({ originalFilename: filename }).commit();
133
+ }
134
+
135
+ // src/components/AssetCard.tsx
136
+ var import_ui = require("@sanity/ui");
137
+
138
+ // src/utils/formatBytes.ts
139
+ var formatBytes = (bytes) => {
140
+ if (bytes === 0) return "0 B";
141
+ const k = 1024;
142
+ const sizes = ["B", "KB", "MB", "GB"];
143
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
144
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
145
+ };
146
+
147
+ // src/components/common/Icons.tsx
148
+ var import_jsx_runtime = require("react/jsx-runtime");
149
+ var SearchIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
150
+ "svg",
151
+ {
152
+ width: "1em",
153
+ height: "1em",
154
+ viewBox: "0 0 24 24",
155
+ fill: "none",
156
+ stroke: "currentColor",
157
+ strokeWidth: "2",
158
+ strokeLinecap: "round",
159
+ strokeLinejoin: "round",
160
+ ...props,
161
+ children: [
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "11", cy: "11", r: "8" }),
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
164
+ ]
165
+ }
166
+ );
167
+ var TrashIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
168
+ "svg",
169
+ {
170
+ width: "1em",
171
+ height: "1em",
172
+ viewBox: "0 0 24 24",
173
+ fill: "none",
174
+ stroke: "currentColor",
175
+ strokeWidth: "2",
176
+ strokeLinecap: "round",
177
+ strokeLinejoin: "round",
178
+ ...props,
179
+ children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
181
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "10", y1: "11", x2: "10", y2: "17" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "14", y1: "11", x2: "14", y2: "17" })
184
+ ]
185
+ }
186
+ );
187
+ var ResetIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
188
+ "svg",
189
+ {
190
+ width: "1em",
191
+ height: "1em",
192
+ viewBox: "0 0 24 24",
193
+ fill: "none",
194
+ stroke: "currentColor",
195
+ strokeWidth: "2",
196
+ strokeLinecap: "round",
197
+ strokeLinejoin: "round",
198
+ ...props,
199
+ children: [
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1 4v6h6" }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.51 15a9 9 0 1 0 2.13-9.36L1 10" })
202
+ ]
203
+ }
204
+ );
205
+ var DocumentsIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
206
+ "svg",
207
+ {
208
+ width: "1em",
209
+ height: "1em",
210
+ viewBox: "0 0 24 24",
211
+ fill: "none",
212
+ stroke: "currentColor",
213
+ strokeWidth: "2",
214
+ strokeLinecap: "round",
215
+ strokeLinejoin: "round",
216
+ ...props,
217
+ children: [
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
219
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
221
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
222
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "10 9 9 9 8 9" })
223
+ ]
224
+ }
225
+ );
226
+ var DownloadIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
227
+ "svg",
228
+ {
229
+ width: "1em",
230
+ height: "1em",
231
+ viewBox: "0 0 24 24",
232
+ fill: "none",
233
+ stroke: "currentColor",
234
+ strokeWidth: "2",
235
+ strokeLinecap: "round",
236
+ strokeLinejoin: "round",
237
+ ...props,
238
+ children: [
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "7 10 12 15 17 10" }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
242
+ ]
243
+ }
244
+ );
245
+ var FolderIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
246
+ "svg",
247
+ {
248
+ width: "1em",
249
+ height: "1em",
250
+ viewBox: "0 0 24 24",
251
+ fill: "none",
252
+ stroke: "currentColor",
253
+ strokeWidth: "2",
254
+ strokeLinecap: "round",
255
+ strokeLinejoin: "round",
256
+ ...props,
257
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
258
+ }
259
+ );
260
+ var UploadIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
261
+ "svg",
262
+ {
263
+ width: "1em",
264
+ height: "1em",
265
+ viewBox: "0 0 24 24",
266
+ fill: "none",
267
+ stroke: "currentColor",
268
+ strokeWidth: "2",
269
+ strokeLinecap: "round",
270
+ strokeLinejoin: "round",
271
+ ...props,
272
+ children: [
273
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "17 8 12 3 7 8" }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
276
+ ]
277
+ }
278
+ );
279
+ var PdfIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
280
+ "svg",
281
+ {
282
+ width: "1em",
283
+ height: "1em",
284
+ viewBox: "0 0 24 24",
285
+ fill: "none",
286
+ stroke: "currentColor",
287
+ strokeWidth: "2",
288
+ strokeLinecap: "round",
289
+ strokeLinejoin: "round",
290
+ ...props,
291
+ children: [
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "14 2 14 8 20 8" }),
294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 15h3a2 2 0 0 0 2-2V13a2 2 0 0 0-2-2H9v4z" }),
295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "9", y1: "11", x2: "9", y2: "17" })
296
+ ]
297
+ }
298
+ );
299
+ var AudioIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
300
+ "svg",
301
+ {
302
+ width: "1em",
303
+ height: "1em",
304
+ viewBox: "0 0 24 24",
305
+ fill: "none",
306
+ stroke: "currentColor",
307
+ strokeWidth: "2",
308
+ strokeLinecap: "round",
309
+ strokeLinejoin: "round",
310
+ ...props,
311
+ children: [
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9 18V5l12-2v13" }),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "6", cy: "18", r: "3" }),
314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "18", cy: "16", r: "3" })
315
+ ]
316
+ }
317
+ );
318
+ var SortIcon = (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
319
+ "svg",
320
+ {
321
+ width: "1em",
322
+ height: "1em",
323
+ viewBox: "0 0 24 24",
324
+ fill: "none",
325
+ stroke: "currentColor",
326
+ strokeWidth: "2",
327
+ strokeLinecap: "round",
328
+ strokeLinejoin: "round",
329
+ ...props,
330
+ children: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "11", y1: "5", x2: "21", y2: "5" }),
332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "11", y1: "9", x2: "18", y2: "9" }),
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "11", y1: "13", x2: "15", y2: "13" }),
334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "3", y1: "17", x2: "21", y2: "17" }),
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: "7 5 3 9 7 13" }),
336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: "3", y1: "9", x2: "3", y2: "1" })
337
+ ]
338
+ }
339
+ );
340
+
341
+ // src/components/AssetCard.tsx
342
+ var import_styled_components = __toESM(require("styled-components"));
343
+ var import_jsx_runtime2 = require("react/jsx-runtime");
344
+ var StyledCard = (0, import_styled_components.default)(import_ui.Card)`
345
+ overflow: hidden;
346
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
347
+ cursor: pointer;
348
+ position: relative;
349
+ border: 1px solid
350
+ ${(props) => props.selected ? "var(--card-focus-ring-color)" : "var(--card-border-color)"};
351
+ background-color: ${(props) => props.selected ? "var(--card-accent-bg-color)" : "inherit"};
352
+
353
+ &:hover {
354
+ transform: translateY(-4px);
355
+ box-shadow: 0 12px 24px rgba(0, 0, 0, 0.08);
356
+ border-color: var(--card-focus-ring-color);
357
+ }
358
+ `;
359
+ var ImageContainer = (0, import_styled_components.default)(import_ui.Box)`
360
+ width: 100%;
361
+ aspect-ratio: 1.25;
362
+ background-color: transparent;
363
+ position: relative;
364
+ display: flex;
365
+ align-items: center;
366
+ justify-content: center;
367
+ border-bottom: 1px solid var(--card-border-color);
368
+ overflow: hidden;
369
+ `;
370
+ var SelectOverlay = (0, import_styled_components.default)(import_ui.Box)`
371
+ position: absolute;
372
+ top: 8px;
373
+ left: 8px;
374
+ z-index: 5;
375
+ `;
376
+ var AssetPreview = import_styled_components.default.img`
377
+ width: 100%;
378
+ height: 100%;
379
+ object-fit: cover;
380
+ transition: transform 0.3s ease;
381
+
382
+ ${StyledCard}:hover & {
383
+ transform: scale(1.05);
384
+ }
385
+ `;
386
+ var VideoPreview = import_styled_components.default.video`
387
+ width: 100%;
388
+ height: 100%;
389
+ object-fit: cover;
390
+ background: #000;
391
+ `;
392
+ var ActionBar = (0, import_styled_components.default)(import_ui.Flex)`
393
+ position: absolute;
394
+ bottom: 8px;
395
+ right: 8px;
396
+ background: var(--card-bg-color);
397
+ opacity: 0.95;
398
+ backdrop-filter: blur(4px);
399
+ border-radius: 8px;
400
+ padding: 2px;
401
+ opacity: 0;
402
+ border: 1px solid var(--card-border-color);
403
+ transition: opacity 0.2s ease;
404
+
405
+ ${StyledCard}:hover & {
406
+ opacity: 1;
407
+ }
408
+ `;
409
+ var AssetCard = ({ asset, onClick, isSelected, onSelect }) => {
410
+ const isImage = asset._type === "sanity.imageAsset";
411
+ const isVideo = asset?.mimeType?.startsWith("video/");
412
+ const isAudio = asset?.mimeType?.startsWith("audio/");
413
+ const isPdf = asset?.extension === "pdf" || asset?.mimeType === "application/pdf";
414
+ const handleCardClick = (e) => {
415
+ if (e.target.closest("button")) return;
416
+ onClick(asset);
417
+ };
418
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(StyledCard, { radius: 3, onClick: handleCardClick, selected: isSelected, children: [
419
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(ImageContainer, { children: [
420
+ onSelect && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SelectOverlay, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
421
+ import_ui.Checkbox,
422
+ {
423
+ checked: isSelected,
424
+ onChange: (e) => onSelect(asset._id, e.target.checked)
425
+ }
426
+ ) }),
427
+ isImage ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
428
+ AssetPreview,
429
+ {
430
+ src: `${asset.url}?w=400&h=320&fit=crop`,
431
+ alt: asset.originalFilename || ""
432
+ }
433
+ ) : isVideo ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VideoPreview, { src: asset.url, muted: true, playsInline: true }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
434
+ import_ui.Flex,
435
+ {
436
+ direction: "column",
437
+ align: "center",
438
+ justify: "center",
439
+ gap: 3,
440
+ style: { height: "100%", width: "100%" },
441
+ children: [
442
+ isPdf ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
443
+ PdfIcon,
444
+ {
445
+ style: {
446
+ fontSize: "48px",
447
+ color: "#ef4444"
448
+ }
449
+ }
450
+ ) : isAudio ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
451
+ AudioIcon,
452
+ {
453
+ style: {
454
+ fontSize: "48px",
455
+ color: "#ef4444"
456
+ }
457
+ }
458
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
459
+ DocumentsIcon,
460
+ {
461
+ style: {
462
+ fontSize: "48px",
463
+ color: "#9ca3af"
464
+ }
465
+ }
466
+ ),
467
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, weight: "bold", muted: true, children: (asset.extension || asset.mimeType?.split("/")[1])?.toUpperCase() || "OTHER" })
468
+ ]
469
+ }
470
+ ),
471
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ActionBar, { gap: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
472
+ import_ui.Button,
473
+ {
474
+ icon: DownloadIcon,
475
+ mode: "bleed",
476
+ fontSize: 1,
477
+ padding: 2,
478
+ onClick: (e) => {
479
+ e.stopPropagation();
480
+ window.open(asset.url);
481
+ }
482
+ }
483
+ ) })
484
+ ] }),
485
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Stack, { space: 3, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_ui.Flex, { align: "center", direction: "column", gap: 2, children: [
486
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Text, { size: 1, weight: "semibold", textOverflow: "ellipsis", style: { flex: 1 }, children: asset.originalFilename || asset._id.substring(0, 10) }),
487
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_ui.Badge, { tone: asset.size > 1048576 ? "caution" : "default", fontSize: 0, children: formatBytes(asset.size) })
488
+ ] }) }) })
489
+ ] });
490
+ };
491
+
492
+ // src/components/AssetDetailsDialog.tsx
493
+ var import_react2 = require("react");
494
+ var import_ui2 = require("@sanity/ui");
495
+ var import_router = require("sanity/router");
496
+ var import_styled_components2 = __toESM(require("styled-components"));
497
+ var import_jsx_runtime3 = require("react/jsx-runtime");
498
+ var PreviewContainer = (0, import_styled_components2.default)(import_ui2.Box)`
499
+ width: 100%;
500
+ border-radius: 8px;
501
+ overflow: hidden;
502
+ display: flex;
503
+ align-items: center;
504
+ justify-content: center;
505
+ min-height: 200px;
506
+ margin-bottom: 16px;
507
+ border: 1px solid var(--card-border-color);
508
+ `;
509
+ var PreviewImage = import_styled_components2.default.img`
510
+ max-width: 100%;
511
+ max-height: 400px;
512
+ object-fit: contain;
513
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
514
+ `;
515
+ var VideoPreview2 = import_styled_components2.default.video`
516
+ max-width: 100%;
517
+ max-height: 400px;
518
+ object-fit: contain;
519
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
520
+ background: #000;
521
+ `;
522
+ var PdfPreview = import_styled_components2.default.iframe`
523
+ width: 100%;
524
+ height: 400px;
525
+ border: none;
526
+ border-radius: 4px;
527
+ `;
528
+ var AudioPreview = import_styled_components2.default.audio`
529
+ width: 100%;
530
+ margin-top: 16px;
531
+ `;
532
+ var UsageCard = (0, import_styled_components2.default)(import_ui2.Card)`
533
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
534
+ cursor: pointer;
535
+ border: 1px solid var(--card-border-color);
536
+ &:hover {
537
+ background: var(--card-bg-color);
538
+ border-color: var(--card-focus-ring-color);
539
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
540
+ }
541
+ `;
542
+ var ViewText = (0, import_styled_components2.default)(import_ui2.Text)`
543
+ transition: all 0.2s ease;
544
+ color: var(--card-focus-ring-color);
545
+ `;
546
+ var AssetDetailsDialog = ({
547
+ asset,
548
+ onClose,
549
+ sanityClient,
550
+ onUpdate,
551
+ onDelete
552
+ }) => {
553
+ const [newName, setNewName] = (0, import_react2.useState)(asset.originalFilename || "");
554
+ const [isUpdating, setIsUpdating] = (0, import_react2.useState)(false);
555
+ const [isDeleting, setIsDeleting] = (0, import_react2.useState)(false);
556
+ const [confirmDelete, setConfirmDelete] = (0, import_react2.useState)(false);
557
+ const [usage, setUsage] = (0, import_react2.useState)([]);
558
+ const [loadingUsage, setLoadingUsage] = (0, import_react2.useState)(true);
559
+ const toast = (0, import_ui2.useToast)();
560
+ const router = (0, import_router.useRouter)();
561
+ (0, import_react2.useEffect)(() => {
562
+ async function fetchUsage() {
563
+ setLoadingUsage(true);
564
+ try {
565
+ const results = await getAssetUsage(sanityClient, asset._id);
566
+ setUsage(results);
567
+ } catch (err) {
568
+ console.error(err);
569
+ } finally {
570
+ setLoadingUsage(false);
571
+ }
572
+ }
573
+ fetchUsage();
574
+ }, [asset._id, sanityClient]);
575
+ const handleUpdate = async () => {
576
+ if (!newName.trim()) return;
577
+ setIsUpdating(true);
578
+ try {
579
+ await updateAssetFilename(sanityClient, asset._id, newName);
580
+ onUpdate(asset._id, newName);
581
+ toast.push({
582
+ status: "success",
583
+ title: "Filename updated"
584
+ });
585
+ } catch (err) {
586
+ toast.push({
587
+ status: "error",
588
+ title: "Failed to update filename"
589
+ });
590
+ } finally {
591
+ setIsUpdating(false);
592
+ }
593
+ };
594
+ const handleDelete = async () => {
595
+ setIsDeleting(true);
596
+ try {
597
+ onDelete(asset._id);
598
+ onClose();
599
+ toast.push({
600
+ status: "success",
601
+ title: "Asset deleted"
602
+ });
603
+ } catch (err) {
604
+ toast.push({
605
+ status: "error",
606
+ title: "Failed to delete asset"
607
+ });
608
+ } finally {
609
+ setIsDeleting(false);
610
+ setConfirmDelete(false);
611
+ }
612
+ };
613
+ const handleDocClick = (docId, docType) => {
614
+ if (router && typeof router.navigateIntent === "function") {
615
+ router.navigateIntent("edit", { id: docId, type: docType });
616
+ onClose();
617
+ } else {
618
+ window.location.hash = `intent/edit/id=${docId};type=${docType}`;
619
+ onClose();
620
+ }
621
+ };
622
+ const isImage = asset._type === "sanity.imageAsset";
623
+ const isVideo = asset?.mimeType?.startsWith("video/");
624
+ const isAudio = asset?.mimeType?.startsWith("audio/");
625
+ const isPdf = asset?.extension === "pdf" || asset?.mimeType === "application/pdf";
626
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
627
+ import_ui2.Dialog,
628
+ {
629
+ id: "asset-details-dialog",
630
+ header: "Asset Details",
631
+ onClose,
632
+ width: 2,
633
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { justify: "space-between", align: "center", children: [
634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
635
+ import_ui2.Tooltip,
636
+ {
637
+ content: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { padding: 2, style: { maxWidth: "350px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, children: usage.length > 0 ? "This asset is being used by documents and cannot be deleted. Remove all references first." : "Delete this asset permanently" }) }),
638
+ placement: "top",
639
+ portal: true,
640
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
641
+ import_ui2.Button,
642
+ {
643
+ icon: TrashIcon,
644
+ text: "Delete Asset",
645
+ tone: "critical",
646
+ mode: "ghost",
647
+ onClick: () => setConfirmDelete(true),
648
+ disabled: usage.length > 0 || loadingUsage
649
+ }
650
+ ) })
651
+ }
652
+ ),
653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Flex, { gap: 2, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Button, { text: "Close", mode: "ghost", onClick: onClose }) })
654
+ ] }) }),
655
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Box, { padding: 4, children: [
656
+ confirmDelete && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
657
+ import_ui2.Dialog,
658
+ {
659
+ id: "confirm-asset-delete",
660
+ header: "Delete Asset?",
661
+ onClose: () => setConfirmDelete(false),
662
+ footer: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { gap: 2, justify: "flex-end", children: [
663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Button, { text: "Cancel", mode: "ghost", onClick: () => setConfirmDelete(false) }),
664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
665
+ import_ui2.Button,
666
+ {
667
+ text: "Delete Permanently",
668
+ tone: "critical",
669
+ onClick: handleDelete,
670
+ loading: isDeleting
671
+ }
672
+ )
673
+ ] }) }),
674
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Text, { children: [
675
+ "Are you sure you want to delete",
676
+ " ",
677
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: asset.originalFilename || "this asset" }),
678
+ "? This action cannot be undone."
679
+ ] }) })
680
+ }
681
+ ),
682
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Grid, { columns: [1, 1, 2], gap: 4, children: [
683
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Stack, { space: 4, children: [
684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PreviewContainer, { children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PreviewImage, { src: `${asset.url}?w=800`, alt: "" }) : isVideo ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(VideoPreview2, { src: asset.url, controls: true, muted: true, playsInline: true }) : isPdf ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PdfPreview, { src: asset.url, title: "PDF Preview" }) : isAudio ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { direction: "column", align: "center", gap: 4, style: { width: "100%" }, children: [
685
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AudioIcon, { style: { fontSize: "64px", color: "#ef4444" } }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AudioPreview, { src: asset.url, controls: true })
687
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
688
+ import_ui2.Flex,
689
+ {
690
+ direction: "column",
691
+ align: "center",
692
+ justify: "center",
693
+ gap: 3,
694
+ style: { height: "300px", width: "100%" },
695
+ children: isPdf ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PdfIcon, { style: { fontSize: "80px", color: "#ef4444" } }) : isAudio ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AudioIcon, { style: { fontSize: "80px", color: "#ef4444" } }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
696
+ DocumentsIcon,
697
+ {
698
+ style: {
699
+ fontSize: "80px",
700
+ color: "#9ca3af"
701
+ }
702
+ }
703
+ )
704
+ }
705
+ ) }),
706
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Stack, { space: 3, children: [
707
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Box, { children: [
708
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Label, { size: 1, children: "Original Filename" }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { gap: 2, marginTop: 2, children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
711
+ import_ui2.TextInput,
712
+ {
713
+ value: newName,
714
+ onChange: (e) => setNewName(e.currentTarget.value),
715
+ placeholder: "Enter filename..."
716
+ }
717
+ ) }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
719
+ import_ui2.Button,
720
+ {
721
+ text: "Save",
722
+ tone: "primary",
723
+ onClick: handleUpdate,
724
+ loading: isUpdating,
725
+ disabled: newName === asset.originalFilename
726
+ }
727
+ )
728
+ ] })
729
+ ] }),
730
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Card, { padding: 3, border: true, radius: 2, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Stack, { space: 3, children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { weight: "bold", size: 1, children: "File Information" }),
732
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Stack, { space: 3, children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "flex-start", gap: 3, children: [
734
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { style: { minWidth: "85px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, muted: true, children: "ID:" }) }),
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, style: { wordBreak: "break-all" }, children: asset._id }) })
736
+ ] }),
737
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "center", gap: 3, children: [
738
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { style: { minWidth: "85px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, muted: true, children: "Type:" }) }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Badge, { tone: "primary", mode: "outline", fontSize: 0, children: (asset.extension || asset.mimeType?.split("/")[1])?.toUpperCase() || "OTHER" }) })
740
+ ] }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "center", gap: 3, children: [
742
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { style: { minWidth: "85px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, muted: true, children: "Size:" }) }),
743
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, children: formatBytes(asset.size) }) })
744
+ ] }),
745
+ asset.metadata?.dimensions && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "center", gap: 3, children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { style: { minWidth: "85px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, muted: true, children: "Dimensions:" }) }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Box, { flex: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Text, { size: 1, children: [
748
+ asset.metadata.dimensions.width,
749
+ " \xD7 ",
750
+ asset.metadata.dimensions.height,
751
+ " ",
752
+ "px"
753
+ ] }) })
754
+ ] })
755
+ ] }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
757
+ import_ui2.Button,
758
+ {
759
+ icon: DownloadIcon,
760
+ text: "Download File",
761
+ mode: "ghost",
762
+ onClick: () => window.open(asset.url)
763
+ }
764
+ )
765
+ ] }) })
766
+ ] })
767
+ ] }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Stack, { space: 4, children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { weight: "bold", size: 1, children: "Usage in Documents" }),
770
+ loadingUsage ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Flex, { align: "center", justify: "center", style: { padding: "40px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Spinner, {}) }) : usage?.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Stack, { space: 2, style: { maxHeight: "500px", overflowY: "auto" }, children: usage?.map((doc) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
771
+ UsageCard,
772
+ {
773
+ padding: 3,
774
+ border: true,
775
+ radius: 2,
776
+ onClick: () => handleDocClick(doc._id, doc._type),
777
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { align: "center", justify: "space-between", gap: 3, children: [
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_ui2.Flex, { direction: "column", align: "flex-start", gap: 2, children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, weight: "semibold", children: doc.title }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Badge, { tone: "caution", fontSize: 0, children: doc._type })
781
+ ] }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Flex, { align: "center", gap: 3, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ViewText, { size: 0, weight: "bold", className: "view-text", children: "VIEW \u2192" }) })
783
+ ] })
784
+ },
785
+ doc._id
786
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Card, { padding: 4, border: true, radius: 2, tone: "transparent", style: { textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_ui2.Text, { size: 1, muted: true, children: "No references found for this asset." }) })
787
+ ] })
788
+ ] })
789
+ ] })
790
+ }
791
+ );
792
+ };
793
+
794
+ // src/components/TopToolbar.tsx
795
+ var import_react3 = __toESM(require("react"));
796
+ var import_ui3 = require("@sanity/ui");
797
+ var import_styled_components3 = __toESM(require("styled-components"));
798
+ var import_jsx_runtime4 = require("react/jsx-runtime");
799
+ var StickyCard = (0, import_styled_components3.default)(import_ui3.Card)`
800
+ position: sticky;
801
+ top: 0;
802
+ z-index: 10;
803
+ `;
804
+ var SearchBox = (0, import_styled_components3.default)(import_ui3.Box)`
805
+ flex: 1;
806
+ `;
807
+ var FilterBox = (0, import_styled_components3.default)(import_ui3.Box)`
808
+ width: 140px;
809
+ `;
810
+ var TopToolbar = ({
811
+ searchQuery,
812
+ setSearchQuery,
813
+ sortBy,
814
+ setSortBy,
815
+ assetType,
816
+ setAssetType,
817
+ sizeFilter,
818
+ setSizeFilter,
819
+ onReset,
820
+ onUpload,
821
+ isUploading
822
+ }) => {
823
+ const fileInputRef = import_react3.default.useRef(null);
824
+ const handleUploadClick = () => {
825
+ fileInputRef.current?.click();
826
+ };
827
+ const handleFileChange = (e) => {
828
+ if (e.target.files && e.target.files.length > 0) {
829
+ onUpload(e.target.files);
830
+ e.target.value = "";
831
+ }
832
+ };
833
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(StickyCard, { padding: 3, borderBottom: true, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_ui3.Flex, { align: "center", gap: 3, children: [
834
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
835
+ "input",
836
+ {
837
+ type: "file",
838
+ ref: fileInputRef,
839
+ style: { display: "none" },
840
+ onChange: handleFileChange,
841
+ multiple: true,
842
+ accept: "image/*,video/*,audio/*,application/*,text/*"
843
+ }
844
+ ),
845
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SearchBox, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
846
+ import_ui3.TextInput,
847
+ {
848
+ icon: SearchIcon,
849
+ placeholder: "Search name, extension, ID...",
850
+ value: searchQuery,
851
+ onChange: (e) => setSearchQuery(e.currentTarget.value),
852
+ fontSize: 1
853
+ }
854
+ ) }),
855
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Box, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
856
+ import_ui3.Select,
857
+ {
858
+ fontSize: 1,
859
+ value: sortBy,
860
+ onChange: (e) => setSortBy(e.currentTarget.value),
861
+ children: [
862
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "_createdAt", children: "Upload Date" }),
863
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "size", children: "File Size" }),
864
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "originalFilename", children: "Name A-Z" })
865
+ ]
866
+ }
867
+ ) }),
868
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FilterBox, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
869
+ import_ui3.Select,
870
+ {
871
+ fontSize: 1,
872
+ value: assetType,
873
+ onChange: (e) => setAssetType(e.currentTarget.value),
874
+ children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "all", children: "All Types" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "image", children: "Images" }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "video", children: "Videos" }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "audio", children: "Audio" }),
879
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "file", children: "Other Files" })
880
+ ]
881
+ }
882
+ ) }),
883
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FilterBox, { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
884
+ import_ui3.Select,
885
+ {
886
+ fontSize: 1,
887
+ value: sizeFilter,
888
+ onChange: (e) => setSizeFilter(e.currentTarget.value),
889
+ children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "all", children: "All Sizes" }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "small", children: "< 100 KB" }),
892
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "medium", children: "100KB - 1MB" }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "large", children: "> 1MB" })
894
+ ]
895
+ }
896
+ ) }),
897
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_ui3.Button, { icon: ResetIcon, mode: "ghost", onClick: onReset, title: "Reset filters" }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
899
+ import_ui3.Button,
900
+ {
901
+ icon: UploadIcon,
902
+ text: isUploading ? "Uploading..." : "Upload",
903
+ tone: "primary",
904
+ onClick: handleUploadClick,
905
+ loading: isUploading
906
+ }
907
+ )
908
+ ] }) });
909
+ };
910
+
911
+ // src/components/tabs/SizeAnalyzer.tsx
912
+ var import_react4 = __toESM(require("react"));
913
+ var import_ui4 = require("@sanity/ui");
914
+ var import_styled_components4 = __toESM(require("styled-components"));
915
+ var import_jsx_runtime5 = require("react/jsx-runtime");
916
+ var TableCard = (0, import_styled_components4.default)(import_ui4.Card)`
917
+ border: 1px solid var(--card-border-color);
918
+ `;
919
+ var TableHeader = (0, import_styled_components4.default)(import_ui4.Box)`
920
+ border-bottom: 1px solid var(--card-border-color);
921
+ `;
922
+ var TableRow = (0, import_styled_components4.default)(import_ui4.Flex)`
923
+ border-bottom: ${(props) => props.$isLast ? "none" : "1px solid var(--card-border-color)"};
924
+ transition: background 0.2s ease;
925
+ &:hover {
926
+ background: var(--card-bg-color);
927
+ filter: brightness(0.95);
928
+ }
929
+ `;
930
+ var Thumbnail = import_styled_components4.default.img`
931
+ width: 40px;
932
+ height: 40px;
933
+ object-fit: cover;
934
+ border-radius: 4px;
935
+ background-color: var(--card-border-color);
936
+ `;
937
+ var SizeAnalyzer = ({ assets }) => {
938
+ const [sortOrder, setSortOrder] = import_react4.default.useState("desc");
939
+ const sortedAssets = import_react4.default.useMemo(() => {
940
+ return [...assets].sort((a, b) => {
941
+ return sortOrder === "desc" ? b.size - a.size : a.size - b.size;
942
+ });
943
+ }, [assets, sortOrder]);
944
+ const toggleSort = () => {
945
+ setSortOrder((prev) => prev === "desc" ? "asc" : "desc");
946
+ };
947
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui4.Stack, { space: 4, children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Heading, { size: 1, children: "Image Size Analysis" }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(TableCard, { border: true, radius: 2, children: [
950
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TableHeader, { padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui4.Flex, { align: "center", gap: 3, children: [
951
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { flex: 1 }, paddingLeft: 3, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Text, { size: 1, weight: "bold", children: "File" }) }),
952
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { width: "120px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Text, { size: 1, weight: "bold", children: "Dimensions" }) }),
953
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { width: "140px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui4.Flex, { align: "center", gap: 1, children: [
954
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Text, { size: 1, weight: "bold", children: "Weight" }),
955
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
956
+ import_ui4.Button,
957
+ {
958
+ icon: SortIcon,
959
+ mode: "bleed",
960
+ padding: 2,
961
+ onClick: toggleSort,
962
+ fontSize: 1,
963
+ title: `Sort by weight ${sortOrder === "desc" ? "Ascending" : "Descending"}`,
964
+ style: { transform: sortOrder === "asc" ? "rotate(180deg)" : "none" }
965
+ }
966
+ )
967
+ ] }) })
968
+ ] }) }),
969
+ sortedAssets.slice(0, 50).map((asset, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
970
+ TableRow,
971
+ {
972
+ padding: 3,
973
+ align: "center",
974
+ gap: 3,
975
+ $isLast: i === (sortedAssets.length < 50 ? sortedAssets.length - 1 : 49),
976
+ children: [
977
+ asset._type === "sanity.imageAsset" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Thumbnail, { src: `${asset.url}?w=40&h=40&fit=crop`, alt: "" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
978
+ import_ui4.Box,
979
+ {
980
+ style: {
981
+ width: "40px",
982
+ height: "40px",
983
+ display: "flex",
984
+ alignItems: "center",
985
+ justifyContent: "center",
986
+ background: "var(--card-border-color)",
987
+ borderRadius: "4px"
988
+ },
989
+ children: asset.mimeType?.startsWith("video/") ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DocumentsIcon, { style: { fontSize: "20px", color: "#3b82f6" } }) : asset.mimeType?.startsWith("audio/") ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AudioIcon, { style: { fontSize: "20px", color: "#ef4444" } }) : asset.extension === "pdf" || asset.mimeType === "application/pdf" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PdfIcon, { style: { fontSize: "20px", color: "#ef4444" } }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(DocumentsIcon, { style: { fontSize: "20px", color: "#9ca3af" } })
990
+ }
991
+ ),
992
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Text, { size: 1, weight: "semibold", children: asset.originalFilename || asset._id }) }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { width: "120px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_ui4.Text, { size: 1, children: [
994
+ asset.metadata?.dimensions?.width || "\u2014",
995
+ " x",
996
+ " ",
997
+ asset.metadata?.dimensions?.height || "\u2014"
998
+ ] }) }),
999
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_ui4.Box, { style: { width: "140px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1000
+ import_ui4.Badge,
1001
+ {
1002
+ tone: asset.size > 1048576 ? "critical" : asset.size > 5e5 ? "caution" : "positive",
1003
+ children: formatBytes(asset.size)
1004
+ }
1005
+ ) })
1006
+ ]
1007
+ },
1008
+ asset._id
1009
+ ))
1010
+ ] })
1011
+ ] });
1012
+ };
1013
+
1014
+ // src/components/tabs/UnusedAssets.tsx
1015
+ var import_react5 = require("react");
1016
+ var import_ui5 = require("@sanity/ui");
1017
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1018
+ var UnusedAssets = ({
1019
+ unusedAssets,
1020
+ onBulkDelete,
1021
+ onAssetClick
1022
+ }) => {
1023
+ const [selectedIds, setSelectedIds] = (0, import_react5.useState)([]);
1024
+ const [confirmDelete, setConfirmDelete] = (0, import_react5.useState)({
1025
+ show: false,
1026
+ ids: []
1027
+ });
1028
+ const handleSelect = (id, selected) => {
1029
+ setSelectedIds((prev) => selected ? [...prev, id] : prev.filter((i) => i !== id));
1030
+ };
1031
+ const handleSelectAll = (checked) => {
1032
+ setSelectedIds(checked ? unusedAssets.map((a) => a._id) : []);
1033
+ };
1034
+ const triggerBulkDelete = (ids) => {
1035
+ setConfirmDelete({ show: true, ids });
1036
+ };
1037
+ const handleConfirmDelete = () => {
1038
+ onBulkDelete(confirmDelete.ids);
1039
+ setSelectedIds((prev) => prev.filter((id) => !confirmDelete.ids.includes(id)));
1040
+ setConfirmDelete({ show: false, ids: [] });
1041
+ };
1042
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Stack, { space: 4, children: [
1043
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Flex, { align: "center", justify: "space-between", children: [
1044
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Stack, { space: 3, children: [
1045
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Heading, { size: 1, children: "Bulk Deletion of Unused Assets" }),
1046
+ unusedAssets.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Flex, { align: "center", gap: 3, children: [
1047
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1048
+ import_ui5.Checkbox,
1049
+ {
1050
+ id: "select-all-unused",
1051
+ checked: selectedIds.length === unusedAssets.length && unusedAssets.length > 0,
1052
+ indeterminate: selectedIds.length > 0 && selectedIds.length < unusedAssets.length,
1053
+ onChange: (e) => handleSelectAll(e.currentTarget.checked)
1054
+ }
1055
+ ),
1056
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Text, { size: 1, weight: "semibold", muted: true, children: selectedIds.length > 0 ? `${selectedIds.length} Selected` : "Select All" })
1057
+ ] })
1058
+ ] }),
1059
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Flex, { gap: 2, children: [
1060
+ selectedIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1061
+ import_ui5.Button,
1062
+ {
1063
+ icon: TrashIcon,
1064
+ tone: "critical",
1065
+ text: `Delete Selected (${selectedIds.length})`,
1066
+ onClick: () => triggerBulkDelete(selectedIds)
1067
+ }
1068
+ ),
1069
+ unusedAssets.length > 0 && selectedIds.length !== unusedAssets.length && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1070
+ import_ui5.Button,
1071
+ {
1072
+ mode: "ghost",
1073
+ icon: TrashIcon,
1074
+ tone: "critical",
1075
+ text: `Delete All Unused (${unusedAssets.length})`,
1076
+ onClick: () => triggerBulkDelete(unusedAssets.map((a) => a._id))
1077
+ }
1078
+ )
1079
+ ] })
1080
+ ] }),
1081
+ unusedAssets.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Card, { padding: 5, border: true, radius: 3, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Text, { align: "center", children: "No unused assets found." }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Grid, { columns: [2, 3, 4, 5, 6], gap: 3, children: unusedAssets.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1082
+ AssetCard,
1083
+ {
1084
+ asset,
1085
+ onClick: onAssetClick,
1086
+ isSelected: selectedIds.includes(asset._id),
1087
+ onSelect: handleSelect
1088
+ },
1089
+ asset._id
1090
+ )) }),
1091
+ confirmDelete.show && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1092
+ import_ui5.Dialog,
1093
+ {
1094
+ id: "confirm-bulk-delete",
1095
+ header: `Delete ${confirmDelete.ids.length} Asset(s)?`,
1096
+ onClose: () => setConfirmDelete({ show: false, ids: [] }),
1097
+ footer: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Box, { padding: 3, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Flex, { gap: 2, justify: "flex-end", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1099
+ import_ui5.Button,
1100
+ {
1101
+ text: "Cancel",
1102
+ mode: "ghost",
1103
+ onClick: () => setConfirmDelete({ show: false, ids: [] })
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Button, { text: "Delete Permanently", tone: "critical", onClick: handleConfirmDelete })
1107
+ ] }) }),
1108
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_ui5.Box, { padding: 4, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_ui5.Text, { children: [
1109
+ "Are you sure you want to delete ",
1110
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: confirmDelete.ids.length }),
1111
+ " asset(s)? This action cannot be undone and will permanently remove them from your project."
1112
+ ] }) })
1113
+ }
1114
+ )
1115
+ ] });
1116
+ };
1117
+
1118
+ // src/components/SmartAssetManagerTool.tsx
1119
+ var import_styled_components5 = __toESM(require("styled-components"));
1120
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1121
+ var AppContainer = (0, import_styled_components5.default)(import_ui6.Card)`
1122
+ height: 100vh;
1123
+ display: flex;
1124
+ flex-direction: column;
1125
+ `;
1126
+ var ScrollableContent = (0, import_styled_components5.default)(import_ui6.Box)`
1127
+ flex: 1;
1128
+ overflow-y: auto;
1129
+ `;
1130
+ var TabNav = (0, import_styled_components5.default)(import_ui6.Card)`
1131
+ z-index: 10;
1132
+ `;
1133
+ function SmartAssetManagerTool() {
1134
+ const sanityClient = (0, import_sanity.useClient)({ apiVersion: "2025-02-07" });
1135
+ const [activeTab, setActiveTab] = (0, import_react6.useState)("all");
1136
+ const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
1137
+ const [sortBy, setSortBy] = (0, import_react6.useState)("_createdAt");
1138
+ const [assetType, setAssetType] = (0, import_react6.useState)("all");
1139
+ const [sizeFilter, setSizeFilter] = (0, import_react6.useState)("all");
1140
+ const limit = 20;
1141
+ const [currentPage, setCurrentPage] = (0, import_react6.useState)(1);
1142
+ const { assets, loading, total, refreshAssets } = useAssets(
1143
+ sanityClient,
1144
+ searchQuery,
1145
+ sortBy,
1146
+ assetType,
1147
+ (currentPage - 1) * limit,
1148
+ limit
1149
+ );
1150
+ const [scanning, setScanning] = (0, import_react6.useState)(false);
1151
+ const [isUploading, setIsUploading] = (0, import_react6.useState)(false);
1152
+ const toast = (0, import_ui6.useToast)();
1153
+ const [unusedAssets, setUnusedAssets] = (0, import_react6.useState)([]);
1154
+ const [selectedAsset, setSelectedAsset] = (0, import_react6.useState)(null);
1155
+ const filteredAssets = (0, import_react6.useMemo)(() => {
1156
+ return assets.filter((asset) => {
1157
+ if (sizeFilter === "small") return asset.size < 102400;
1158
+ if (sizeFilter === "medium") return asset.size >= 102400 && asset.size < 1048576;
1159
+ if (sizeFilter === "large") return asset.size >= 1048576;
1160
+ return true;
1161
+ });
1162
+ }, [assets, sizeFilter]);
1163
+ (0, import_react6.useEffect)(() => {
1164
+ setCurrentPage(1);
1165
+ }, [searchQuery, assetType, sizeFilter, activeTab]);
1166
+ const handleFindUnused = async () => {
1167
+ setScanning(true);
1168
+ const unused = await findUnusedAssets(sanityClient);
1169
+ setUnusedAssets(unused);
1170
+ setScanning(false);
1171
+ setActiveTab("unused");
1172
+ };
1173
+ const handleDeleteAsset = async (id) => {
1174
+ const ids = Array.isArray(id) ? id : [id];
1175
+ await deleteAssets(sanityClient, ids);
1176
+ refreshAssets();
1177
+ setUnusedAssets((prev) => prev.filter((a) => !ids.includes(a._id)));
1178
+ if (activeTab === "unused") handleFindUnused();
1179
+ };
1180
+ const handleResetFilters = () => {
1181
+ setSearchQuery("");
1182
+ setAssetType("all");
1183
+ setSizeFilter("all");
1184
+ setCurrentPage(1);
1185
+ };
1186
+ const handleUpload = async (files) => {
1187
+ if (files.length > 5) {
1188
+ toast.push({
1189
+ status: "error",
1190
+ title: "Upload limit exceeded",
1191
+ description: "You can only upload up to 5 files at a time."
1192
+ });
1193
+ return;
1194
+ }
1195
+ setIsUploading(true);
1196
+ const fileArray = Array.from(files);
1197
+ const filenames = fileArray.map((f) => f.name);
1198
+ try {
1199
+ const existingAssetNames = await sanityClient.fetch(
1200
+ `*[_type in ["sanity.imageAsset", "sanity.fileAsset"] && originalFilename in $filenames].originalFilename`,
1201
+ { filenames }
1202
+ );
1203
+ const filesToUpload = fileArray.filter((f) => !existingAssetNames.includes(f.name));
1204
+ const skippedCount = fileArray.length - filesToUpload.length;
1205
+ if (filesToUpload.length === 0) {
1206
+ setIsUploading(false);
1207
+ if (skippedCount > 0) {
1208
+ toast.push({
1209
+ status: "info",
1210
+ title: "No new files were added",
1211
+ description: `All ${skippedCount} file(s) already exist in your library.`
1212
+ });
1213
+ }
1214
+ return;
1215
+ }
1216
+ const uploadPromises = filesToUpload.map(async (file) => {
1217
+ const type = file.type.startsWith("image/") ? "image" : "file";
1218
+ return sanityClient.assets.upload(type, file, {
1219
+ filename: file.name
1220
+ });
1221
+ });
1222
+ await Promise.all(uploadPromises);
1223
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1224
+ refreshAssets();
1225
+ setIsUploading(false);
1226
+ toast.push({
1227
+ status: "success",
1228
+ title: "Upload successful",
1229
+ description: `Uploaded ${filesToUpload.length} new file(s). ${skippedCount > 0 ? `Skipped ${skippedCount} existing files.` : ""}`
1230
+ });
1231
+ } catch (err) {
1232
+ console.error("Upload failed:", err);
1233
+ setIsUploading(false);
1234
+ toast.push({
1235
+ status: "error",
1236
+ title: "Upload failed",
1237
+ description: "An error occurred while uploading your files."
1238
+ });
1239
+ }
1240
+ };
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(AppContainer, { height: "fill", children: [
1242
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(TabNav, { borderBottom: true, padding: 2, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Flex, { gap: 1, children: [
1243
+ { id: "all", label: "All Assets" },
1244
+ { id: "analysis", label: "Size Analyzer" },
1245
+ { id: "unused", label: "Unused Assets", onClick: handleFindUnused }
1246
+ ].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1247
+ import_ui6.Button,
1248
+ {
1249
+ mode: activeTab === tab.id ? "default" : "bleed",
1250
+ padding: 3,
1251
+ text: tab.label,
1252
+ onClick: () => {
1253
+ setActiveTab(tab.id);
1254
+ if (tab.onClick) tab.onClick();
1255
+ },
1256
+ selected: activeTab === tab.id
1257
+ },
1258
+ tab.id
1259
+ )) }) }),
1260
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(ScrollableContent, { children: [
1261
+ activeTab === "all" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1262
+ TopToolbar,
1263
+ {
1264
+ searchQuery,
1265
+ setSearchQuery,
1266
+ sortBy,
1267
+ setSortBy,
1268
+ assetType,
1269
+ setAssetType,
1270
+ sizeFilter,
1271
+ setSizeFilter,
1272
+ onReset: handleResetFilters,
1273
+ onUpload: handleUpload,
1274
+ isUploading
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Box, { padding: 4, children: loading || scanning ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Flex, { align: "center", justify: "center", style: { minHeight: "400px" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui6.Stack, { space: 4, children: [
1278
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Flex, { align: "center", justify: "center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Spinner, { muted: true }) }),
1279
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Text, { size: 1, muted: true, children: "Syncing with media library..." })
1280
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui6.Box, { children: [
1281
+ activeTab === "all" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui6.Box, { children: [
1282
+ filteredAssets.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Card, { padding: 5, border: true, radius: 3, style: { textAlign: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Text, { muted: true, children: "No assets found matching your filters." }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Grid, { columns: [2, 3, 4, 5, 6], gap: 3, children: filteredAssets.map((asset) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AssetCard, { asset, onClick: setSelectedAsset }, asset._id)) }),
1283
+ total > limit && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui6.Flex, { justify: "center", align: "center", gap: 3, marginTop: 5, marginBottom: 2, children: [
1284
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1285
+ import_ui6.Button,
1286
+ {
1287
+ padding: 3,
1288
+ mode: "ghost",
1289
+ text: "Previous",
1290
+ disabled: currentPage === 1 || loading,
1291
+ onClick: () => setCurrentPage((p) => Math.max(1, p - 1))
1292
+ }
1293
+ ),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_ui6.Card, { padding: 3, radius: 2, border: true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_ui6.Text, { size: 1, weight: "semibold", children: [
1295
+ "Page ",
1296
+ currentPage,
1297
+ " of ",
1298
+ Math.ceil(total / limit)
1299
+ ] }) }),
1300
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1301
+ import_ui6.Button,
1302
+ {
1303
+ padding: 3,
1304
+ mode: "ghost",
1305
+ text: "Next",
1306
+ disabled: currentPage >= Math.ceil(total / limit) || loading,
1307
+ onClick: () => setCurrentPage((p) => p + 1)
1308
+ }
1309
+ )
1310
+ ] })
1311
+ ] }),
1312
+ activeTab === "analysis" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SizeAnalyzer, { assets }),
1313
+ activeTab === "unused" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1314
+ UnusedAssets,
1315
+ {
1316
+ unusedAssets,
1317
+ onBulkDelete: handleDeleteAsset,
1318
+ onAssetClick: setSelectedAsset
1319
+ }
1320
+ )
1321
+ ] }) })
1322
+ ] }),
1323
+ selectedAsset && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1324
+ AssetDetailsDialog,
1325
+ {
1326
+ asset: selectedAsset,
1327
+ sanityClient,
1328
+ onClose: () => setSelectedAsset(null),
1329
+ onUpdate: (_id, newName) => {
1330
+ refreshAssets();
1331
+ setSelectedAsset((prev) => prev ? { ...prev, originalFilename: newName } : null);
1332
+ },
1333
+ onDelete: handleDeleteAsset
1334
+ }
1335
+ )
1336
+ ] });
1337
+ }
1338
+
1339
+ // src/index.ts
1340
+ var smartAssetManager = (0, import_sanity2.definePlugin)(() => {
1341
+ return {
1342
+ name: "sanity-plugin-smart-asset-manager",
1343
+ tools: [
1344
+ {
1345
+ name: "smart-asset-manager",
1346
+ title: "Smart Asset Manager",
1347
+ component: SmartAssetManagerTool,
1348
+ icon: FolderIcon
1349
+ }
1350
+ ]
1351
+ };
1352
+ });
1353
+ // Annotate the CommonJS export names for ESM import in node:
1354
+ 0 && (module.exports = {
1355
+ smartAssetManager
1356
+ });