@thetechfossil/upfiles 1.0.9 → 1.0.12
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/README.md +10 -2
- package/dist/index.d.mts +54 -7
- package/dist/index.d.ts +54 -7
- package/dist/index.js +1100 -411
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1051 -347
- package/dist/index.mjs.map +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,64 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React4 = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var Dialog2 = require('@radix-ui/react-dialog');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
17
20
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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);
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
var
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
ConnectProjectDialog: () => ConnectProjectDialog,
|
|
34
|
-
ImageManager: () => ImageManager,
|
|
35
|
-
ProjectFilesWidget: () => ProjectFilesWidget,
|
|
36
|
-
UpfilesClient: () => UpfilesClient,
|
|
37
|
-
Uploader: () => Uploader,
|
|
38
|
-
addApiKeyManually: () => addApiKeyManually,
|
|
39
|
-
connectProject: () => connectProject,
|
|
40
|
-
connectUsingApiKey: () => connectUsingApiKey,
|
|
41
|
-
createClientWithKey: () => createClientWithKey,
|
|
42
|
-
createProject: () => createProject,
|
|
43
|
-
fetchFileThumbnails: () => fetchFileThumbnails,
|
|
44
|
-
fetchProjectFiles: () => fetchProjectFiles,
|
|
45
|
-
fetchProjectImagesWithThumbs: () => fetchProjectImagesWithThumbs,
|
|
46
|
-
listProjects: () => listProjects
|
|
47
|
-
});
|
|
48
|
-
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
var React4__namespace = /*#__PURE__*/_interopNamespace(React4);
|
|
26
|
+
var Dialog2__namespace = /*#__PURE__*/_interopNamespace(Dialog2);
|
|
49
27
|
|
|
50
28
|
// src/client.ts
|
|
51
29
|
var UpfilesClient = class {
|
|
52
30
|
constructor(opts) {
|
|
53
31
|
this.baseUrl = opts.baseUrl?.replace(/\/$/, "");
|
|
54
|
-
this.presignPath = opts.presignPath ?? "/api/plugin/upload
|
|
32
|
+
this.presignPath = opts.presignPath ?? "/api/plugin/upload";
|
|
55
33
|
this.presignUrl = opts.presignUrl;
|
|
56
34
|
this.headers = opts.headers;
|
|
57
35
|
this.withCredentials = opts.withCredentials;
|
|
58
36
|
this.apiKey = opts.apiKey;
|
|
59
37
|
this.apiKeyHeader = opts.apiKeyHeader ?? "authorization";
|
|
60
38
|
this.thumbnailsPath = opts.thumbnailsPath ?? "/api/plugin/thumbnails";
|
|
61
|
-
this.completionPath = opts.completionPath ?? "/api/plugin/
|
|
39
|
+
this.completionPath = opts.completionPath ?? "/api/plugin/files";
|
|
40
|
+
this.foldersPath = opts.foldersPath ?? "/api/plugin/folders";
|
|
62
41
|
this.callCompletionEndpoint = opts.callCompletionEndpoint ?? false;
|
|
63
42
|
}
|
|
64
43
|
async getPresignedUrl(params) {
|
|
@@ -181,6 +160,7 @@ var UpfilesClient = class {
|
|
|
181
160
|
}
|
|
182
161
|
throw new Error(msg);
|
|
183
162
|
}
|
|
163
|
+
return res.json();
|
|
184
164
|
}
|
|
185
165
|
async getThumbnails(fileKey) {
|
|
186
166
|
const target = this.baseUrl ? `${this.baseUrl}${this.thumbnailsPath}` : this.thumbnailsPath;
|
|
@@ -241,6 +221,65 @@ var UpfilesClient = class {
|
|
|
241
221
|
const data = await res.json();
|
|
242
222
|
return { masterWebp: data.masterWebp, thumbnails: data.thumbnails ?? [] };
|
|
243
223
|
}
|
|
224
|
+
async getFolders(parentId) {
|
|
225
|
+
const target = this.baseUrl ? `${this.baseUrl}${this.foldersPath}` : this.foldersPath;
|
|
226
|
+
const url = parentId ? `${target}?parentId=${encodeURIComponent(parentId)}` : target;
|
|
227
|
+
const headers = { ...this.headers || {} };
|
|
228
|
+
if (this.apiKey) {
|
|
229
|
+
if (this.apiKeyHeader === "authorization") {
|
|
230
|
+
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
231
|
+
} else if (this.apiKeyHeader === "x-api-key") {
|
|
232
|
+
headers["x-api-key"] = this.apiKey;
|
|
233
|
+
} else {
|
|
234
|
+
headers["x-up-api-key"] = this.apiKey;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const res = await fetch(url, { method: "GET", headers, credentials: this.withCredentials ? "include" : "same-origin" });
|
|
238
|
+
if (!res.ok) {
|
|
239
|
+
let msg = "Failed to fetch folders";
|
|
240
|
+
try {
|
|
241
|
+
const data2 = await res.json();
|
|
242
|
+
msg = data2.error || msg;
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
throw new Error(msg);
|
|
246
|
+
}
|
|
247
|
+
const data = await res.json();
|
|
248
|
+
return data?.folders ?? [];
|
|
249
|
+
}
|
|
250
|
+
async createFolder(name, parentId) {
|
|
251
|
+
const target = this.baseUrl ? `${this.baseUrl}${this.foldersPath}` : this.foldersPath;
|
|
252
|
+
const headers = {
|
|
253
|
+
"content-type": "application/json",
|
|
254
|
+
...this.headers || {}
|
|
255
|
+
};
|
|
256
|
+
if (this.apiKey) {
|
|
257
|
+
if (this.apiKeyHeader === "authorization") {
|
|
258
|
+
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
259
|
+
} else if (this.apiKeyHeader === "x-api-key") {
|
|
260
|
+
headers["x-api-key"] = this.apiKey;
|
|
261
|
+
} else {
|
|
262
|
+
headers["x-up-api-key"] = this.apiKey;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const res = await fetch(target, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers,
|
|
268
|
+
credentials: this.withCredentials ? "include" : "same-origin",
|
|
269
|
+
body: JSON.stringify({ name, parentId })
|
|
270
|
+
});
|
|
271
|
+
if (!res.ok) {
|
|
272
|
+
let msg = "Failed to create folder";
|
|
273
|
+
try {
|
|
274
|
+
const data2 = await res.json();
|
|
275
|
+
msg = data2.error || msg;
|
|
276
|
+
} catch {
|
|
277
|
+
}
|
|
278
|
+
throw new Error(msg);
|
|
279
|
+
}
|
|
280
|
+
const data = await res.json();
|
|
281
|
+
return data.folder;
|
|
282
|
+
}
|
|
244
283
|
async getProjectFiles(params) {
|
|
245
284
|
const basePath = this.thumbnailsPath.replace(/\/thumbnails$/, "");
|
|
246
285
|
const filesPath = `${basePath}/files`;
|
|
@@ -267,13 +306,24 @@ var UpfilesClient = class {
|
|
|
267
306
|
throw new Error(msg);
|
|
268
307
|
}
|
|
269
308
|
const data = await res.json();
|
|
270
|
-
return
|
|
309
|
+
return {
|
|
310
|
+
files: data?.files ?? [],
|
|
311
|
+
updatedAt: res.headers.get("X-Files-Updated-At")
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
getEventsUrl(projectId) {
|
|
315
|
+
this.thumbnailsPath.replace(/\/thumbnails$/, "");
|
|
316
|
+
const target = this.baseUrl ? `${this.baseUrl}/api/events` : "/api/events";
|
|
317
|
+
const url = new URL(target, window.location.href);
|
|
318
|
+
if (this.apiKey) {
|
|
319
|
+
url.searchParams.set("apiKey", this.apiKey);
|
|
320
|
+
}
|
|
321
|
+
if (projectId) {
|
|
322
|
+
url.searchParams.set("projectId", projectId);
|
|
323
|
+
}
|
|
324
|
+
return url.toString();
|
|
271
325
|
}
|
|
272
326
|
};
|
|
273
|
-
|
|
274
|
-
// src/Uploader.tsx
|
|
275
|
-
var import_react = __toESM(require("react"));
|
|
276
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
277
327
|
var Uploader = ({
|
|
278
328
|
clientOptions,
|
|
279
329
|
multiple = true,
|
|
@@ -292,16 +342,16 @@ var Uploader = ({
|
|
|
292
342
|
projectId,
|
|
293
343
|
folderPath,
|
|
294
344
|
fetchThumbnails,
|
|
295
|
-
autoRecordToDb =
|
|
345
|
+
autoRecordToDb = true,
|
|
296
346
|
recordUrl = "/api/files",
|
|
297
347
|
autoUpload = false
|
|
298
348
|
}) => {
|
|
299
|
-
const inputRef =
|
|
300
|
-
const client =
|
|
301
|
-
const [files, setFiles] =
|
|
302
|
-
const [isUploading, setIsUploading] =
|
|
303
|
-
const [isDragOver, setIsDragOver] =
|
|
304
|
-
const addFiles =
|
|
349
|
+
const inputRef = React4.useRef(null);
|
|
350
|
+
const client = React4.useMemo(() => new UpfilesClient(clientOptions ?? {}), [clientOptions]);
|
|
351
|
+
const [files, setFiles] = React4.useState([]);
|
|
352
|
+
const [isUploading, setIsUploading] = React4.useState(false);
|
|
353
|
+
const [isDragOver, setIsDragOver] = React4.useState(false);
|
|
354
|
+
const addFiles = React4.useCallback((incoming) => {
|
|
305
355
|
const filtered = incoming.filter((f2) => {
|
|
306
356
|
if (f2.size > maxFileSize) {
|
|
307
357
|
return false;
|
|
@@ -320,18 +370,18 @@ var Uploader = ({
|
|
|
320
370
|
}));
|
|
321
371
|
setFiles((prev) => [...prev, ...mapped]);
|
|
322
372
|
}, [files.length, maxFileSize, maxFiles]);
|
|
323
|
-
const onInputChange =
|
|
373
|
+
const onInputChange = React4.useCallback((e) => {
|
|
324
374
|
const fs = Array.from(e.target.files || []);
|
|
325
375
|
addFiles(fs);
|
|
326
376
|
if (inputRef.current) inputRef.current.value = "";
|
|
327
377
|
}, [addFiles]);
|
|
328
|
-
const handleDrop =
|
|
378
|
+
const handleDrop = React4.useCallback((e) => {
|
|
329
379
|
e.preventDefault();
|
|
330
380
|
e.stopPropagation();
|
|
331
381
|
setIsDragOver(false);
|
|
332
382
|
addFiles(Array.from(e.dataTransfer.files || []));
|
|
333
383
|
}, [addFiles]);
|
|
334
|
-
const uploadOne =
|
|
384
|
+
const uploadOne = React4.useCallback(async (item) => {
|
|
335
385
|
const update = (patch) => {
|
|
336
386
|
setFiles((prev) => prev.map((f2) => f2.id === item.id ? { ...f2, ...patch } : f2));
|
|
337
387
|
};
|
|
@@ -366,9 +416,10 @@ var Uploader = ({
|
|
|
366
416
|
xhr.setRequestHeader("Content-Type", item.type || "application/octet-stream");
|
|
367
417
|
xhr.send(item.file);
|
|
368
418
|
});
|
|
419
|
+
let completionResult = null;
|
|
369
420
|
if (autoRecordToDb && projectId) {
|
|
370
421
|
try {
|
|
371
|
-
await client.completeUpload({
|
|
422
|
+
completionResult = await client.completeUpload({
|
|
372
423
|
fileKey: presign.fileKey,
|
|
373
424
|
originalName: item.name,
|
|
374
425
|
size: item.size,
|
|
@@ -382,15 +433,21 @@ var Uploader = ({
|
|
|
382
433
|
}
|
|
383
434
|
let thumbs;
|
|
384
435
|
let masterWebp;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
436
|
+
if (completionResult?.thumbnails?.length > 0) {
|
|
437
|
+
thumbs = completionResult.thumbnails;
|
|
438
|
+
masterWebp = completionResult.masterWebp;
|
|
439
|
+
} else if (fetchThumbnails) {
|
|
440
|
+
const fileKeyToUse = completionResult?.file?.key || presign.fileKey;
|
|
441
|
+
try {
|
|
442
|
+
const result2 = await client.generateThumbnails(fileKeyToUse);
|
|
388
443
|
thumbs = result2.thumbnails;
|
|
389
444
|
masterWebp = result2.masterWebp;
|
|
445
|
+
} catch (e) {
|
|
446
|
+
console.warn("Failed to generate thumbnails:", e);
|
|
390
447
|
}
|
|
391
|
-
} catch (e) {
|
|
392
|
-
console.warn("Failed to generate thumbnails:", e);
|
|
393
448
|
}
|
|
449
|
+
const finalFileKey = completionResult?.file?.key || presign.fileKey;
|
|
450
|
+
const finalPublicUrl = completionResult?.file?.url || presign.publicUrl;
|
|
394
451
|
const result = {
|
|
395
452
|
...item,
|
|
396
453
|
status: "success",
|
|
@@ -413,7 +470,7 @@ var Uploader = ({
|
|
|
413
470
|
throw err;
|
|
414
471
|
}
|
|
415
472
|
}, [client, projectId, folderPath, fetchThumbnails, autoRecordToDb, recordUrl, clientOptions, onUploadBegin, onUploadProgress, onClientUploadComplete]);
|
|
416
|
-
const uploadAll =
|
|
473
|
+
const uploadAll = React4.useCallback(async () => {
|
|
417
474
|
const targets = files.filter((f2) => f2.status === "pending");
|
|
418
475
|
if (!targets.length) return;
|
|
419
476
|
setIsUploading(true);
|
|
@@ -433,7 +490,7 @@ var Uploader = ({
|
|
|
433
490
|
setIsUploading(false);
|
|
434
491
|
}
|
|
435
492
|
}, [files, onComplete, onError, uploadOne]);
|
|
436
|
-
|
|
493
|
+
React4__namespace.default.useEffect(() => {
|
|
437
494
|
if (autoUpload && files.some((f2) => f2.status === "pending") && !isUploading) {
|
|
438
495
|
const timer = setTimeout(() => {
|
|
439
496
|
uploadAll();
|
|
@@ -441,8 +498,8 @@ var Uploader = ({
|
|
|
441
498
|
return () => clearTimeout(timer);
|
|
442
499
|
}
|
|
443
500
|
}, [autoUpload, files, isUploading, uploadAll]);
|
|
444
|
-
return /* @__PURE__ */
|
|
445
|
-
/* @__PURE__ */
|
|
501
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
502
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
446
503
|
"input",
|
|
447
504
|
{
|
|
448
505
|
ref: inputRef,
|
|
@@ -453,10 +510,10 @@ var Uploader = ({
|
|
|
453
510
|
onChange: onInputChange
|
|
454
511
|
}
|
|
455
512
|
),
|
|
456
|
-
/* @__PURE__ */
|
|
513
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
457
514
|
"div",
|
|
458
515
|
{
|
|
459
|
-
className: dropzoneClassName
|
|
516
|
+
className: `${dropzoneClassName} border-2 border-dashed rounded-xl transition-all duration-200 cursor-pointer p-10 flex flex-col items-center justify-center gap-4 ${isDragOver ? "border-blue-500 bg-blue-50 dark:bg-blue-900/20" : "border-gray-200 dark:border-gray-800 hover:border-gray-300 dark:hover:border-gray-700 bg-gray-50/50 dark:bg-gray-900/50"}`,
|
|
460
517
|
onDragOver: (e) => {
|
|
461
518
|
e.preventDefault();
|
|
462
519
|
e.stopPropagation();
|
|
@@ -471,60 +528,126 @@ var Uploader = ({
|
|
|
471
528
|
onClick: () => inputRef.current?.click(),
|
|
472
529
|
role: "button",
|
|
473
530
|
"aria-label": "Upload files",
|
|
474
|
-
children:
|
|
475
|
-
/* @__PURE__ */
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
"
|
|
479
|
-
|
|
480
|
-
|
|
531
|
+
children: [
|
|
532
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-full bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center text-blue-600 dark:text-blue-400", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
533
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
534
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }),
|
|
535
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
536
|
+
] }) }),
|
|
537
|
+
children ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
538
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: "Drop files here or click to browse" }),
|
|
539
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-gray-500 dark:text-gray-400 mt-1", children: [
|
|
540
|
+
multiple ? `Up to ${maxFiles} files` : "Single file",
|
|
541
|
+
" \u2022 Max ",
|
|
542
|
+
(maxFileSize / (1024 * 1024)).toFixed(0),
|
|
543
|
+
"MB"
|
|
544
|
+
] })
|
|
481
545
|
] })
|
|
482
|
-
]
|
|
546
|
+
]
|
|
483
547
|
}
|
|
484
548
|
),
|
|
485
|
-
files.length > 0 && /* @__PURE__ */
|
|
486
|
-
/* @__PURE__ */
|
|
487
|
-
/* @__PURE__ */ (
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
549
|
+
files.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 space-y-4", children: [
|
|
550
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 dark:border-gray-800 pb-4", children: [
|
|
551
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-gray-900 dark:text-gray-100", children: "Selected Files" }),
|
|
552
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
|
|
553
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
554
|
+
"button",
|
|
555
|
+
{
|
|
556
|
+
className: "px-4 py-2 bg-blue-600 text-white text-sm font-semibold rounded-lg hover:bg-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors shadow-sm",
|
|
557
|
+
disabled: isUploading || files.every((f2) => f2.status !== "pending"),
|
|
558
|
+
onClick: uploadAll,
|
|
559
|
+
children: isUploading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
560
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "animate-spin h-4 w-4 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
|
|
561
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
562
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
563
|
+
] }),
|
|
564
|
+
"Uploading..."
|
|
565
|
+
] }) : "Upload All"
|
|
566
|
+
}
|
|
567
|
+
),
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
569
|
+
"button",
|
|
570
|
+
{
|
|
571
|
+
className: "px-4 py-2 bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm font-semibold rounded-lg hover:bg-gray-200 dark:hover:bg-gray-700 disabled:opacity-50 transition-colors",
|
|
572
|
+
disabled: isUploading,
|
|
573
|
+
onClick: () => setFiles([]),
|
|
574
|
+
children: "Clear"
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
] })
|
|
505
578
|
] }),
|
|
506
|
-
/* @__PURE__ */
|
|
507
|
-
/* @__PURE__ */ (0,
|
|
508
|
-
/* @__PURE__ */
|
|
509
|
-
/* @__PURE__ */
|
|
510
|
-
|
|
511
|
-
|
|
579
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3", children: files.map((f2) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "group relative bg-white dark:bg-gray-900 border border-gray-100 dark:border-gray-800 rounded-xl p-4 transition-all hover:shadow-md", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
580
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded-lg bg-gray-50 dark:bg-gray-800 flex items-center justify-center flex-shrink-0", children: f2.type.startsWith("image/") ? /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-indigo-500", children: [
|
|
581
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
582
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
583
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
584
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-blue-500", children: [
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }),
|
|
586
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "14 2 14 8 20 8" })
|
|
587
|
+
] }) }),
|
|
588
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
589
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
590
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 truncate pr-4", children: f2.name }),
|
|
591
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 font-medium whitespace-nowrap", children: [
|
|
592
|
+
(f2.size / (1024 * 1024)).toFixed(2),
|
|
593
|
+
" MB"
|
|
594
|
+
] })
|
|
512
595
|
] }),
|
|
513
|
-
f2.error
|
|
514
|
-
|
|
596
|
+
f2.status === "error" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-red-500 font-medium mt-1", children: f2.error }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-full bg-gray-100 dark:bg-gray-800 rounded-full overflow-hidden mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
597
|
+
"div",
|
|
598
|
+
{
|
|
599
|
+
className: `h-full transition-all duration-300 ${f2.status === "success" ? "bg-green-500" : "bg-blue-500"}`,
|
|
600
|
+
style: { width: `${f2.progress}%` }
|
|
601
|
+
}
|
|
602
|
+
) })
|
|
515
603
|
] }),
|
|
516
|
-
/* @__PURE__ */
|
|
517
|
-
|
|
518
|
-
|
|
604
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 ml-4", children: [
|
|
605
|
+
f2.status === "success" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
606
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30 text-green-600 dark:text-green-400", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }) }),
|
|
607
|
+
f2.publicUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
608
|
+
"a",
|
|
609
|
+
{
|
|
610
|
+
href: f2.publicUrl,
|
|
611
|
+
target: "_blank",
|
|
612
|
+
rel: "noreferrer",
|
|
613
|
+
className: "p-1.5 text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-md transition-colors",
|
|
614
|
+
title: "View online",
|
|
615
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
|
|
617
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
|
|
619
|
+
] })
|
|
620
|
+
}
|
|
621
|
+
)
|
|
622
|
+
] }),
|
|
623
|
+
f2.status === "error" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/30 text-red-600 dark:text-red-400", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
624
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
625
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
626
|
+
] }) }),
|
|
627
|
+
f2.status === "uploading" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-blue-600 dark:text-blue-400", children: [
|
|
628
|
+
f2.progress,
|
|
629
|
+
"%"
|
|
630
|
+
] }),
|
|
631
|
+
f2.status === "pending" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
632
|
+
"button",
|
|
633
|
+
{
|
|
634
|
+
onClick: (e) => {
|
|
635
|
+
e.stopPropagation();
|
|
636
|
+
setFiles((prev) => prev.filter((file) => file.id !== f2.id));
|
|
637
|
+
},
|
|
638
|
+
className: "p-1.5 text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-md transition-colors",
|
|
639
|
+
title: "Remove",
|
|
640
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
641
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
642
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
643
|
+
] })
|
|
644
|
+
}
|
|
645
|
+
)
|
|
519
646
|
] })
|
|
520
647
|
] }) }, f2.id)) })
|
|
521
648
|
] })
|
|
522
649
|
] });
|
|
523
650
|
};
|
|
524
|
-
|
|
525
|
-
// src/ProjectFilesWidget.tsx
|
|
526
|
-
var import_react2 = require("react");
|
|
527
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
528
651
|
var ProjectFilesWidget = ({
|
|
529
652
|
clientOptions,
|
|
530
653
|
folderPath,
|
|
@@ -537,34 +660,47 @@ var ProjectFilesWidget = ({
|
|
|
537
660
|
onSaved,
|
|
538
661
|
onDelete,
|
|
539
662
|
deleteUrl = "/api/files",
|
|
540
|
-
showDelete = false
|
|
663
|
+
showDelete = false,
|
|
664
|
+
refreshInterval = 5e3
|
|
541
665
|
}) => {
|
|
542
|
-
const client =
|
|
543
|
-
const [files, setFiles] =
|
|
544
|
-
const [loading, setLoading] =
|
|
545
|
-
const [error, setError] =
|
|
546
|
-
const [query, setQuery] =
|
|
547
|
-
const [savingKey, setSavingKey] =
|
|
548
|
-
const [deletingKey, setDeletingKey] =
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
if (!cancelled) setLoading(false);
|
|
666
|
+
const client = React4.useMemo(() => new UpfilesClient(clientOptions ?? {}), [clientOptions]);
|
|
667
|
+
const [files, setFiles] = React4.useState([]);
|
|
668
|
+
const [loading, setLoading] = React4.useState(false);
|
|
669
|
+
const [error, setError] = React4.useState(null);
|
|
670
|
+
const [query, setQuery] = React4.useState("");
|
|
671
|
+
const [savingKey, setSavingKey] = React4.useState(null);
|
|
672
|
+
const [deletingKey, setDeletingKey] = React4.useState(null);
|
|
673
|
+
const lastUpdatedRef = React4__namespace.default.useRef(null);
|
|
674
|
+
const isFetchingRef = React4__namespace.default.useRef(false);
|
|
675
|
+
const fetchFiles = React4__namespace.default.useCallback(async (isPolling = false) => {
|
|
676
|
+
if (isFetchingRef.current) return;
|
|
677
|
+
isFetchingRef.current = true;
|
|
678
|
+
if (!isPolling) setLoading(true);
|
|
679
|
+
setError(null);
|
|
680
|
+
try {
|
|
681
|
+
const { files: newFiles, updatedAt } = await client.getProjectFiles({ folderPath });
|
|
682
|
+
if (isPolling && updatedAt && updatedAt === lastUpdatedRef.current) {
|
|
683
|
+
return;
|
|
561
684
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
685
|
+
setFiles(newFiles);
|
|
686
|
+
lastUpdatedRef.current = updatedAt;
|
|
687
|
+
} catch (e) {
|
|
688
|
+
if (!isPolling) setError(e?.message || "Failed to load files");
|
|
689
|
+
} finally {
|
|
690
|
+
if (!isPolling) setLoading(false);
|
|
691
|
+
isFetchingRef.current = false;
|
|
692
|
+
}
|
|
567
693
|
}, [client, folderPath]);
|
|
694
|
+
React4.useEffect(() => {
|
|
695
|
+
fetchFiles();
|
|
696
|
+
}, [fetchFiles]);
|
|
697
|
+
React4.useEffect(() => {
|
|
698
|
+
if (!refreshInterval || refreshInterval <= 0) return;
|
|
699
|
+
const timer = setInterval(() => {
|
|
700
|
+
fetchFiles(true);
|
|
701
|
+
}, refreshInterval);
|
|
702
|
+
return () => clearInterval(timer);
|
|
703
|
+
}, [fetchFiles, refreshInterval]);
|
|
568
704
|
const visible = files.filter(
|
|
569
705
|
(f2) => !query ? true : (f2.originalName || "").toLowerCase().includes(query.toLowerCase())
|
|
570
706
|
);
|
|
@@ -604,9 +740,9 @@ var ProjectFilesWidget = ({
|
|
|
604
740
|
setDeletingKey(null);
|
|
605
741
|
}
|
|
606
742
|
};
|
|
607
|
-
return /* @__PURE__ */
|
|
608
|
-
/* @__PURE__ */
|
|
609
|
-
/* @__PURE__ */
|
|
743
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }, children: [
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
610
746
|
"input",
|
|
611
747
|
{
|
|
612
748
|
type: "text",
|
|
@@ -616,49 +752,38 @@ var ProjectFilesWidget = ({
|
|
|
616
752
|
style: { flex: 1, padding: 8, border: "1px solid #e5e7eb", borderRadius: 6 }
|
|
617
753
|
}
|
|
618
754
|
),
|
|
619
|
-
/* @__PURE__ */
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
620
756
|
"button",
|
|
621
757
|
{
|
|
622
758
|
onClick: () => {
|
|
623
759
|
setQuery("");
|
|
624
|
-
(
|
|
625
|
-
setLoading(true);
|
|
626
|
-
setError(null);
|
|
627
|
-
try {
|
|
628
|
-
const data = await client.getProjectFiles({ folderPath });
|
|
629
|
-
setFiles(data);
|
|
630
|
-
} catch (e) {
|
|
631
|
-
setError(e?.message || "Failed to load files");
|
|
632
|
-
} finally {
|
|
633
|
-
setLoading(false);
|
|
634
|
-
}
|
|
635
|
-
})();
|
|
760
|
+
fetchFiles();
|
|
636
761
|
},
|
|
637
762
|
style: { padding: "8px 12px", border: "1px solid #e5e7eb", borderRadius: 6, background: "#fff" },
|
|
638
763
|
children: "Refresh"
|
|
639
764
|
}
|
|
640
765
|
)
|
|
641
766
|
] }),
|
|
642
|
-
loading && /* @__PURE__ */
|
|
643
|
-
error && /* @__PURE__ */
|
|
644
|
-
!loading && !error && /* @__PURE__ */
|
|
645
|
-
visible.map((f2) => /* @__PURE__ */
|
|
767
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#666" }, children: "Loading files\u2026" }),
|
|
768
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#b91c1c" }, children: error }),
|
|
769
|
+
!loading && !error && /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: listClassName, style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 8 }, children: [
|
|
770
|
+
visible.map((f2) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
646
771
|
"li",
|
|
647
772
|
{
|
|
648
773
|
className: itemClassName,
|
|
649
774
|
style: { border: "1px solid #e5e7eb", borderRadius: 8, padding: 10, display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
650
775
|
children: [
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
776
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
|
|
777
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis" }, children: f2.originalName }),
|
|
778
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "#666" }, children: [
|
|
654
779
|
(f2.size / (1024 * 1024)).toFixed(2),
|
|
655
780
|
" MB \u2022 ",
|
|
656
781
|
f2.contentType
|
|
657
782
|
] })
|
|
658
783
|
] }),
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
784
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
785
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: f2.url, target: "_blank", rel: "noreferrer", style: { fontSize: 12, color: "#2563eb" }, children: "View" }),
|
|
786
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
662
787
|
"button",
|
|
663
788
|
{
|
|
664
789
|
onClick: async () => {
|
|
@@ -695,7 +820,7 @@ var ProjectFilesWidget = ({
|
|
|
695
820
|
children: savingKey === f2.key ? "Saving\u2026" : "Use"
|
|
696
821
|
}
|
|
697
822
|
),
|
|
698
|
-
showDelete && /* @__PURE__ */
|
|
823
|
+
showDelete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
699
824
|
"button",
|
|
700
825
|
{
|
|
701
826
|
onClick: () => handleDelete(f2.key),
|
|
@@ -709,15 +834,11 @@ var ProjectFilesWidget = ({
|
|
|
709
834
|
},
|
|
710
835
|
f2.key
|
|
711
836
|
)),
|
|
712
|
-
visible.length === 0 && /* @__PURE__ */
|
|
837
|
+
visible.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { style: { fontSize: 12, color: "#666" }, children: "No files found" })
|
|
713
838
|
] })
|
|
714
839
|
] });
|
|
715
840
|
};
|
|
716
841
|
|
|
717
|
-
// src/ConnectProjectDialog.tsx
|
|
718
|
-
var React3 = __toESM(require("react"));
|
|
719
|
-
var Dialog = __toESM(require("@radix-ui/react-dialog"));
|
|
720
|
-
|
|
721
842
|
// src/projectConnect.ts
|
|
722
843
|
var f = (globalThis.fetch || fetch).bind(globalThis);
|
|
723
844
|
async function httpJson(input, init, useFetch) {
|
|
@@ -807,19 +928,16 @@ function connectUsingApiKey(opts) {
|
|
|
807
928
|
const client = new UpfilesClient({ baseUrl, apiKey, apiKeyHeader: opts.apiKeyHeader ?? "authorization" });
|
|
808
929
|
return { apiKey, client };
|
|
809
930
|
}
|
|
810
|
-
|
|
811
|
-
// src/ConnectProjectDialog.tsx
|
|
812
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
813
931
|
function ConnectProjectDialog(props) {
|
|
814
932
|
const { baseUrl, open, onOpenChange, onConnected, fetchImpl, title, description, className } = props;
|
|
815
|
-
const [mode, setMode] =
|
|
816
|
-
const [loading, setLoading] =
|
|
817
|
-
const [error, setError] =
|
|
818
|
-
const [projects, setProjects] =
|
|
819
|
-
const [selectedProjectId, setSelectedProjectId] =
|
|
820
|
-
const [manualKey, setManualKey] =
|
|
821
|
-
const [newProjectName, setNewProjectName] =
|
|
822
|
-
const resetState =
|
|
933
|
+
const [mode, setMode] = React4__namespace.useState("existing");
|
|
934
|
+
const [loading, setLoading] = React4__namespace.useState(false);
|
|
935
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
936
|
+
const [projects, setProjects] = React4__namespace.useState([]);
|
|
937
|
+
const [selectedProjectId, setSelectedProjectId] = React4__namespace.useState("");
|
|
938
|
+
const [manualKey, setManualKey] = React4__namespace.useState("");
|
|
939
|
+
const [newProjectName, setNewProjectName] = React4__namespace.useState("");
|
|
940
|
+
const resetState = React4__namespace.useCallback(() => {
|
|
823
941
|
setMode("existing");
|
|
824
942
|
setLoading(false);
|
|
825
943
|
setError(null);
|
|
@@ -828,7 +946,7 @@ function ConnectProjectDialog(props) {
|
|
|
828
946
|
setManualKey("");
|
|
829
947
|
setNewProjectName("");
|
|
830
948
|
}, []);
|
|
831
|
-
|
|
949
|
+
React4__namespace.useEffect(() => {
|
|
832
950
|
const run = async () => {
|
|
833
951
|
if (!open || mode !== "existing") return;
|
|
834
952
|
try {
|
|
@@ -890,8 +1008,8 @@ function ConnectProjectDialog(props) {
|
|
|
890
1008
|
setLoading(false);
|
|
891
1009
|
}
|
|
892
1010
|
};
|
|
893
|
-
const ModeSwitcher = /* @__PURE__ */
|
|
894
|
-
/* @__PURE__ */
|
|
1011
|
+
const ModeSwitcher = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1012
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
895
1013
|
"button",
|
|
896
1014
|
{
|
|
897
1015
|
type: "button",
|
|
@@ -900,7 +1018,7 @@ function ConnectProjectDialog(props) {
|
|
|
900
1018
|
children: "Connect existing"
|
|
901
1019
|
}
|
|
902
1020
|
),
|
|
903
|
-
/* @__PURE__ */
|
|
1021
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
904
1022
|
"button",
|
|
905
1023
|
{
|
|
906
1024
|
type: "button",
|
|
@@ -909,7 +1027,7 @@ function ConnectProjectDialog(props) {
|
|
|
909
1027
|
children: "Add API key"
|
|
910
1028
|
}
|
|
911
1029
|
),
|
|
912
|
-
/* @__PURE__ */
|
|
1030
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
913
1031
|
"button",
|
|
914
1032
|
{
|
|
915
1033
|
type: "button",
|
|
@@ -919,19 +1037,19 @@ function ConnectProjectDialog(props) {
|
|
|
919
1037
|
}
|
|
920
1038
|
)
|
|
921
1039
|
] });
|
|
922
|
-
return /* @__PURE__ */
|
|
1040
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open, onOpenChange: (o) => {
|
|
923
1041
|
if (!o) resetState();
|
|
924
1042
|
onOpenChange(o);
|
|
925
|
-
}, children: /* @__PURE__ */
|
|
926
|
-
/* @__PURE__ */
|
|
927
|
-
/* @__PURE__ */
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
/* @__PURE__ */
|
|
1043
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1044
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40" }),
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Content, { className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] max-w-md rounded-lg border bg-white p-4 shadow-lg ${className ?? ""}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-lg font-semibold", children: title ?? "Connect to Upfiles" }),
|
|
1047
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Description, { className: "text-sm text-muted-foreground", children: description ?? "Choose how to connect your project to retrieve an API key." }),
|
|
930
1048
|
ModeSwitcher,
|
|
931
|
-
error && /* @__PURE__ */
|
|
932
|
-
mode === "existing" && /* @__PURE__ */
|
|
933
|
-
/* @__PURE__ */
|
|
934
|
-
/* @__PURE__ */
|
|
1049
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: error }),
|
|
1050
|
+
mode === "existing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1051
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Project" }),
|
|
1052
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
935
1053
|
"select",
|
|
936
1054
|
{
|
|
937
1055
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -939,14 +1057,14 @@ function ConnectProjectDialog(props) {
|
|
|
939
1057
|
onChange: (e) => setSelectedProjectId(e.target.value),
|
|
940
1058
|
disabled: loading,
|
|
941
1059
|
children: [
|
|
942
|
-
projects.length === 0 && /* @__PURE__ */
|
|
943
|
-
projects.map((p) => /* @__PURE__ */
|
|
1060
|
+
projects.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: loading ? "Loading..." : "No projects found" }),
|
|
1061
|
+
projects.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: p.name }, p.id))
|
|
944
1062
|
]
|
|
945
1063
|
}
|
|
946
1064
|
),
|
|
947
|
-
/* @__PURE__ */
|
|
948
|
-
/* @__PURE__ */
|
|
949
|
-
/* @__PURE__ */
|
|
1065
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1066
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "px-3 py-2 text-sm rounded border", children: "Cancel" }) }),
|
|
1067
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
950
1068
|
"button",
|
|
951
1069
|
{
|
|
952
1070
|
type: "button",
|
|
@@ -958,9 +1076,9 @@ function ConnectProjectDialog(props) {
|
|
|
958
1076
|
)
|
|
959
1077
|
] })
|
|
960
1078
|
] }),
|
|
961
|
-
mode === "manual" && /* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
1079
|
+
mode === "manual" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1080
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "API key" }),
|
|
1081
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
964
1082
|
"input",
|
|
965
1083
|
{
|
|
966
1084
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -970,9 +1088,9 @@ function ConnectProjectDialog(props) {
|
|
|
970
1088
|
disabled: loading
|
|
971
1089
|
}
|
|
972
1090
|
),
|
|
973
|
-
/* @__PURE__ */
|
|
974
|
-
/* @__PURE__ */
|
|
975
|
-
/* @__PURE__ */
|
|
1091
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1092
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "px-3 py-2 text-sm rounded border", children: "Cancel" }) }),
|
|
1093
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
976
1094
|
"button",
|
|
977
1095
|
{
|
|
978
1096
|
type: "button",
|
|
@@ -984,9 +1102,9 @@ function ConnectProjectDialog(props) {
|
|
|
984
1102
|
)
|
|
985
1103
|
] })
|
|
986
1104
|
] }),
|
|
987
|
-
mode === "new" && /* @__PURE__ */
|
|
988
|
-
/* @__PURE__ */
|
|
989
|
-
/* @__PURE__ */
|
|
1105
|
+
mode === "new" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1106
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Project name" }),
|
|
1107
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
990
1108
|
"input",
|
|
991
1109
|
{
|
|
992
1110
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -996,10 +1114,10 @@ function ConnectProjectDialog(props) {
|
|
|
996
1114
|
disabled: loading
|
|
997
1115
|
}
|
|
998
1116
|
),
|
|
999
|
-
/* @__PURE__ */
|
|
1000
|
-
/* @__PURE__ */
|
|
1001
|
-
/* @__PURE__ */
|
|
1002
|
-
/* @__PURE__ */
|
|
1117
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "A project will be created in your Upfiles account and an API key generated." }),
|
|
1118
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1119
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "px-3 py-2 text-sm rounded border", children: "Cancel" }) }),
|
|
1120
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1003
1121
|
"button",
|
|
1004
1122
|
{
|
|
1005
1123
|
type: "button",
|
|
@@ -1025,7 +1143,7 @@ async function fetchFileThumbnails(clientOptions, fileKey) {
|
|
|
1025
1143
|
return client.getThumbnails(fileKey);
|
|
1026
1144
|
}
|
|
1027
1145
|
async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
1028
|
-
const files = await fetchProjectFiles(clientOptions, opts?.folderPath);
|
|
1146
|
+
const { files, updatedAt } = await fetchProjectFiles(clientOptions, opts?.folderPath);
|
|
1029
1147
|
const limit = Math.max(1, Math.min(opts?.limitConcurrent ?? 6, 12));
|
|
1030
1148
|
const queue = [...files];
|
|
1031
1149
|
const results = [];
|
|
@@ -1047,13 +1165,62 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
1047
1165
|
await Promise.all(Array.from({ length: workers }, () => worker()));
|
|
1048
1166
|
const keyToIndex = new Map(files.map((f2, i) => [f2.key, i]));
|
|
1049
1167
|
results.sort((a, b) => (keyToIndex.get(a.key) ?? 0) - (keyToIndex.get(b.key) ?? 0));
|
|
1050
|
-
return results;
|
|
1168
|
+
return { items: results, updatedAt };
|
|
1051
1169
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1170
|
+
var IconGrid = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1171
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "3", y: "3", rx: "1" }),
|
|
1172
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "14", y: "3", rx: "1" }),
|
|
1173
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1174
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1175
|
+
] });
|
|
1176
|
+
var IconUploadCloud = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1177
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 14.899A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.5 8.242" }),
|
|
1178
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12v9" }),
|
|
1179
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m16 16-4-4-4 4" })
|
|
1180
|
+
] });
|
|
1181
|
+
var IconFolder = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", stroke: "currentColor", strokeWidth: "0", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.5 21a3 3 0 0 0 3-3v-4.5a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3V18a3 3 0 0 0 3 3h15ZM1.5 10.146V6a3 3 0 0 1 3-3h5.379a2.25 2.25 0 0 1 1.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 0 1 3 3v1.146A4.483 4.483 0 0 0 19.5 9h-15a4.483 4.483 0 0 0-3 1.146Z" }) });
|
|
1182
|
+
var IconFolderPlus = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1183
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 2H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2Z" }),
|
|
1184
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "10", y2: "16" }),
|
|
1185
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", x2: "15", y1: "13", y2: "13" })
|
|
1186
|
+
] });
|
|
1187
|
+
var IconSearch = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1188
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
1189
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
|
|
1190
|
+
] });
|
|
1191
|
+
var IconRefresh = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1192
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" }),
|
|
1193
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3v5h-5" }),
|
|
1194
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" }),
|
|
1195
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 16H3v5" })
|
|
1196
|
+
] });
|
|
1197
|
+
var IconChevronRight = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m9 18 6-6-6-6" }) });
|
|
1198
|
+
var IconTrash = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1199
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 6h18" }),
|
|
1200
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
1201
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
|
|
1202
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", x2: "10", y1: "11", y2: "17" }),
|
|
1203
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14", x2: "14", y1: "11", y2: "17" })
|
|
1204
|
+
] });
|
|
1205
|
+
var IconX = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1206
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
|
|
1207
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
|
|
1208
|
+
] });
|
|
1209
|
+
var IconHome = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1210
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
|
|
1211
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
|
|
1212
|
+
] });
|
|
1213
|
+
var IconImage = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1214
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
|
|
1215
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
1216
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
1217
|
+
] });
|
|
1218
|
+
var IconLoader = ({ className }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
|
|
1219
|
+
var IconLayers = ({ className }) => /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
|
|
1220
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
|
|
1221
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 17 12 22 22 17" }),
|
|
1222
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 12 12 17 22 12" })
|
|
1223
|
+
] });
|
|
1057
1224
|
function ImageManager(props) {
|
|
1058
1225
|
const {
|
|
1059
1226
|
open,
|
|
@@ -1073,224 +1240,746 @@ function ImageManager(props) {
|
|
|
1073
1240
|
maxFileSize = 100 * 1024 * 1024,
|
|
1074
1241
|
maxFiles = 10,
|
|
1075
1242
|
mode = "full",
|
|
1076
|
-
showDelete = true
|
|
1243
|
+
showDelete = true,
|
|
1244
|
+
refreshInterval = 5e3
|
|
1077
1245
|
} = props;
|
|
1078
|
-
const [
|
|
1079
|
-
const [loading, setLoading] =
|
|
1080
|
-
const [error, setError] =
|
|
1081
|
-
const [
|
|
1082
|
-
const [
|
|
1083
|
-
const [
|
|
1084
|
-
const
|
|
1246
|
+
const [activeView, setActiveView] = React4__namespace.useState(mode === "upload" ? "upload" : "browse");
|
|
1247
|
+
const [loading, setLoading] = React4__namespace.useState(false);
|
|
1248
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
1249
|
+
const [currentFolderId, setCurrentFolderId] = React4__namespace.useState(void 0);
|
|
1250
|
+
const [breadcrumbs, setBreadcrumbs] = React4__namespace.useState([{ id: void 0, name: "Home" }]);
|
|
1251
|
+
const [files, setFiles] = React4__namespace.useState([]);
|
|
1252
|
+
const [folders, setFolders] = React4__namespace.useState([]);
|
|
1253
|
+
const [query, setQuery] = React4__namespace.useState("");
|
|
1254
|
+
const [deleting, setDeleting] = React4__namespace.useState(null);
|
|
1255
|
+
const [showCreateFolder, setShowCreateFolder] = React4__namespace.useState(false);
|
|
1256
|
+
const [newFolderName, setNewFolderName] = React4__namespace.useState("");
|
|
1257
|
+
const [creatingFolder, setCreatingFolder] = React4__namespace.useState(false);
|
|
1258
|
+
const [fileToDelete, setFileToDelete] = React4__namespace.useState(null);
|
|
1259
|
+
const [thumbnailSelectorFile, setThumbnailSelectorFile] = React4__namespace.useState(null);
|
|
1260
|
+
const [selectedThumbnailForFile, setSelectedThumbnailForFile] = React4__namespace.useState(null);
|
|
1261
|
+
const [previewFile, setPreviewFile] = React4__namespace.useState(null);
|
|
1262
|
+
const lastUpdatedRef = React4__namespace.useRef(null);
|
|
1263
|
+
const isFetchingRef = React4__namespace.useRef(false);
|
|
1264
|
+
const deduplicateFiles = React4__namespace.useCallback((files2) => {
|
|
1265
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1266
|
+
return files2.filter((file) => {
|
|
1267
|
+
if (seen.has(file.key)) {
|
|
1268
|
+
return false;
|
|
1269
|
+
}
|
|
1270
|
+
seen.add(file.key);
|
|
1271
|
+
return true;
|
|
1272
|
+
});
|
|
1273
|
+
}, []);
|
|
1274
|
+
const load = React4__namespace.useCallback(async (isPolling = false) => {
|
|
1275
|
+
if (isFetchingRef.current) return;
|
|
1276
|
+
isFetchingRef.current = true;
|
|
1085
1277
|
try {
|
|
1086
|
-
setLoading(true);
|
|
1278
|
+
if (!isPolling) setLoading(true);
|
|
1087
1279
|
setError(null);
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1280
|
+
const client = new UpfilesClient(clientOptions);
|
|
1281
|
+
const currentPathPrefix = breadcrumbs.length > 1 ? breadcrumbs.slice(1).map((b) => b.name).join("/") + "/" : void 0;
|
|
1282
|
+
const effectiveFolderPath = currentPathPrefix || folderPath;
|
|
1283
|
+
const [filesData, foldersData] = await Promise.all([
|
|
1284
|
+
fetchProjectImagesWithThumbs(clientOptions, { folderPath: effectiveFolderPath, limitConcurrent: 6 }),
|
|
1285
|
+
client.getFolders(currentFolderId)
|
|
1286
|
+
]);
|
|
1287
|
+
const { items: list, updatedAt } = filesData;
|
|
1288
|
+
if (isPolling && updatedAt && updatedAt === lastUpdatedRef.current) {
|
|
1289
|
+
}
|
|
1290
|
+
setFiles(deduplicateFiles(list));
|
|
1291
|
+
setFolders(foldersData);
|
|
1292
|
+
lastUpdatedRef.current = updatedAt;
|
|
1090
1293
|
} catch (e) {
|
|
1091
|
-
setError(e?.message || "Failed to load images");
|
|
1294
|
+
if (!isPolling) setError(e?.message || "Failed to load images");
|
|
1092
1295
|
} finally {
|
|
1093
|
-
setLoading(false);
|
|
1296
|
+
if (!isPolling) setLoading(false);
|
|
1297
|
+
isFetchingRef.current = false;
|
|
1094
1298
|
}
|
|
1095
|
-
}, [clientOptions, folderPath]);
|
|
1096
|
-
|
|
1299
|
+
}, [clientOptions, folderPath, currentFolderId, breadcrumbs]);
|
|
1300
|
+
React4__namespace.useEffect(() => {
|
|
1097
1301
|
if (!open) {
|
|
1098
|
-
|
|
1302
|
+
setFiles([]);
|
|
1303
|
+
setFolders([]);
|
|
1099
1304
|
setQuery("");
|
|
1100
1305
|
setError(null);
|
|
1306
|
+
lastUpdatedRef.current = null;
|
|
1101
1307
|
return;
|
|
1102
1308
|
}
|
|
1103
|
-
if (mode !== "upload" &&
|
|
1309
|
+
if (mode !== "upload" && activeView === "browse") {
|
|
1104
1310
|
load();
|
|
1105
1311
|
}
|
|
1106
|
-
}, [open,
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if (!confirm("Delete this file?")) return;
|
|
1312
|
+
}, [open, activeView, load, mode]);
|
|
1313
|
+
React4__namespace.useEffect(() => {
|
|
1314
|
+
if (!open) return;
|
|
1315
|
+
const client = new UpfilesClient(clientOptions);
|
|
1316
|
+
const url = client.getEventsUrl(projectId);
|
|
1317
|
+
const eventSource = new EventSource(url);
|
|
1318
|
+
eventSource.onmessage = (event) => {
|
|
1114
1319
|
try {
|
|
1115
|
-
|
|
1116
|
-
if (
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
const headers = {
|
|
1122
|
-
"content-type": "application/json",
|
|
1123
|
-
...clientOptions?.headers || {}
|
|
1124
|
-
};
|
|
1125
|
-
if (clientOptions?.apiKey) {
|
|
1126
|
-
const keyHeader = clientOptions.apiKeyHeader || "authorization";
|
|
1127
|
-
if (keyHeader === "authorization") {
|
|
1128
|
-
headers["authorization"] = clientOptions.apiKey.startsWith("upk_") ? `Bearer ${clientOptions.apiKey}` : clientOptions.apiKey;
|
|
1129
|
-
} else {
|
|
1130
|
-
headers[keyHeader] = clientOptions.apiKey;
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
const res = await fetch(url, {
|
|
1134
|
-
method: "DELETE",
|
|
1135
|
-
headers,
|
|
1136
|
-
credentials: clientOptions?.withCredentials ? "include" : "same-origin",
|
|
1137
|
-
body: JSON.stringify({ fileKey: key })
|
|
1320
|
+
const payload = JSON.parse(event.data);
|
|
1321
|
+
if (payload.type === "file:uploaded") {
|
|
1322
|
+
const newFile = payload.data.file;
|
|
1323
|
+
setFiles((prev) => {
|
|
1324
|
+
if (prev.find((f2) => f2.key === newFile.key)) return prev;
|
|
1325
|
+
return deduplicateFiles([newFile, ...prev]);
|
|
1138
1326
|
});
|
|
1139
|
-
|
|
1327
|
+
} else if (payload.type === "file:deleted") {
|
|
1328
|
+
const deletedKey = payload.data.fileKey || payload.data.file?.key;
|
|
1329
|
+
setFiles((prev) => prev.filter((f2) => f2.key !== deletedKey));
|
|
1140
1330
|
}
|
|
1141
|
-
setItems((prev) => prev.filter((f2) => f2.key !== key));
|
|
1142
1331
|
} catch (e) {
|
|
1143
|
-
|
|
1144
|
-
} finally {
|
|
1145
|
-
setDeleting(null);
|
|
1332
|
+
console.error("SSE Error parsing message", e);
|
|
1146
1333
|
}
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
);
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1334
|
+
};
|
|
1335
|
+
return () => eventSource.close();
|
|
1336
|
+
}, [open, clientOptions, projectId]);
|
|
1337
|
+
const visibleFiles = React4__namespace.useMemo(() => {
|
|
1338
|
+
const q = query.trim().toLowerCase();
|
|
1339
|
+
return !q ? files : files.filter((i) => (i.originalName || "").toLowerCase().includes(q));
|
|
1340
|
+
}, [files, query]);
|
|
1341
|
+
const visibleFolders = React4__namespace.useMemo(() => {
|
|
1342
|
+
const q = query.trim().toLowerCase();
|
|
1343
|
+
return !q ? folders : folders.filter((f2) => (f2.name || "").toLowerCase().includes(q));
|
|
1344
|
+
}, [folders, query]);
|
|
1345
|
+
const handleDelete = async () => {
|
|
1346
|
+
if (!fileToDelete) return;
|
|
1347
|
+
const key = fileToDelete.key;
|
|
1348
|
+
try {
|
|
1349
|
+
setDeleting(key);
|
|
1350
|
+
if (onDelete) {
|
|
1351
|
+
await onDelete(key);
|
|
1352
|
+
} else {
|
|
1353
|
+
const client = new UpfilesClient(clientOptions);
|
|
1354
|
+
const baseUrl = clientOptions?.baseUrl || "";
|
|
1355
|
+
const url = baseUrl ? `${baseUrl}${deleteUrl}` : deleteUrl;
|
|
1356
|
+
const headers = {
|
|
1357
|
+
"content-type": "application/json",
|
|
1358
|
+
...clientOptions?.headers || {}
|
|
1359
|
+
};
|
|
1360
|
+
if (clientOptions?.apiKey) {
|
|
1361
|
+
const keyHeader = clientOptions.apiKeyHeader || "authorization";
|
|
1362
|
+
if (keyHeader === "authorization") {
|
|
1363
|
+
headers["authorization"] = clientOptions.apiKey.startsWith("upk_") ? `Bearer ${clientOptions.apiKey}` : clientOptions.apiKey;
|
|
1364
|
+
} else {
|
|
1365
|
+
headers[keyHeader] = clientOptions.apiKey;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
const res = await fetch(url, {
|
|
1369
|
+
method: "DELETE",
|
|
1370
|
+
headers,
|
|
1371
|
+
credentials: clientOptions?.withCredentials ? "include" : "same-origin",
|
|
1372
|
+
body: JSON.stringify({ fileKey: key })
|
|
1373
|
+
});
|
|
1374
|
+
if (!res.ok) throw new Error("Delete failed");
|
|
1375
|
+
}
|
|
1376
|
+
setFiles((prev) => prev.filter((f2) => f2.key !== key));
|
|
1377
|
+
setFileToDelete(null);
|
|
1378
|
+
} catch (e) {
|
|
1379
|
+
alert(e?.message || "Failed to delete");
|
|
1380
|
+
} finally {
|
|
1381
|
+
setDeleting(null);
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
const handleCreateFolder = async () => {
|
|
1385
|
+
if (!newFolderName.trim()) return;
|
|
1386
|
+
setCreatingFolder(true);
|
|
1387
|
+
try {
|
|
1388
|
+
const client = new UpfilesClient(clientOptions);
|
|
1389
|
+
await client.createFolder(newFolderName, currentFolderId);
|
|
1390
|
+
setNewFolderName("");
|
|
1391
|
+
setShowCreateFolder(false);
|
|
1392
|
+
load();
|
|
1393
|
+
} catch (e) {
|
|
1394
|
+
alert(e.message || "Failed to create folder");
|
|
1395
|
+
} finally {
|
|
1396
|
+
setCreatingFolder(false);
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
const handleNavigate = (folder) => {
|
|
1400
|
+
setCurrentFolderId(folder.id);
|
|
1401
|
+
setBreadcrumbs((prev) => [...prev, { id: folder.id, name: folder.name }]);
|
|
1402
|
+
setQuery("");
|
|
1403
|
+
};
|
|
1404
|
+
const handleBreadcrumbClick = (index) => {
|
|
1405
|
+
const target = breadcrumbs[index];
|
|
1406
|
+
setBreadcrumbs(breadcrumbs.slice(0, index + 1));
|
|
1407
|
+
setCurrentFolderId(target.id);
|
|
1408
|
+
setQuery("");
|
|
1409
|
+
};
|
|
1410
|
+
const uploadFoldersList = React4__namespace.useMemo(() => {
|
|
1411
|
+
const options = [];
|
|
1412
|
+
options.push({ id: "root", name: "Root ( / )", path: void 0 });
|
|
1413
|
+
if (currentFolderId) {
|
|
1414
|
+
const currentPath = breadcrumbs.slice(1).map((b) => b.name).join("/") + "/";
|
|
1415
|
+
options.push({ id: "current", name: `Current ( ${breadcrumbs[breadcrumbs.length - 1].name} )`, path: currentPath });
|
|
1416
|
+
}
|
|
1417
|
+
folders.forEach((f2) => {
|
|
1418
|
+
const parentPath = breadcrumbs.length > 1 ? breadcrumbs.slice(1).map((b) => b.name).join("/") + "/" : "";
|
|
1419
|
+
options.push({ id: f2.id, name: f2.name, path: parentPath + f2.name + "/" });
|
|
1420
|
+
});
|
|
1421
|
+
return options;
|
|
1422
|
+
}, [breadcrumbs, currentFolderId, folders]);
|
|
1423
|
+
const [selectedUploadFolder, setSelectedUploadFolder] = React4__namespace.useState("current");
|
|
1424
|
+
const activeBreadcrumbPath = React4__namespace.useMemo(() => {
|
|
1425
|
+
return breadcrumbs.length > 1 ? breadcrumbs.slice(1).map((b) => b.name).join("/") + "/" : void 0;
|
|
1426
|
+
}, [breadcrumbs]);
|
|
1427
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Root, { open, onOpenChange, children: [
|
|
1428
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1429
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/60 backdrop-blur-sm z-50 transition-all duration-300 flex items-center justify-center" }),
|
|
1430
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1431
|
+
Dialog2__namespace.Content,
|
|
1432
|
+
{
|
|
1433
|
+
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[95vw] h-[90vh] max-w-7xl min-w-[800px] min-h-[600px] rounded-2xl bg-white dark:bg-gray-950 shadow-2xl z-[51] overflow-hidden flex flex-row ${className ?? ""}`,
|
|
1434
|
+
children: [
|
|
1435
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1436
|
+
Dialog2__namespace.Title,
|
|
1162
1437
|
{
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1438
|
+
style: {
|
|
1439
|
+
position: "absolute",
|
|
1440
|
+
width: "1px",
|
|
1441
|
+
height: "1px",
|
|
1442
|
+
padding: "0",
|
|
1443
|
+
margin: "-1px",
|
|
1444
|
+
overflow: "hidden",
|
|
1445
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1446
|
+
whiteSpace: "nowrap",
|
|
1447
|
+
border: "0"
|
|
1448
|
+
},
|
|
1449
|
+
children: title ?? "Media Library"
|
|
1167
1450
|
}
|
|
1168
1451
|
),
|
|
1169
|
-
/* @__PURE__ */ (0,
|
|
1170
|
-
"
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
children: "
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1452
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 min-w-[256px] bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 flex flex-col flex-shrink-0", children: [
|
|
1453
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
|
|
1454
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2", children: [
|
|
1455
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconImage, { className: "text-blue-600" }),
|
|
1456
|
+
title ?? "Media Library"
|
|
1457
|
+
] }),
|
|
1458
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: description ?? "Manage your assets" })
|
|
1459
|
+
] }),
|
|
1460
|
+
/* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex-1 px-3 space-y-1", children: [
|
|
1461
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1462
|
+
"button",
|
|
1463
|
+
{
|
|
1464
|
+
onClick: () => setActiveView("browse"),
|
|
1465
|
+
className: `w-full flex items-center gap-3 px-3 py-2.5 text-sm font-medium rounded-lg transition-colors ${activeView === "browse" ? "bg-white dark:bg-gray-800 text-blue-600 dark:text-blue-400 shadow-sm" : "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"}`,
|
|
1466
|
+
children: [
|
|
1467
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconGrid, { className: "w-4 h-4" }),
|
|
1468
|
+
"All Files"
|
|
1469
|
+
]
|
|
1470
|
+
}
|
|
1471
|
+
),
|
|
1472
|
+
mode === "full" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1473
|
+
"button",
|
|
1474
|
+
{
|
|
1475
|
+
onClick: () => setActiveView("upload"),
|
|
1476
|
+
className: `w-full flex items-center gap-3 px-3 py-2.5 text-sm font-medium rounded-lg transition-colors ${activeView === "upload" ? "bg-white dark:bg-gray-800 text-blue-600 dark:text-blue-400 shadow-sm" : "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"}`,
|
|
1477
|
+
children: [
|
|
1478
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconUploadCloud, { className: "w-4 h-4" }),
|
|
1479
|
+
"Upload New"
|
|
1480
|
+
]
|
|
1481
|
+
}
|
|
1482
|
+
)
|
|
1483
|
+
] }),
|
|
1484
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 border-t border-gray-200 dark:border-gray-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-center text-gray-400", children: [
|
|
1485
|
+
files.length,
|
|
1486
|
+
" files \u2022 ",
|
|
1487
|
+
folders.length,
|
|
1488
|
+
" folders"
|
|
1489
|
+
] }) })
|
|
1191
1490
|
] }),
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1491
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col bg-white dark:bg-gray-950 relative", children: [
|
|
1492
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-16 border-b border-gray-100 dark:border-gray-800 flex items-center justify-between px-6 gap-4", children: [
|
|
1493
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 flex-1", children: activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1494
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 overflow-hidden whitespace-nowrap", children: [
|
|
1495
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleBreadcrumbClick(0), className: "hover:text-blue-600 transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(IconHome, { className: "w-4 h-4" }) }),
|
|
1496
|
+
breadcrumbs.slice(1).map((b, i) => /* @__PURE__ */ jsxRuntime.jsxs(React4__namespace.Fragment, { children: [
|
|
1497
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconChevronRight, { className: "w-4 h-4 text-gray-300 mx-1" }),
|
|
1498
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1499
|
+
"button",
|
|
1500
|
+
{
|
|
1501
|
+
onClick: () => handleBreadcrumbClick(i + 1),
|
|
1502
|
+
className: `hover:text-blue-600 transition-colors truncate max-w-[100px] ${i === breadcrumbs.length - 2 ? "font-semibold text-gray-900 dark:text-white" : ""}`,
|
|
1503
|
+
children: b.name
|
|
1504
|
+
}
|
|
1505
|
+
)
|
|
1506
|
+
] }, `${b.id || "breadcrumb"}-${i}`))
|
|
1507
|
+
] }),
|
|
1508
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-200 dark:bg-gray-700 mx-2" }),
|
|
1509
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1510
|
+
"button",
|
|
1511
|
+
{
|
|
1512
|
+
onClick: () => load(),
|
|
1513
|
+
disabled: loading,
|
|
1514
|
+
className: "p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-md transition-all disabled:opacity-50",
|
|
1515
|
+
title: "Refresh",
|
|
1516
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconRefresh, { className: `w-4 h-4 ${loading ? "animate-spin" : ""}` })
|
|
1517
|
+
}
|
|
1518
|
+
),
|
|
1519
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-200 dark:bg-gray-700 mx-2" }),
|
|
1520
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 max-w-sm group", children: [
|
|
1521
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconSearch, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 group-focus-within:text-blue-500 transition-colors" }),
|
|
1522
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1523
|
+
"input",
|
|
1220
1524
|
{
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
className: "
|
|
1225
|
-
children: deleting === f2.key ? "..." : "Delete"
|
|
1525
|
+
value: query,
|
|
1526
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1527
|
+
placeholder: "Search files...",
|
|
1528
|
+
className: "w-full pl-9 pr-4 py-1.5 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all"
|
|
1226
1529
|
}
|
|
1227
1530
|
)
|
|
1531
|
+
] })
|
|
1532
|
+
] }) }),
|
|
1533
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1534
|
+
activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1535
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setShowCreateFolder(true), className: "p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/10 rounded-lg transition-colors", title: "New Folder", children: /* @__PURE__ */ jsxRuntime.jsx(IconFolderPlus, { className: "w-5 h-5" }) }),
|
|
1536
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => load(), className: `p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/10 rounded-lg transition-colors ${loading ? "animate-spin text-blue-600" : ""}`, title: "Refresh", children: /* @__PURE__ */ jsxRuntime.jsx(IconLoader, { className: "w-5 h-5" }) })
|
|
1228
1537
|
] }),
|
|
1229
|
-
/* @__PURE__ */ (
|
|
1230
|
-
] }
|
|
1231
|
-
}),
|
|
1232
|
-
|
|
1233
|
-
|
|
1538
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("button", { className: "ml-2 p-2 text-gray-400 hover:text-red-500 hover:bg-red-50 dark:hover:bg-red-900/10 rounded-lg transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { className: "w-5 h-5" }) }) })
|
|
1539
|
+
] })
|
|
1540
|
+
] }),
|
|
1541
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-6 bg-gray-50/30 dark:bg-gray-900/30", children: [
|
|
1542
|
+
activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1543
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-6 bg-red-50 text-red-600 p-4 rounded-lg text-sm border border-red-100 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: error }) }),
|
|
1544
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: gridClassName ?? "grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-6", children: [
|
|
1545
|
+
visibleFolders.map((folder) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1546
|
+
"div",
|
|
1547
|
+
{
|
|
1548
|
+
onClick: () => handleNavigate(folder),
|
|
1549
|
+
className: "group flex flex-col items-center gap-3 p-4 rounded-xl border border-gray-200/60 bg-white hover:border-blue-200 hover:shadow-lg hover:shadow-blue-500/5 hover:-translate-y-1 transition-all cursor-pointer",
|
|
1550
|
+
children: [
|
|
1551
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-12 bg-blue-50 rounded-lg flex items-center justify-center text-blue-500 group-hover:scale-110 transition-transform", children: /* @__PURE__ */ jsxRuntime.jsx(IconFolder, { className: "w-8 h-8 drop-shadow-sm" }) }),
|
|
1552
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 truncate w-full text-center", children: folder.name })
|
|
1553
|
+
]
|
|
1554
|
+
},
|
|
1555
|
+
folder.id
|
|
1556
|
+
)),
|
|
1557
|
+
visibleFiles.map((f2) => {
|
|
1558
|
+
const fWithThumbs = f2;
|
|
1559
|
+
const bestThumb = fWithThumbs.thumbnails?.sort((a, b) => a.size - b.size).find((t) => t.size >= 300) || fWithThumbs.thumbnails?.[fWithThumbs.thumbnails?.length - 1];
|
|
1560
|
+
const thumbUrl = bestThumb?.url || f2.url;
|
|
1561
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
onClick: () => setPreviewFile(f2),
|
|
1565
|
+
className: "group relative aspect-[4/3] bg-white rounded-xl border border-gray-200 overflow-hidden shadow-sm hover:shadow-xl hover:shadow-gray-200/50 hover:border-blue-300 transition-all duration-300 cursor-pointer",
|
|
1566
|
+
children: [
|
|
1567
|
+
/* @__PURE__ */ jsxRuntime.jsx("img", { src: thumbUrl, alt: f2.originalName, className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" }),
|
|
1568
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex flex-col justify-end p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 justify-center transform translate-y-2 group-hover:translate-y-0 transition-transform duration-300", children: [
|
|
1569
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1570
|
+
"button",
|
|
1571
|
+
{
|
|
1572
|
+
onClick: (e) => {
|
|
1573
|
+
e.stopPropagation();
|
|
1574
|
+
onSelect({
|
|
1575
|
+
url: f2.url,
|
|
1576
|
+
key: f2.key,
|
|
1577
|
+
originalName: f2.originalName,
|
|
1578
|
+
size: f2.size,
|
|
1579
|
+
contentType: f2.contentType,
|
|
1580
|
+
thumbnails: f2.thumbnails
|
|
1581
|
+
});
|
|
1582
|
+
onOpenChange(false);
|
|
1583
|
+
},
|
|
1584
|
+
className: "px-3 py-1.5 bg-blue-600 text-white text-xs font-semibold rounded-md hover:bg-blue-500 shadow-lg",
|
|
1585
|
+
children: "Select"
|
|
1586
|
+
}
|
|
1587
|
+
),
|
|
1588
|
+
fWithThumbs.thumbnails && fWithThumbs.thumbnails.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1589
|
+
"button",
|
|
1590
|
+
{
|
|
1591
|
+
onClick: (e) => {
|
|
1592
|
+
e.stopPropagation();
|
|
1593
|
+
setThumbnailSelectorFile(f2);
|
|
1594
|
+
setSelectedThumbnailForFile(null);
|
|
1595
|
+
},
|
|
1596
|
+
className: "p-1.5 bg-white/20 text-white text-xs font-semibold rounded-md hover:bg-purple-500 backdrop-blur-md transition-colors",
|
|
1597
|
+
title: "Select Thumbnail",
|
|
1598
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconLayers, { className: "w-4 h-4" })
|
|
1599
|
+
}
|
|
1600
|
+
),
|
|
1601
|
+
showDelete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1602
|
+
"button",
|
|
1603
|
+
{
|
|
1604
|
+
onClick: (e) => {
|
|
1605
|
+
e.stopPropagation();
|
|
1606
|
+
setFileToDelete(f2);
|
|
1607
|
+
},
|
|
1608
|
+
className: "p-1.5 bg-white/20 text-white text-xs font-semibold rounded-md hover:bg-red-500 backdrop-blur-md transition-colors",
|
|
1609
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconTrash, { className: "w-4 h-4" })
|
|
1610
|
+
}
|
|
1611
|
+
)
|
|
1612
|
+
] }) }),
|
|
1613
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-2 right-2 bg-black/50 backdrop-blur-md text-white text-[10px] px-1.5 py-0.5 rounded opacity-0 group-hover:opacity-100 transition-opacity", children: f2.originalName.split(".").pop()?.toUpperCase() })
|
|
1614
|
+
]
|
|
1615
|
+
},
|
|
1616
|
+
f2.key
|
|
1617
|
+
);
|
|
1618
|
+
})
|
|
1619
|
+
] }),
|
|
1620
|
+
!loading && visibleFolders.length === 0 && visibleFiles.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col items-center justify-center text-gray-400", children: [
|
|
1621
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 bg-gray-100 rounded-2xl flex items-center justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(IconSearch, { className: "w-8 h-8 opacity-40" }) }),
|
|
1622
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-gray-900 font-medium mb-1", children: "No Assets Found" }),
|
|
1623
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "Try uploading a file or creating a new folder." })
|
|
1624
|
+
] })
|
|
1625
|
+
] }),
|
|
1626
|
+
activeView === "upload" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex flex-col items-center justify-center max-w-2xl mx-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-white rounded-2xl border border-gray-200 p-8 shadow-sm", children: [
|
|
1627
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2", children: [
|
|
1628
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconUploadCloud, { className: "text-blue-500" }),
|
|
1629
|
+
"Upload Files"
|
|
1630
|
+
] }),
|
|
1631
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
|
|
1632
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Target Folder" }),
|
|
1633
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
1634
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1635
|
+
"select",
|
|
1636
|
+
{
|
|
1637
|
+
value: selectedUploadFolder,
|
|
1638
|
+
onChange: (e) => setSelectedUploadFolder(e.target.value),
|
|
1639
|
+
className: "block w-full rounded-lg border-gray-200 bg-gray-50 text-gray-900 text-sm focus:ring-blue-500 focus:border-blue-500 p-2.5",
|
|
1640
|
+
children: uploadFoldersList.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.id, children: opt.name }, opt.id))
|
|
1641
|
+
}
|
|
1642
|
+
),
|
|
1643
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setShowCreateFolder(true), className: "px-3 bg-gray-100 hover:bg-gray-200 rounded-lg text-gray-600 transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(IconFolderPlus, { className: "w-5 h-5" }) })
|
|
1644
|
+
] })
|
|
1645
|
+
] }),
|
|
1646
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1647
|
+
Uploader,
|
|
1648
|
+
{
|
|
1649
|
+
clientOptions,
|
|
1650
|
+
projectId,
|
|
1651
|
+
folderPath: uploadFoldersList.find((o) => o.id === selectedUploadFolder)?.path || activeBreadcrumbPath || folderPath,
|
|
1652
|
+
accept: ["image/*"],
|
|
1653
|
+
maxFileSize,
|
|
1654
|
+
maxFiles,
|
|
1655
|
+
autoRecordToDb,
|
|
1656
|
+
fetchThumbnails,
|
|
1657
|
+
autoUpload: true,
|
|
1658
|
+
onClientUploadComplete: (file) => {
|
|
1659
|
+
if (file.status === "success" && file.publicUrl && file.fileKey) {
|
|
1660
|
+
const newItem = {
|
|
1661
|
+
key: file.fileKey,
|
|
1662
|
+
originalName: file.name,
|
|
1663
|
+
size: file.size,
|
|
1664
|
+
contentType: file.type,
|
|
1665
|
+
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1666
|
+
url: file.publicUrl,
|
|
1667
|
+
thumbnails: file.thumbnails
|
|
1668
|
+
};
|
|
1669
|
+
setFiles((prev) => {
|
|
1670
|
+
if (prev.find((f2) => f2.key === newItem.key)) return prev;
|
|
1671
|
+
return deduplicateFiles([newItem, ...prev]);
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
},
|
|
1675
|
+
onComplete: () => {
|
|
1676
|
+
if (mode === "full") {
|
|
1677
|
+
setTimeout(() => {
|
|
1678
|
+
setActiveView("browse");
|
|
1679
|
+
load();
|
|
1680
|
+
}, 1e3);
|
|
1681
|
+
}
|
|
1682
|
+
},
|
|
1683
|
+
dropzoneClassName: "border-2 border-dashed border-gray-300 bg-gray-50 rounded-xl p-12 text-center cursor-pointer hover:border-blue-500 hover:bg-blue-50/50 transition-all",
|
|
1684
|
+
buttonClassName: "hidden",
|
|
1685
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
1686
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 bg-blue-100 text-blue-600 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconUploadCloud, { className: "w-6 h-6" }) }),
|
|
1687
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1688
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900 font-medium", children: "Click to upload or drag and drop" }),
|
|
1689
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1", children: [
|
|
1690
|
+
"Supports PNG, JPG, GIF up to ",
|
|
1691
|
+
Math.round(maxFileSize / 1024 / 1024),
|
|
1692
|
+
"MB"
|
|
1693
|
+
] })
|
|
1694
|
+
] })
|
|
1695
|
+
] })
|
|
1696
|
+
}
|
|
1697
|
+
)
|
|
1698
|
+
] }) })
|
|
1699
|
+
] })
|
|
1234
1700
|
] })
|
|
1701
|
+
]
|
|
1702
|
+
}
|
|
1703
|
+
)
|
|
1704
|
+
] }),
|
|
1705
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: showCreateFolder, onOpenChange: setShowCreateFolder, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1706
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40 z-[60]" }),
|
|
1707
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-sm rounded-xl bg-white p-6 shadow-2xl z-[61] outline-none", "aria-describedby": "new-folder-description", children: [
|
|
1708
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-lg font-semibold text-gray-900 mb-4", children: "New Folder" }),
|
|
1709
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Description, { id: "new-folder-description", className: "sr-only", children: "Create a new folder to organize your files" }),
|
|
1710
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1711
|
+
"input",
|
|
1712
|
+
{
|
|
1713
|
+
autoFocus: true,
|
|
1714
|
+
className: "w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-2 text-sm focus:ring-2 focus:ring-blue-500 outline-none mb-6",
|
|
1715
|
+
placeholder: "Folder Name",
|
|
1716
|
+
value: newFolderName,
|
|
1717
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
1718
|
+
onKeyDown: (e) => e.key === "Enter" && handleCreateFolder()
|
|
1719
|
+
}
|
|
1720
|
+
),
|
|
1721
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
1722
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setShowCreateFolder(false), className: "px-4 py-2 text-sm font-medium text-gray-600 hover:bg-gray-100 rounded-lg", children: "Cancel" }),
|
|
1723
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1724
|
+
"button",
|
|
1725
|
+
{
|
|
1726
|
+
onClick: handleCreateFolder,
|
|
1727
|
+
disabled: creatingFolder || !newFolderName.trim(),
|
|
1728
|
+
className: "px-4 py-2 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50",
|
|
1729
|
+
children: "Create"
|
|
1730
|
+
}
|
|
1731
|
+
)
|
|
1732
|
+
] })
|
|
1733
|
+
] })
|
|
1734
|
+
] }) }),
|
|
1735
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!fileToDelete, onOpenChange: (open2) => !open2 && setFileToDelete(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1736
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40 z-[80]" }),
|
|
1737
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-md rounded-xl bg-white p-6 shadow-2xl z-[81]", "aria-describedby": "delete-file-description", children: [
|
|
1738
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold text-gray-900 mb-2", children: "Delete File" }),
|
|
1739
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "delete-file-description", className: "text-gray-600 mb-6", children: [
|
|
1740
|
+
"Are you sure you want to delete ",
|
|
1741
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900", children: [
|
|
1742
|
+
'"',
|
|
1743
|
+
fileToDelete?.originalName,
|
|
1744
|
+
'"'
|
|
1235
1745
|
] }),
|
|
1236
|
-
|
|
1237
|
-
|
|
1746
|
+
"?"
|
|
1747
|
+
] }),
|
|
1748
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
1749
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1750
|
+
"button",
|
|
1751
|
+
{
|
|
1752
|
+
onClick: () => setFileToDelete(null),
|
|
1753
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-lg",
|
|
1754
|
+
children: "Cancel"
|
|
1755
|
+
}
|
|
1756
|
+
),
|
|
1757
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1758
|
+
"button",
|
|
1759
|
+
{
|
|
1760
|
+
onClick: handleDelete,
|
|
1761
|
+
disabled: !!deleting,
|
|
1762
|
+
className: "px-4 py-2 text-sm font-medium rounded-lg bg-red-600 text-white hover:bg-red-700 disabled:opacity-70",
|
|
1763
|
+
children: deleting ? "Deleting..." : "Delete"
|
|
1764
|
+
}
|
|
1765
|
+
)
|
|
1766
|
+
] })
|
|
1767
|
+
] })
|
|
1768
|
+
] }) }),
|
|
1769
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!thumbnailSelectorFile, onOpenChange: (open2) => !open2 && setThumbnailSelectorFile(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1770
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/50 z-[90]" }),
|
|
1771
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Content, { className: "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-full max-w-4xl max-h-[80vh] rounded-xl bg-white shadow-2xl z-[91] overflow-hidden", "aria-describedby": "thumbnail-selector-description", children: [
|
|
1772
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 border-b border-gray-200", children: [
|
|
1773
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold text-gray-900 mb-2", children: "Select Thumbnail" }),
|
|
1774
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "thumbnail-selector-description", className: "text-gray-600", children: [
|
|
1775
|
+
"Choose a thumbnail size for ",
|
|
1776
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900", children: [
|
|
1777
|
+
'"',
|
|
1778
|
+
thumbnailSelectorFile?.originalName,
|
|
1779
|
+
'"'
|
|
1780
|
+
] })
|
|
1781
|
+
] })
|
|
1782
|
+
] }),
|
|
1783
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 overflow-y-auto max-h-[60vh]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4", children: [
|
|
1784
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1785
|
+
"div",
|
|
1238
1786
|
{
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1787
|
+
onClick: () => setSelectedThumbnailForFile(null),
|
|
1788
|
+
className: `group cursor-pointer rounded-lg border-2 overflow-hidden transition-all ${selectedThumbnailForFile === null ? "border-blue-500 ring-2 ring-blue-200" : "border-gray-200 hover:border-blue-300"}`,
|
|
1789
|
+
children: [
|
|
1790
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aspect-square bg-gray-100 flex items-center justify-center relative", children: [
|
|
1791
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1792
|
+
"img",
|
|
1793
|
+
{
|
|
1794
|
+
src: thumbnailSelectorFile?.url,
|
|
1795
|
+
alt: "Original",
|
|
1796
|
+
className: "w-full h-full object-cover"
|
|
1797
|
+
}
|
|
1798
|
+
),
|
|
1799
|
+
selectedThumbnailForFile === null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-blue-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 text-white", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) }) })
|
|
1800
|
+
] }),
|
|
1801
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-white", children: [
|
|
1802
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-sm text-gray-900", children: "Original" }),
|
|
1803
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: "Full size" })
|
|
1804
|
+
] })
|
|
1805
|
+
]
|
|
1806
|
+
}
|
|
1807
|
+
),
|
|
1808
|
+
thumbnailSelectorFile?.thumbnails?.map((thumbnail) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1809
|
+
"div",
|
|
1810
|
+
{
|
|
1811
|
+
onClick: () => setSelectedThumbnailForFile(thumbnail),
|
|
1812
|
+
className: `group cursor-pointer rounded-lg border-2 overflow-hidden transition-all ${selectedThumbnailForFile?.id === thumbnail.id ? "border-blue-500 ring-2 ring-blue-200" : "border-gray-200 hover:border-blue-300"}`,
|
|
1813
|
+
children: [
|
|
1814
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aspect-square bg-gray-100 flex items-center justify-center relative", children: [
|
|
1815
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1816
|
+
"img",
|
|
1817
|
+
{
|
|
1818
|
+
src: thumbnail.url,
|
|
1819
|
+
alt: `${thumbnail.size}px thumbnail`,
|
|
1820
|
+
className: "w-full h-full object-cover"
|
|
1821
|
+
}
|
|
1822
|
+
),
|
|
1823
|
+
selectedThumbnailForFile?.id === thumbnail.id && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-blue-500/20 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4 text-white", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }) }) })
|
|
1824
|
+
] }),
|
|
1825
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-white", children: [
|
|
1826
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-sm text-gray-900", children: [
|
|
1827
|
+
thumbnail.size,
|
|
1828
|
+
"px"
|
|
1829
|
+
] }),
|
|
1830
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: thumbnail.sizeType || "Thumbnail" })
|
|
1831
|
+
] })
|
|
1832
|
+
]
|
|
1833
|
+
},
|
|
1834
|
+
thumbnail.id
|
|
1835
|
+
))
|
|
1836
|
+
] }) }),
|
|
1837
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 border-t border-gray-200 flex justify-end gap-3", children: [
|
|
1838
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1839
|
+
"button",
|
|
1840
|
+
{
|
|
1841
|
+
onClick: () => setThumbnailSelectorFile(null),
|
|
1842
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-lg",
|
|
1843
|
+
children: "Cancel"
|
|
1844
|
+
}
|
|
1845
|
+
),
|
|
1846
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1847
|
+
"button",
|
|
1848
|
+
{
|
|
1849
|
+
onClick: () => {
|
|
1850
|
+
if (thumbnailSelectorFile) {
|
|
1851
|
+
onSelect({
|
|
1852
|
+
url: selectedThumbnailForFile?.url || thumbnailSelectorFile.url,
|
|
1853
|
+
key: thumbnailSelectorFile.key,
|
|
1854
|
+
originalName: thumbnailSelectorFile.originalName,
|
|
1855
|
+
size: thumbnailSelectorFile.size,
|
|
1856
|
+
contentType: thumbnailSelectorFile.contentType,
|
|
1857
|
+
thumbnails: thumbnailSelectorFile.thumbnails,
|
|
1858
|
+
selectedThumbnail: selectedThumbnailForFile || void 0
|
|
1859
|
+
});
|
|
1860
|
+
onOpenChange(false);
|
|
1861
|
+
setThumbnailSelectorFile(null);
|
|
1260
1862
|
}
|
|
1261
1863
|
},
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1864
|
+
className: "px-4 py-2 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700",
|
|
1865
|
+
children: [
|
|
1866
|
+
"Select ",
|
|
1867
|
+
selectedThumbnailForFile ? `${selectedThumbnailForFile.size}px Thumbnail` : "Original"
|
|
1868
|
+
]
|
|
1869
|
+
}
|
|
1870
|
+
)
|
|
1871
|
+
] })
|
|
1872
|
+
] })
|
|
1873
|
+
] }) }),
|
|
1874
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!previewFile, onOpenChange: (open2) => !open2 && setPreviewFile(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1875
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/90 z-[100]", onClick: () => setPreviewFile(null) }),
|
|
1876
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Content, { className: "fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[90vw] h-[90vh] z-[101] outline-none", "aria-describedby": "image-preview-description", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full flex flex-col", children: [
|
|
1877
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-0 left-0 right-0 z-10 bg-gradient-to-b from-black/70 to-transparent p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
1878
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-white", children: [
|
|
1879
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold mb-1", children: previewFile?.originalName }),
|
|
1880
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "image-preview-description", className: "text-sm text-gray-300", children: [
|
|
1881
|
+
previewFile?.size ? `${(previewFile.size / 1024).toFixed(1)} KB` : "",
|
|
1882
|
+
" \u2022 ",
|
|
1883
|
+
previewFile?.contentType
|
|
1884
|
+
] })
|
|
1885
|
+
] }),
|
|
1886
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1887
|
+
"button",
|
|
1888
|
+
{
|
|
1889
|
+
onClick: () => setPreviewFile(null),
|
|
1890
|
+
className: "p-2 bg-white/10 hover:bg-white/20 rounded-lg backdrop-blur-md transition-colors",
|
|
1891
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { className: "w-6 h-6 text-white" })
|
|
1892
|
+
}
|
|
1893
|
+
)
|
|
1894
|
+
] }) }),
|
|
1895
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1896
|
+
"img",
|
|
1897
|
+
{
|
|
1898
|
+
src: previewFile?.url,
|
|
1899
|
+
alt: previewFile?.originalName,
|
|
1900
|
+
className: "max-w-full max-h-full object-contain",
|
|
1901
|
+
onError: (e) => {
|
|
1902
|
+
const target = e.target;
|
|
1903
|
+
target.style.display = "none";
|
|
1904
|
+
const errorDiv = document.createElement("div");
|
|
1905
|
+
errorDiv.className = "text-white text-center";
|
|
1906
|
+
errorDiv.innerHTML = `
|
|
1907
|
+
<div class="text-red-400 mb-2">Failed to load image</div>
|
|
1908
|
+
<div class="text-sm text-gray-400">URL: ${previewFile?.url}</div>
|
|
1909
|
+
`;
|
|
1910
|
+
target.parentElement?.appendChild(errorDiv);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
) }),
|
|
1914
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 z-10 bg-gradient-to-t from-black/70 to-transparent p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
1915
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1916
|
+
"button",
|
|
1917
|
+
{
|
|
1918
|
+
onClick: (e) => {
|
|
1919
|
+
e.stopPropagation();
|
|
1920
|
+
if (previewFile) {
|
|
1921
|
+
onSelect({
|
|
1922
|
+
url: previewFile.url,
|
|
1923
|
+
key: previewFile.key,
|
|
1924
|
+
originalName: previewFile.originalName,
|
|
1925
|
+
size: previewFile.size,
|
|
1926
|
+
contentType: previewFile.contentType,
|
|
1927
|
+
thumbnails: previewFile.thumbnails
|
|
1928
|
+
});
|
|
1929
|
+
onOpenChange(false);
|
|
1930
|
+
setPreviewFile(null);
|
|
1268
1931
|
}
|
|
1269
1932
|
},
|
|
1270
|
-
|
|
1271
|
-
|
|
1933
|
+
className: "px-6 py-2.5 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-500 shadow-lg",
|
|
1934
|
+
children: "Select Image"
|
|
1272
1935
|
}
|
|
1273
1936
|
),
|
|
1274
|
-
/* @__PURE__ */
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1937
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1938
|
+
"a",
|
|
1939
|
+
{
|
|
1940
|
+
href: previewFile?.url,
|
|
1941
|
+
target: "_blank",
|
|
1942
|
+
rel: "noopener noreferrer",
|
|
1943
|
+
onClick: (e) => e.stopPropagation(),
|
|
1944
|
+
className: "px-6 py-2.5 bg-white/10 text-white font-semibold rounded-lg hover:bg-white/20 backdrop-blur-md transition-colors",
|
|
1945
|
+
children: "Open in New Tab"
|
|
1946
|
+
}
|
|
1947
|
+
),
|
|
1948
|
+
previewFile?.thumbnails && previewFile.thumbnails.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1949
|
+
"button",
|
|
1950
|
+
{
|
|
1951
|
+
onClick: (e) => {
|
|
1952
|
+
e.stopPropagation();
|
|
1953
|
+
setThumbnailSelectorFile(previewFile);
|
|
1954
|
+
setSelectedThumbnailForFile(null);
|
|
1955
|
+
setPreviewFile(null);
|
|
1956
|
+
},
|
|
1957
|
+
className: "px-6 py-2.5 bg-purple-600 text-white font-semibold rounded-lg hover:bg-purple-500 shadow-lg flex items-center gap-2",
|
|
1958
|
+
children: [
|
|
1959
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconLayers, { className: "w-5 h-5" }),
|
|
1960
|
+
"Select Thumbnail"
|
|
1961
|
+
]
|
|
1962
|
+
}
|
|
1963
|
+
)
|
|
1964
|
+
] }) })
|
|
1965
|
+
] }) })
|
|
1966
|
+
] }) })
|
|
1967
|
+
] });
|
|
1279
1968
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1969
|
+
|
|
1970
|
+
exports.ConnectProjectDialog = ConnectProjectDialog;
|
|
1971
|
+
exports.ImageManager = ImageManager;
|
|
1972
|
+
exports.ProjectFilesWidget = ProjectFilesWidget;
|
|
1973
|
+
exports.UpfilesClient = UpfilesClient;
|
|
1974
|
+
exports.Uploader = Uploader;
|
|
1975
|
+
exports.addApiKeyManually = addApiKeyManually;
|
|
1976
|
+
exports.connectProject = connectProject;
|
|
1977
|
+
exports.connectUsingApiKey = connectUsingApiKey;
|
|
1978
|
+
exports.createClientWithKey = createClientWithKey;
|
|
1979
|
+
exports.createProject = createProject;
|
|
1980
|
+
exports.fetchFileThumbnails = fetchFileThumbnails;
|
|
1981
|
+
exports.fetchProjectFiles = fetchProjectFiles;
|
|
1982
|
+
exports.fetchProjectImagesWithThumbs = fetchProjectImagesWithThumbs;
|
|
1983
|
+
exports.listProjects = listProjects;
|
|
1984
|
+
//# sourceMappingURL=index.js.map
|
|
1985
|
+
//# sourceMappingURL=index.js.map
|