@thetechfossil/upfiles 1.0.11 → 1.0.13
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 +11 -11
- package/dist/index.d.mts +136 -13
- package/dist/index.d.ts +136 -13
- package/dist/index.js +1230 -536
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1110 -405
- package/dist/index.mjs.map +1 -0
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,85 +1,111 @@
|
|
|
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
|
|
29
|
+
var StorageQuotaError = class extends Error {
|
|
30
|
+
constructor(message, details) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "StorageQuotaError";
|
|
33
|
+
this.details = details;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
51
36
|
var UpfilesClient = class {
|
|
52
37
|
constructor(opts) {
|
|
53
38
|
this.baseUrl = opts.baseUrl?.replace(/\/$/, "");
|
|
54
|
-
this.presignPath = opts.presignPath ?? "/api/plugin/upload
|
|
39
|
+
this.presignPath = opts.presignPath ?? "/api/plugin/upload";
|
|
55
40
|
this.presignUrl = opts.presignUrl;
|
|
56
41
|
this.headers = opts.headers;
|
|
57
42
|
this.withCredentials = opts.withCredentials;
|
|
58
43
|
this.apiKey = opts.apiKey;
|
|
59
44
|
this.apiKeyHeader = opts.apiKeyHeader ?? "authorization";
|
|
60
45
|
this.thumbnailsPath = opts.thumbnailsPath ?? "/api/plugin/thumbnails";
|
|
61
|
-
this.completionPath = opts.completionPath ?? "/api/plugin/
|
|
46
|
+
this.completionPath = opts.completionPath ?? "/api/plugin/files";
|
|
62
47
|
this.foldersPath = opts.foldersPath ?? "/api/plugin/folders";
|
|
63
48
|
this.callCompletionEndpoint = opts.callCompletionEndpoint ?? false;
|
|
49
|
+
this.getToken = opts.getToken;
|
|
50
|
+
this.projectId = opts.projectId;
|
|
51
|
+
this.projectName = opts.projectName;
|
|
64
52
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
53
|
+
/** Build auth headers for API requests. Priority: getToken (JWT) > apiKey > static headers */
|
|
54
|
+
async buildAuthHeaders() {
|
|
55
|
+
const headers = { ...this.headers || {} };
|
|
56
|
+
if (this.getToken) {
|
|
57
|
+
const token = await this.getToken();
|
|
58
|
+
if (token) {
|
|
59
|
+
headers["authorization"] = `Bearer ${token}`;
|
|
60
|
+
}
|
|
61
|
+
if (this.projectName) {
|
|
62
|
+
headers["x-project-name"] = this.projectName;
|
|
63
|
+
} else if (this.projectId) {
|
|
64
|
+
headers["x-project-id"] = this.projectId;
|
|
65
|
+
}
|
|
66
|
+
return headers;
|
|
67
|
+
}
|
|
74
68
|
if (this.apiKey) {
|
|
75
69
|
if (this.apiKeyHeader === "authorization") {
|
|
76
|
-
headers["authorization"] = this.apiKey.startsWith("
|
|
70
|
+
headers["authorization"] = this.apiKey.startsWith("upf_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
77
71
|
} else if (this.apiKeyHeader === "x-api-key") {
|
|
78
72
|
headers["x-api-key"] = this.apiKey;
|
|
79
73
|
} else {
|
|
80
74
|
headers["x-up-api-key"] = this.apiKey;
|
|
81
75
|
}
|
|
82
76
|
}
|
|
77
|
+
return headers;
|
|
78
|
+
}
|
|
79
|
+
/** Get storage quota info for the project */
|
|
80
|
+
async getQuota() {
|
|
81
|
+
const basePath = this.presignPath.replace(/\/upload$/, "");
|
|
82
|
+
const target = this.baseUrl ? `${this.baseUrl}${basePath}/quota` : `${basePath}/quota`;
|
|
83
|
+
const headers = await this.buildAuthHeaders();
|
|
84
|
+
const res = await fetch(target, {
|
|
85
|
+
method: "GET",
|
|
86
|
+
headers,
|
|
87
|
+
credentials: this.withCredentials ? "include" : "same-origin"
|
|
88
|
+
});
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
let msg = "Failed to get storage quota";
|
|
91
|
+
try {
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
msg = data.message || data.error || msg;
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
throw new Error(msg);
|
|
97
|
+
}
|
|
98
|
+
return res.json();
|
|
99
|
+
}
|
|
100
|
+
async getPresignedUrl(params) {
|
|
101
|
+
if (!params.fileName) throw new Error("fileName is required");
|
|
102
|
+
if (!params.fileType) throw new Error("fileType is required");
|
|
103
|
+
if (!params.fileSize || params.fileSize <= 0) throw new Error("fileSize must be > 0");
|
|
104
|
+
const target = this.presignUrl ? this.presignUrl : this.baseUrl ? `${this.baseUrl}${this.presignPath}` : this.presignPath;
|
|
105
|
+
const headers = {
|
|
106
|
+
"content-type": "application/json",
|
|
107
|
+
...await this.buildAuthHeaders()
|
|
108
|
+
};
|
|
83
109
|
const res = await fetch(target, {
|
|
84
110
|
method: "POST",
|
|
85
111
|
headers,
|
|
@@ -90,8 +116,12 @@ var UpfilesClient = class {
|
|
|
90
116
|
let msg = "Failed to get upload URL";
|
|
91
117
|
try {
|
|
92
118
|
const data = await res.json();
|
|
93
|
-
|
|
94
|
-
|
|
119
|
+
if (res.status === 413 && data.error === "STORAGE_QUOTA_EXCEEDED") {
|
|
120
|
+
throw new StorageQuotaError(data.message || "Storage quota exceeded", data.details);
|
|
121
|
+
}
|
|
122
|
+
msg = data.message || data.error || msg;
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (e instanceof StorageQuotaError) throw e;
|
|
95
125
|
}
|
|
96
126
|
throw new Error(msg);
|
|
97
127
|
}
|
|
@@ -127,7 +157,11 @@ var UpfilesClient = class {
|
|
|
127
157
|
originalName: file.name,
|
|
128
158
|
size: file.size,
|
|
129
159
|
contentType: file.type || "application/octet-stream",
|
|
130
|
-
projectId: extras?.projectId
|
|
160
|
+
projectId: extras?.projectId,
|
|
161
|
+
url: presign.publicUrl,
|
|
162
|
+
userId: extras?.userId
|
|
163
|
+
// userId might be passed in extras but not typed yet in this method signature, easy fix is to cast or update signature. Better to update signature but for now access via cast to keep change minimal if extras type isn't fully updated here locally. Wait, the plan said "Update upload method to pass...". I should probably check if I can update extras type locally or just cast. The `upload` method signature has `extras`. I should probably update `extras` type in the signature if I can.
|
|
164
|
+
// Actually, looking at the code, `extras` is defined inline: `extras?: { projectId?: string; folderPath?: string; fetchThumbnails?: boolean; callCompletionEndpoint?: boolean }`. I should update this type definition too.
|
|
131
165
|
});
|
|
132
166
|
} catch (error) {
|
|
133
167
|
throw new Error(`Upload verification failed: ${error.message}`);
|
|
@@ -156,22 +190,16 @@ var UpfilesClient = class {
|
|
|
156
190
|
const target = this.baseUrl ? `${this.baseUrl}${this.completionPath}` : this.completionPath;
|
|
157
191
|
const headers = {
|
|
158
192
|
"content-type": "application/json",
|
|
159
|
-
...this.
|
|
193
|
+
...await this.buildAuthHeaders()
|
|
160
194
|
};
|
|
161
|
-
if (this.apiKey) {
|
|
162
|
-
if (this.apiKeyHeader === "authorization") {
|
|
163
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
164
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
165
|
-
headers["x-api-key"] = this.apiKey;
|
|
166
|
-
} else {
|
|
167
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
195
|
const res = await fetch(target, {
|
|
171
196
|
method: "POST",
|
|
172
197
|
headers,
|
|
173
198
|
credentials: this.withCredentials ? "include" : "same-origin",
|
|
174
|
-
body: JSON.stringify(
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
...params,
|
|
201
|
+
key: params.fileKey
|
|
202
|
+
})
|
|
175
203
|
});
|
|
176
204
|
if (!res.ok) {
|
|
177
205
|
let msg = "Failed to complete upload";
|
|
@@ -187,16 +215,7 @@ var UpfilesClient = class {
|
|
|
187
215
|
async getThumbnails(fileKey) {
|
|
188
216
|
const target = this.baseUrl ? `${this.baseUrl}${this.thumbnailsPath}` : this.thumbnailsPath;
|
|
189
217
|
const url = `${target}?fileKey=${encodeURIComponent(fileKey)}`;
|
|
190
|
-
const headers =
|
|
191
|
-
if (this.apiKey) {
|
|
192
|
-
if (this.apiKeyHeader === "authorization") {
|
|
193
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
194
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
195
|
-
headers["x-api-key"] = this.apiKey;
|
|
196
|
-
} else {
|
|
197
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
218
|
+
const headers = await this.buildAuthHeaders();
|
|
200
219
|
const res = await fetch(url, { method: "GET", headers, credentials: this.withCredentials ? "include" : "same-origin" });
|
|
201
220
|
if (!res.ok) {
|
|
202
221
|
let msg = "Failed to fetch thumbnails";
|
|
@@ -210,26 +229,19 @@ var UpfilesClient = class {
|
|
|
210
229
|
const data = await res.json();
|
|
211
230
|
return data?.thumbnails ?? [];
|
|
212
231
|
}
|
|
213
|
-
async generateThumbnails(fileKey) {
|
|
232
|
+
async generateThumbnails(fileKey, sizes) {
|
|
214
233
|
const target = this.baseUrl ? `${this.baseUrl}${this.thumbnailsPath}` : this.thumbnailsPath;
|
|
215
234
|
const headers = {
|
|
216
235
|
"content-type": "application/json",
|
|
217
|
-
...this.
|
|
236
|
+
...await this.buildAuthHeaders()
|
|
218
237
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
222
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
223
|
-
headers["x-api-key"] = this.apiKey;
|
|
224
|
-
} else {
|
|
225
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
238
|
+
const body = { fileKey };
|
|
239
|
+
if (sizes && sizes.length > 0) body.sizes = sizes;
|
|
228
240
|
const res = await fetch(target, {
|
|
229
241
|
method: "POST",
|
|
230
242
|
headers,
|
|
231
243
|
credentials: this.withCredentials ? "include" : "same-origin",
|
|
232
|
-
body: JSON.stringify(
|
|
244
|
+
body: JSON.stringify(body)
|
|
233
245
|
});
|
|
234
246
|
if (!res.ok) {
|
|
235
247
|
let msg = "Failed to generate thumbnails";
|
|
@@ -243,19 +255,53 @@ var UpfilesClient = class {
|
|
|
243
255
|
const data = await res.json();
|
|
244
256
|
return { masterWebp: data.masterWebp, thumbnails: data.thumbnails ?? [] };
|
|
245
257
|
}
|
|
258
|
+
/** Get thumbnail settings for the project (requires API key) */
|
|
259
|
+
async getThumbnailSettings() {
|
|
260
|
+
const basePath = this.presignPath.replace(/\/upload$/, "");
|
|
261
|
+
const target = this.baseUrl ? `${this.baseUrl}${basePath}/settings/thumbnails` : `${basePath}/settings/thumbnails`;
|
|
262
|
+
const headers = await this.buildAuthHeaders();
|
|
263
|
+
const res = await fetch(target, {
|
|
264
|
+
method: "GET",
|
|
265
|
+
headers,
|
|
266
|
+
credentials: this.withCredentials ? "include" : "same-origin"
|
|
267
|
+
});
|
|
268
|
+
if (!res.ok) {
|
|
269
|
+
let msg = "Failed to get thumbnail settings";
|
|
270
|
+
try {
|
|
271
|
+
const d = await res.json();
|
|
272
|
+
msg = d.message || msg;
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
throw new Error(msg);
|
|
276
|
+
}
|
|
277
|
+
return res.json();
|
|
278
|
+
}
|
|
279
|
+
/** Update thumbnail settings for the project (requires API key with write scope) */
|
|
280
|
+
async updateThumbnailSettings(settings) {
|
|
281
|
+
const basePath = this.presignPath.replace(/\/upload$/, "");
|
|
282
|
+
const target = this.baseUrl ? `${this.baseUrl}${basePath}/settings/thumbnails` : `${basePath}/settings/thumbnails`;
|
|
283
|
+
const headers = { "content-type": "application/json", ...await this.buildAuthHeaders() };
|
|
284
|
+
const res = await fetch(target, {
|
|
285
|
+
method: "PATCH",
|
|
286
|
+
headers,
|
|
287
|
+
credentials: this.withCredentials ? "include" : "same-origin",
|
|
288
|
+
body: JSON.stringify(settings)
|
|
289
|
+
});
|
|
290
|
+
if (!res.ok) {
|
|
291
|
+
let msg = "Failed to update thumbnail settings";
|
|
292
|
+
try {
|
|
293
|
+
const d = await res.json();
|
|
294
|
+
msg = d.message || msg;
|
|
295
|
+
} catch {
|
|
296
|
+
}
|
|
297
|
+
throw new Error(msg);
|
|
298
|
+
}
|
|
299
|
+
return res.json();
|
|
300
|
+
}
|
|
246
301
|
async getFolders(parentId) {
|
|
247
302
|
const target = this.baseUrl ? `${this.baseUrl}${this.foldersPath}` : this.foldersPath;
|
|
248
303
|
const url = parentId ? `${target}?parentId=${encodeURIComponent(parentId)}` : target;
|
|
249
|
-
const headers =
|
|
250
|
-
if (this.apiKey) {
|
|
251
|
-
if (this.apiKeyHeader === "authorization") {
|
|
252
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
253
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
254
|
-
headers["x-api-key"] = this.apiKey;
|
|
255
|
-
} else {
|
|
256
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
304
|
+
const headers = await this.buildAuthHeaders();
|
|
259
305
|
const res = await fetch(url, { method: "GET", headers, credentials: this.withCredentials ? "include" : "same-origin" });
|
|
260
306
|
if (!res.ok) {
|
|
261
307
|
let msg = "Failed to fetch folders";
|
|
@@ -273,17 +319,8 @@ var UpfilesClient = class {
|
|
|
273
319
|
const target = this.baseUrl ? `${this.baseUrl}${this.foldersPath}` : this.foldersPath;
|
|
274
320
|
const headers = {
|
|
275
321
|
"content-type": "application/json",
|
|
276
|
-
...this.
|
|
322
|
+
...await this.buildAuthHeaders()
|
|
277
323
|
};
|
|
278
|
-
if (this.apiKey) {
|
|
279
|
-
if (this.apiKeyHeader === "authorization") {
|
|
280
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
281
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
282
|
-
headers["x-api-key"] = this.apiKey;
|
|
283
|
-
} else {
|
|
284
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
324
|
const res = await fetch(target, {
|
|
288
325
|
method: "POST",
|
|
289
326
|
headers,
|
|
@@ -307,16 +344,7 @@ var UpfilesClient = class {
|
|
|
307
344
|
const filesPath = `${basePath}/files`;
|
|
308
345
|
const target = this.baseUrl ? `${this.baseUrl}${filesPath}` : filesPath;
|
|
309
346
|
const url = params?.folderPath ? `${target}?folderPath=${encodeURIComponent(params.folderPath)}` : target;
|
|
310
|
-
const headers =
|
|
311
|
-
if (this.apiKey) {
|
|
312
|
-
if (this.apiKeyHeader === "authorization") {
|
|
313
|
-
headers["authorization"] = this.apiKey.startsWith("upk_") ? `Bearer ${this.apiKey}` : this.apiKey;
|
|
314
|
-
} else if (this.apiKeyHeader === "x-api-key") {
|
|
315
|
-
headers["x-api-key"] = this.apiKey;
|
|
316
|
-
} else {
|
|
317
|
-
headers["x-up-api-key"] = this.apiKey;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
347
|
+
const headers = await this.buildAuthHeaders();
|
|
320
348
|
const res = await fetch(url, { method: "GET", headers, credentials: this.withCredentials ? "include" : "same-origin" });
|
|
321
349
|
if (!res.ok) {
|
|
322
350
|
let msg = "Failed to fetch files";
|
|
@@ -334,29 +362,21 @@ var UpfilesClient = class {
|
|
|
334
362
|
};
|
|
335
363
|
}
|
|
336
364
|
getEventsUrl(projectId) {
|
|
337
|
-
const basePath = this.thumbnailsPath.replace(/\/thumbnails$/, "");
|
|
338
|
-
const eventsPath = `${basePath}/events`;
|
|
339
365
|
const target = this.baseUrl ? `${this.baseUrl}/api/events` : "/api/events";
|
|
340
366
|
const url = new URL(target, window.location.href);
|
|
341
|
-
if (this.apiKey) {
|
|
342
|
-
url.searchParams.set("
|
|
343
|
-
}
|
|
344
|
-
if (projectId) {
|
|
345
|
-
url.searchParams.set("projectId", projectId);
|
|
367
|
+
if (!this.getToken && !this.apiKey && (projectId || this.projectId)) {
|
|
368
|
+
url.searchParams.set("projectId", projectId || this.projectId);
|
|
346
369
|
}
|
|
347
370
|
return url.toString();
|
|
348
371
|
}
|
|
349
372
|
};
|
|
350
|
-
|
|
351
|
-
// src/Uploader.tsx
|
|
352
|
-
var import_react = __toESM(require("react"));
|
|
353
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
373
|
+
var THUMBNAIL_SIZE_OPTIONS = [64, 128, 256, 512];
|
|
354
374
|
var Uploader = ({
|
|
355
375
|
clientOptions,
|
|
356
376
|
multiple = true,
|
|
357
377
|
accept = ["image/*", "video/*", "audio/*", "application/pdf", "text/plain", "application/json"],
|
|
358
|
-
maxFileSize =
|
|
359
|
-
maxFiles =
|
|
378
|
+
maxFileSize = 0,
|
|
379
|
+
maxFiles = 0,
|
|
360
380
|
onComplete,
|
|
361
381
|
onError,
|
|
362
382
|
onUploadBegin,
|
|
@@ -367,48 +387,136 @@ var Uploader = ({
|
|
|
367
387
|
dropzoneClassName,
|
|
368
388
|
children,
|
|
369
389
|
projectId,
|
|
390
|
+
userId,
|
|
370
391
|
folderPath,
|
|
371
392
|
fetchThumbnails,
|
|
372
393
|
autoRecordToDb = true,
|
|
373
394
|
recordUrl = "/api/files",
|
|
374
|
-
autoUpload = false
|
|
395
|
+
autoUpload = false,
|
|
396
|
+
thumbnailEnabled,
|
|
397
|
+
thumbnailSizes
|
|
375
398
|
}) => {
|
|
376
|
-
const inputRef =
|
|
377
|
-
const client =
|
|
378
|
-
const [files, setFiles] =
|
|
379
|
-
const [isUploading, setIsUploading] =
|
|
380
|
-
const [isDragOver, setIsDragOver] =
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
399
|
+
const inputRef = React4.useRef(null);
|
|
400
|
+
const client = React4.useMemo(() => new UpfilesClient(clientOptions ?? {}), [clientOptions]);
|
|
401
|
+
const [files, setFiles] = React4.useState([]);
|
|
402
|
+
const [isUploading, setIsUploading] = React4.useState(false);
|
|
403
|
+
const [isDragOver, setIsDragOver] = React4.useState(false);
|
|
404
|
+
const [quotaError, setQuotaError] = React4.useState(null);
|
|
405
|
+
const showThumbnailUI = thumbnailEnabled !== void 0;
|
|
406
|
+
const [thumbEnabled, setThumbEnabled] = React4.useState(thumbnailEnabled ?? true);
|
|
407
|
+
const [thumbSizes, setThumbSizes] = React4.useState(thumbnailSizes ?? [256, 512]);
|
|
408
|
+
const [thumbFileIds, setThumbFileIds] = React4.useState(/* @__PURE__ */ new Set());
|
|
409
|
+
const thumbEnabledRef = React4.useRef(thumbnailEnabled ?? true);
|
|
410
|
+
const thumbSizesRef = React4.useRef(thumbnailSizes ?? [256, 512]);
|
|
411
|
+
const thumbFileIdsRef = React4.useRef(/* @__PURE__ */ new Set());
|
|
412
|
+
React4.useEffect(() => {
|
|
413
|
+
thumbEnabledRef.current = thumbEnabled;
|
|
414
|
+
}, [thumbEnabled]);
|
|
415
|
+
React4.useEffect(() => {
|
|
416
|
+
thumbSizesRef.current = thumbSizes;
|
|
417
|
+
}, [thumbSizes]);
|
|
418
|
+
React4.useEffect(() => {
|
|
419
|
+
thumbFileIdsRef.current = thumbFileIds;
|
|
420
|
+
}, [thumbFileIds]);
|
|
421
|
+
const pendingImages = React4.useMemo(
|
|
422
|
+
() => files.filter((f2) => f2.type.startsWith("image/") && f2.status === "pending"),
|
|
423
|
+
[files]
|
|
424
|
+
);
|
|
425
|
+
const thumbSelectedCount = React4.useMemo(
|
|
426
|
+
() => pendingImages.filter((f2) => thumbFileIds.has(f2.id)).length,
|
|
427
|
+
[pendingImages, thumbFileIds]
|
|
428
|
+
);
|
|
429
|
+
const handleThumbEnabledToggle = React4.useCallback(() => {
|
|
430
|
+
const newVal = !thumbEnabledRef.current;
|
|
431
|
+
thumbEnabledRef.current = newVal;
|
|
432
|
+
setThumbEnabled(newVal);
|
|
433
|
+
if (newVal) {
|
|
434
|
+
setFiles((prev) => {
|
|
435
|
+
const ids = prev.filter((f2) => f2.type.startsWith("image/") && f2.status === "pending").map((f2) => f2.id);
|
|
436
|
+
if (ids.length > 0) {
|
|
437
|
+
const next = new Set(thumbFileIdsRef.current);
|
|
438
|
+
ids.forEach((id) => next.add(id));
|
|
439
|
+
thumbFileIdsRef.current = next;
|
|
440
|
+
setThumbFileIds(next);
|
|
441
|
+
}
|
|
442
|
+
return prev;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}, []);
|
|
446
|
+
const toggleThumbSize = React4.useCallback((size) => {
|
|
447
|
+
setThumbSizes((prev) => {
|
|
448
|
+
const next = prev.includes(size) ? prev.filter((s) => s !== size) : [...prev, size].sort((a, b) => a - b);
|
|
449
|
+
thumbSizesRef.current = next;
|
|
450
|
+
return next;
|
|
451
|
+
});
|
|
452
|
+
}, []);
|
|
453
|
+
const toggleFileThumb = React4.useCallback((fileId) => {
|
|
454
|
+
setThumbFileIds((prev) => {
|
|
455
|
+
const next = new Set(prev);
|
|
456
|
+
if (next.has(fileId)) next.delete(fileId);
|
|
457
|
+
else next.add(fileId);
|
|
458
|
+
thumbFileIdsRef.current = next;
|
|
459
|
+
return next;
|
|
387
460
|
});
|
|
388
|
-
|
|
461
|
+
}, []);
|
|
462
|
+
const selectAllThumbs = React4.useCallback(() => {
|
|
463
|
+
const ids = pendingImages.map((f2) => f2.id);
|
|
464
|
+
const next = new Set(thumbFileIdsRef.current);
|
|
465
|
+
ids.forEach((id) => next.add(id));
|
|
466
|
+
thumbFileIdsRef.current = next;
|
|
467
|
+
setThumbFileIds(next);
|
|
468
|
+
}, [pendingImages]);
|
|
469
|
+
const deselectAllThumbs = React4.useCallback(() => {
|
|
470
|
+
const pendingSet = new Set(pendingImages.map((f2) => f2.id));
|
|
471
|
+
const next = new Set([...thumbFileIdsRef.current].filter((id) => !pendingSet.has(id)));
|
|
472
|
+
thumbFileIdsRef.current = next;
|
|
473
|
+
setThumbFileIds(next);
|
|
474
|
+
}, [pendingImages]);
|
|
475
|
+
const addFiles = React4.useCallback((incoming) => {
|
|
476
|
+
setQuotaError(null);
|
|
477
|
+
const filtered = maxFileSize > 0 ? incoming.filter((f2) => f2.size <= maxFileSize) : incoming;
|
|
478
|
+
const limited = maxFiles > 0 ? filtered.slice(0, Math.max(0, maxFiles - files.length)) : filtered;
|
|
389
479
|
const mapped = limited.map((f2) => ({
|
|
390
480
|
id: Math.random().toString(36).slice(2),
|
|
391
481
|
file: f2,
|
|
392
482
|
name: f2.name,
|
|
393
483
|
size: f2.size,
|
|
394
|
-
type: f2.type,
|
|
484
|
+
type: f2.type || "application/octet-stream",
|
|
395
485
|
progress: 0,
|
|
396
486
|
status: "pending"
|
|
397
487
|
}));
|
|
398
488
|
setFiles((prev) => [...prev, ...mapped]);
|
|
399
|
-
|
|
400
|
-
|
|
489
|
+
if (showThumbnailUI && thumbEnabledRef.current) {
|
|
490
|
+
const imageIds = mapped.filter((f2) => f2.type.startsWith("image/")).map((f2) => f2.id);
|
|
491
|
+
if (imageIds.length > 0) {
|
|
492
|
+
const next = new Set(thumbFileIdsRef.current);
|
|
493
|
+
imageIds.forEach((id) => next.add(id));
|
|
494
|
+
thumbFileIdsRef.current = next;
|
|
495
|
+
setThumbFileIds(next);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}, [files.length, maxFileSize, maxFiles, showThumbnailUI]);
|
|
499
|
+
const removeFile = React4.useCallback((fileId) => {
|
|
500
|
+
setFiles((prev) => prev.filter((f2) => f2.id !== fileId));
|
|
501
|
+
if (showThumbnailUI) {
|
|
502
|
+
const next = new Set(thumbFileIdsRef.current);
|
|
503
|
+
next.delete(fileId);
|
|
504
|
+
thumbFileIdsRef.current = next;
|
|
505
|
+
setThumbFileIds(next);
|
|
506
|
+
}
|
|
507
|
+
}, [showThumbnailUI]);
|
|
508
|
+
const onInputChange = React4.useCallback((e) => {
|
|
401
509
|
const fs = Array.from(e.target.files || []);
|
|
402
510
|
addFiles(fs);
|
|
403
511
|
if (inputRef.current) inputRef.current.value = "";
|
|
404
512
|
}, [addFiles]);
|
|
405
|
-
const handleDrop =
|
|
513
|
+
const handleDrop = React4.useCallback((e) => {
|
|
406
514
|
e.preventDefault();
|
|
407
515
|
e.stopPropagation();
|
|
408
516
|
setIsDragOver(false);
|
|
409
517
|
addFiles(Array.from(e.dataTransfer.files || []));
|
|
410
518
|
}, [addFiles]);
|
|
411
|
-
const uploadOne =
|
|
519
|
+
const uploadOne = React4.useCallback(async (item) => {
|
|
412
520
|
const update = (patch) => {
|
|
413
521
|
setFiles((prev) => prev.map((f2) => f2.id === item.id ? { ...f2, ...patch } : f2));
|
|
414
522
|
};
|
|
@@ -417,7 +525,7 @@ var Uploader = ({
|
|
|
417
525
|
update({ status: "uploading", progress: 1 });
|
|
418
526
|
const presign = await client.getPresignedUrl({
|
|
419
527
|
fileName: item.name,
|
|
420
|
-
fileType: item.type,
|
|
528
|
+
fileType: item.type || "application/octet-stream",
|
|
421
529
|
fileSize: item.size,
|
|
422
530
|
projectId,
|
|
423
531
|
folderPath
|
|
@@ -444,14 +552,17 @@ var Uploader = ({
|
|
|
444
552
|
xhr.send(item.file);
|
|
445
553
|
});
|
|
446
554
|
let completionResult = null;
|
|
447
|
-
|
|
555
|
+
const canRecordToDb = autoRecordToDb && (projectId || clientOptions?.projectName || clientOptions?.projectId || clientOptions?.apiKey);
|
|
556
|
+
if (canRecordToDb) {
|
|
448
557
|
try {
|
|
449
558
|
completionResult = await client.completeUpload({
|
|
450
559
|
fileKey: presign.fileKey,
|
|
451
560
|
originalName: item.name,
|
|
452
561
|
size: item.size,
|
|
453
|
-
contentType: item.type,
|
|
454
|
-
projectId
|
|
562
|
+
contentType: item.type || "application/octet-stream",
|
|
563
|
+
projectId,
|
|
564
|
+
url: presign.publicUrl,
|
|
565
|
+
userId
|
|
455
566
|
});
|
|
456
567
|
} catch (e) {
|
|
457
568
|
console.error("Failed to complete upload:", e);
|
|
@@ -475,39 +586,86 @@ var Uploader = ({
|
|
|
475
586
|
}
|
|
476
587
|
const finalFileKey = completionResult?.file?.key || presign.fileKey;
|
|
477
588
|
const finalPublicUrl = completionResult?.file?.url || presign.publicUrl;
|
|
589
|
+
const fileThumbEnabled = showThumbnailUI && thumbEnabledRef.current && thumbFileIdsRef.current.has(item.id);
|
|
478
590
|
const result = {
|
|
479
591
|
...item,
|
|
480
592
|
status: "success",
|
|
481
593
|
progress: 100,
|
|
482
|
-
fileKey:
|
|
483
|
-
publicUrl:
|
|
594
|
+
fileKey: finalFileKey,
|
|
595
|
+
publicUrl: finalPublicUrl,
|
|
484
596
|
projectId: presign.projectId,
|
|
485
597
|
apiKeyId: presign.apiKeyId,
|
|
598
|
+
thumbnailEnabled: showThumbnailUI ? fileThumbEnabled : void 0,
|
|
599
|
+
thumbnailSizesSelected: showThumbnailUI ? fileThumbEnabled ? [...thumbSizesRef.current] : [] : void 0,
|
|
486
600
|
// @ts-ignore - allow downstream to access thumbs if present
|
|
487
601
|
thumbnails: thumbs,
|
|
488
602
|
// @ts-ignore - allow downstream to access masterWebp if present
|
|
489
603
|
masterWebp
|
|
490
604
|
};
|
|
491
|
-
update({ status: "success", progress: 100, fileKey:
|
|
605
|
+
update({ status: "success", progress: 100, fileKey: finalFileKey, publicUrl: finalPublicUrl });
|
|
492
606
|
onClientUploadComplete?.(result);
|
|
493
607
|
return result;
|
|
494
608
|
} catch (err) {
|
|
495
|
-
const message = err instanceof Error ? err.message : "Upload failed";
|
|
609
|
+
const message = err instanceof StorageQuotaError ? err.message : err instanceof Error ? err.message : "Upload failed";
|
|
496
610
|
update({ status: "error", error: message });
|
|
497
611
|
throw err;
|
|
498
612
|
}
|
|
499
|
-
}, [client, projectId, folderPath, fetchThumbnails, autoRecordToDb, recordUrl, clientOptions, onUploadBegin, onUploadProgress, onClientUploadComplete]);
|
|
500
|
-
const uploadAll =
|
|
613
|
+
}, [client, projectId, userId, folderPath, fetchThumbnails, autoRecordToDb, recordUrl, clientOptions, onUploadBegin, onUploadProgress, onClientUploadComplete, showThumbnailUI]);
|
|
614
|
+
const uploadAll = React4.useCallback(async () => {
|
|
501
615
|
const targets = files.filter((f2) => f2.status === "pending");
|
|
502
616
|
if (!targets.length) return;
|
|
617
|
+
setQuotaError(null);
|
|
503
618
|
setIsUploading(true);
|
|
504
619
|
try {
|
|
620
|
+
try {
|
|
621
|
+
const quota = await client.getQuota();
|
|
622
|
+
if (quota.limitBytes > 0) {
|
|
623
|
+
const totalPendingSize = targets.reduce((sum, f2) => sum + f2.size, 0);
|
|
624
|
+
const remaining = quota.limitBytes - quota.usedBytes;
|
|
625
|
+
if (totalPendingSize > remaining) {
|
|
626
|
+
const fmt = (n) => {
|
|
627
|
+
if (n >= 1024 * 1024 * 1024) return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
628
|
+
if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(2)} MB`;
|
|
629
|
+
if (n >= 1024) return `${(n / 1024).toFixed(2)} KB`;
|
|
630
|
+
return `${n} B`;
|
|
631
|
+
};
|
|
632
|
+
const errMsg = `Storage quota exceeded. Remaining: ${fmt(remaining)}, Upload size: ${fmt(totalPendingSize)}`;
|
|
633
|
+
setQuotaError(errMsg);
|
|
634
|
+
targets.forEach((t) => {
|
|
635
|
+
setFiles((prev) => prev.map((f2) => f2.id === t.id ? { ...f2, status: "error", error: errMsg } : f2));
|
|
636
|
+
});
|
|
637
|
+
onError?.(new StorageQuotaError(errMsg, {
|
|
638
|
+
limit: quota.limitBytes,
|
|
639
|
+
used: quota.usedBytes,
|
|
640
|
+
remaining,
|
|
641
|
+
fileSize: totalPendingSize
|
|
642
|
+
}));
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
} catch (e) {
|
|
647
|
+
if (e instanceof StorageQuotaError) {
|
|
648
|
+
setQuotaError(e.message);
|
|
649
|
+
targets.forEach((t) => {
|
|
650
|
+
setFiles((prev) => prev.map((f2) => f2.id === t.id ? { ...f2, status: "error", error: e.message } : f2));
|
|
651
|
+
});
|
|
652
|
+
onError?.(e);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
505
656
|
const results = [];
|
|
506
657
|
for (const f2 of targets) {
|
|
507
658
|
try {
|
|
508
659
|
const done = await uploadOne(f2);
|
|
509
660
|
results.push(done);
|
|
510
661
|
} catch (e) {
|
|
662
|
+
if (e instanceof StorageQuotaError) {
|
|
663
|
+
setQuotaError(e.message);
|
|
664
|
+
setFiles((prev) => prev.map(
|
|
665
|
+
(file) => file.status === "pending" ? { ...file, status: "error", error: e.message } : file
|
|
666
|
+
));
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
511
669
|
}
|
|
512
670
|
}
|
|
513
671
|
onComplete?.(results.filter((f2) => f2.status === "success"));
|
|
@@ -516,8 +674,8 @@ var Uploader = ({
|
|
|
516
674
|
} finally {
|
|
517
675
|
setIsUploading(false);
|
|
518
676
|
}
|
|
519
|
-
}, [files, onComplete, onError, uploadOne]);
|
|
520
|
-
|
|
677
|
+
}, [files, client, onComplete, onError, uploadOne]);
|
|
678
|
+
React4__namespace.default.useEffect(() => {
|
|
521
679
|
if (autoUpload && files.some((f2) => f2.status === "pending") && !isUploading) {
|
|
522
680
|
const timer = setTimeout(() => {
|
|
523
681
|
uploadAll();
|
|
@@ -525,8 +683,8 @@ var Uploader = ({
|
|
|
525
683
|
return () => clearTimeout(timer);
|
|
526
684
|
}
|
|
527
685
|
}, [autoUpload, files, isUploading, uploadAll]);
|
|
528
|
-
return /* @__PURE__ */
|
|
529
|
-
/* @__PURE__ */
|
|
686
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
687
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
530
688
|
"input",
|
|
531
689
|
{
|
|
532
690
|
ref: inputRef,
|
|
@@ -537,7 +695,7 @@ var Uploader = ({
|
|
|
537
695
|
onChange: onInputChange
|
|
538
696
|
}
|
|
539
697
|
),
|
|
540
|
-
/* @__PURE__ */
|
|
698
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
541
699
|
"div",
|
|
542
700
|
{
|
|
543
701
|
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"}`,
|
|
@@ -556,82 +714,175 @@ var Uploader = ({
|
|
|
556
714
|
role: "button",
|
|
557
715
|
"aria-label": "Upload files",
|
|
558
716
|
children: [
|
|
559
|
-
/* @__PURE__ */
|
|
560
|
-
/* @__PURE__ */
|
|
561
|
-
/* @__PURE__ */
|
|
562
|
-
/* @__PURE__ */
|
|
717
|
+
/* @__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: [
|
|
718
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
719
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "17 8 12 3 7 8" }),
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
563
721
|
] }) }),
|
|
564
|
-
children ?? /* @__PURE__ */
|
|
565
|
-
/* @__PURE__ */
|
|
566
|
-
/* @__PURE__ */
|
|
567
|
-
multiple ? `Up to ${maxFiles} files` : "
|
|
568
|
-
|
|
569
|
-
(maxFileSize / (1024 * 1024)).toFixed(0),
|
|
570
|
-
"MB"
|
|
722
|
+
children ?? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-lg font-semibold text-gray-900 dark:text-gray-100", children: "Drop files here or click to browse" }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-gray-500 dark:text-gray-400 mt-1", children: [
|
|
725
|
+
!multiple ? "Single file" : maxFiles > 0 ? `Up to ${maxFiles} files` : "Multiple files",
|
|
726
|
+
maxFileSize > 0 ? ` \u2022 Max ${(maxFileSize / (1024 * 1024)).toFixed(0)}MB per file` : ""
|
|
571
727
|
] })
|
|
572
728
|
] })
|
|
573
729
|
]
|
|
574
730
|
}
|
|
575
731
|
),
|
|
576
|
-
|
|
577
|
-
/* @__PURE__ */
|
|
578
|
-
/* @__PURE__ */
|
|
579
|
-
/* @__PURE__ */
|
|
580
|
-
|
|
732
|
+
quotaError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
733
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 w-8 h-8 bg-red-100 dark:bg-red-900/30 rounded-full flex items-center justify-center", 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", className: "text-red-600 dark:text-red-400", children: [
|
|
734
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
735
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
737
|
+
] }) }),
|
|
738
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-red-800 dark:text-red-300", children: quotaError }) }),
|
|
739
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
740
|
+
"button",
|
|
741
|
+
{
|
|
742
|
+
onClick: () => setQuotaError(null),
|
|
743
|
+
className: "text-red-400 hover:text-red-600 dark:hover:text-red-300 transition-colors",
|
|
744
|
+
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: [
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
747
|
+
] })
|
|
748
|
+
}
|
|
749
|
+
)
|
|
750
|
+
] }) }),
|
|
751
|
+
files.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 space-y-4", children: [
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 dark:border-gray-800 pb-4", children: [
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-gray-900 dark:text-gray-100", children: "Selected Files" }),
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3", children: [
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
581
756
|
"button",
|
|
582
757
|
{
|
|
583
758
|
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",
|
|
584
759
|
disabled: isUploading || files.every((f2) => f2.status !== "pending"),
|
|
585
760
|
onClick: uploadAll,
|
|
586
|
-
children: isUploading ? /* @__PURE__ */
|
|
587
|
-
/* @__PURE__ */
|
|
588
|
-
/* @__PURE__ */
|
|
589
|
-
/* @__PURE__ */
|
|
761
|
+
children: isUploading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
|
|
762
|
+
/* @__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: [
|
|
763
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
764
|
+
/* @__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" })
|
|
590
765
|
] }),
|
|
591
766
|
"Uploading..."
|
|
592
767
|
] }) : "Upload All"
|
|
593
768
|
}
|
|
594
769
|
),
|
|
595
|
-
/* @__PURE__ */
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
596
771
|
"button",
|
|
597
772
|
{
|
|
598
773
|
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",
|
|
599
774
|
disabled: isUploading,
|
|
600
|
-
onClick: () =>
|
|
775
|
+
onClick: () => {
|
|
776
|
+
setFiles([]);
|
|
777
|
+
if (showThumbnailUI) {
|
|
778
|
+
const empty = /* @__PURE__ */ new Set();
|
|
779
|
+
thumbFileIdsRef.current = empty;
|
|
780
|
+
setThumbFileIds(empty);
|
|
781
|
+
}
|
|
782
|
+
},
|
|
601
783
|
children: "Clear"
|
|
602
784
|
}
|
|
603
785
|
)
|
|
604
786
|
] })
|
|
605
787
|
] }),
|
|
606
|
-
|
|
607
|
-
/* @__PURE__ */
|
|
608
|
-
/* @__PURE__ */
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
788
|
+
showThumbnailUI && pendingImages.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-purple-50 dark:bg-purple-900/10 border border-purple-200 dark:border-purple-800/50 rounded-xl p-4", children: [
|
|
789
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
|
|
790
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
791
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2", children: [
|
|
792
|
+
/* @__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", className: "text-purple-500", children: [
|
|
793
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
|
|
794
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 17 12 22 22 17" }),
|
|
795
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 12 12 17 22 12" })
|
|
796
|
+
] }),
|
|
797
|
+
"Generate Thumbnails"
|
|
798
|
+
] }),
|
|
799
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-0.5", children: thumbEnabled ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
800
|
+
thumbSelectedCount,
|
|
801
|
+
" of ",
|
|
802
|
+
pendingImages.length,
|
|
803
|
+
" image",
|
|
804
|
+
pendingImages.length !== 1 ? "s" : "",
|
|
805
|
+
" selected",
|
|
806
|
+
" \xB7 ",
|
|
807
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: selectAllThumbs, className: "text-purple-600 hover:text-purple-700 dark:text-purple-400 dark:hover:text-purple-300", children: "Select all" }),
|
|
808
|
+
" \xB7 ",
|
|
809
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: deselectAllThumbs, className: "text-gray-500 hover:text-gray-600 dark:text-gray-400 dark:hover:text-gray-300", children: "Deselect all" })
|
|
810
|
+
] }) : "Disabled for all images" })
|
|
811
|
+
] }),
|
|
812
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
813
|
+
"button",
|
|
814
|
+
{
|
|
815
|
+
type: "button",
|
|
816
|
+
onClick: handleThumbEnabledToggle,
|
|
817
|
+
className: `relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none ${thumbEnabled ? "bg-blue-600" : "bg-gray-200 dark:bg-gray-700"}`,
|
|
818
|
+
role: "switch",
|
|
819
|
+
"aria-checked": thumbEnabled,
|
|
820
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition ${thumbEnabled ? "translate-x-5" : "translate-x-0"}` })
|
|
821
|
+
}
|
|
822
|
+
)
|
|
823
|
+
] }),
|
|
824
|
+
thumbEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
825
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-gray-600 dark:text-gray-400 mb-2", children: "Sizes:" }),
|
|
826
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 flex-wrap", children: THUMBNAIL_SIZE_OPTIONS.map((size) => {
|
|
827
|
+
const active = thumbSizes.includes(size);
|
|
828
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
829
|
+
"button",
|
|
830
|
+
{
|
|
831
|
+
type: "button",
|
|
832
|
+
onClick: () => toggleThumbSize(size),
|
|
833
|
+
className: `px-3 py-1 rounded-full text-xs font-medium border transition-colors ${active ? "bg-purple-600 border-purple-600 text-white" : "bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-400 hover:border-purple-400 hover:text-purple-600"}`,
|
|
834
|
+
children: [
|
|
835
|
+
size,
|
|
836
|
+
"px"
|
|
837
|
+
]
|
|
838
|
+
},
|
|
839
|
+
size
|
|
840
|
+
);
|
|
841
|
+
}) }),
|
|
842
|
+
thumbSizes.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-amber-600 dark:text-amber-400 mt-2", children: "Select at least one size." })
|
|
843
|
+
] })
|
|
844
|
+
] }),
|
|
845
|
+
/* @__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: [
|
|
846
|
+
/* @__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: [
|
|
847
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
848
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
849
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
850
|
+
] }) : /* @__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: [
|
|
851
|
+
/* @__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" }),
|
|
852
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "14 2 14 8 20 8" })
|
|
614
853
|
] }) }),
|
|
615
|
-
/* @__PURE__ */
|
|
616
|
-
/* @__PURE__ */
|
|
617
|
-
/* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
854
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
855
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
856
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 truncate pr-4", children: f2.name }),
|
|
857
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 font-medium whitespace-nowrap", children: [
|
|
619
858
|
(f2.size / (1024 * 1024)).toFixed(2),
|
|
620
859
|
" MB"
|
|
621
860
|
] })
|
|
622
861
|
] }),
|
|
623
|
-
f2.status === "error" ? /* @__PURE__ */
|
|
862
|
+
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(
|
|
624
863
|
"div",
|
|
625
864
|
{
|
|
626
865
|
className: `h-full transition-all duration-300 ${f2.status === "success" ? "bg-green-500" : "bg-blue-500"}`,
|
|
627
866
|
style: { width: `${f2.progress}%` }
|
|
628
867
|
}
|
|
629
|
-
) })
|
|
868
|
+
) }),
|
|
869
|
+
showThumbnailUI && thumbEnabled && f2.type.startsWith("image/") && f2.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "mt-1.5 inline-flex items-center gap-1.5 cursor-pointer select-none", children: [
|
|
870
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
871
|
+
"input",
|
|
872
|
+
{
|
|
873
|
+
type: "checkbox",
|
|
874
|
+
checked: thumbFileIds.has(f2.id),
|
|
875
|
+
onChange: () => toggleFileThumb(f2.id),
|
|
876
|
+
className: "w-3.5 h-3.5 rounded accent-purple-600 cursor-pointer"
|
|
877
|
+
}
|
|
878
|
+
),
|
|
879
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Generate thumbnail" })
|
|
880
|
+
] })
|
|
630
881
|
] }),
|
|
631
|
-
/* @__PURE__ */
|
|
632
|
-
f2.status === "success" && /* @__PURE__ */
|
|
633
|
-
/* @__PURE__ */
|
|
634
|
-
f2.publicUrl && /* @__PURE__ */
|
|
882
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 ml-4", children: [
|
|
883
|
+
f2.status === "success" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
884
|
+
/* @__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" }) }) }),
|
|
885
|
+
f2.publicUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
635
886
|
"a",
|
|
636
887
|
{
|
|
637
888
|
href: f2.publicUrl,
|
|
@@ -639,34 +890,34 @@ var Uploader = ({
|
|
|
639
890
|
rel: "noreferrer",
|
|
640
891
|
className: "p-1.5 text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20 rounded-md transition-colors",
|
|
641
892
|
title: "View online",
|
|
642
|
-
children: /* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
644
|
-
/* @__PURE__ */
|
|
645
|
-
/* @__PURE__ */
|
|
893
|
+
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: [
|
|
894
|
+
/* @__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" }),
|
|
895
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }),
|
|
896
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
|
|
646
897
|
] })
|
|
647
898
|
}
|
|
648
899
|
)
|
|
649
900
|
] }),
|
|
650
|
-
f2.status === "error" && /* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
901
|
+
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: [
|
|
902
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
903
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
653
904
|
] }) }),
|
|
654
|
-
f2.status === "uploading" && /* @__PURE__ */
|
|
905
|
+
f2.status === "uploading" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-semibold text-blue-600 dark:text-blue-400", children: [
|
|
655
906
|
f2.progress,
|
|
656
907
|
"%"
|
|
657
908
|
] }),
|
|
658
|
-
f2.status === "pending" && /* @__PURE__ */
|
|
909
|
+
f2.status === "pending" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
659
910
|
"button",
|
|
660
911
|
{
|
|
661
912
|
onClick: (e) => {
|
|
662
913
|
e.stopPropagation();
|
|
663
|
-
|
|
914
|
+
removeFile(f2.id);
|
|
664
915
|
},
|
|
665
916
|
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",
|
|
666
917
|
title: "Remove",
|
|
667
|
-
children: /* @__PURE__ */
|
|
668
|
-
/* @__PURE__ */
|
|
669
|
-
/* @__PURE__ */
|
|
918
|
+
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: [
|
|
919
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
920
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
670
921
|
] })
|
|
671
922
|
}
|
|
672
923
|
)
|
|
@@ -675,10 +926,6 @@ var Uploader = ({
|
|
|
675
926
|
] })
|
|
676
927
|
] });
|
|
677
928
|
};
|
|
678
|
-
|
|
679
|
-
// src/ProjectFilesWidget.tsx
|
|
680
|
-
var import_react2 = __toESM(require("react"));
|
|
681
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
682
929
|
var ProjectFilesWidget = ({
|
|
683
930
|
clientOptions,
|
|
684
931
|
folderPath,
|
|
@@ -694,16 +941,16 @@ var ProjectFilesWidget = ({
|
|
|
694
941
|
showDelete = false,
|
|
695
942
|
refreshInterval = 5e3
|
|
696
943
|
}) => {
|
|
697
|
-
const client =
|
|
698
|
-
const [files, setFiles] =
|
|
699
|
-
const [loading, setLoading] =
|
|
700
|
-
const [error, setError] =
|
|
701
|
-
const [query, setQuery] =
|
|
702
|
-
const [savingKey, setSavingKey] =
|
|
703
|
-
const [deletingKey, setDeletingKey] =
|
|
704
|
-
const lastUpdatedRef =
|
|
705
|
-
const isFetchingRef =
|
|
706
|
-
const fetchFiles =
|
|
944
|
+
const client = React4.useMemo(() => new UpfilesClient(clientOptions ?? {}), [clientOptions]);
|
|
945
|
+
const [files, setFiles] = React4.useState([]);
|
|
946
|
+
const [loading, setLoading] = React4.useState(false);
|
|
947
|
+
const [error, setError] = React4.useState(null);
|
|
948
|
+
const [query, setQuery] = React4.useState("");
|
|
949
|
+
const [savingKey, setSavingKey] = React4.useState(null);
|
|
950
|
+
const [deletingKey, setDeletingKey] = React4.useState(null);
|
|
951
|
+
const lastUpdatedRef = React4__namespace.default.useRef(null);
|
|
952
|
+
const isFetchingRef = React4__namespace.default.useRef(false);
|
|
953
|
+
const fetchFiles = React4__namespace.default.useCallback(async (isPolling = false) => {
|
|
707
954
|
if (isFetchingRef.current) return;
|
|
708
955
|
isFetchingRef.current = true;
|
|
709
956
|
if (!isPolling) setLoading(true);
|
|
@@ -722,10 +969,10 @@ var ProjectFilesWidget = ({
|
|
|
722
969
|
isFetchingRef.current = false;
|
|
723
970
|
}
|
|
724
971
|
}, [client, folderPath]);
|
|
725
|
-
|
|
972
|
+
React4.useEffect(() => {
|
|
726
973
|
fetchFiles();
|
|
727
974
|
}, [fetchFiles]);
|
|
728
|
-
|
|
975
|
+
React4.useEffect(() => {
|
|
729
976
|
if (!refreshInterval || refreshInterval <= 0) return;
|
|
730
977
|
const timer = setInterval(() => {
|
|
731
978
|
fetchFiles(true);
|
|
@@ -748,10 +995,18 @@ var ProjectFilesWidget = ({
|
|
|
748
995
|
"content-type": "application/json",
|
|
749
996
|
...clientOptions?.headers || {}
|
|
750
997
|
};
|
|
751
|
-
|
|
998
|
+
const token = clientOptions?.getToken?.();
|
|
999
|
+
if (token) {
|
|
1000
|
+
headers["authorization"] = `Bearer ${token}`;
|
|
1001
|
+
if (clientOptions.projectName) {
|
|
1002
|
+
headers["x-project-name"] = clientOptions.projectName;
|
|
1003
|
+
} else if (clientOptions.projectId) {
|
|
1004
|
+
headers["x-project-id"] = clientOptions.projectId;
|
|
1005
|
+
}
|
|
1006
|
+
} else if (clientOptions?.apiKey) {
|
|
752
1007
|
const keyHeader = clientOptions.apiKeyHeader || "authorization";
|
|
753
1008
|
if (keyHeader === "authorization") {
|
|
754
|
-
headers["authorization"] = clientOptions.apiKey.startsWith("
|
|
1009
|
+
headers["authorization"] = clientOptions.apiKey.startsWith("upf_") ? `Bearer ${clientOptions.apiKey}` : clientOptions.apiKey;
|
|
755
1010
|
} else {
|
|
756
1011
|
headers[keyHeader] = clientOptions.apiKey;
|
|
757
1012
|
}
|
|
@@ -771,9 +1026,9 @@ var ProjectFilesWidget = ({
|
|
|
771
1026
|
setDeletingKey(null);
|
|
772
1027
|
}
|
|
773
1028
|
};
|
|
774
|
-
return /* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
776
|
-
/* @__PURE__ */
|
|
1029
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
1030
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", marginBottom: 8 }, children: [
|
|
1031
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
777
1032
|
"input",
|
|
778
1033
|
{
|
|
779
1034
|
type: "text",
|
|
@@ -783,7 +1038,7 @@ var ProjectFilesWidget = ({
|
|
|
783
1038
|
style: { flex: 1, padding: 8, border: "1px solid #e5e7eb", borderRadius: 6 }
|
|
784
1039
|
}
|
|
785
1040
|
),
|
|
786
|
-
/* @__PURE__ */
|
|
1041
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
787
1042
|
"button",
|
|
788
1043
|
{
|
|
789
1044
|
onClick: () => {
|
|
@@ -795,26 +1050,26 @@ var ProjectFilesWidget = ({
|
|
|
795
1050
|
}
|
|
796
1051
|
)
|
|
797
1052
|
] }),
|
|
798
|
-
loading && /* @__PURE__ */
|
|
799
|
-
error && /* @__PURE__ */
|
|
800
|
-
!loading && !error && /* @__PURE__ */
|
|
801
|
-
visible.map((f2) => /* @__PURE__ */
|
|
1053
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#666" }, children: "Loading files\u2026" }),
|
|
1054
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: "#b91c1c" }, children: error }),
|
|
1055
|
+
!loading && !error && /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: listClassName, style: { listStyle: "none", padding: 0, margin: 0, display: "grid", gap: 8 }, children: [
|
|
1056
|
+
visible.map((f2) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
802
1057
|
"li",
|
|
803
1058
|
{
|
|
804
1059
|
className: itemClassName,
|
|
805
1060
|
style: { border: "1px solid #e5e7eb", borderRadius: 8, padding: 10, display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
806
1061
|
children: [
|
|
807
|
-
/* @__PURE__ */
|
|
808
|
-
/* @__PURE__ */
|
|
809
|
-
/* @__PURE__ */
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
|
|
1063
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis" }, children: f2.originalName }),
|
|
1064
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: "#666" }, children: [
|
|
810
1065
|
(f2.size / (1024 * 1024)).toFixed(2),
|
|
811
1066
|
" MB \u2022 ",
|
|
812
1067
|
f2.contentType
|
|
813
1068
|
] })
|
|
814
1069
|
] }),
|
|
815
|
-
/* @__PURE__ */
|
|
816
|
-
/* @__PURE__ */
|
|
817
|
-
/* @__PURE__ */
|
|
1070
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
1071
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: f2.url, target: "_blank", rel: "noreferrer", style: { fontSize: 12, color: "#2563eb" }, children: "View" }),
|
|
1072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
818
1073
|
"button",
|
|
819
1074
|
{
|
|
820
1075
|
onClick: async () => {
|
|
@@ -851,7 +1106,7 @@ var ProjectFilesWidget = ({
|
|
|
851
1106
|
children: savingKey === f2.key ? "Saving\u2026" : "Use"
|
|
852
1107
|
}
|
|
853
1108
|
),
|
|
854
|
-
showDelete && /* @__PURE__ */
|
|
1109
|
+
showDelete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
855
1110
|
"button",
|
|
856
1111
|
{
|
|
857
1112
|
onClick: () => handleDelete(f2.key),
|
|
@@ -865,17 +1120,26 @@ var ProjectFilesWidget = ({
|
|
|
865
1120
|
},
|
|
866
1121
|
f2.key
|
|
867
1122
|
)),
|
|
868
|
-
visible.length === 0 && /* @__PURE__ */
|
|
1123
|
+
visible.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { style: { fontSize: 12, color: "#666" }, children: "No files found" })
|
|
869
1124
|
] })
|
|
870
1125
|
] });
|
|
871
1126
|
};
|
|
872
1127
|
|
|
873
|
-
// src/ConnectProjectDialog.tsx
|
|
874
|
-
var React3 = __toESM(require("react"));
|
|
875
|
-
var Dialog = __toESM(require("@radix-ui/react-dialog"));
|
|
876
|
-
|
|
877
1128
|
// src/projectConnect.ts
|
|
878
1129
|
var f = (globalThis.fetch || fetch).bind(globalThis);
|
|
1130
|
+
function buildInit(opts, extra) {
|
|
1131
|
+
const headers = {
|
|
1132
|
+
...extra?.headers || {}
|
|
1133
|
+
};
|
|
1134
|
+
if (opts.token) {
|
|
1135
|
+
headers["authorization"] = `Bearer ${opts.token}`;
|
|
1136
|
+
}
|
|
1137
|
+
return {
|
|
1138
|
+
...extra,
|
|
1139
|
+
headers,
|
|
1140
|
+
credentials: opts.token ? "same-origin" : "include"
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
879
1143
|
async function httpJson(input, init, useFetch) {
|
|
880
1144
|
const doFetch = useFetch ?? f;
|
|
881
1145
|
const res = await doFetch(input, init);
|
|
@@ -883,7 +1147,7 @@ async function httpJson(input, init, useFetch) {
|
|
|
883
1147
|
let msg = `Request failed (${res.status})`;
|
|
884
1148
|
try {
|
|
885
1149
|
const j = await res.json();
|
|
886
|
-
msg = j?.error || msg;
|
|
1150
|
+
msg = j?.error || j?.message || msg;
|
|
887
1151
|
} catch {
|
|
888
1152
|
}
|
|
889
1153
|
throw new Error(msg);
|
|
@@ -894,7 +1158,7 @@ async function listProjects(opts) {
|
|
|
894
1158
|
const url = `${opts.baseUrl.replace(/\/$/, "")}/api/projects`;
|
|
895
1159
|
const data = await httpJson(
|
|
896
1160
|
url,
|
|
897
|
-
{ method: "GET"
|
|
1161
|
+
buildInit(opts, { method: "GET" }),
|
|
898
1162
|
opts.fetch
|
|
899
1163
|
);
|
|
900
1164
|
return data?.projects ?? [];
|
|
@@ -903,53 +1167,74 @@ async function createProject(opts) {
|
|
|
903
1167
|
const base = opts.baseUrl.replace(/\/$/, "");
|
|
904
1168
|
const created = await httpJson(
|
|
905
1169
|
`${base}/api/projects`,
|
|
906
|
-
{
|
|
1170
|
+
buildInit(opts, {
|
|
907
1171
|
method: "POST",
|
|
908
|
-
credentials: "include",
|
|
909
1172
|
headers: { "content-type": "application/json" },
|
|
910
|
-
body: JSON.stringify({
|
|
911
|
-
|
|
1173
|
+
body: JSON.stringify({
|
|
1174
|
+
name: opts.name,
|
|
1175
|
+
isInHouse: opts.isInHouse,
|
|
1176
|
+
freeStorageLimitBytes: opts.freeStorageLimitBytes
|
|
1177
|
+
})
|
|
1178
|
+
}),
|
|
912
1179
|
opts.fetch
|
|
913
1180
|
);
|
|
914
1181
|
const projectId = created?.project?.id;
|
|
915
1182
|
if (!projectId) throw new Error("Failed to create project");
|
|
1183
|
+
return { projectId, project: created?.project };
|
|
1184
|
+
}
|
|
1185
|
+
async function createProjectWithApiKey(opts) {
|
|
1186
|
+
const base = opts.baseUrl.replace(/\/$/, "");
|
|
1187
|
+
const { projectId } = await createProject(opts);
|
|
916
1188
|
const keyLabel = opts.keyName || `Plugin key ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
917
1189
|
const key = await httpJson(
|
|
918
1190
|
`${base}/api/projects/${projectId}/keys`,
|
|
919
|
-
{
|
|
1191
|
+
buildInit(opts, {
|
|
920
1192
|
method: "POST",
|
|
921
|
-
credentials: "include",
|
|
922
1193
|
headers: { "content-type": "application/json" },
|
|
923
1194
|
body: JSON.stringify({ name: keyLabel })
|
|
924
|
-
},
|
|
1195
|
+
}),
|
|
925
1196
|
opts.fetch
|
|
926
1197
|
);
|
|
927
|
-
const plaintext = key?.key?.plaintext;
|
|
1198
|
+
const plaintext = key?.key?.secret ?? key?.key?.plaintext;
|
|
928
1199
|
if (!plaintext) throw new Error("Failed to create API key");
|
|
929
1200
|
return { apiKey: plaintext, projectId };
|
|
930
1201
|
}
|
|
1202
|
+
async function updateProject(opts) {
|
|
1203
|
+
const base = opts.baseUrl.replace(/\/$/, "");
|
|
1204
|
+
const body = {};
|
|
1205
|
+
if (opts.isInHouse !== void 0) body.isInHouse = opts.isInHouse;
|
|
1206
|
+
if (opts.freeStorageLimitBytes !== void 0) body.freeStorageLimitBytes = opts.freeStorageLimitBytes;
|
|
1207
|
+
await httpJson(
|
|
1208
|
+
`${base}/api/projects/${opts.projectId}`,
|
|
1209
|
+
buildInit(opts, {
|
|
1210
|
+
method: "PUT",
|
|
1211
|
+
headers: { "content-type": "application/json" },
|
|
1212
|
+
body: JSON.stringify(body)
|
|
1213
|
+
}),
|
|
1214
|
+
opts.fetch
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
931
1217
|
async function connectProject(opts) {
|
|
932
1218
|
const base = opts.baseUrl.replace(/\/$/, "");
|
|
933
1219
|
const keyLabel = opts.keyName || `Plugin key ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
|
|
934
1220
|
const key = await httpJson(
|
|
935
1221
|
`${base}/api/projects/${opts.projectId}/keys`,
|
|
936
|
-
{
|
|
1222
|
+
buildInit(opts, {
|
|
937
1223
|
method: "POST",
|
|
938
|
-
credentials: "include",
|
|
939
1224
|
headers: { "content-type": "application/json" },
|
|
940
1225
|
body: JSON.stringify({ name: keyLabel })
|
|
941
|
-
},
|
|
1226
|
+
}),
|
|
942
1227
|
opts.fetch
|
|
943
1228
|
);
|
|
944
|
-
const plaintext = key?.key?.plaintext;
|
|
1229
|
+
const plaintext = key?.key?.secret ?? key?.key?.plaintext;
|
|
945
1230
|
if (!plaintext) throw new Error("Failed to create API key");
|
|
946
1231
|
return { apiKey: plaintext };
|
|
947
1232
|
}
|
|
948
1233
|
function addApiKeyManually(apiKey) {
|
|
949
1234
|
const k = (apiKey || "").trim();
|
|
950
1235
|
if (!k) throw new Error("API key is required");
|
|
951
|
-
if (!/^
|
|
952
|
-
console.warn("Provided API key does not match expected format
|
|
1236
|
+
if (!/^upf_[A-Za-z0-9_-]+/.test(k)) {
|
|
1237
|
+
console.warn("Provided API key does not match expected format upf_*");
|
|
953
1238
|
}
|
|
954
1239
|
return { apiKey: k };
|
|
955
1240
|
}
|
|
@@ -963,19 +1248,16 @@ function connectUsingApiKey(opts) {
|
|
|
963
1248
|
const client = new UpfilesClient({ baseUrl, apiKey, apiKeyHeader: opts.apiKeyHeader ?? "authorization" });
|
|
964
1249
|
return { apiKey, client };
|
|
965
1250
|
}
|
|
966
|
-
|
|
967
|
-
// src/ConnectProjectDialog.tsx
|
|
968
|
-
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
969
1251
|
function ConnectProjectDialog(props) {
|
|
970
1252
|
const { baseUrl, open, onOpenChange, onConnected, fetchImpl, title, description, className } = props;
|
|
971
|
-
const [mode, setMode] =
|
|
972
|
-
const [loading, setLoading] =
|
|
973
|
-
const [error, setError] =
|
|
974
|
-
const [projects, setProjects] =
|
|
975
|
-
const [selectedProjectId, setSelectedProjectId] =
|
|
976
|
-
const [manualKey, setManualKey] =
|
|
977
|
-
const [newProjectName, setNewProjectName] =
|
|
978
|
-
const resetState =
|
|
1253
|
+
const [mode, setMode] = React4__namespace.useState("existing");
|
|
1254
|
+
const [loading, setLoading] = React4__namespace.useState(false);
|
|
1255
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
1256
|
+
const [projects, setProjects] = React4__namespace.useState([]);
|
|
1257
|
+
const [selectedProjectId, setSelectedProjectId] = React4__namespace.useState("");
|
|
1258
|
+
const [manualKey, setManualKey] = React4__namespace.useState("");
|
|
1259
|
+
const [newProjectName, setNewProjectName] = React4__namespace.useState("");
|
|
1260
|
+
const resetState = React4__namespace.useCallback(() => {
|
|
979
1261
|
setMode("existing");
|
|
980
1262
|
setLoading(false);
|
|
981
1263
|
setError(null);
|
|
@@ -984,7 +1266,7 @@ function ConnectProjectDialog(props) {
|
|
|
984
1266
|
setManualKey("");
|
|
985
1267
|
setNewProjectName("");
|
|
986
1268
|
}, []);
|
|
987
|
-
|
|
1269
|
+
React4__namespace.useEffect(() => {
|
|
988
1270
|
const run = async () => {
|
|
989
1271
|
if (!open || mode !== "existing") return;
|
|
990
1272
|
try {
|
|
@@ -1036,7 +1318,7 @@ function ConnectProjectDialog(props) {
|
|
|
1036
1318
|
setLoading(true);
|
|
1037
1319
|
setError(null);
|
|
1038
1320
|
if (!newProjectName.trim()) throw new Error("Project name is required");
|
|
1039
|
-
const { apiKey, projectId } = await
|
|
1321
|
+
const { apiKey, projectId } = await createProjectWithApiKey({ baseUrl, name: newProjectName.trim(), fetch: fetchImpl });
|
|
1040
1322
|
onConnected(apiKey, { projectId, source: "new" });
|
|
1041
1323
|
onOpenChange(false);
|
|
1042
1324
|
resetState();
|
|
@@ -1046,8 +1328,8 @@ function ConnectProjectDialog(props) {
|
|
|
1046
1328
|
setLoading(false);
|
|
1047
1329
|
}
|
|
1048
1330
|
};
|
|
1049
|
-
const ModeSwitcher = /* @__PURE__ */
|
|
1050
|
-
/* @__PURE__ */
|
|
1331
|
+
const ModeSwitcher = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
|
|
1332
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1051
1333
|
"button",
|
|
1052
1334
|
{
|
|
1053
1335
|
type: "button",
|
|
@@ -1056,7 +1338,7 @@ function ConnectProjectDialog(props) {
|
|
|
1056
1338
|
children: "Connect existing"
|
|
1057
1339
|
}
|
|
1058
1340
|
),
|
|
1059
|
-
/* @__PURE__ */
|
|
1341
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1060
1342
|
"button",
|
|
1061
1343
|
{
|
|
1062
1344
|
type: "button",
|
|
@@ -1065,7 +1347,7 @@ function ConnectProjectDialog(props) {
|
|
|
1065
1347
|
children: "Add API key"
|
|
1066
1348
|
}
|
|
1067
1349
|
),
|
|
1068
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1069
1351
|
"button",
|
|
1070
1352
|
{
|
|
1071
1353
|
type: "button",
|
|
@@ -1075,19 +1357,19 @@ function ConnectProjectDialog(props) {
|
|
|
1075
1357
|
}
|
|
1076
1358
|
)
|
|
1077
1359
|
] });
|
|
1078
|
-
return /* @__PURE__ */
|
|
1360
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open, onOpenChange: (o) => {
|
|
1079
1361
|
if (!o) resetState();
|
|
1080
1362
|
onOpenChange(o);
|
|
1081
|
-
}, children: /* @__PURE__ */
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1084
|
-
/* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1363
|
+
}, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1364
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40" }),
|
|
1365
|
+
/* @__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: [
|
|
1366
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-lg font-semibold", children: title ?? "Connect to Upfiles" }),
|
|
1367
|
+
/* @__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." }),
|
|
1086
1368
|
ModeSwitcher,
|
|
1087
|
-
error && /* @__PURE__ */
|
|
1088
|
-
mode === "existing" && /* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1090
|
-
/* @__PURE__ */
|
|
1369
|
+
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 }),
|
|
1370
|
+
mode === "existing" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1371
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Project" }),
|
|
1372
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1091
1373
|
"select",
|
|
1092
1374
|
{
|
|
1093
1375
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -1095,14 +1377,14 @@ function ConnectProjectDialog(props) {
|
|
|
1095
1377
|
onChange: (e) => setSelectedProjectId(e.target.value),
|
|
1096
1378
|
disabled: loading,
|
|
1097
1379
|
children: [
|
|
1098
|
-
projects.length === 0 && /* @__PURE__ */
|
|
1099
|
-
projects.map((p) => /* @__PURE__ */
|
|
1380
|
+
projects.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: loading ? "Loading..." : "No projects found" }),
|
|
1381
|
+
projects.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: p.name }, p.id))
|
|
1100
1382
|
]
|
|
1101
1383
|
}
|
|
1102
1384
|
),
|
|
1103
|
-
/* @__PURE__ */
|
|
1104
|
-
/* @__PURE__ */
|
|
1105
|
-
/* @__PURE__ */
|
|
1385
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1386
|
+
/* @__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" }) }),
|
|
1387
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1106
1388
|
"button",
|
|
1107
1389
|
{
|
|
1108
1390
|
type: "button",
|
|
@@ -1114,9 +1396,9 @@ function ConnectProjectDialog(props) {
|
|
|
1114
1396
|
)
|
|
1115
1397
|
] })
|
|
1116
1398
|
] }),
|
|
1117
|
-
mode === "manual" && /* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1119
|
-
/* @__PURE__ */
|
|
1399
|
+
mode === "manual" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1400
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "API key" }),
|
|
1401
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1120
1402
|
"input",
|
|
1121
1403
|
{
|
|
1122
1404
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -1126,9 +1408,9 @@ function ConnectProjectDialog(props) {
|
|
|
1126
1408
|
disabled: loading
|
|
1127
1409
|
}
|
|
1128
1410
|
),
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
/* @__PURE__ */
|
|
1131
|
-
/* @__PURE__ */
|
|
1411
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1412
|
+
/* @__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" }) }),
|
|
1413
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1132
1414
|
"button",
|
|
1133
1415
|
{
|
|
1134
1416
|
type: "button",
|
|
@@ -1140,9 +1422,9 @@ function ConnectProjectDialog(props) {
|
|
|
1140
1422
|
)
|
|
1141
1423
|
] })
|
|
1142
1424
|
] }),
|
|
1143
|
-
mode === "new" && /* @__PURE__ */
|
|
1144
|
-
/* @__PURE__ */
|
|
1145
|
-
/* @__PURE__ */
|
|
1425
|
+
mode === "new" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1426
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium", children: "Project name" }),
|
|
1427
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1146
1428
|
"input",
|
|
1147
1429
|
{
|
|
1148
1430
|
className: "w-full rounded border px-3 py-2 text-sm",
|
|
@@ -1152,10 +1434,10 @@ function ConnectProjectDialog(props) {
|
|
|
1152
1434
|
disabled: loading
|
|
1153
1435
|
}
|
|
1154
1436
|
),
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
/* @__PURE__ */
|
|
1157
|
-
/* @__PURE__ */
|
|
1158
|
-
/* @__PURE__ */
|
|
1437
|
+
/* @__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." }),
|
|
1438
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
1439
|
+
/* @__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" }) }),
|
|
1440
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1159
1441
|
"button",
|
|
1160
1442
|
{
|
|
1161
1443
|
type: "button",
|
|
@@ -1205,60 +1487,77 @@ async function fetchProjectImagesWithThumbs(clientOptions, opts) {
|
|
|
1205
1487
|
results.sort((a, b) => (keyToIndex.get(a.key) ?? 0) - (keyToIndex.get(b.key) ?? 0));
|
|
1206
1488
|
return { items: results, updatedAt };
|
|
1207
1489
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
/* @__PURE__ */
|
|
1216
|
-
/* @__PURE__ */
|
|
1217
|
-
/* @__PURE__ */
|
|
1490
|
+
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: [
|
|
1491
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "3", y: "3", rx: "1" }),
|
|
1492
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "14", y: "3", rx: "1" }),
|
|
1493
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" }),
|
|
1494
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "7", height: "7", x: "3", y: "14", rx: "1" })
|
|
1495
|
+
] });
|
|
1496
|
+
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: [
|
|
1497
|
+
/* @__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" }),
|
|
1498
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 12v9" }),
|
|
1499
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m16 16-4-4-4 4" })
|
|
1218
1500
|
] });
|
|
1219
|
-
var
|
|
1220
|
-
|
|
1221
|
-
/* @__PURE__ */
|
|
1222
|
-
/* @__PURE__ */
|
|
1501
|
+
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" }) });
|
|
1502
|
+
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: [
|
|
1503
|
+
/* @__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" }),
|
|
1504
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", x2: "12", y1: "10", y2: "16" }),
|
|
1505
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", x2: "15", y1: "13", y2: "13" })
|
|
1223
1506
|
] });
|
|
1224
|
-
var
|
|
1225
|
-
|
|
1226
|
-
/* @__PURE__ */
|
|
1227
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "12", x2: "12", y1: "10", y2: "16" }),
|
|
1228
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "9", x2: "15", y1: "13", y2: "13" })
|
|
1507
|
+
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: [
|
|
1508
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
1509
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
|
|
1229
1510
|
] });
|
|
1230
|
-
var
|
|
1231
|
-
/* @__PURE__ */
|
|
1232
|
-
/* @__PURE__ */
|
|
1511
|
+
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: [
|
|
1512
|
+
/* @__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" }),
|
|
1513
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 3v5h-5" }),
|
|
1514
|
+
/* @__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" }),
|
|
1515
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 16H3v5" })
|
|
1233
1516
|
] });
|
|
1234
|
-
var
|
|
1235
|
-
|
|
1236
|
-
/* @__PURE__ */
|
|
1237
|
-
/* @__PURE__ */
|
|
1238
|
-
/* @__PURE__ */
|
|
1517
|
+
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" }) });
|
|
1518
|
+
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: [
|
|
1519
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 6h18" }),
|
|
1520
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
1521
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
|
|
1522
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "10", x2: "10", y1: "11", y2: "17" }),
|
|
1523
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "14", x2: "14", y1: "11", y2: "17" })
|
|
1239
1524
|
] });
|
|
1240
|
-
var
|
|
1241
|
-
|
|
1242
|
-
/* @__PURE__ */
|
|
1243
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
|
|
1244
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
|
|
1245
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "10", x2: "10", y1: "11", y2: "17" }),
|
|
1246
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "14", x2: "14", y1: "11", y2: "17" })
|
|
1525
|
+
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: [
|
|
1526
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 6 6 18" }),
|
|
1527
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m6 6 12 12" })
|
|
1247
1528
|
] });
|
|
1248
|
-
var
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1529
|
+
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: [
|
|
1530
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" }),
|
|
1531
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 22 9 12 15 12 15 22" })
|
|
1251
1532
|
] });
|
|
1252
|
-
var
|
|
1253
|
-
/* @__PURE__ */
|
|
1254
|
-
/* @__PURE__ */
|
|
1533
|
+
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: [
|
|
1534
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2" }),
|
|
1535
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
1536
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
|
|
1255
1537
|
] });
|
|
1256
|
-
var
|
|
1257
|
-
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1538
|
+
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" }) });
|
|
1539
|
+
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: [
|
|
1540
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }),
|
|
1541
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 17 12 22 22 17" }),
|
|
1542
|
+
/* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "2 12 12 17 22 12" })
|
|
1260
1543
|
] });
|
|
1261
|
-
var
|
|
1544
|
+
var VisuallyHidden = ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1545
|
+
"span",
|
|
1546
|
+
{
|
|
1547
|
+
style: {
|
|
1548
|
+
position: "absolute",
|
|
1549
|
+
width: "1px",
|
|
1550
|
+
height: "1px",
|
|
1551
|
+
padding: "0",
|
|
1552
|
+
margin: "-1px",
|
|
1553
|
+
overflow: "hidden",
|
|
1554
|
+
clip: "rect(0, 0, 0, 0)",
|
|
1555
|
+
whiteSpace: "nowrap",
|
|
1556
|
+
border: "0"
|
|
1557
|
+
},
|
|
1558
|
+
children
|
|
1559
|
+
}
|
|
1560
|
+
);
|
|
1262
1561
|
function ImageManager(props) {
|
|
1263
1562
|
const {
|
|
1264
1563
|
open,
|
|
@@ -1266,6 +1565,7 @@ function ImageManager(props) {
|
|
|
1266
1565
|
clientOptions,
|
|
1267
1566
|
projectId,
|
|
1268
1567
|
folderPath,
|
|
1568
|
+
userId,
|
|
1269
1569
|
title,
|
|
1270
1570
|
description,
|
|
1271
1571
|
className,
|
|
@@ -1279,25 +1579,40 @@ function ImageManager(props) {
|
|
|
1279
1579
|
maxFiles = 10,
|
|
1280
1580
|
mode = "full",
|
|
1281
1581
|
showDelete = true,
|
|
1282
|
-
refreshInterval = 5e3
|
|
1582
|
+
refreshInterval = 5e3,
|
|
1583
|
+
stayOnUploadAfterComplete = false,
|
|
1584
|
+
thumbnailConfig
|
|
1283
1585
|
} = props;
|
|
1284
|
-
const [activeView, setActiveView] =
|
|
1285
|
-
const [loading, setLoading] =
|
|
1286
|
-
const [error, setError] =
|
|
1287
|
-
const [currentFolderId, setCurrentFolderId] =
|
|
1288
|
-
const [breadcrumbs, setBreadcrumbs] =
|
|
1289
|
-
const [files, setFiles] =
|
|
1290
|
-
const [folders, setFolders] =
|
|
1291
|
-
const [query, setQuery] =
|
|
1292
|
-
const [deleting, setDeleting] =
|
|
1293
|
-
const [showCreateFolder, setShowCreateFolder] =
|
|
1294
|
-
const [newFolderName, setNewFolderName] =
|
|
1295
|
-
const [creatingFolder, setCreatingFolder] =
|
|
1296
|
-
const [fileToDelete, setFileToDelete] =
|
|
1297
|
-
const [thumbnailSelectorFile, setThumbnailSelectorFile] =
|
|
1298
|
-
const
|
|
1299
|
-
const
|
|
1300
|
-
const
|
|
1586
|
+
const [activeView, setActiveView] = React4__namespace.useState(mode === "upload" ? "upload" : "browse");
|
|
1587
|
+
const [loading, setLoading] = React4__namespace.useState(false);
|
|
1588
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
1589
|
+
const [currentFolderId, setCurrentFolderId] = React4__namespace.useState(void 0);
|
|
1590
|
+
const [breadcrumbs, setBreadcrumbs] = React4__namespace.useState([{ id: void 0, name: "Home" }]);
|
|
1591
|
+
const [files, setFiles] = React4__namespace.useState([]);
|
|
1592
|
+
const [folders, setFolders] = React4__namespace.useState([]);
|
|
1593
|
+
const [query, setQuery] = React4__namespace.useState("");
|
|
1594
|
+
const [deleting, setDeleting] = React4__namespace.useState(null);
|
|
1595
|
+
const [showCreateFolder, setShowCreateFolder] = React4__namespace.useState(false);
|
|
1596
|
+
const [newFolderName, setNewFolderName] = React4__namespace.useState("");
|
|
1597
|
+
const [creatingFolder, setCreatingFolder] = React4__namespace.useState(false);
|
|
1598
|
+
const [fileToDelete, setFileToDelete] = React4__namespace.useState(null);
|
|
1599
|
+
const [thumbnailSelectorFile, setThumbnailSelectorFile] = React4__namespace.useState(null);
|
|
1600
|
+
const [selectedThumbnailForFile, setSelectedThumbnailForFile] = React4__namespace.useState(null);
|
|
1601
|
+
const [previewFile, setPreviewFile] = React4__namespace.useState(null);
|
|
1602
|
+
const [generatingThumbnails, setGeneratingThumbnails] = React4__namespace.useState(false);
|
|
1603
|
+
const lastUpdatedRef = React4__namespace.useRef(null);
|
|
1604
|
+
const isFetchingRef = React4__namespace.useRef(false);
|
|
1605
|
+
const deduplicateFiles = React4__namespace.useCallback((files2) => {
|
|
1606
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1607
|
+
return files2.filter((file) => {
|
|
1608
|
+
if (seen.has(file.key)) {
|
|
1609
|
+
return false;
|
|
1610
|
+
}
|
|
1611
|
+
seen.add(file.key);
|
|
1612
|
+
return true;
|
|
1613
|
+
});
|
|
1614
|
+
}, []);
|
|
1615
|
+
const load = React4__namespace.useCallback(async (isPolling = false) => {
|
|
1301
1616
|
if (isFetchingRef.current) return;
|
|
1302
1617
|
isFetchingRef.current = true;
|
|
1303
1618
|
try {
|
|
@@ -1307,13 +1622,14 @@ function ImageManager(props) {
|
|
|
1307
1622
|
const currentPathPrefix = breadcrumbs.length > 1 ? breadcrumbs.slice(1).map((b) => b.name).join("/") + "/" : void 0;
|
|
1308
1623
|
const effectiveFolderPath = currentPathPrefix || folderPath;
|
|
1309
1624
|
const [filesData, foldersData] = await Promise.all([
|
|
1310
|
-
|
|
1625
|
+
fetchProjectFiles(clientOptions, effectiveFolderPath),
|
|
1311
1626
|
client.getFolders(currentFolderId)
|
|
1312
1627
|
]);
|
|
1313
|
-
const {
|
|
1628
|
+
const { files: rawFiles, updatedAt } = filesData;
|
|
1629
|
+
const list = rawFiles;
|
|
1314
1630
|
if (isPolling && updatedAt && updatedAt === lastUpdatedRef.current) {
|
|
1315
1631
|
}
|
|
1316
|
-
setFiles(list);
|
|
1632
|
+
setFiles(deduplicateFiles(list));
|
|
1317
1633
|
setFolders(foldersData);
|
|
1318
1634
|
lastUpdatedRef.current = updatedAt;
|
|
1319
1635
|
} catch (e) {
|
|
@@ -1323,7 +1639,7 @@ function ImageManager(props) {
|
|
|
1323
1639
|
isFetchingRef.current = false;
|
|
1324
1640
|
}
|
|
1325
1641
|
}, [clientOptions, folderPath, currentFolderId, breadcrumbs]);
|
|
1326
|
-
|
|
1642
|
+
React4__namespace.useEffect(() => {
|
|
1327
1643
|
if (!open) {
|
|
1328
1644
|
setFiles([]);
|
|
1329
1645
|
setFolders([]);
|
|
@@ -1336,35 +1652,11 @@ function ImageManager(props) {
|
|
|
1336
1652
|
load();
|
|
1337
1653
|
}
|
|
1338
1654
|
}, [open, activeView, load, mode]);
|
|
1339
|
-
|
|
1340
|
-
if (!open) return;
|
|
1341
|
-
const client = new UpfilesClient(clientOptions);
|
|
1342
|
-
const url = client.getEventsUrl(projectId);
|
|
1343
|
-
const eventSource = new EventSource(url);
|
|
1344
|
-
eventSource.onmessage = (event) => {
|
|
1345
|
-
try {
|
|
1346
|
-
const payload = JSON.parse(event.data);
|
|
1347
|
-
if (payload.type === "file:uploaded") {
|
|
1348
|
-
const newFile = payload.data.file;
|
|
1349
|
-
setFiles((prev) => {
|
|
1350
|
-
if (prev.find((f2) => f2.key === newFile.key)) return prev;
|
|
1351
|
-
return [newFile, ...prev];
|
|
1352
|
-
});
|
|
1353
|
-
} else if (payload.type === "file:deleted") {
|
|
1354
|
-
const deletedKey = payload.data.fileKey || payload.data.file?.key;
|
|
1355
|
-
setFiles((prev) => prev.filter((f2) => f2.key !== deletedKey));
|
|
1356
|
-
}
|
|
1357
|
-
} catch (e) {
|
|
1358
|
-
console.error("SSE Error parsing message", e);
|
|
1359
|
-
}
|
|
1360
|
-
};
|
|
1361
|
-
return () => eventSource.close();
|
|
1362
|
-
}, [open, clientOptions, projectId]);
|
|
1363
|
-
const visibleFiles = React4.useMemo(() => {
|
|
1655
|
+
const visibleFiles = React4__namespace.useMemo(() => {
|
|
1364
1656
|
const q = query.trim().toLowerCase();
|
|
1365
1657
|
return !q ? files : files.filter((i) => (i.originalName || "").toLowerCase().includes(q));
|
|
1366
1658
|
}, [files, query]);
|
|
1367
|
-
const visibleFolders =
|
|
1659
|
+
const visibleFolders = React4__namespace.useMemo(() => {
|
|
1368
1660
|
const q = query.trim().toLowerCase();
|
|
1369
1661
|
return !q ? folders : folders.filter((f2) => (f2.name || "").toLowerCase().includes(q));
|
|
1370
1662
|
}, [folders, query]);
|
|
@@ -1383,10 +1675,18 @@ function ImageManager(props) {
|
|
|
1383
1675
|
"content-type": "application/json",
|
|
1384
1676
|
...clientOptions?.headers || {}
|
|
1385
1677
|
};
|
|
1386
|
-
|
|
1678
|
+
const token = clientOptions?.getToken?.();
|
|
1679
|
+
if (token) {
|
|
1680
|
+
headers["authorization"] = `Bearer ${token}`;
|
|
1681
|
+
if (clientOptions.projectName) {
|
|
1682
|
+
headers["x-project-name"] = clientOptions.projectName;
|
|
1683
|
+
} else if (clientOptions.projectId) {
|
|
1684
|
+
headers["x-project-id"] = clientOptions.projectId;
|
|
1685
|
+
}
|
|
1686
|
+
} else if (clientOptions?.apiKey) {
|
|
1387
1687
|
const keyHeader = clientOptions.apiKeyHeader || "authorization";
|
|
1388
1688
|
if (keyHeader === "authorization") {
|
|
1389
|
-
headers["authorization"] = clientOptions.apiKey.startsWith("
|
|
1689
|
+
headers["authorization"] = clientOptions.apiKey.startsWith("upf_") ? `Bearer ${clientOptions.apiKey}` : clientOptions.apiKey;
|
|
1390
1690
|
} else {
|
|
1391
1691
|
headers[keyHeader] = clientOptions.apiKey;
|
|
1392
1692
|
}
|
|
@@ -1433,7 +1733,7 @@ function ImageManager(props) {
|
|
|
1433
1733
|
setCurrentFolderId(target.id);
|
|
1434
1734
|
setQuery("");
|
|
1435
1735
|
};
|
|
1436
|
-
const uploadFoldersList =
|
|
1736
|
+
const uploadFoldersList = React4__namespace.useMemo(() => {
|
|
1437
1737
|
const options = [];
|
|
1438
1738
|
options.push({ id: "root", name: "Root ( / )", path: void 0 });
|
|
1439
1739
|
if (currentFolderId) {
|
|
@@ -1446,65 +1746,87 @@ function ImageManager(props) {
|
|
|
1446
1746
|
});
|
|
1447
1747
|
return options;
|
|
1448
1748
|
}, [breadcrumbs, currentFolderId, folders]);
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1749
|
+
const handleThumbnailClick = React4__namespace.useCallback(async (f2) => {
|
|
1750
|
+
if (f2.thumbnails && f2.thumbnails.length > 0) {
|
|
1751
|
+
setThumbnailSelectorFile(f2);
|
|
1752
|
+
setSelectedThumbnailForFile(null);
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
setGeneratingThumbnails(true);
|
|
1756
|
+
setThumbnailSelectorFile(f2);
|
|
1757
|
+
setSelectedThumbnailForFile(null);
|
|
1758
|
+
try {
|
|
1759
|
+
const client = new UpfilesClient(clientOptions);
|
|
1760
|
+
const result = await client.generateThumbnails(f2.key);
|
|
1761
|
+
const updatedFile = { ...f2, thumbnails: result.thumbnails };
|
|
1762
|
+
setThumbnailSelectorFile(updatedFile);
|
|
1763
|
+
setFiles((prev) => prev.map((file) => file.key === f2.key ? updatedFile : file));
|
|
1764
|
+
} catch (e) {
|
|
1765
|
+
console.error("Failed to generate thumbnails:", e);
|
|
1766
|
+
} finally {
|
|
1767
|
+
setGeneratingThumbnails(false);
|
|
1768
|
+
}
|
|
1769
|
+
}, [clientOptions]);
|
|
1770
|
+
const [selectedUploadFolder, setSelectedUploadFolder] = React4__namespace.useState("current");
|
|
1771
|
+
const activeBreadcrumbPath = React4__namespace.useMemo(() => {
|
|
1451
1772
|
return breadcrumbs.length > 1 ? breadcrumbs.slice(1).map((b) => b.name).join("/") + "/" : void 0;
|
|
1452
1773
|
}, [breadcrumbs]);
|
|
1453
|
-
return /* @__PURE__ */
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
/* @__PURE__ */
|
|
1457
|
-
|
|
1774
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Root, { open, onOpenChange, children: [
|
|
1775
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
1776
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/60 z-[9999]" }),
|
|
1777
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1778
|
+
Dialog2__namespace.Content,
|
|
1458
1779
|
{
|
|
1459
|
-
className: `fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[
|
|
1780
|
+
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-[10000] overflow-hidden flex flex-row ${className ?? ""}`,
|
|
1460
1781
|
children: [
|
|
1461
|
-
/* @__PURE__ */
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1782
|
+
/* @__PURE__ */ jsxRuntime.jsx(VisuallyHidden, { children: /* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { children: title ?? "Media Library" }) }),
|
|
1783
|
+
/* @__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: [
|
|
1784
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6", children: [
|
|
1785
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-lg font-bold text-gray-900 dark:text-white flex items-center gap-2", children: [
|
|
1786
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconImage, { className: "text-blue-600" }),
|
|
1465
1787
|
title ?? "Media Library"
|
|
1466
1788
|
] }),
|
|
1467
|
-
/* @__PURE__ */
|
|
1789
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500 mt-1", children: description ?? "Manage your assets" })
|
|
1468
1790
|
] }),
|
|
1469
|
-
/* @__PURE__ */
|
|
1470
|
-
/* @__PURE__ */
|
|
1791
|
+
/* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex-1 px-3 space-y-1", children: [
|
|
1792
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1471
1793
|
"button",
|
|
1472
1794
|
{
|
|
1473
1795
|
onClick: () => setActiveView("browse"),
|
|
1474
1796
|
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"}`,
|
|
1475
1797
|
children: [
|
|
1476
|
-
/* @__PURE__ */
|
|
1798
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconGrid, { className: "w-4 h-4" }),
|
|
1477
1799
|
"All Files"
|
|
1478
1800
|
]
|
|
1479
1801
|
}
|
|
1480
1802
|
),
|
|
1481
|
-
mode === "full" && /* @__PURE__ */
|
|
1803
|
+
mode === "full" && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1482
1804
|
"button",
|
|
1483
1805
|
{
|
|
1484
1806
|
onClick: () => setActiveView("upload"),
|
|
1485
1807
|
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"}`,
|
|
1486
1808
|
children: [
|
|
1487
|
-
/* @__PURE__ */
|
|
1809
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconUploadCloud, { className: "w-4 h-4" }),
|
|
1488
1810
|
"Upload New"
|
|
1489
1811
|
]
|
|
1490
1812
|
}
|
|
1491
1813
|
)
|
|
1492
1814
|
] }),
|
|
1493
|
-
/* @__PURE__ */
|
|
1815
|
+
/* @__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: [
|
|
1494
1816
|
files.length,
|
|
1495
1817
|
" files \u2022 ",
|
|
1496
1818
|
folders.length,
|
|
1497
1819
|
" folders"
|
|
1498
1820
|
] }) })
|
|
1499
1821
|
] }),
|
|
1500
|
-
/* @__PURE__ */
|
|
1501
|
-
/* @__PURE__ */
|
|
1502
|
-
/* @__PURE__ */
|
|
1503
|
-
/* @__PURE__ */
|
|
1504
|
-
/* @__PURE__ */
|
|
1505
|
-
breadcrumbs.slice(1).map((b, i) => /* @__PURE__ */
|
|
1506
|
-
/* @__PURE__ */
|
|
1507
|
-
/* @__PURE__ */
|
|
1822
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col bg-white dark:bg-gray-950 relative", children: [
|
|
1823
|
+
/* @__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: [
|
|
1824
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 flex-1", children: activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1825
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 overflow-hidden whitespace-nowrap", children: [
|
|
1826
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => handleBreadcrumbClick(0), className: "hover:text-blue-600 transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(IconHome, { className: "w-4 h-4" }) }),
|
|
1827
|
+
breadcrumbs.slice(1).map((b, i) => /* @__PURE__ */ jsxRuntime.jsxs(React4__namespace.Fragment, { children: [
|
|
1828
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconChevronRight, { className: "w-4 h-4 text-gray-300 mx-1" }),
|
|
1829
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1508
1830
|
"button",
|
|
1509
1831
|
{
|
|
1510
1832
|
onClick: () => handleBreadcrumbClick(i + 1),
|
|
@@ -1512,23 +1834,23 @@ function ImageManager(props) {
|
|
|
1512
1834
|
children: b.name
|
|
1513
1835
|
}
|
|
1514
1836
|
)
|
|
1515
|
-
] }, b.id || i))
|
|
1837
|
+
] }, `${b.id || "breadcrumb"}-${i}`))
|
|
1516
1838
|
] }),
|
|
1517
|
-
/* @__PURE__ */
|
|
1518
|
-
/* @__PURE__ */
|
|
1839
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-200 dark:bg-gray-700 mx-2" }),
|
|
1840
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1519
1841
|
"button",
|
|
1520
1842
|
{
|
|
1521
1843
|
onClick: () => load(),
|
|
1522
1844
|
disabled: loading,
|
|
1523
1845
|
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",
|
|
1524
1846
|
title: "Refresh",
|
|
1525
|
-
children: /* @__PURE__ */
|
|
1847
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconRefresh, { className: `w-4 h-4 ${loading ? "animate-spin" : ""}` })
|
|
1526
1848
|
}
|
|
1527
1849
|
),
|
|
1528
|
-
/* @__PURE__ */
|
|
1529
|
-
/* @__PURE__ */
|
|
1530
|
-
/* @__PURE__ */
|
|
1531
|
-
/* @__PURE__ */
|
|
1850
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-px bg-gray-200 dark:bg-gray-700 mx-2" }),
|
|
1851
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 max-w-sm group", children: [
|
|
1852
|
+
/* @__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" }),
|
|
1853
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1532
1854
|
"input",
|
|
1533
1855
|
{
|
|
1534
1856
|
value: query,
|
|
@@ -1539,45 +1861,58 @@ function ImageManager(props) {
|
|
|
1539
1861
|
)
|
|
1540
1862
|
] })
|
|
1541
1863
|
] }) }),
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
activeView === "browse" && /* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1545
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1865
|
+
activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1866
|
+
/* @__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" }) }),
|
|
1867
|
+
/* @__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" }) })
|
|
1546
1868
|
] }),
|
|
1547
|
-
/* @__PURE__ */
|
|
1869
|
+
/* @__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" }) }) })
|
|
1548
1870
|
] })
|
|
1549
1871
|
] }),
|
|
1550
|
-
/* @__PURE__ */
|
|
1551
|
-
activeView === "browse" && /* @__PURE__ */
|
|
1552
|
-
error && /* @__PURE__ */
|
|
1553
|
-
/* @__PURE__ */
|
|
1554
|
-
visibleFolders.map((folder) => /* @__PURE__ */
|
|
1872
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-6 bg-gray-50/30 dark:bg-gray-900/30", children: [
|
|
1873
|
+
activeView === "browse" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1874
|
+
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 }) }),
|
|
1875
|
+
/* @__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: [
|
|
1876
|
+
visibleFolders.map((folder) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1555
1877
|
"div",
|
|
1556
1878
|
{
|
|
1557
1879
|
onClick: () => handleNavigate(folder),
|
|
1558
1880
|
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",
|
|
1559
1881
|
children: [
|
|
1560
|
-
/* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1882
|
+
/* @__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" }) }),
|
|
1883
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 truncate w-full text-center", children: folder.name })
|
|
1562
1884
|
]
|
|
1563
1885
|
},
|
|
1564
1886
|
folder.id
|
|
1565
1887
|
)),
|
|
1566
1888
|
visibleFiles.map((f2) => {
|
|
1567
|
-
const
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1889
|
+
const displayUrl = f2.url;
|
|
1890
|
+
const hasThumbnails = f2.thumbnails && f2.thumbnails.length > 0;
|
|
1891
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1571
1892
|
"div",
|
|
1572
1893
|
{
|
|
1573
|
-
|
|
1894
|
+
onClick: () => setPreviewFile(f2),
|
|
1895
|
+
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",
|
|
1574
1896
|
children: [
|
|
1575
|
-
/* @__PURE__ */
|
|
1576
|
-
|
|
1577
|
-
|
|
1897
|
+
/* @__PURE__ */ jsxRuntime.jsx("img", { src: displayUrl, alt: f2.originalName, className: "w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" }),
|
|
1898
|
+
f2.contentType?.startsWith("image/") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1899
|
+
"button",
|
|
1900
|
+
{
|
|
1901
|
+
onClick: (e) => {
|
|
1902
|
+
e.stopPropagation();
|
|
1903
|
+
handleThumbnailClick(f2);
|
|
1904
|
+
},
|
|
1905
|
+
className: `absolute top-2 left-2 p-1.5 text-white rounded-md opacity-0 group-hover:opacity-100 transition-opacity shadow-lg ${hasThumbnails ? "bg-purple-600/90 hover:bg-purple-500" : "bg-gray-600/90 hover:bg-purple-500"}`,
|
|
1906
|
+
title: hasThumbnails ? "View Thumbnails" : "Generate Thumbnails",
|
|
1907
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconLayers, { className: "w-4 h-4" })
|
|
1908
|
+
}
|
|
1909
|
+
),
|
|
1910
|
+
/* @__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: [
|
|
1911
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1578
1912
|
"button",
|
|
1579
1913
|
{
|
|
1580
|
-
onClick: () => {
|
|
1914
|
+
onClick: (e) => {
|
|
1915
|
+
e.stopPropagation();
|
|
1581
1916
|
onSelect({
|
|
1582
1917
|
url: f2.url,
|
|
1583
1918
|
key: f2.key,
|
|
@@ -1592,7 +1927,7 @@ function ImageManager(props) {
|
|
|
1592
1927
|
children: "Select"
|
|
1593
1928
|
}
|
|
1594
1929
|
),
|
|
1595
|
-
showDelete && /* @__PURE__ */
|
|
1930
|
+
showDelete && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1596
1931
|
"button",
|
|
1597
1932
|
{
|
|
1598
1933
|
onClick: (e) => {
|
|
@@ -1600,57 +1935,70 @@ function ImageManager(props) {
|
|
|
1600
1935
|
setFileToDelete(f2);
|
|
1601
1936
|
},
|
|
1602
1937
|
className: "p-1.5 bg-white/20 text-white text-xs font-semibold rounded-md hover:bg-red-500 backdrop-blur-md transition-colors",
|
|
1603
|
-
children: /* @__PURE__ */
|
|
1938
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconTrash, { className: "w-4 h-4" })
|
|
1604
1939
|
}
|
|
1605
1940
|
)
|
|
1606
1941
|
] }) }),
|
|
1607
|
-
/* @__PURE__ */
|
|
1942
|
+
/* @__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() })
|
|
1608
1943
|
]
|
|
1609
1944
|
},
|
|
1610
1945
|
f2.key
|
|
1611
1946
|
);
|
|
1612
1947
|
})
|
|
1613
1948
|
] }),
|
|
1614
|
-
!loading && visibleFolders.length === 0 && visibleFiles.length === 0 && /* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1949
|
+
!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: [
|
|
1950
|
+
/* @__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" }) }),
|
|
1951
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-gray-900 font-medium mb-1", children: "No Assets Found" }),
|
|
1952
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: "Try uploading a file or creating a new folder." })
|
|
1618
1953
|
] })
|
|
1619
1954
|
] }),
|
|
1620
|
-
activeView === "upload" && /* @__PURE__ */
|
|
1621
|
-
/* @__PURE__ */
|
|
1622
|
-
/* @__PURE__ */
|
|
1955
|
+
activeView === "upload" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex flex-col items-center justify-center max-w-2xl mx-auto gap-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full bg-white rounded-2xl border border-gray-200 p-8 shadow-sm", children: [
|
|
1956
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "text-lg font-semibold text-gray-900 mb-6 flex items-center gap-2", children: [
|
|
1957
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconUploadCloud, { className: "text-blue-500" }),
|
|
1623
1958
|
"Upload Files"
|
|
1624
1959
|
] }),
|
|
1625
|
-
/* @__PURE__ */
|
|
1626
|
-
/* @__PURE__ */
|
|
1627
|
-
/* @__PURE__ */
|
|
1628
|
-
/* @__PURE__ */
|
|
1960
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
|
|
1961
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Target Folder" }),
|
|
1962
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
1963
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1629
1964
|
"select",
|
|
1630
1965
|
{
|
|
1631
1966
|
value: selectedUploadFolder,
|
|
1632
1967
|
onChange: (e) => setSelectedUploadFolder(e.target.value),
|
|
1633
1968
|
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",
|
|
1634
|
-
children: uploadFoldersList.map((opt) => /* @__PURE__ */
|
|
1969
|
+
children: uploadFoldersList.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt.id, children: opt.name }, opt.id))
|
|
1635
1970
|
}
|
|
1636
1971
|
),
|
|
1637
|
-
/* @__PURE__ */
|
|
1972
|
+
/* @__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" }) })
|
|
1638
1973
|
] })
|
|
1639
1974
|
] }),
|
|
1640
|
-
/* @__PURE__ */
|
|
1975
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1641
1976
|
Uploader,
|
|
1642
1977
|
{
|
|
1643
1978
|
clientOptions,
|
|
1644
1979
|
projectId,
|
|
1980
|
+
userId,
|
|
1645
1981
|
folderPath: uploadFoldersList.find((o) => o.id === selectedUploadFolder)?.path || activeBreadcrumbPath || folderPath,
|
|
1646
1982
|
accept: ["image/*"],
|
|
1647
1983
|
maxFileSize,
|
|
1648
1984
|
maxFiles,
|
|
1649
1985
|
autoRecordToDb,
|
|
1650
|
-
fetchThumbnails,
|
|
1651
|
-
autoUpload:
|
|
1652
|
-
|
|
1986
|
+
fetchThumbnails: false,
|
|
1987
|
+
autoUpload: false,
|
|
1988
|
+
thumbnailEnabled: thumbnailConfig?.enabled ?? true,
|
|
1989
|
+
thumbnailSizes: thumbnailConfig?.sizes,
|
|
1990
|
+
onClientUploadComplete: async (file) => {
|
|
1653
1991
|
if (file.status === "success" && file.publicUrl && file.fileKey) {
|
|
1992
|
+
let thumbnails = file.thumbnails;
|
|
1993
|
+
if (file.thumbnailEnabled && file.thumbnailSizesSelected && file.thumbnailSizesSelected.length > 0 && file.type?.startsWith("image/")) {
|
|
1994
|
+
try {
|
|
1995
|
+
const client = new UpfilesClient(clientOptions);
|
|
1996
|
+
const result = await client.generateThumbnails(file.fileKey, file.thumbnailSizesSelected);
|
|
1997
|
+
thumbnails = result.thumbnails;
|
|
1998
|
+
} catch (e) {
|
|
1999
|
+
console.warn("Failed to generate thumbnails:", e);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
1654
2002
|
const newItem = {
|
|
1655
2003
|
key: file.fileKey,
|
|
1656
2004
|
originalName: file.name,
|
|
@@ -1658,26 +2006,31 @@ function ImageManager(props) {
|
|
|
1658
2006
|
contentType: file.type,
|
|
1659
2007
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1660
2008
|
url: file.publicUrl,
|
|
1661
|
-
thumbnails
|
|
2009
|
+
thumbnails
|
|
1662
2010
|
};
|
|
1663
|
-
setFiles((prev) =>
|
|
2011
|
+
setFiles((prev) => {
|
|
2012
|
+
if (prev.find((f2) => f2.key === newItem.key)) return prev;
|
|
2013
|
+
return deduplicateFiles([newItem, ...prev]);
|
|
2014
|
+
});
|
|
1664
2015
|
}
|
|
1665
2016
|
},
|
|
1666
2017
|
onComplete: () => {
|
|
1667
|
-
if (mode === "full") {
|
|
2018
|
+
if (mode === "full" && !stayOnUploadAfterComplete) {
|
|
1668
2019
|
setTimeout(() => {
|
|
1669
2020
|
setActiveView("browse");
|
|
1670
2021
|
load();
|
|
1671
2022
|
}, 1e3);
|
|
2023
|
+
} else {
|
|
2024
|
+
load();
|
|
1672
2025
|
}
|
|
1673
2026
|
},
|
|
1674
2027
|
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",
|
|
1675
2028
|
buttonClassName: "hidden",
|
|
1676
|
-
children: /* @__PURE__ */
|
|
1677
|
-
/* @__PURE__ */
|
|
1678
|
-
/* @__PURE__ */
|
|
1679
|
-
/* @__PURE__ */
|
|
1680
|
-
/* @__PURE__ */
|
|
2029
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
|
|
2030
|
+
/* @__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" }) }),
|
|
2031
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2032
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-900 font-medium", children: "Click to upload or drag and drop" }),
|
|
2033
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mt-1", children: [
|
|
1681
2034
|
"Supports PNG, JPG, GIF up to ",
|
|
1682
2035
|
Math.round(maxFileSize / 1024 / 1024),
|
|
1683
2036
|
"MB"
|
|
@@ -1693,11 +2046,12 @@ function ImageManager(props) {
|
|
|
1693
2046
|
}
|
|
1694
2047
|
)
|
|
1695
2048
|
] }),
|
|
1696
|
-
/* @__PURE__ */
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
/* @__PURE__ */
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
/* @__PURE__ */ (
|
|
2049
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: showCreateFolder, onOpenChange: setShowCreateFolder, children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
2050
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40 z-[10010]" }),
|
|
2051
|
+
/* @__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-[10011] outline-none", "aria-describedby": "new-folder-description", children: [
|
|
2052
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-lg font-semibold text-gray-900 mb-4", children: "New Folder" }),
|
|
2053
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Description, { id: "new-folder-description", className: "sr-only", children: "Create a new folder to organize your files" }),
|
|
2054
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1701
2055
|
"input",
|
|
1702
2056
|
{
|
|
1703
2057
|
autoFocus: true,
|
|
@@ -1708,9 +2062,9 @@ function ImageManager(props) {
|
|
|
1708
2062
|
onKeyDown: (e) => e.key === "Enter" && handleCreateFolder()
|
|
1709
2063
|
}
|
|
1710
2064
|
),
|
|
1711
|
-
/* @__PURE__ */
|
|
1712
|
-
/* @__PURE__ */
|
|
1713
|
-
/* @__PURE__ */
|
|
2065
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
2066
|
+
/* @__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" }),
|
|
2067
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1714
2068
|
"button",
|
|
1715
2069
|
{
|
|
1716
2070
|
onClick: handleCreateFolder,
|
|
@@ -1722,21 +2076,21 @@ function ImageManager(props) {
|
|
|
1722
2076
|
] })
|
|
1723
2077
|
] })
|
|
1724
2078
|
] }) }),
|
|
1725
|
-
/* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1728
|
-
/* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
2079
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!fileToDelete, onOpenChange: (open2) => !open2 && setFileToDelete(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/40 z-[10020]" }),
|
|
2081
|
+
/* @__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-[10021]", "aria-describedby": "delete-file-description", children: [
|
|
2082
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold text-gray-900 mb-2", children: "Delete File" }),
|
|
2083
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "delete-file-description", className: "text-gray-600 mb-6", children: [
|
|
1730
2084
|
"Are you sure you want to delete ",
|
|
1731
|
-
/* @__PURE__ */
|
|
2085
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900", children: [
|
|
1732
2086
|
'"',
|
|
1733
2087
|
fileToDelete?.originalName,
|
|
1734
2088
|
'"'
|
|
1735
2089
|
] }),
|
|
1736
2090
|
"?"
|
|
1737
2091
|
] }),
|
|
1738
|
-
/* @__PURE__ */
|
|
1739
|
-
/* @__PURE__ */
|
|
2092
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-3", children: [
|
|
2093
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1740
2094
|
"button",
|
|
1741
2095
|
{
|
|
1742
2096
|
onClick: () => setFileToDelete(null),
|
|
@@ -1744,7 +2098,7 @@ function ImageManager(props) {
|
|
|
1744
2098
|
children: "Cancel"
|
|
1745
2099
|
}
|
|
1746
2100
|
),
|
|
1747
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1748
2102
|
"button",
|
|
1749
2103
|
{
|
|
1750
2104
|
onClick: handleDelete,
|
|
@@ -1755,23 +2109,363 @@ function ImageManager(props) {
|
|
|
1755
2109
|
)
|
|
1756
2110
|
] })
|
|
1757
2111
|
] })
|
|
2112
|
+
] }) }),
|
|
2113
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!thumbnailSelectorFile, onOpenChange: (open2) => !open2 && setThumbnailSelectorFile(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
2114
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/50 z-[10030]" }),
|
|
2115
|
+
/* @__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-[10031] overflow-hidden", "aria-describedby": "thumbnail-selector-description", children: [
|
|
2116
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 border-b border-gray-200", children: [
|
|
2117
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold text-gray-900 mb-2", children: "Select Thumbnail" }),
|
|
2118
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "thumbnail-selector-description", className: "text-gray-600", children: [
|
|
2119
|
+
"Choose a thumbnail size for ",
|
|
2120
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-gray-900", children: [
|
|
2121
|
+
'"',
|
|
2122
|
+
thumbnailSelectorFile?.originalName,
|
|
2123
|
+
'"'
|
|
2124
|
+
] })
|
|
2125
|
+
] })
|
|
2126
|
+
] }),
|
|
2127
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 overflow-y-auto max-h-[60vh]", children: [
|
|
2128
|
+
generatingThumbnails && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-3 py-8 text-gray-500", children: [
|
|
2129
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconLoader, { className: "w-5 h-5 animate-spin" }),
|
|
2130
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Generating thumbnails..." })
|
|
2131
|
+
] }),
|
|
2132
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 ${generatingThumbnails ? "opacity-50 pointer-events-none" : ""}`, children: [
|
|
2133
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2134
|
+
"div",
|
|
2135
|
+
{
|
|
2136
|
+
onClick: () => setSelectedThumbnailForFile(null),
|
|
2137
|
+
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"}`,
|
|
2138
|
+
children: [
|
|
2139
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aspect-square bg-gray-100 flex items-center justify-center relative", children: [
|
|
2140
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2141
|
+
"img",
|
|
2142
|
+
{
|
|
2143
|
+
src: thumbnailSelectorFile?.url,
|
|
2144
|
+
alt: "Original",
|
|
2145
|
+
className: "w-full h-full object-cover"
|
|
2146
|
+
}
|
|
2147
|
+
),
|
|
2148
|
+
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" }) }) }) })
|
|
2149
|
+
] }),
|
|
2150
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-white", children: [
|
|
2151
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-sm text-gray-900", children: "Original" }),
|
|
2152
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: "Full size" })
|
|
2153
|
+
] })
|
|
2154
|
+
]
|
|
2155
|
+
}
|
|
2156
|
+
),
|
|
2157
|
+
thumbnailSelectorFile?.thumbnails?.map((thumbnail) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2158
|
+
"div",
|
|
2159
|
+
{
|
|
2160
|
+
onClick: () => setSelectedThumbnailForFile(thumbnail),
|
|
2161
|
+
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"}`,
|
|
2162
|
+
children: [
|
|
2163
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aspect-square bg-gray-100 flex items-center justify-center relative", children: [
|
|
2164
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2165
|
+
"img",
|
|
2166
|
+
{
|
|
2167
|
+
src: thumbnail.url,
|
|
2168
|
+
alt: `${thumbnail.size}px thumbnail`,
|
|
2169
|
+
className: "w-full h-full object-cover"
|
|
2170
|
+
}
|
|
2171
|
+
),
|
|
2172
|
+
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" }) }) }) })
|
|
2173
|
+
] }),
|
|
2174
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3 bg-white", children: [
|
|
2175
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "font-medium text-sm text-gray-900", children: [
|
|
2176
|
+
thumbnail.size,
|
|
2177
|
+
"px"
|
|
2178
|
+
] }),
|
|
2179
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: thumbnail.sizeType || "Thumbnail" })
|
|
2180
|
+
] })
|
|
2181
|
+
]
|
|
2182
|
+
},
|
|
2183
|
+
thumbnail.id
|
|
2184
|
+
))
|
|
2185
|
+
] })
|
|
2186
|
+
] }),
|
|
2187
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 border-t border-gray-200 flex justify-end gap-3", children: [
|
|
2188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2189
|
+
"button",
|
|
2190
|
+
{
|
|
2191
|
+
onClick: () => setThumbnailSelectorFile(null),
|
|
2192
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100 rounded-lg",
|
|
2193
|
+
children: "Cancel"
|
|
2194
|
+
}
|
|
2195
|
+
),
|
|
2196
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2197
|
+
"button",
|
|
2198
|
+
{
|
|
2199
|
+
onClick: () => {
|
|
2200
|
+
if (thumbnailSelectorFile) {
|
|
2201
|
+
onSelect({
|
|
2202
|
+
url: selectedThumbnailForFile?.url || thumbnailSelectorFile.url,
|
|
2203
|
+
key: thumbnailSelectorFile.key,
|
|
2204
|
+
originalName: thumbnailSelectorFile.originalName,
|
|
2205
|
+
size: thumbnailSelectorFile.size,
|
|
2206
|
+
contentType: thumbnailSelectorFile.contentType,
|
|
2207
|
+
thumbnails: thumbnailSelectorFile.thumbnails,
|
|
2208
|
+
selectedThumbnail: selectedThumbnailForFile || void 0
|
|
2209
|
+
});
|
|
2210
|
+
onOpenChange(false);
|
|
2211
|
+
setThumbnailSelectorFile(null);
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
2214
|
+
className: "px-4 py-2 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700",
|
|
2215
|
+
children: [
|
|
2216
|
+
"Select ",
|
|
2217
|
+
selectedThumbnailForFile ? `${selectedThumbnailForFile.size}px Thumbnail` : "Original"
|
|
2218
|
+
]
|
|
2219
|
+
}
|
|
2220
|
+
)
|
|
2221
|
+
] })
|
|
2222
|
+
] })
|
|
2223
|
+
] }) }),
|
|
2224
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Root, { open: !!previewFile, onOpenChange: (open2) => !open2 && setPreviewFile(null), children: /* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Portal, { children: [
|
|
2225
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Overlay, { className: "fixed inset-0 bg-black/90 z-[10040]", onClick: () => setPreviewFile(null) }),
|
|
2226
|
+
/* @__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-[10041] outline-none", "aria-describedby": "image-preview-description", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full flex flex-col", children: [
|
|
2227
|
+
/* @__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: [
|
|
2228
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-white", children: [
|
|
2229
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog2__namespace.Title, { className: "text-xl font-semibold mb-1", children: previewFile?.originalName }),
|
|
2230
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Dialog2__namespace.Description, { id: "image-preview-description", className: "text-sm text-gray-300", children: [
|
|
2231
|
+
previewFile?.size ? `${(previewFile.size / 1024).toFixed(1)} KB` : "",
|
|
2232
|
+
" \u2022 ",
|
|
2233
|
+
previewFile?.contentType
|
|
2234
|
+
] })
|
|
2235
|
+
] }),
|
|
2236
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2237
|
+
"button",
|
|
2238
|
+
{
|
|
2239
|
+
onClick: () => setPreviewFile(null),
|
|
2240
|
+
className: "p-2 bg-white/10 hover:bg-white/20 rounded-lg backdrop-blur-md transition-colors",
|
|
2241
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(IconX, { className: "w-6 h-6 text-white" })
|
|
2242
|
+
}
|
|
2243
|
+
)
|
|
2244
|
+
] }) }),
|
|
2245
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2246
|
+
"img",
|
|
2247
|
+
{
|
|
2248
|
+
src: previewFile?.url,
|
|
2249
|
+
alt: previewFile?.originalName,
|
|
2250
|
+
className: "max-w-full max-h-full object-contain",
|
|
2251
|
+
onError: (e) => {
|
|
2252
|
+
const target = e.target;
|
|
2253
|
+
target.style.display = "none";
|
|
2254
|
+
const errorDiv = document.createElement("div");
|
|
2255
|
+
errorDiv.className = "text-white text-center";
|
|
2256
|
+
errorDiv.innerHTML = `
|
|
2257
|
+
<div class="text-red-400 mb-2">Failed to load image</div>
|
|
2258
|
+
<div class="text-sm text-gray-400">URL: ${previewFile?.url}</div>
|
|
2259
|
+
`;
|
|
2260
|
+
target.parentElement?.appendChild(errorDiv);
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
) }),
|
|
2264
|
+
/* @__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: [
|
|
2265
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2266
|
+
"button",
|
|
2267
|
+
{
|
|
2268
|
+
onClick: (e) => {
|
|
2269
|
+
e.stopPropagation();
|
|
2270
|
+
if (previewFile) {
|
|
2271
|
+
onSelect({
|
|
2272
|
+
url: previewFile.url,
|
|
2273
|
+
key: previewFile.key,
|
|
2274
|
+
originalName: previewFile.originalName,
|
|
2275
|
+
size: previewFile.size,
|
|
2276
|
+
contentType: previewFile.contentType,
|
|
2277
|
+
thumbnails: previewFile.thumbnails
|
|
2278
|
+
});
|
|
2279
|
+
onOpenChange(false);
|
|
2280
|
+
setPreviewFile(null);
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
className: "px-6 py-2.5 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-500 shadow-lg",
|
|
2284
|
+
children: "Select Image"
|
|
2285
|
+
}
|
|
2286
|
+
),
|
|
2287
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2288
|
+
"a",
|
|
2289
|
+
{
|
|
2290
|
+
href: previewFile?.url,
|
|
2291
|
+
target: "_blank",
|
|
2292
|
+
rel: "noopener noreferrer",
|
|
2293
|
+
onClick: (e) => e.stopPropagation(),
|
|
2294
|
+
className: "px-6 py-2.5 bg-white/10 text-white font-semibold rounded-lg hover:bg-white/20 backdrop-blur-md transition-colors",
|
|
2295
|
+
children: "Open in New Tab"
|
|
2296
|
+
}
|
|
2297
|
+
),
|
|
2298
|
+
previewFile?.contentType?.startsWith("image/") && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2299
|
+
"button",
|
|
2300
|
+
{
|
|
2301
|
+
onClick: (e) => {
|
|
2302
|
+
e.stopPropagation();
|
|
2303
|
+
if (previewFile) {
|
|
2304
|
+
handleThumbnailClick(previewFile);
|
|
2305
|
+
setPreviewFile(null);
|
|
2306
|
+
}
|
|
2307
|
+
},
|
|
2308
|
+
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",
|
|
2309
|
+
children: [
|
|
2310
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconLayers, { className: "w-5 h-5" }),
|
|
2311
|
+
"Select Thumbnail"
|
|
2312
|
+
]
|
|
2313
|
+
}
|
|
2314
|
+
)
|
|
2315
|
+
] }) })
|
|
2316
|
+
] }) })
|
|
1758
2317
|
] }) })
|
|
1759
2318
|
] });
|
|
1760
2319
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
2320
|
+
function useUpfilesInhouse(config) {
|
|
2321
|
+
const {
|
|
2322
|
+
baseUrl,
|
|
2323
|
+
getToken,
|
|
2324
|
+
userId,
|
|
2325
|
+
projectName = "default",
|
|
2326
|
+
enabled = true
|
|
2327
|
+
} = config;
|
|
2328
|
+
const [state, setState] = React4.useState({ clientOptions: null, projectId: null, loading: false, error: null });
|
|
2329
|
+
const [retryCount, setRetryCount] = React4.useState(0);
|
|
2330
|
+
const [token, setToken] = React4.useState(
|
|
2331
|
+
typeof getToken === "string" ? getToken : null
|
|
2332
|
+
);
|
|
2333
|
+
const getTokenFn = React4.useCallback(() => {
|
|
2334
|
+
return typeof getToken === "function" ? getToken() : getToken;
|
|
2335
|
+
}, [getToken]);
|
|
2336
|
+
React4.useEffect(() => {
|
|
2337
|
+
let cancelled = false;
|
|
2338
|
+
async function resolveToken() {
|
|
2339
|
+
const next = await getTokenFn();
|
|
2340
|
+
if (!cancelled) {
|
|
2341
|
+
setToken(next ?? null);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
resolveToken();
|
|
2345
|
+
window.addEventListener("focus", resolveToken);
|
|
2346
|
+
window.addEventListener("storage", resolveToken);
|
|
2347
|
+
return () => {
|
|
2348
|
+
cancelled = true;
|
|
2349
|
+
window.removeEventListener("focus", resolveToken);
|
|
2350
|
+
window.removeEventListener("storage", resolveToken);
|
|
2351
|
+
};
|
|
2352
|
+
}, [getTokenFn]);
|
|
2353
|
+
const mountedRef = React4.useRef(true);
|
|
2354
|
+
React4.useEffect(() => {
|
|
2355
|
+
mountedRef.current = true;
|
|
2356
|
+
return () => {
|
|
2357
|
+
mountedRef.current = false;
|
|
2358
|
+
};
|
|
2359
|
+
}, []);
|
|
2360
|
+
const buildClientOptions = React4.useCallback(
|
|
2361
|
+
() => ({
|
|
2362
|
+
baseUrl,
|
|
2363
|
+
getToken: getTokenFn,
|
|
2364
|
+
projectName
|
|
2365
|
+
}),
|
|
2366
|
+
[baseUrl, getTokenFn, projectName]
|
|
2367
|
+
);
|
|
2368
|
+
React4.useEffect(() => {
|
|
2369
|
+
if (!enabled || !token || !userId) {
|
|
2370
|
+
setState({ clientOptions: null, projectId: null, loading: false, error: null });
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
let cancelled = false;
|
|
2374
|
+
async function provision() {
|
|
2375
|
+
if (!mountedRef.current) return;
|
|
2376
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
2377
|
+
try {
|
|
2378
|
+
const connectOpts = { baseUrl, token };
|
|
2379
|
+
const projects = await listProjects(connectOpts);
|
|
2380
|
+
if (cancelled || !mountedRef.current) return;
|
|
2381
|
+
const existing = projects.find((p) => p.name === projectName);
|
|
2382
|
+
if (existing) {
|
|
2383
|
+
await updateProject({
|
|
2384
|
+
...connectOpts,
|
|
2385
|
+
projectId: existing.id,
|
|
2386
|
+
isInHouse: true,
|
|
2387
|
+
freeStorageLimitBytes: 2147483648
|
|
2388
|
+
// 2GB
|
|
2389
|
+
});
|
|
2390
|
+
} else {
|
|
2391
|
+
try {
|
|
2392
|
+
await createProject({
|
|
2393
|
+
...connectOpts,
|
|
2394
|
+
name: projectName,
|
|
2395
|
+
isInHouse: true,
|
|
2396
|
+
freeStorageLimitBytes: 2147483648
|
|
2397
|
+
// 2GB in bytes
|
|
2398
|
+
});
|
|
2399
|
+
} catch (err) {
|
|
2400
|
+
if (err?.message?.includes("409") || err?.message?.includes("duplicate") || err?.message?.includes("already exists")) {
|
|
2401
|
+
const retryProjects = await listProjects(connectOpts);
|
|
2402
|
+
const found = retryProjects.find((p) => p.name === projectName);
|
|
2403
|
+
if (!found) throw err;
|
|
2404
|
+
await updateProject({
|
|
2405
|
+
...connectOpts,
|
|
2406
|
+
projectId: found.id,
|
|
2407
|
+
isInHouse: true,
|
|
2408
|
+
freeStorageLimitBytes: 2147483648
|
|
2409
|
+
// 2GB
|
|
2410
|
+
});
|
|
2411
|
+
} else {
|
|
2412
|
+
throw err;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (cancelled || !mountedRef.current) return;
|
|
2417
|
+
setState({
|
|
2418
|
+
clientOptions: buildClientOptions(),
|
|
2419
|
+
projectId: null,
|
|
2420
|
+
loading: false,
|
|
2421
|
+
error: null
|
|
2422
|
+
});
|
|
2423
|
+
} catch (err) {
|
|
2424
|
+
if (cancelled || !mountedRef.current) return;
|
|
2425
|
+
setState({
|
|
2426
|
+
clientOptions: null,
|
|
2427
|
+
projectId: null,
|
|
2428
|
+
loading: false,
|
|
2429
|
+
error: err?.message || "Failed to auto-connect upfiles"
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
provision();
|
|
2434
|
+
return () => {
|
|
2435
|
+
cancelled = true;
|
|
2436
|
+
};
|
|
2437
|
+
}, [enabled, token, userId, baseUrl, projectName, retryCount, buildClientOptions]);
|
|
2438
|
+
const retry = React4.useCallback(() => {
|
|
2439
|
+
setRetryCount((c) => c + 1);
|
|
2440
|
+
}, []);
|
|
2441
|
+
const clearCache = React4.useCallback(() => {
|
|
2442
|
+
setState({ clientOptions: null, projectId: null, loading: false, error: null });
|
|
2443
|
+
}, []);
|
|
2444
|
+
return {
|
|
2445
|
+
...state,
|
|
2446
|
+
isReady: !!state.clientOptions,
|
|
2447
|
+
retry,
|
|
2448
|
+
clearCache
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
exports.ConnectProjectDialog = ConnectProjectDialog;
|
|
2453
|
+
exports.ImageManager = ImageManager;
|
|
2454
|
+
exports.ProjectFilesWidget = ProjectFilesWidget;
|
|
2455
|
+
exports.StorageQuotaError = StorageQuotaError;
|
|
2456
|
+
exports.UpfilesClient = UpfilesClient;
|
|
2457
|
+
exports.Uploader = Uploader;
|
|
2458
|
+
exports.addApiKeyManually = addApiKeyManually;
|
|
2459
|
+
exports.connectProject = connectProject;
|
|
2460
|
+
exports.connectUsingApiKey = connectUsingApiKey;
|
|
2461
|
+
exports.createClientWithKey = createClientWithKey;
|
|
2462
|
+
exports.createProject = createProject;
|
|
2463
|
+
exports.createProjectWithApiKey = createProjectWithApiKey;
|
|
2464
|
+
exports.fetchFileThumbnails = fetchFileThumbnails;
|
|
2465
|
+
exports.fetchProjectFiles = fetchProjectFiles;
|
|
2466
|
+
exports.fetchProjectImagesWithThumbs = fetchProjectImagesWithThumbs;
|
|
2467
|
+
exports.listProjects = listProjects;
|
|
2468
|
+
exports.updateProject = updateProject;
|
|
2469
|
+
exports.useUpfilesInhouse = useUpfilesInhouse;
|
|
2470
|
+
//# sourceMappingURL=index.js.map
|
|
2471
|
+
//# sourceMappingURL=index.js.map
|