react-upload-pro 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2985 @@
1
+ "use client";
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { createContext, useRef, useState, useEffect, useMemo, useCallback, useContext } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+
7
+ // src/utils/cn.ts
8
+ function cn(...inputs) {
9
+ return twMerge(clsx(inputs));
10
+ }
11
+
12
+ // src/utils/id.ts
13
+ function generateId() {
14
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
15
+ return crypto.randomUUID();
16
+ }
17
+ return `rup-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
18
+ }
19
+
20
+ // src/utils/files.ts
21
+ var SSR = typeof window === "undefined";
22
+ function wrapFile(file, metadata = {}) {
23
+ const path = typeof file.webkitRelativePath === "string" ? file.webkitRelativePath : "";
24
+ return {
25
+ id: generateId(),
26
+ file,
27
+ name: file.name,
28
+ size: file.size,
29
+ type: file.type,
30
+ path,
31
+ status: "idle",
32
+ progress: 0,
33
+ bytesUploaded: 0,
34
+ speed: 0,
35
+ eta: Infinity,
36
+ addedAt: Date.now(),
37
+ attempts: 0,
38
+ metadata
39
+ };
40
+ }
41
+ function generatePreview(file) {
42
+ if (SSR) return void 0;
43
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") return void 0;
44
+ try {
45
+ return URL.createObjectURL(file);
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
50
+ function revokePreview(url) {
51
+ if (!url || SSR) return;
52
+ if (typeof URL === "undefined" || typeof URL.revokeObjectURL !== "function") return;
53
+ try {
54
+ URL.revokeObjectURL(url);
55
+ } catch {
56
+ }
57
+ }
58
+ function getFileCategory(file) {
59
+ const name = "file" in file ? file.file.name : file.name;
60
+ const type = "file" in file ? file.file.type : file.type;
61
+ if (type.startsWith("image/")) return "image";
62
+ if (type.startsWith("video/")) return "video";
63
+ if (type.startsWith("audio/")) return "audio";
64
+ if (type === "application/pdf") return "pdf";
65
+ if (type.startsWith("text/") || type === "application/json" || type === "application/xml") {
66
+ return "text";
67
+ }
68
+ const ext = name.toLowerCase().split(".").pop() ?? "";
69
+ if (["doc", "docx", "xls", "xlsx", "ppt", "pptx", "odt", "ods", "odp"].includes(ext)) {
70
+ return "office";
71
+ }
72
+ if (["zip", "rar", "tar", "gz", "7z"].includes(ext)) return "archive";
73
+ if (["md", "log", "csv", "tsv"].includes(ext)) return "text";
74
+ return "other";
75
+ }
76
+ async function detectSignature(file) {
77
+ const head = file.slice(0, 16);
78
+ const ab = await readArrayBuffer(head);
79
+ const buf = new Uint8Array(ab);
80
+ const hex = Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
81
+ if (hex.startsWith("ffd8ff")) return "image/jpeg";
82
+ if (hex.startsWith("89504e470d0a1a0a")) return "image/png";
83
+ if (hex.startsWith("474946383761") || hex.startsWith("474946383961")) return "image/gif";
84
+ if (hex.startsWith("52494646") && hex.slice(16, 24) === "57454250") return "image/webp";
85
+ if (hex.startsWith("25504446")) return "application/pdf";
86
+ if (hex.startsWith("504b0304") || hex.startsWith("504b0506") || hex.startsWith("504b0708")) {
87
+ return "application/zip";
88
+ }
89
+ if (hex.slice(8, 16) === "66747970") return "video/mp4";
90
+ if (hex.startsWith("1f8b")) return "application/gzip";
91
+ return null;
92
+ }
93
+ async function collectFiles(items) {
94
+ if (!items) return [];
95
+ const collected = [];
96
+ const promises = [];
97
+ for (let i = 0; i < items.length; i++) {
98
+ const item = items[i];
99
+ if (!item) continue;
100
+ if (item.kind !== "file") continue;
101
+ const entry = typeof item.webkitGetAsEntry === "function" ? item.webkitGetAsEntry() : null;
102
+ if (entry) {
103
+ promises.push(walkEntry(entry, "", collected));
104
+ } else {
105
+ const file = item.getAsFile();
106
+ if (file) collected.push(file);
107
+ }
108
+ }
109
+ await Promise.all(promises);
110
+ return collected;
111
+ }
112
+ async function walkEntry(entry, basePath, out) {
113
+ if (entry.isFile) {
114
+ const fileEntry = entry;
115
+ return new Promise((resolve) => {
116
+ fileEntry.file(
117
+ (f) => {
118
+ if (basePath) {
119
+ Object.defineProperty(f, "webkitRelativePath", {
120
+ value: `${basePath}/${f.name}`,
121
+ configurable: true
122
+ });
123
+ }
124
+ out.push(f);
125
+ resolve();
126
+ },
127
+ () => resolve()
128
+ );
129
+ });
130
+ }
131
+ if (entry.isDirectory) {
132
+ const dirEntry = entry;
133
+ const reader = dirEntry.createReader();
134
+ const children = [];
135
+ await new Promise((resolve) => {
136
+ const readAll = () => {
137
+ reader.readEntries((batch) => {
138
+ if (batch.length === 0) {
139
+ resolve();
140
+ return;
141
+ }
142
+ children.push(...batch);
143
+ readAll();
144
+ }, () => resolve());
145
+ };
146
+ readAll();
147
+ });
148
+ const nextPath = basePath ? `${basePath}/${entry.name}` : entry.name;
149
+ await Promise.all(children.map((c) => walkEntry(c, nextPath, out)));
150
+ }
151
+ }
152
+ function fileKey(file) {
153
+ return `${file.name}::${file.size}::${file.lastModified}`;
154
+ }
155
+ async function readArrayBuffer(blob) {
156
+ const maybe = blob.arrayBuffer;
157
+ if (typeof maybe === "function") {
158
+ return maybe.call(blob);
159
+ }
160
+ return new Promise((resolve, reject) => {
161
+ const reader = new FileReader();
162
+ reader.onload = () => resolve(reader.result);
163
+ reader.onerror = () => reject(reader.error);
164
+ reader.readAsArrayBuffer(blob);
165
+ });
166
+ }
167
+
168
+ // src/utils/format.ts
169
+ function formatBytes(bytes, decimals = 1) {
170
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
171
+ const k = 1024;
172
+ const units = ["B", "KB", "MB", "GB", "TB"];
173
+ const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(k)));
174
+ return `${(bytes / Math.pow(k, i)).toFixed(decimals)} ${units[i]}`;
175
+ }
176
+ function formatSpeed(bps) {
177
+ if (!Number.isFinite(bps) || bps <= 0) return "\u2014";
178
+ return `${formatBytes(bps)}/s`;
179
+ }
180
+ function formatEta(seconds) {
181
+ if (!Number.isFinite(seconds) || seconds <= 0) return "\u2014";
182
+ if (seconds < 60) return `${Math.ceil(seconds)}s`;
183
+ if (seconds < 3600) {
184
+ const m2 = Math.floor(seconds / 60);
185
+ const s = Math.ceil(seconds % 60);
186
+ return `${m2}m ${s}s`;
187
+ }
188
+ const h = Math.floor(seconds / 3600);
189
+ const m = Math.floor(seconds % 3600 / 60);
190
+ return `${h}h ${m}m`;
191
+ }
192
+ function formatPercent(progress) {
193
+ return `${Math.round(Math.max(0, Math.min(1, progress)) * 100)}%`;
194
+ }
195
+
196
+ // src/core/validation.ts
197
+ function normalizeAccept(accept) {
198
+ if (!accept) return [];
199
+ if (typeof accept === "string") {
200
+ return accept.split(",").map((s) => s.trim()).filter(Boolean);
201
+ }
202
+ if (Array.isArray(accept)) {
203
+ return accept.map((s) => s.trim()).filter(Boolean);
204
+ }
205
+ const out = [];
206
+ for (const [mime, exts] of Object.entries(accept)) {
207
+ out.push(mime);
208
+ if (Array.isArray(exts)) out.push(...exts);
209
+ }
210
+ return out;
211
+ }
212
+ function matchesAccept(file, acceptors) {
213
+ if (acceptors.length === 0) return true;
214
+ const fileMime = (file.type || "").toLowerCase();
215
+ const fileName = file.name.toLowerCase();
216
+ for (const raw of acceptors) {
217
+ const a = raw.toLowerCase();
218
+ if (a.startsWith(".")) {
219
+ if (fileName.endsWith(a)) return true;
220
+ continue;
221
+ }
222
+ if (a.endsWith("/*")) {
223
+ const prefix = a.slice(0, -1);
224
+ if (fileMime.startsWith(prefix)) return true;
225
+ continue;
226
+ }
227
+ if (fileMime === a) return true;
228
+ }
229
+ return false;
230
+ }
231
+ function validateBuiltin(file, config) {
232
+ if (typeof config.minSize === "number" && file.size < config.minSize) {
233
+ return {
234
+ code: "file-too-small",
235
+ message: `File is smaller than ${formatBytes(config.minSize)}.`,
236
+ file
237
+ };
238
+ }
239
+ if (typeof config.maxSize === "number" && file.size > config.maxSize) {
240
+ return {
241
+ code: "file-too-large",
242
+ message: `File is larger than ${formatBytes(config.maxSize)}.`,
243
+ file
244
+ };
245
+ }
246
+ const acceptors = normalizeAccept(config.accept);
247
+ if (!matchesAccept(file, acceptors)) {
248
+ return {
249
+ code: "file-invalid-type",
250
+ message: `File type "${file.type || "unknown"}" is not allowed.`,
251
+ file
252
+ };
253
+ }
254
+ return null;
255
+ }
256
+ async function validateFile(file, config) {
257
+ const built = validateBuiltin(file, config);
258
+ if (built) return built;
259
+ if (config.validators?.length) {
260
+ for (const validator of config.validators) {
261
+ const result = await validator(file);
262
+ if (result) return result;
263
+ }
264
+ }
265
+ return null;
266
+ }
267
+ async function validateBatch(files, config, existingCount = 0, existingKeys = /* @__PURE__ */ new Set()) {
268
+ const accepted = [];
269
+ const rejected = [];
270
+ const seenInBatch = /* @__PURE__ */ new Set();
271
+ let count = existingCount;
272
+ for (const file of files) {
273
+ if (typeof config.maxFiles === "number" && count >= config.maxFiles) {
274
+ rejected.push({
275
+ code: "too-many-files",
276
+ message: `Maximum of ${config.maxFiles} files allowed.`,
277
+ file
278
+ });
279
+ continue;
280
+ }
281
+ if (config.rejectDuplicates) {
282
+ const key = fileKey(file);
283
+ if (existingKeys.has(key) || seenInBatch.has(key)) {
284
+ rejected.push({
285
+ code: "duplicate-file",
286
+ message: `Duplicate file: ${file.name}.`,
287
+ file
288
+ });
289
+ continue;
290
+ }
291
+ seenInBatch.add(key);
292
+ }
293
+ const error = await validateFile(file, config);
294
+ if (error) {
295
+ rejected.push(error);
296
+ } else {
297
+ accepted.push(file);
298
+ count += 1;
299
+ }
300
+ }
301
+ return { accepted, rejected };
302
+ }
303
+
304
+ // src/core/chunkUpload.ts
305
+ async function chunkUpload(params) {
306
+ const { file, config, signal, onProgress } = params;
307
+ const chunkSize = config.chunkSize ?? 5 * 1024 * 1024;
308
+ const uploadId = file.id;
309
+ if (!file.chunks || file.chunks.length === 0) {
310
+ file.chunks = buildChunks(file.size, chunkSize);
311
+ }
312
+ const total = file.size;
313
+ let loadedBaseline = file.chunks.filter((c) => c.status === "success").reduce((sum, c) => sum + (c.end - c.start), 0);
314
+ for (const chunk of file.chunks) {
315
+ if (signal.aborted) throw makeError("cancelled", "Upload cancelled", false);
316
+ if (chunk.status === "success") continue;
317
+ chunk.status = "uploading";
318
+ chunk.attempts += 1;
319
+ const blob = file.file.slice(chunk.start, chunk.end);
320
+ try {
321
+ await sendChunk({
322
+ blob,
323
+ chunk,
324
+ total,
325
+ uploadId,
326
+ config,
327
+ signal,
328
+ onProgress: (loadedInChunk) => {
329
+ onProgress(loadedBaseline + loadedInChunk, total);
330
+ }
331
+ });
332
+ chunk.status = "success";
333
+ loadedBaseline += chunk.end - chunk.start;
334
+ onProgress(loadedBaseline, total);
335
+ } catch (err) {
336
+ chunk.status = "error";
337
+ throw err;
338
+ }
339
+ }
340
+ return { uploadId, chunks: file.chunks.length };
341
+ }
342
+ function buildChunks(total, size) {
343
+ const chunks = [];
344
+ let start = 0;
345
+ let index = 0;
346
+ while (start < total) {
347
+ const end = Math.min(total, start + size);
348
+ chunks.push({ index, start, end, status: "pending", attempts: 0 });
349
+ start = end;
350
+ index += 1;
351
+ }
352
+ if (chunks.length === 0) {
353
+ chunks.push({ index: 0, start: 0, end: 0, status: "pending", attempts: 0 });
354
+ }
355
+ return chunks;
356
+ }
357
+ function sendChunk(params) {
358
+ const { blob, chunk, total, uploadId, config, signal, onProgress } = params;
359
+ return new Promise(async (resolve, reject) => {
360
+ if (!config.endpoint) {
361
+ reject(makeError("config-error", "No endpoint configured", false));
362
+ return;
363
+ }
364
+ let headers = {};
365
+ if (config.headers) {
366
+ try {
367
+ headers = typeof config.headers === "function" ? await config.headers() : { ...config.headers };
368
+ } catch (cause) {
369
+ reject(makeError("headers-failed", "Failed to compute headers", true, cause));
370
+ return;
371
+ }
372
+ }
373
+ headers["Content-Range"] = `bytes ${chunk.start}-${Math.max(0, chunk.end - 1)}/${total}`;
374
+ headers["X-Upload-Id"] = uploadId;
375
+ headers["X-Chunk-Index"] = String(chunk.index);
376
+ const xhr = new XMLHttpRequest();
377
+ xhr.open(config.method ?? "POST", config.endpoint, true);
378
+ xhr.withCredentials = !!config.withCredentials;
379
+ for (const [k, v] of Object.entries(headers)) {
380
+ try {
381
+ xhr.setRequestHeader(k, v);
382
+ } catch {
383
+ }
384
+ }
385
+ xhr.upload.addEventListener("progress", (ev) => {
386
+ if (ev.lengthComputable) onProgress(ev.loaded);
387
+ });
388
+ xhr.addEventListener("load", () => {
389
+ if (xhr.status >= 200 && xhr.status < 300) resolve();
390
+ else {
391
+ reject(
392
+ makeError(
393
+ `http-${xhr.status}`,
394
+ `Chunk ${chunk.index} failed: ${xhr.status}`,
395
+ xhr.status >= 500 || xhr.status === 408 || xhr.status === 429
396
+ )
397
+ );
398
+ }
399
+ });
400
+ xhr.addEventListener("error", () => reject(makeError("network", "Network error", true)));
401
+ xhr.addEventListener("abort", () => reject(makeError("cancelled", "Upload cancelled", false)));
402
+ signal.addEventListener("abort", () => xhr.abort(), { once: true });
403
+ xhr.send(blob);
404
+ });
405
+ }
406
+ function makeError(code, message, retryable, cause) {
407
+ return { code, message, retryable, cause };
408
+ }
409
+
410
+ // src/core/speed.ts
411
+ var SpeedMeter = class {
412
+ constructor(totalBytes, alpha = 0.3) {
413
+ this.bytes = 0;
414
+ this.lastTime = 0;
415
+ this.speed = 0;
416
+ this.totalBytes = totalBytes;
417
+ this.alpha = alpha;
418
+ }
419
+ update(currentBytes) {
420
+ const now = performance.now ? performance.now() : Date.now();
421
+ if (this.lastTime === 0) {
422
+ this.lastTime = now;
423
+ this.bytes = currentBytes;
424
+ return { speed: 0, eta: Infinity };
425
+ }
426
+ const dt = (now - this.lastTime) / 1e3;
427
+ if (dt <= 0) return { speed: this.speed, eta: this.eta() };
428
+ const db = currentBytes - this.bytes;
429
+ if (db < 0) {
430
+ this.bytes = currentBytes;
431
+ this.lastTime = now;
432
+ return { speed: this.speed, eta: this.eta() };
433
+ }
434
+ const sample = db / dt;
435
+ this.speed = this.speed === 0 ? sample : this.alpha * sample + (1 - this.alpha) * this.speed;
436
+ this.bytes = currentBytes;
437
+ this.lastTime = now;
438
+ return { speed: this.speed, eta: this.eta() };
439
+ }
440
+ eta() {
441
+ if (this.speed <= 0) return Infinity;
442
+ const remaining = this.totalBytes - this.bytes;
443
+ if (remaining <= 0) return 0;
444
+ return remaining / this.speed;
445
+ }
446
+ };
447
+
448
+ // src/core/xhrUpload.ts
449
+ function xhrUpload(params) {
450
+ const { file, config, signal, onProgress } = params;
451
+ return new Promise(async (resolve, reject) => {
452
+ if (signal.aborted) {
453
+ reject(makeError2("cancelled", "Upload cancelled", false));
454
+ return;
455
+ }
456
+ let url = config.endpoint;
457
+ let method = config.method ?? "POST";
458
+ let extraFields = {};
459
+ let extraHeaders = {};
460
+ if (typeof config.getUploadToken === "function") {
461
+ try {
462
+ const token = await config.getUploadToken(file);
463
+ url = token.url;
464
+ method = token.method ?? method;
465
+ extraFields = token.fields ?? {};
466
+ extraHeaders = token.headers ?? {};
467
+ } catch (cause) {
468
+ reject(makeError2("token-failed", "Failed to obtain upload token", true, cause));
469
+ return;
470
+ }
471
+ }
472
+ if (!url) {
473
+ reject(makeError2("config-error", "No endpoint configured", false));
474
+ return;
475
+ }
476
+ let headers = { ...extraHeaders };
477
+ if (config.headers) {
478
+ try {
479
+ const dynamic = typeof config.headers === "function" ? await config.headers() : config.headers;
480
+ headers = { ...headers, ...dynamic };
481
+ } catch (cause) {
482
+ reject(makeError2("headers-failed", "Failed to compute headers", true, cause));
483
+ return;
484
+ }
485
+ }
486
+ const formExtra = typeof config.formData === "function" ? config.formData(file) : config.formData ?? {};
487
+ const xhr = new XMLHttpRequest();
488
+ xhr.open(method, url, true);
489
+ xhr.withCredentials = !!config.withCredentials;
490
+ for (const [k, v] of Object.entries(headers)) {
491
+ try {
492
+ xhr.setRequestHeader(k, v);
493
+ } catch {
494
+ }
495
+ }
496
+ xhr.upload.addEventListener("progress", (ev) => {
497
+ if (ev.lengthComputable) onProgress({ loaded: ev.loaded, total: ev.total });
498
+ });
499
+ xhr.addEventListener("load", () => {
500
+ if (xhr.status >= 200 && xhr.status < 300) {
501
+ const ct = xhr.getResponseHeader("content-type") ?? "";
502
+ let body = xhr.responseText;
503
+ if (ct.includes("application/json")) {
504
+ try {
505
+ body = JSON.parse(xhr.responseText);
506
+ } catch {
507
+ }
508
+ }
509
+ resolve(body);
510
+ } else {
511
+ reject(
512
+ makeError2(
513
+ `http-${xhr.status}`,
514
+ `Upload failed with status ${xhr.status}`,
515
+ xhr.status >= 500 || xhr.status === 408 || xhr.status === 429,
516
+ xhr.responseText
517
+ )
518
+ );
519
+ }
520
+ });
521
+ xhr.addEventListener("error", () => {
522
+ reject(makeError2("network", "Network error", true));
523
+ });
524
+ xhr.addEventListener("abort", () => {
525
+ reject(makeError2("cancelled", "Upload cancelled", false));
526
+ });
527
+ xhr.addEventListener("timeout", () => {
528
+ reject(makeError2("timeout", "Upload timed out", true));
529
+ });
530
+ const onAbort = () => xhr.abort();
531
+ signal.addEventListener("abort", onAbort, { once: true });
532
+ const fieldName = config.fieldName ?? "file";
533
+ if (method === "POST" && Object.keys(formExtra).length + Object.keys(extraFields).length > 0) {
534
+ const fd = new FormData();
535
+ for (const [k, v] of Object.entries({ ...extraFields, ...formExtra })) fd.append(k, v);
536
+ fd.append(fieldName, file.file, file.name);
537
+ xhr.send(fd);
538
+ } else if (method === "POST") {
539
+ const fd = new FormData();
540
+ fd.append(fieldName, file.file, file.name);
541
+ xhr.send(fd);
542
+ } else {
543
+ xhr.send(file.file);
544
+ }
545
+ });
546
+ }
547
+ function makeError2(code, message, retryable, cause) {
548
+ return { code, message, retryable, cause };
549
+ }
550
+
551
+ // src/core/queue.ts
552
+ var UploadQueue = class {
553
+ constructor(config = {}, callbacks = {}) {
554
+ this.files = [];
555
+ this.listeners = /* @__PURE__ */ new Set();
556
+ this.inFlight = /* @__PURE__ */ new Set();
557
+ this.completionFired = false;
558
+ this.config = { concurrency: 3, retries: 2, retryBackoffMs: 500, ...config };
559
+ this.callbacks = callbacks;
560
+ }
561
+ // ───────────── public state ─────────────
562
+ getFiles() {
563
+ return this.files;
564
+ }
565
+ subscribe(listener) {
566
+ this.listeners.add(listener);
567
+ listener(this.files);
568
+ return () => {
569
+ this.listeners.delete(listener);
570
+ };
571
+ }
572
+ updateConfig(config) {
573
+ this.config = { ...this.config, ...config };
574
+ }
575
+ updateCallbacks(callbacks) {
576
+ this.callbacks = callbacks;
577
+ }
578
+ // ───────────── mutations ─────────────
579
+ add(files) {
580
+ if (files.length === 0) return;
581
+ this.files = [...this.files, ...files];
582
+ this.completionFired = false;
583
+ this.emit();
584
+ const mode = this.config.mode ?? "manual";
585
+ if (mode === "instant" || mode === "auto") {
586
+ void this.start();
587
+ }
588
+ }
589
+ remove(id2) {
590
+ const file = this.files.find((f) => f.id === id2);
591
+ if (!file) return;
592
+ if (file.status === "uploading") {
593
+ file.controller?.abort();
594
+ }
595
+ revokePreview(file.previewUrl);
596
+ this.files = this.files.filter((f) => f.id !== id2);
597
+ this.inFlight.delete(id2);
598
+ this.callbacks.onRemove?.(file);
599
+ this.emit();
600
+ }
601
+ removeAll() {
602
+ for (const f of this.files) {
603
+ if (f.status === "uploading") f.controller?.abort();
604
+ revokePreview(f.previewUrl);
605
+ }
606
+ const removed = this.files;
607
+ this.files = [];
608
+ this.inFlight.clear();
609
+ for (const f of removed) this.callbacks.onRemove?.(f);
610
+ this.emit();
611
+ }
612
+ reorder(fromIndex, toIndex) {
613
+ if (fromIndex < 0 || toIndex < 0 || fromIndex >= this.files.length || toIndex >= this.files.length || fromIndex === toIndex) {
614
+ return;
615
+ }
616
+ const next = [...this.files];
617
+ const [moved] = next.splice(fromIndex, 1);
618
+ if (moved) next.splice(toIndex, 0, moved);
619
+ this.files = next;
620
+ this.emit();
621
+ }
622
+ rename(id2, newName) {
623
+ this.patch(id2, { name: newName });
624
+ }
625
+ setMetadata(id2, metadata) {
626
+ const file = this.files.find((f) => f.id === id2);
627
+ if (!file) return;
628
+ this.patch(id2, { metadata: { ...file.metadata, ...metadata } });
629
+ }
630
+ // ───────────── lifecycle ─────────────
631
+ async start() {
632
+ const strategy = this.config.strategy ?? "parallel";
633
+ const concurrency = strategy === "sequential" ? 1 : Math.max(1, this.config.concurrency ?? 3);
634
+ const pending = () => this.files.filter((f) => f.status === "idle" || f.status === "queued" || f.status === "paused");
635
+ for (const f of pending()) {
636
+ if (f.status === "idle") this.patch(f.id, { status: "queued" });
637
+ }
638
+ const next = () => pending().find((f) => f.status === "queued");
639
+ const runners = [];
640
+ const launch = async () => {
641
+ while (true) {
642
+ const file = next();
643
+ if (!file) return;
644
+ await this.runFile(file);
645
+ }
646
+ };
647
+ for (let i = 0; i < concurrency; i++) runners.push(launch());
648
+ await Promise.all(runners);
649
+ this.maybeFireCompletion();
650
+ }
651
+ pause(id2) {
652
+ const file = this.files.find((f) => f.id === id2);
653
+ if (!file) return;
654
+ if (file.status !== "uploading") return;
655
+ file.controller?.abort();
656
+ this.patch(id2, { status: "paused" });
657
+ this.callbacks.onPause?.(file);
658
+ }
659
+ resume(id2) {
660
+ const file = this.files.find((f) => f.id === id2);
661
+ if (!file) return;
662
+ if (file.status !== "paused") return;
663
+ this.patch(id2, { status: "queued" });
664
+ this.callbacks.onResume?.(file);
665
+ void this.start();
666
+ }
667
+ retry(id2) {
668
+ const file = this.files.find((f) => f.id === id2);
669
+ if (!file) return;
670
+ if (file.status !== "error" && file.status !== "cancelled") return;
671
+ this.patch(id2, {
672
+ status: "queued",
673
+ progress: 0,
674
+ bytesUploaded: 0,
675
+ speed: 0,
676
+ eta: Infinity,
677
+ error: void 0
678
+ });
679
+ this.callbacks.onRetry?.(file);
680
+ void this.start();
681
+ }
682
+ cancel(id2) {
683
+ const file = this.files.find((f) => f.id === id2);
684
+ if (!file) return;
685
+ if (file.status === "uploading") file.controller?.abort();
686
+ this.patch(id2, { status: "cancelled" });
687
+ }
688
+ // ───────────── internals ─────────────
689
+ async runFile(file) {
690
+ if (this.inFlight.has(file.id)) return;
691
+ this.inFlight.add(file.id);
692
+ const controller = new AbortController();
693
+ file.controller = controller;
694
+ const startedAt = Date.now();
695
+ this.patch(file.id, {
696
+ status: "uploading",
697
+ startedAt,
698
+ attempts: file.attempts + 1,
699
+ controller
700
+ });
701
+ this.callbacks.onUploadStart?.(this.lookup(file.id));
702
+ const meter = new SpeedMeter(file.size);
703
+ const updateProgress = (loaded, total) => {
704
+ const { speed, eta } = meter.update(loaded);
705
+ const progress = total > 0 ? loaded / total : 0;
706
+ this.patch(file.id, { progress, bytesUploaded: loaded, speed, eta });
707
+ this.callbacks.onUploadProgress?.(this.lookup(file.id));
708
+ };
709
+ try {
710
+ if (this.config.virusScan) {
711
+ const result = await this.config.virusScan(this.lookup(file.id));
712
+ if (!result.clean) {
713
+ throw {
714
+ code: "virus-detected",
715
+ message: result.reason ?? "File failed virus scan",
716
+ retryable: false
717
+ };
718
+ }
719
+ }
720
+ let response;
721
+ if (this.config.cloud) {
722
+ response = await this.config.cloud.upload(this.lookup(file.id), {
723
+ signal: controller.signal,
724
+ onProgress: updateProgress
725
+ });
726
+ } else if (this.config.chunkSize && this.config.chunkSize > 0) {
727
+ response = await chunkUpload({
728
+ file: this.lookup(file.id),
729
+ config: this.config,
730
+ signal: controller.signal,
731
+ onProgress: updateProgress
732
+ });
733
+ } else {
734
+ response = await xhrUpload({
735
+ file: this.lookup(file.id),
736
+ config: this.config,
737
+ signal: controller.signal,
738
+ onProgress: (ev) => updateProgress(ev.loaded, ev.total)
739
+ });
740
+ }
741
+ this.patch(file.id, {
742
+ status: "success",
743
+ progress: 1,
744
+ bytesUploaded: file.size,
745
+ finishedAt: Date.now(),
746
+ response,
747
+ controller: void 0,
748
+ eta: 0
749
+ });
750
+ this.callbacks.onUploadSuccess?.(this.lookup(file.id));
751
+ } catch (err) {
752
+ const error = normalizeError(err);
753
+ const current = this.lookup(file.id);
754
+ if (current.status === "paused") ; else if (error.code === "cancelled") {
755
+ this.patch(file.id, { status: "cancelled", error, controller: void 0 });
756
+ } else {
757
+ const maxRetries = this.config.retries ?? 0;
758
+ const canRetry = error.retryable && current.attempts <= maxRetries;
759
+ if (canRetry) {
760
+ const backoff = (this.config.retryBackoffMs ?? 500) * Math.pow(2, current.attempts - 1);
761
+ await new Promise((r) => setTimeout(r, backoff));
762
+ this.inFlight.delete(file.id);
763
+ this.callbacks.onRetry?.(current);
764
+ this.patch(file.id, { status: "queued", controller: void 0 });
765
+ return this.runFile(this.lookup(file.id));
766
+ }
767
+ this.patch(file.id, { status: "error", error, controller: void 0 });
768
+ this.callbacks.onUploadError?.(this.lookup(file.id), error);
769
+ }
770
+ } finally {
771
+ this.inFlight.delete(file.id);
772
+ }
773
+ }
774
+ lookup(id2) {
775
+ const f = this.files.find((x) => x.id === id2);
776
+ if (!f) throw new Error(`File ${id2} not found`);
777
+ return f;
778
+ }
779
+ patch(id2, patch) {
780
+ const idx = this.files.findIndex((f) => f.id === id2);
781
+ if (idx < 0) return;
782
+ const prev = this.files[idx];
783
+ if (!prev) return;
784
+ this.files = [
785
+ ...this.files.slice(0, idx),
786
+ { ...prev, ...patch },
787
+ ...this.files.slice(idx + 1)
788
+ ];
789
+ this.emit();
790
+ }
791
+ emit() {
792
+ for (const l of this.listeners) l(this.files);
793
+ }
794
+ maybeFireCompletion() {
795
+ if (this.completionFired) return;
796
+ if (this.files.length === 0) return;
797
+ const allDone = this.files.every(
798
+ (f) => f.status === "success" || f.status === "error" || f.status === "cancelled"
799
+ );
800
+ if (allDone) {
801
+ this.completionFired = true;
802
+ this.callbacks.onAllComplete?.(this.files);
803
+ }
804
+ }
805
+ };
806
+ function normalizeError(err) {
807
+ if (err && typeof err === "object" && "code" in err && "message" in err && "retryable" in err) {
808
+ return err;
809
+ }
810
+ if (err instanceof Error) {
811
+ return { code: "unknown", message: err.message, retryable: false, cause: err };
812
+ }
813
+ return { code: "unknown", message: "Upload failed", retryable: false, cause: err };
814
+ }
815
+ function useUploadQueue(options = {}) {
816
+ const queueRef = useRef(null);
817
+ if (queueRef.current === null) {
818
+ queueRef.current = new UploadQueue(options, options);
819
+ }
820
+ const queue = queueRef.current;
821
+ const [files, setFiles] = useState(() => queue.getFiles());
822
+ useEffect(() => {
823
+ queue.updateConfig(options);
824
+ queue.updateCallbacks(options);
825
+ }, [queue, options]);
826
+ useEffect(() => {
827
+ return queue.subscribe(setFiles);
828
+ }, [queue]);
829
+ return {
830
+ queue,
831
+ files,
832
+ add: (f) => queue.add(f),
833
+ remove: (id2) => queue.remove(id2),
834
+ removeAll: () => queue.removeAll(),
835
+ reorder: (from, to) => queue.reorder(from, to),
836
+ rename: (id2, name) => queue.rename(id2, name),
837
+ setMetadata: (id2, m) => queue.setMetadata(id2, m),
838
+ start: () => queue.start(),
839
+ pause: (id2) => queue.pause(id2),
840
+ resume: (id2) => queue.resume(id2),
841
+ retry: (id2) => queue.retry(id2),
842
+ cancel: (id2) => queue.cancel(id2)
843
+ };
844
+ }
845
+ function useDropzone(options = {}) {
846
+ const {
847
+ multiple = true,
848
+ directory = false,
849
+ disabled = false,
850
+ clipboard = true,
851
+ noBubble: _noBubble = true,
852
+ autoRevoke = true,
853
+ accept,
854
+ minSize,
855
+ maxSize,
856
+ maxFiles,
857
+ rejectDuplicates,
858
+ validators,
859
+ onDrop,
860
+ onDropAccepted,
861
+ onDropRejected,
862
+ ...uploaderConfig
863
+ } = options;
864
+ const inputRef = useRef(null);
865
+ const dragCounterRef = useRef(0);
866
+ const [dragState, setDragState] = useState("idle");
867
+ const [rejected, setRejected] = useState([]);
868
+ const queueApi = useUploadQueue({ ...uploaderConfig, ...options });
869
+ const existingKeys = useMemo(() => {
870
+ const set = /* @__PURE__ */ new Set();
871
+ for (const f of queueApi.files) set.add(fileKey(f.file));
872
+ return set;
873
+ }, [queueApi.files]);
874
+ const add = useCallback(
875
+ async (rawFiles) => {
876
+ let pool = rawFiles;
877
+ if (!multiple && pool.length > 1) pool = pool.slice(0, 1);
878
+ const isReplacement = !multiple && pool.length > 0 && queueApi.files.length > 0;
879
+ if (isReplacement) {
880
+ queueApi.removeAll();
881
+ }
882
+ const { accepted, rejected: rejectedFiles } = await validateBatch(
883
+ pool,
884
+ { accept, minSize, maxSize, maxFiles, rejectDuplicates, validators },
885
+ isReplacement ? 0 : queueApi.files.length,
886
+ isReplacement ? /* @__PURE__ */ new Set() : existingKeys
887
+ );
888
+ const wrapped = accepted.map((f) => {
889
+ const wf = wrapFile(f);
890
+ wf.previewUrl = generatePreview(f);
891
+ return wf;
892
+ });
893
+ queueApi.add(wrapped);
894
+ setRejected(rejectedFiles);
895
+ onDrop?.(wrapped, rejectedFiles);
896
+ if (wrapped.length) onDropAccepted?.(wrapped);
897
+ if (rejectedFiles.length) onDropRejected?.(rejectedFiles);
898
+ return { accepted: wrapped, rejected: rejectedFiles };
899
+ },
900
+ [
901
+ multiple,
902
+ accept,
903
+ minSize,
904
+ maxSize,
905
+ maxFiles,
906
+ rejectDuplicates,
907
+ validators,
908
+ queueApi,
909
+ existingKeys,
910
+ onDrop,
911
+ onDropAccepted,
912
+ onDropRejected
913
+ ]
914
+ );
915
+ const open = useCallback(() => {
916
+ if (disabled) return;
917
+ inputRef.current?.click();
918
+ }, [disabled]);
919
+ const handleInputChange = useCallback(
920
+ async (event) => {
921
+ const list = event.target.files;
922
+ if (!list) return;
923
+ await add(Array.from(list));
924
+ event.target.value = "";
925
+ },
926
+ [add]
927
+ );
928
+ const handleDragEnter = useCallback(
929
+ (event) => {
930
+ if (disabled) return;
931
+ event.preventDefault();
932
+ dragCounterRef.current += 1;
933
+ const hasFiles = Array.from(event.dataTransfer?.types ?? []).includes("Files");
934
+ setDragState(hasFiles ? "drag-accept" : "dragging");
935
+ },
936
+ [disabled]
937
+ );
938
+ const handleDragOver = useCallback(
939
+ (event) => {
940
+ if (disabled) return;
941
+ event.preventDefault();
942
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
943
+ },
944
+ [disabled]
945
+ );
946
+ const handleDragLeave = useCallback(
947
+ (event) => {
948
+ if (disabled) return;
949
+ event.preventDefault();
950
+ dragCounterRef.current = Math.max(0, dragCounterRef.current - 1);
951
+ if (dragCounterRef.current === 0) setDragState("idle");
952
+ },
953
+ [disabled]
954
+ );
955
+ const handleDrop = useCallback(
956
+ async (event) => {
957
+ if (disabled) return;
958
+ event.preventDefault();
959
+ dragCounterRef.current = 0;
960
+ setDragState("idle");
961
+ const dt = event.dataTransfer;
962
+ if (!dt) return;
963
+ let files = [];
964
+ if (dt.items && typeof dt.items[0]?.webkitGetAsEntry === "function") {
965
+ files = await collectFiles(dt.items);
966
+ } else if (dt.files) {
967
+ files = Array.from(dt.files);
968
+ }
969
+ await add(files);
970
+ },
971
+ [add, disabled]
972
+ );
973
+ const handleKeyDown = useCallback(
974
+ (event) => {
975
+ if (disabled) return;
976
+ if (event.key === "Enter" || event.key === " ") {
977
+ event.preventDefault();
978
+ open();
979
+ }
980
+ },
981
+ [disabled, open]
982
+ );
983
+ useEffect(() => {
984
+ if (!clipboard || disabled) return;
985
+ const onPaste = (event) => {
986
+ const ev = event;
987
+ const items = ev.clipboardData?.items;
988
+ if (!items) return;
989
+ const files = [];
990
+ for (let i = 0; i < items.length; i++) {
991
+ const item = items[i];
992
+ if (!item) continue;
993
+ if (item.kind === "file") {
994
+ const f = item.getAsFile();
995
+ if (f) files.push(f);
996
+ }
997
+ }
998
+ if (files.length) void add(files);
999
+ };
1000
+ document.addEventListener("paste", onPaste);
1001
+ return () => document.removeEventListener("paste", onPaste);
1002
+ }, [clipboard, disabled, add]);
1003
+ useEffect(() => {
1004
+ if (!autoRevoke) return;
1005
+ return () => {
1006
+ for (const f of queueApi.queue.getFiles()) {
1007
+ if (f.previewUrl && typeof URL !== "undefined") {
1008
+ try {
1009
+ URL.revokeObjectURL(f.previewUrl);
1010
+ } catch {
1011
+ }
1012
+ }
1013
+ }
1014
+ };
1015
+ }, [autoRevoke, queueApi.queue]);
1016
+ const state = dragState;
1017
+ const isDragging = state !== "idle";
1018
+ const getRootProps = useCallback(
1019
+ (props = {}) => {
1020
+ return {
1021
+ ...props,
1022
+ role: props.role ?? "button",
1023
+ tabIndex: props.tabIndex ?? 0,
1024
+ "aria-disabled": disabled || void 0,
1025
+ "aria-label": props["aria-label"] ?? "File upload area",
1026
+ "data-dropzone-state": state,
1027
+ onClick: (e) => {
1028
+ props.onClick?.(e);
1029
+ if (!e.defaultPrevented) open();
1030
+ },
1031
+ onKeyDown: (e) => {
1032
+ props.onKeyDown?.(e);
1033
+ if (!e.defaultPrevented) handleKeyDown(e);
1034
+ },
1035
+ onDragEnter: (e) => {
1036
+ props.onDragEnter?.(e);
1037
+ handleDragEnter(e);
1038
+ },
1039
+ onDragOver: (e) => {
1040
+ props.onDragOver?.(e);
1041
+ handleDragOver(e);
1042
+ },
1043
+ onDragLeave: (e) => {
1044
+ props.onDragLeave?.(e);
1045
+ handleDragLeave(e);
1046
+ },
1047
+ onDrop: (e) => {
1048
+ props.onDrop?.(e);
1049
+ void handleDrop(e);
1050
+ }
1051
+ };
1052
+ },
1053
+ [
1054
+ disabled,
1055
+ state,
1056
+ open,
1057
+ handleKeyDown,
1058
+ handleDragEnter,
1059
+ handleDragOver,
1060
+ handleDragLeave,
1061
+ handleDrop
1062
+ ]
1063
+ );
1064
+ const getInputProps = useCallback(
1065
+ (props = {}) => {
1066
+ const directoryProps = directory ? { webkitdirectory: "", directory: "", mozdirectory: "" } : {};
1067
+ return {
1068
+ ...directoryProps,
1069
+ ...props,
1070
+ ref: (node) => {
1071
+ inputRef.current = node;
1072
+ },
1073
+ type: "file",
1074
+ multiple,
1075
+ disabled,
1076
+ accept: typeof accept === "string" ? accept : Array.isArray(accept) ? accept.join(",") : void 0,
1077
+ style: { display: "none", ...props.style },
1078
+ onChange: (e) => {
1079
+ props.onChange?.(e);
1080
+ void handleInputChange(e);
1081
+ },
1082
+ tabIndex: -1,
1083
+ "aria-hidden": true
1084
+ };
1085
+ },
1086
+ [multiple, disabled, accept, directory, handleInputChange]
1087
+ );
1088
+ return {
1089
+ getRootProps,
1090
+ getInputProps,
1091
+ open,
1092
+ files: queueApi.files,
1093
+ rejected,
1094
+ isDragging,
1095
+ isDragAccept: state === "drag-accept",
1096
+ isDragReject: state === "drag-reject",
1097
+ state,
1098
+ add,
1099
+ remove: queueApi.remove,
1100
+ removeAll: queueApi.removeAll,
1101
+ reorder: queueApi.reorder,
1102
+ start: queueApi.start,
1103
+ pause: queueApi.pause,
1104
+ resume: queueApi.resume,
1105
+ retry: queueApi.retry,
1106
+ cancel: queueApi.cancel,
1107
+ rename: queueApi.rename,
1108
+ setMetadata: queueApi.setMetadata,
1109
+ queue: queueApi.queue,
1110
+ inputRef
1111
+ };
1112
+ }
1113
+ function useUploadProgress(files) {
1114
+ return useMemo(() => {
1115
+ let total = 0;
1116
+ let uploaded = 0;
1117
+ let speed = 0;
1118
+ let uploading = 0;
1119
+ let completed = 0;
1120
+ let errored = 0;
1121
+ let pending = 0;
1122
+ for (const f of files) {
1123
+ total += f.size;
1124
+ uploaded += f.bytesUploaded;
1125
+ if (f.status === "uploading") {
1126
+ uploading += 1;
1127
+ speed += f.speed;
1128
+ } else if (f.status === "success") {
1129
+ completed += 1;
1130
+ } else if (f.status === "error" || f.status === "cancelled") {
1131
+ errored += 1;
1132
+ } else {
1133
+ pending += 1;
1134
+ }
1135
+ }
1136
+ const progress = total > 0 ? uploaded / total : 0;
1137
+ const remaining = total - uploaded;
1138
+ const eta = speed > 0 ? remaining / speed : Infinity;
1139
+ return { total, uploaded, progress, speed, eta, uploading, completed, errored, pending };
1140
+ }, [files]);
1141
+ }
1142
+ var TEXT_PREVIEW_MAX_BYTES = 64 * 1024;
1143
+ function useFilePreview(file) {
1144
+ const [state, setState] = useState({
1145
+ kind: file ? getFileCategory(file) : "other",
1146
+ loading: !!file
1147
+ });
1148
+ useEffect(() => {
1149
+ if (!file) {
1150
+ setState({ kind: "other", loading: false });
1151
+ return;
1152
+ }
1153
+ const kind = getFileCategory(file);
1154
+ if (kind === "text") {
1155
+ let cancelled = false;
1156
+ setState({ kind, loading: true });
1157
+ const slice = file.file.slice(0, TEXT_PREVIEW_MAX_BYTES);
1158
+ slice.text().then((text) => {
1159
+ if (!cancelled) setState({ kind, text, loading: false });
1160
+ }).catch((err) => {
1161
+ if (!cancelled) {
1162
+ setState({
1163
+ kind,
1164
+ loading: false,
1165
+ error: err instanceof Error ? err.message : "Failed to read file"
1166
+ });
1167
+ }
1168
+ });
1169
+ return () => {
1170
+ cancelled = true;
1171
+ };
1172
+ }
1173
+ setState({ kind, url: file.previewUrl, loading: false });
1174
+ return;
1175
+ }, [file]);
1176
+ return state;
1177
+ }
1178
+
1179
+ // src/i18n/translations.ts
1180
+ var en = {
1181
+ dropHere: "Drop files here",
1182
+ dragOrClick: "Drag & drop files or click to browse",
1183
+ browse: "Browse files",
1184
+ uploading: "Uploading",
1185
+ paused: "Paused",
1186
+ success: "Uploaded",
1187
+ error: "Failed",
1188
+ retry: "Retry",
1189
+ pause: "Pause",
1190
+ resume: "Resume",
1191
+ cancel: "Cancel",
1192
+ remove: "Remove",
1193
+ removeAll: "Remove all",
1194
+ preview: "Preview",
1195
+ download: "Download",
1196
+ edit: "Edit",
1197
+ save: "Save",
1198
+ fullscreen: "Fullscreen",
1199
+ rename: "Rename",
1200
+ tags: "Tags",
1201
+ description: "Description",
1202
+ category: "Category",
1203
+ addTag: "Add tag",
1204
+ fileTooLarge: "File is too large",
1205
+ fileTooSmall: "File is too small",
1206
+ invalidType: "File type not allowed",
1207
+ tooManyFiles: "Too many files",
1208
+ duplicateFile: "Duplicate file",
1209
+ speed: "Speed",
1210
+ eta: "ETA",
1211
+ ofText: "of",
1212
+ filesText: "files",
1213
+ bytes: "B",
1214
+ kb: "KB",
1215
+ mb: "MB",
1216
+ gb: "GB"
1217
+ };
1218
+ var hi = {
1219
+ dropHere: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u092F\u0939\u093E\u0901 \u091B\u094B\u0921\u093C\u0947\u0902",
1220
+ dragOrClick: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u0916\u0940\u0902\u091A\u0947\u0902 \u0914\u0930 \u091B\u094B\u0921\u093C\u0947\u0902 \u092F\u093E \u092C\u094D\u0930\u093E\u0909\u091C\u093C \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0915\u094D\u0932\u093F\u0915 \u0915\u0930\u0947\u0902",
1221
+ browse: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u092C\u094D\u0930\u093E\u0909\u091C\u093C \u0915\u0930\u0947\u0902",
1222
+ uploading: "\u0905\u092A\u0932\u094B\u0921 \u0939\u094B \u0930\u0939\u093E \u0939\u0948",
1223
+ paused: "\u0930\u0941\u0915\u093E \u0939\u0941\u0906",
1224
+ success: "\u0905\u092A\u0932\u094B\u0921 \u0939\u094B \u0917\u092F\u093E",
1225
+ error: "\u0935\u093F\u092B\u0932",
1226
+ retry: "\u092A\u0941\u0928\u0903 \u092A\u094D\u0930\u092F\u093E\u0938",
1227
+ pause: "\u0930\u094B\u0915\u0947\u0902",
1228
+ resume: "\u091C\u093E\u0930\u0940 \u0930\u0916\u0947\u0902",
1229
+ cancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
1230
+ remove: "\u0939\u091F\u093E\u090F\u0901",
1231
+ removeAll: "\u0938\u092D\u0940 \u0939\u091F\u093E\u090F\u0901",
1232
+ preview: "\u092A\u0942\u0930\u094D\u0935\u093E\u0935\u0932\u094B\u0915\u0928",
1233
+ download: "\u0921\u093E\u0909\u0928\u0932\u094B\u0921",
1234
+ edit: "\u0938\u0902\u092A\u093E\u0926\u093F\u0924 \u0915\u0930\u0947\u0902",
1235
+ save: "\u0938\u0939\u0947\u091C\u0947\u0902",
1236
+ fullscreen: "\u092A\u0942\u0930\u094D\u0923 \u0938\u094D\u0915\u094D\u0930\u0940\u0928",
1237
+ rename: "\u0928\u093E\u092E \u092C\u0926\u0932\u0947\u0902",
1238
+ tags: "\u091F\u0948\u0917",
1239
+ description: "\u0935\u093F\u0935\u0930\u0923",
1240
+ category: "\u0936\u094D\u0930\u0947\u0923\u0940",
1241
+ addTag: "\u091F\u0948\u0917 \u091C\u094B\u0921\u093C\u0947\u0902",
1242
+ fileTooLarge: "\u092B\u093C\u093E\u0907\u0932 \u092C\u0939\u0941\u0924 \u092C\u0921\u093C\u0940 \u0939\u0948",
1243
+ fileTooSmall: "\u092B\u093C\u093E\u0907\u0932 \u092C\u0939\u0941\u0924 \u091B\u094B\u091F\u0940 \u0939\u0948",
1244
+ invalidType: "\u092B\u093C\u093E\u0907\u0932 \u092A\u094D\u0930\u0915\u093E\u0930 \u0905\u0928\u0941\u092E\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948",
1245
+ tooManyFiles: "\u092C\u0939\u0941\u0924 \u0938\u0940 \u092B\u093C\u093E\u0907\u0932\u0947\u0902",
1246
+ duplicateFile: "\u0921\u0941\u092A\u094D\u0932\u093F\u0915\u0947\u091F \u092B\u093C\u093E\u0907\u0932",
1247
+ speed: "\u0917\u0924\u093F",
1248
+ eta: "\u0905\u0928\u0941\u092E\u093E\u0928\u093F\u0924 \u0938\u092E\u092F",
1249
+ ofText: "\u092E\u0947\u0902 \u0938\u0947",
1250
+ filesText: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902",
1251
+ bytes: "B",
1252
+ kb: "KB",
1253
+ mb: "MB",
1254
+ gb: "GB"
1255
+ };
1256
+ var gu = {
1257
+ dropHere: "\u0AAB\u0ABE\u0A87\u0AB2\u0ACB \u0A85\u0AB9\u0AC0\u0A82 \u0AAE\u0AC2\u0A95\u0ACB",
1258
+ dragOrClick: "\u0AAB\u0ABE\u0A87\u0AB2\u0ACB \u0A96\u0AC7\u0A82\u0A9A\u0AC0\u0AA8\u0AC7 \u0AAE\u0AC2\u0A95\u0ACB \u0A85\u0AA5\u0AB5\u0ABE \u0AAC\u0ACD\u0AB0\u0ABE\u0A89\u0A9D \u0A95\u0AB0\u0AB5\u0ABE \u0AAE\u0ABE\u0A9F\u0AC7 \u0A95\u0ACD\u0AB2\u0ABF\u0A95 \u0A95\u0AB0\u0ACB",
1259
+ browse: "\u0AAB\u0ABE\u0A87\u0AB2\u0ACB \u0AAC\u0ACD\u0AB0\u0ABE\u0A89\u0A9D \u0A95\u0AB0\u0ACB",
1260
+ uploading: "\u0A85\u0AAA\u0AB2\u0ACB\u0AA1 \u0AA5\u0A88 \u0AB0\u0AB9\u0ACD\u0AAF\u0AC1\u0A82 \u0A9B\u0AC7",
1261
+ paused: "\u0AA5\u0ACB\u0AAD\u0ABE\u0AB5\u0AC7\u0AB2\u0AC1\u0A82",
1262
+ success: "\u0A85\u0AAA\u0AB2\u0ACB\u0AA1 \u0AA5\u0A88 \u0A97\u0AAF\u0AC1\u0A82",
1263
+ error: "\u0AA8\u0ABF\u0AB7\u0ACD\u0AAB\u0AB3",
1264
+ retry: "\u0AAB\u0AB0\u0AC0 \u0AAA\u0ACD\u0AB0\u0AAF\u0ABE\u0AB8 \u0A95\u0AB0\u0ACB",
1265
+ pause: "\u0AA5\u0ACB\u0AAD\u0ACB",
1266
+ resume: "\u0AAB\u0AB0\u0AC0 \u0AB6\u0AB0\u0AC2 \u0A95\u0AB0\u0ACB",
1267
+ cancel: "\u0AB0\u0AA6 \u0A95\u0AB0\u0ACB",
1268
+ remove: "\u0AA6\u0AC2\u0AB0 \u0A95\u0AB0\u0ACB",
1269
+ removeAll: "\u0AAC\u0AA7\u0AC1\u0A82 \u0AA6\u0AC2\u0AB0 \u0A95\u0AB0\u0ACB",
1270
+ preview: "\u0AAA\u0AC2\u0AB0\u0ACD\u0AB5\u0ABE\u0AB5\u0AB2\u0ACB\u0A95\u0AA8",
1271
+ download: "\u0AA1\u0ABE\u0A89\u0AA8\u0AB2\u0ACB\u0AA1",
1272
+ edit: "\u0AB8\u0A82\u0AAA\u0ABE\u0AA6\u0ABF\u0AA4 \u0A95\u0AB0\u0ACB",
1273
+ save: "\u0AB8\u0ABE\u0A9A\u0AB5\u0ACB",
1274
+ fullscreen: "\u0AB8\u0A82\u0AAA\u0AC2\u0AB0\u0ACD\u0AA3 \u0AB8\u0ACD\u0A95\u0ACD\u0AB0\u0AC0\u0AA8",
1275
+ rename: "\u0AA8\u0ABE\u0AAE \u0AAC\u0AA6\u0AB2\u0ACB",
1276
+ tags: "\u0A9F\u0AC5\u0A97\u0ACD\u0AB8",
1277
+ description: "\u0AB5\u0AB0\u0ACD\u0AA3\u0AA8",
1278
+ category: "\u0AB6\u0ACD\u0AB0\u0AC7\u0AA3\u0AC0",
1279
+ addTag: "\u0A9F\u0AC5\u0A97 \u0A89\u0AAE\u0AC7\u0AB0\u0ACB",
1280
+ fileTooLarge: "\u0AAB\u0ABE\u0A87\u0AB2 \u0AAC\u0AB9\u0AC1 \u0AAE\u0ACB\u0A9F\u0AC0 \u0A9B\u0AC7",
1281
+ fileTooSmall: "\u0AAB\u0ABE\u0A87\u0AB2 \u0AAC\u0AB9\u0AC1 \u0AA8\u0ABE\u0AA8\u0AC0 \u0A9B\u0AC7",
1282
+ invalidType: "\u0AAB\u0ABE\u0A87\u0AB2 \u0AAA\u0ACD\u0AB0\u0A95\u0ABE\u0AB0 \u0AAE\u0A82\u0A9C\u0AC2\u0AB0 \u0AA8\u0AA5\u0AC0",
1283
+ tooManyFiles: "\u0A98\u0AA3\u0AC0 \u0AAC\u0AA7\u0AC0 \u0AAB\u0ABE\u0A87\u0AB2\u0ACB",
1284
+ duplicateFile: "\u0AA1\u0AC1\u0AAA\u0ACD\u0AB2\u0ABF\u0A95\u0AC7\u0A9F \u0AAB\u0ABE\u0A87\u0AB2",
1285
+ speed: "\u0A9D\u0AA1\u0AAA",
1286
+ eta: "\u0A85\u0A82\u0AA6\u0ABE\u0A9C\u0ABF\u0AA4 \u0AB8\u0AAE\u0AAF",
1287
+ ofText: "\u0AAE\u0ABE\u0A82\u0AA5\u0AC0",
1288
+ filesText: "\u0AAB\u0ABE\u0A87\u0AB2\u0ACB",
1289
+ bytes: "B",
1290
+ kb: "KB",
1291
+ mb: "MB",
1292
+ gb: "GB"
1293
+ };
1294
+ var fr = {
1295
+ dropHere: "D\xE9posez les fichiers ici",
1296
+ dragOrClick: "Glissez-d\xE9posez ou cliquez pour parcourir",
1297
+ browse: "Parcourir",
1298
+ uploading: "Envoi en cours",
1299
+ paused: "En pause",
1300
+ success: "Envoy\xE9",
1301
+ error: "\xC9chec",
1302
+ retry: "R\xE9essayer",
1303
+ pause: "Pause",
1304
+ resume: "Reprendre",
1305
+ cancel: "Annuler",
1306
+ remove: "Supprimer",
1307
+ removeAll: "Tout supprimer",
1308
+ preview: "Aper\xE7u",
1309
+ download: "T\xE9l\xE9charger",
1310
+ edit: "Modifier",
1311
+ save: "Enregistrer",
1312
+ fullscreen: "Plein \xE9cran",
1313
+ rename: "Renommer",
1314
+ tags: "\xC9tiquettes",
1315
+ description: "Description",
1316
+ category: "Cat\xE9gorie",
1317
+ addTag: "Ajouter une \xE9tiquette",
1318
+ fileTooLarge: "Fichier trop volumineux",
1319
+ fileTooSmall: "Fichier trop petit",
1320
+ invalidType: "Type de fichier non autoris\xE9",
1321
+ tooManyFiles: "Trop de fichiers",
1322
+ duplicateFile: "Fichier en double",
1323
+ speed: "Vitesse",
1324
+ eta: "Restant",
1325
+ ofText: "sur",
1326
+ filesText: "fichiers",
1327
+ bytes: "o",
1328
+ kb: "Ko",
1329
+ mb: "Mo",
1330
+ gb: "Go"
1331
+ };
1332
+ var de = {
1333
+ dropHere: "Dateien hier ablegen",
1334
+ dragOrClick: "Dateien ablegen oder klicken zum Ausw\xE4hlen",
1335
+ browse: "Dateien ausw\xE4hlen",
1336
+ uploading: "Wird hochgeladen",
1337
+ paused: "Angehalten",
1338
+ success: "Hochgeladen",
1339
+ error: "Fehler",
1340
+ retry: "Erneut versuchen",
1341
+ pause: "Pause",
1342
+ resume: "Fortsetzen",
1343
+ cancel: "Abbrechen",
1344
+ remove: "Entfernen",
1345
+ removeAll: "Alle entfernen",
1346
+ preview: "Vorschau",
1347
+ download: "Herunterladen",
1348
+ edit: "Bearbeiten",
1349
+ save: "Speichern",
1350
+ fullscreen: "Vollbild",
1351
+ rename: "Umbenennen",
1352
+ tags: "Tags",
1353
+ description: "Beschreibung",
1354
+ category: "Kategorie",
1355
+ addTag: "Tag hinzuf\xFCgen",
1356
+ fileTooLarge: "Datei ist zu gro\xDF",
1357
+ fileTooSmall: "Datei ist zu klein",
1358
+ invalidType: "Dateityp nicht zul\xE4ssig",
1359
+ tooManyFiles: "Zu viele Dateien",
1360
+ duplicateFile: "Doppelte Datei",
1361
+ speed: "Geschwindigkeit",
1362
+ eta: "Verbleibend",
1363
+ ofText: "von",
1364
+ filesText: "Dateien",
1365
+ bytes: "B",
1366
+ kb: "KB",
1367
+ mb: "MB",
1368
+ gb: "GB"
1369
+ };
1370
+ var ar = {
1371
+ dropHere: "\u0623\u0641\u0644\u062A \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0647\u0646\u0627",
1372
+ dragOrClick: "\u0627\u0633\u062D\u0628 \u0648\u0623\u0641\u0644\u062A \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0623\u0648 \u0627\u0646\u0642\u0631 \u0644\u0644\u062A\u0635\u0641\u062D",
1373
+ browse: "\u062A\u0635\u0641\u062D \u0627\u0644\u0645\u0644\u0641\u0627\u062A",
1374
+ uploading: "\u062C\u0627\u0631\u064A \u0627\u0644\u0631\u0641\u0639",
1375
+ paused: "\u0645\u062A\u0648\u0642\u0641",
1376
+ success: "\u062A\u0645 \u0627\u0644\u0631\u0641\u0639",
1377
+ error: "\u0641\u0634\u0644",
1378
+ retry: "\u0625\u0639\u0627\u062F\u0629",
1379
+ pause: "\u0625\u064A\u0642\u0627\u0641",
1380
+ resume: "\u0627\u0633\u062A\u0626\u0646\u0627\u0641",
1381
+ cancel: "\u0625\u0644\u063A\u0627\u0621",
1382
+ remove: "\u0625\u0632\u0627\u0644\u0629",
1383
+ removeAll: "\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0643\u0644",
1384
+ preview: "\u0645\u0639\u0627\u064A\u0646\u0629",
1385
+ download: "\u062A\u0646\u0632\u064A\u0644",
1386
+ edit: "\u062A\u062D\u0631\u064A\u0631",
1387
+ save: "\u062D\u0641\u0638",
1388
+ fullscreen: "\u0645\u0644\u0621 \u0627\u0644\u0634\u0627\u0634\u0629",
1389
+ rename: "\u0625\u0639\u0627\u062F\u0629 \u062A\u0633\u0645\u064A\u0629",
1390
+ tags: "\u0639\u0644\u0627\u0645\u0627\u062A",
1391
+ description: "\u0627\u0644\u0648\u0635\u0641",
1392
+ category: "\u0627\u0644\u0641\u0626\u0629",
1393
+ addTag: "\u0625\u0636\u0627\u0641\u0629 \u0639\u0644\u0627\u0645\u0629",
1394
+ fileTooLarge: "\u0627\u0644\u0645\u0644\u0641 \u0643\u0628\u064A\u0631 \u062C\u062F\u064B\u0627",
1395
+ fileTooSmall: "\u0627\u0644\u0645\u0644\u0641 \u0635\u063A\u064A\u0631 \u062C\u062F\u064B\u0627",
1396
+ invalidType: "\u0646\u0648\u0639 \u0627\u0644\u0645\u0644\u0641 \u063A\u064A\u0631 \u0645\u0633\u0645\u0648\u062D",
1397
+ tooManyFiles: "\u0627\u0644\u0643\u062B\u064A\u0631 \u0645\u0646 \u0627\u0644\u0645\u0644\u0641\u0627\u062A",
1398
+ duplicateFile: "\u0645\u0644\u0641 \u0645\u0643\u0631\u0631",
1399
+ speed: "\u0627\u0644\u0633\u0631\u0639\u0629",
1400
+ eta: "\u0627\u0644\u0648\u0642\u062A \u0627\u0644\u0645\u062A\u0628\u0642\u064A",
1401
+ ofText: "\u0645\u0646",
1402
+ filesText: "\u0645\u0644\u0641\u0627\u062A",
1403
+ bytes: "\u0628\u0627\u064A\u062A",
1404
+ kb: "\u0643.\u0628",
1405
+ mb: "\u0645.\u0628",
1406
+ gb: "\u062C.\u0628"
1407
+ };
1408
+ var zh = {
1409
+ dropHere: "\u5C06\u6587\u4EF6\u62D6\u653E\u5230\u6B64\u5904",
1410
+ dragOrClick: "\u62D6\u653E\u6587\u4EF6\u6216\u70B9\u51FB\u6D4F\u89C8",
1411
+ browse: "\u6D4F\u89C8\u6587\u4EF6",
1412
+ uploading: "\u4E0A\u4F20\u4E2D",
1413
+ paused: "\u5DF2\u6682\u505C",
1414
+ success: "\u5DF2\u4E0A\u4F20",
1415
+ error: "\u5931\u8D25",
1416
+ retry: "\u91CD\u8BD5",
1417
+ pause: "\u6682\u505C",
1418
+ resume: "\u7EE7\u7EED",
1419
+ cancel: "\u53D6\u6D88",
1420
+ remove: "\u79FB\u9664",
1421
+ removeAll: "\u5168\u90E8\u79FB\u9664",
1422
+ preview: "\u9884\u89C8",
1423
+ download: "\u4E0B\u8F7D",
1424
+ edit: "\u7F16\u8F91",
1425
+ save: "\u4FDD\u5B58",
1426
+ fullscreen: "\u5168\u5C4F",
1427
+ rename: "\u91CD\u547D\u540D",
1428
+ tags: "\u6807\u7B7E",
1429
+ description: "\u63CF\u8FF0",
1430
+ category: "\u7C7B\u522B",
1431
+ addTag: "\u6DFB\u52A0\u6807\u7B7E",
1432
+ fileTooLarge: "\u6587\u4EF6\u8FC7\u5927",
1433
+ fileTooSmall: "\u6587\u4EF6\u8FC7\u5C0F",
1434
+ invalidType: "\u4E0D\u5141\u8BB8\u7684\u6587\u4EF6\u7C7B\u578B",
1435
+ tooManyFiles: "\u6587\u4EF6\u8FC7\u591A",
1436
+ duplicateFile: "\u91CD\u590D\u6587\u4EF6",
1437
+ speed: "\u901F\u5EA6",
1438
+ eta: "\u5269\u4F59\u65F6\u95F4",
1439
+ ofText: "/",
1440
+ filesText: "\u4E2A\u6587\u4EF6",
1441
+ bytes: "B",
1442
+ kb: "KB",
1443
+ mb: "MB",
1444
+ gb: "GB"
1445
+ };
1446
+ var es = {
1447
+ dropHere: "Suelta los archivos aqu\xED",
1448
+ dragOrClick: "Arrastra y suelta archivos o haz clic para explorar",
1449
+ browse: "Explorar archivos",
1450
+ uploading: "Subiendo",
1451
+ paused: "Pausado",
1452
+ success: "Subido",
1453
+ error: "Error",
1454
+ retry: "Reintentar",
1455
+ pause: "Pausar",
1456
+ resume: "Reanudar",
1457
+ cancel: "Cancelar",
1458
+ remove: "Eliminar",
1459
+ removeAll: "Eliminar todo",
1460
+ preview: "Vista previa",
1461
+ download: "Descargar",
1462
+ edit: "Editar",
1463
+ save: "Guardar",
1464
+ fullscreen: "Pantalla completa",
1465
+ rename: "Renombrar",
1466
+ tags: "Etiquetas",
1467
+ description: "Descripci\xF3n",
1468
+ category: "Categor\xEDa",
1469
+ addTag: "A\xF1adir etiqueta",
1470
+ fileTooLarge: "El archivo es demasiado grande",
1471
+ fileTooSmall: "El archivo es demasiado peque\xF1o",
1472
+ invalidType: "Tipo de archivo no permitido",
1473
+ tooManyFiles: "Demasiados archivos",
1474
+ duplicateFile: "Archivo duplicado",
1475
+ speed: "Velocidad",
1476
+ eta: "Restante",
1477
+ ofText: "de",
1478
+ filesText: "archivos",
1479
+ bytes: "B",
1480
+ kb: "KB",
1481
+ mb: "MB",
1482
+ gb: "GB"
1483
+ };
1484
+ var pt = {
1485
+ dropHere: "Solte os arquivos aqui",
1486
+ dragOrClick: "Arraste e solte arquivos ou clique para procurar",
1487
+ browse: "Procurar arquivos",
1488
+ uploading: "Enviando",
1489
+ paused: "Pausado",
1490
+ success: "Enviado",
1491
+ error: "Falhou",
1492
+ retry: "Tentar novamente",
1493
+ pause: "Pausar",
1494
+ resume: "Retomar",
1495
+ cancel: "Cancelar",
1496
+ remove: "Remover",
1497
+ removeAll: "Remover todos",
1498
+ preview: "Pr\xE9-visualizar",
1499
+ download: "Baixar",
1500
+ edit: "Editar",
1501
+ save: "Salvar",
1502
+ fullscreen: "Tela cheia",
1503
+ rename: "Renomear",
1504
+ tags: "Etiquetas",
1505
+ description: "Descri\xE7\xE3o",
1506
+ category: "Categoria",
1507
+ addTag: "Adicionar etiqueta",
1508
+ fileTooLarge: "Arquivo muito grande",
1509
+ fileTooSmall: "Arquivo muito pequeno",
1510
+ invalidType: "Tipo de arquivo n\xE3o permitido",
1511
+ tooManyFiles: "Muitos arquivos",
1512
+ duplicateFile: "Arquivo duplicado",
1513
+ speed: "Velocidade",
1514
+ eta: "Restante",
1515
+ ofText: "de",
1516
+ filesText: "arquivos",
1517
+ bytes: "B",
1518
+ kb: "KB",
1519
+ mb: "MB",
1520
+ gb: "GB"
1521
+ };
1522
+ var ru = {
1523
+ dropHere: "\u041F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B \u0441\u044E\u0434\u0430",
1524
+ dragOrClick: "\u041F\u0435\u0440\u0435\u0442\u0430\u0449\u0438\u0442\u0435 \u0444\u0430\u0439\u043B\u044B \u0438\u043B\u0438 \u043D\u0430\u0436\u043C\u0438\u0442\u0435 \u0434\u043B\u044F \u0432\u044B\u0431\u043E\u0440\u0430",
1525
+ browse: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0444\u0430\u0439\u043B\u044B",
1526
+ uploading: "\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430",
1527
+ paused: "\u041F\u0440\u0438\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E",
1528
+ success: "\u0417\u0430\u0433\u0440\u0443\u0436\u0435\u043D\u043E",
1529
+ error: "\u041E\u0448\u0438\u0431\u043A\u0430",
1530
+ retry: "\u041F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C",
1531
+ pause: "\u041F\u0430\u0443\u0437\u0430",
1532
+ resume: "\u041F\u0440\u043E\u0434\u043E\u043B\u0436\u0438\u0442\u044C",
1533
+ cancel: "\u041E\u0442\u043C\u0435\u043D\u0430",
1534
+ remove: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C",
1535
+ removeAll: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u0441\u0435",
1536
+ preview: "\u041F\u0440\u0435\u0434\u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440",
1537
+ download: "\u0421\u043A\u0430\u0447\u0430\u0442\u044C",
1538
+ edit: "\u0420\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
1539
+ save: "\u0421\u043E\u0445\u0440\u0430\u043D\u0438\u0442\u044C",
1540
+ fullscreen: "\u041F\u043E\u043B\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D",
1541
+ rename: "\u041F\u0435\u0440\u0435\u0438\u043C\u0435\u043D\u043E\u0432\u0430\u0442\u044C",
1542
+ tags: "\u0422\u0435\u0433\u0438",
1543
+ description: "\u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435",
1544
+ category: "\u041A\u0430\u0442\u0435\u0433\u043E\u0440\u0438\u044F",
1545
+ addTag: "\u0414\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0442\u0435\u0433",
1546
+ fileTooLarge: "\u0424\u0430\u0439\u043B \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u0431\u043E\u043B\u044C\u0448\u043E\u0439",
1547
+ fileTooSmall: "\u0424\u0430\u0439\u043B \u0441\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0439",
1548
+ invalidType: "\u0422\u0438\u043F \u0444\u0430\u0439\u043B\u0430 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0451\u043D",
1549
+ tooManyFiles: "\u0421\u043B\u0438\u0448\u043A\u043E\u043C \u043C\u043D\u043E\u0433\u043E \u0444\u0430\u0439\u043B\u043E\u0432",
1550
+ duplicateFile: "\u0414\u0443\u0431\u043B\u0438\u0440\u0443\u044E\u0449\u0438\u0439 \u0444\u0430\u0439\u043B",
1551
+ speed: "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C",
1552
+ eta: "\u041E\u0441\u0442\u0430\u043B\u043E\u0441\u044C",
1553
+ ofText: "\u0438\u0437",
1554
+ filesText: "\u0444\u0430\u0439\u043B\u043E\u0432",
1555
+ bytes: "\u0411",
1556
+ kb: "\u041A\u0411",
1557
+ mb: "\u041C\u0411",
1558
+ gb: "\u0413\u0411"
1559
+ };
1560
+ var ja = {
1561
+ dropHere: "\u3053\u3053\u306B\u30D5\u30A1\u30A4\u30EB\u3092\u30C9\u30ED\u30C3\u30D7",
1562
+ dragOrClick: "\u30D5\u30A1\u30A4\u30EB\u3092\u30C9\u30E9\u30C3\u30B0\uFF06\u30C9\u30ED\u30C3\u30D7\u307E\u305F\u306F\u30AF\u30EA\u30C3\u30AF\u3057\u3066\u9078\u629E",
1563
+ browse: "\u30D5\u30A1\u30A4\u30EB\u3092\u9078\u629E",
1564
+ uploading: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D",
1565
+ paused: "\u4E00\u6642\u505C\u6B62",
1566
+ success: "\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u5B8C\u4E86",
1567
+ error: "\u5931\u6557",
1568
+ retry: "\u518D\u8A66\u884C",
1569
+ pause: "\u4E00\u6642\u505C\u6B62",
1570
+ resume: "\u518D\u958B",
1571
+ cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB",
1572
+ remove: "\u524A\u9664",
1573
+ removeAll: "\u3059\u3079\u3066\u524A\u9664",
1574
+ preview: "\u30D7\u30EC\u30D3\u30E5\u30FC",
1575
+ download: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9",
1576
+ edit: "\u7DE8\u96C6",
1577
+ save: "\u4FDD\u5B58",
1578
+ fullscreen: "\u5168\u753B\u9762",
1579
+ rename: "\u540D\u524D\u3092\u5909\u66F4",
1580
+ tags: "\u30BF\u30B0",
1581
+ description: "\u8AAC\u660E",
1582
+ category: "\u30AB\u30C6\u30B4\u30EA\u30FC",
1583
+ addTag: "\u30BF\u30B0\u3092\u8FFD\u52A0",
1584
+ fileTooLarge: "\u30D5\u30A1\u30A4\u30EB\u304C\u5927\u304D\u3059\u304E\u307E\u3059",
1585
+ fileTooSmall: "\u30D5\u30A1\u30A4\u30EB\u304C\u5C0F\u3055\u3059\u304E\u307E\u3059",
1586
+ invalidType: "\u8A31\u53EF\u3055\u308C\u3066\u3044\u306A\u3044\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F",
1587
+ tooManyFiles: "\u30D5\u30A1\u30A4\u30EB\u6570\u304C\u591A\u3059\u304E\u307E\u3059",
1588
+ duplicateFile: "\u91CD\u8907\u30D5\u30A1\u30A4\u30EB",
1589
+ speed: "\u901F\u5EA6",
1590
+ eta: "\u6B8B\u308A\u6642\u9593",
1591
+ ofText: "/",
1592
+ filesText: "\u30D5\u30A1\u30A4\u30EB",
1593
+ bytes: "B",
1594
+ kb: "KB",
1595
+ mb: "MB",
1596
+ gb: "GB"
1597
+ };
1598
+ var ko = {
1599
+ dropHere: "\uC5EC\uAE30\uC5D0 \uD30C\uC77C\uC744 \uB193\uC73C\uC138\uC694",
1600
+ dragOrClick: "\uD30C\uC77C\uC744 \uB04C\uC5B4\uB2E4 \uB193\uAC70\uB098 \uD074\uB9AD\uD558\uC5EC \uC120\uD0DD",
1601
+ browse: "\uD30C\uC77C \uCC3E\uC544\uBCF4\uAE30",
1602
+ uploading: "\uC5C5\uB85C\uB4DC \uC911",
1603
+ paused: "\uC77C\uC2DC \uC911\uC9C0\uB428",
1604
+ success: "\uC5C5\uB85C\uB4DC\uB428",
1605
+ error: "\uC2E4\uD328",
1606
+ retry: "\uB2E4\uC2DC \uC2DC\uB3C4",
1607
+ pause: "\uC77C\uC2DC \uC911\uC9C0",
1608
+ resume: "\uC7AC\uAC1C",
1609
+ cancel: "\uCDE8\uC18C",
1610
+ remove: "\uC81C\uAC70",
1611
+ removeAll: "\uBAA8\uB450 \uC81C\uAC70",
1612
+ preview: "\uBBF8\uB9AC\uBCF4\uAE30",
1613
+ download: "\uB2E4\uC6B4\uB85C\uB4DC",
1614
+ edit: "\uD3B8\uC9D1",
1615
+ save: "\uC800\uC7A5",
1616
+ fullscreen: "\uC804\uCCB4 \uD654\uBA74",
1617
+ rename: "\uC774\uB984 \uBCC0\uACBD",
1618
+ tags: "\uD0DC\uADF8",
1619
+ description: "\uC124\uBA85",
1620
+ category: "\uCE74\uD14C\uACE0\uB9AC",
1621
+ addTag: "\uD0DC\uADF8 \uCD94\uAC00",
1622
+ fileTooLarge: "\uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4",
1623
+ fileTooSmall: "\uD30C\uC77C\uC774 \uB108\uBB34 \uC791\uC2B5\uB2C8\uB2E4",
1624
+ invalidType: "\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD",
1625
+ tooManyFiles: "\uD30C\uC77C\uC774 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4",
1626
+ duplicateFile: "\uC911\uBCF5 \uD30C\uC77C",
1627
+ speed: "\uC18D\uB3C4",
1628
+ eta: "\uB0A8\uC740 \uC2DC\uAC04",
1629
+ ofText: "/",
1630
+ filesText: "\uAC1C \uD30C\uC77C",
1631
+ bytes: "B",
1632
+ kb: "KB",
1633
+ mb: "MB",
1634
+ gb: "GB"
1635
+ };
1636
+ var it = {
1637
+ dropHere: "Trascina i file qui",
1638
+ dragOrClick: "Trascina i file o clicca per sfogliare",
1639
+ browse: "Sfoglia file",
1640
+ uploading: "Caricamento in corso",
1641
+ paused: "In pausa",
1642
+ success: "Caricato",
1643
+ error: "Errore",
1644
+ retry: "Riprova",
1645
+ pause: "Pausa",
1646
+ resume: "Riprendi",
1647
+ cancel: "Annulla",
1648
+ remove: "Rimuovi",
1649
+ removeAll: "Rimuovi tutto",
1650
+ preview: "Anteprima",
1651
+ download: "Scarica",
1652
+ edit: "Modifica",
1653
+ save: "Salva",
1654
+ fullscreen: "Schermo intero",
1655
+ rename: "Rinomina",
1656
+ tags: "Etichette",
1657
+ description: "Descrizione",
1658
+ category: "Categoria",
1659
+ addTag: "Aggiungi etichetta",
1660
+ fileTooLarge: "File troppo grande",
1661
+ fileTooSmall: "File troppo piccolo",
1662
+ invalidType: "Tipo di file non consentito",
1663
+ tooManyFiles: "Troppi file",
1664
+ duplicateFile: "File duplicato",
1665
+ speed: "Velocit\xE0",
1666
+ eta: "Rimanente",
1667
+ ofText: "di",
1668
+ filesText: "file",
1669
+ bytes: "B",
1670
+ kb: "KB",
1671
+ mb: "MB",
1672
+ gb: "GB"
1673
+ };
1674
+ var tr = {
1675
+ dropHere: "Dosyalar\u0131 buraya b\u0131rak\u0131n",
1676
+ dragOrClick: "Dosyalar\u0131 s\xFCr\xFCkleyip b\u0131rak\u0131n veya g\xF6zatmak i\xE7in t\u0131klay\u0131n",
1677
+ browse: "Dosyalara g\xF6zat",
1678
+ uploading: "Y\xFCkleniyor",
1679
+ paused: "Duraklat\u0131ld\u0131",
1680
+ success: "Y\xFCklendi",
1681
+ error: "Ba\u015Far\u0131s\u0131z",
1682
+ retry: "Tekrar dene",
1683
+ pause: "Duraklat",
1684
+ resume: "Devam et",
1685
+ cancel: "\u0130ptal",
1686
+ remove: "Kald\u0131r",
1687
+ removeAll: "T\xFCm\xFCn\xFC kald\u0131r",
1688
+ preview: "\xD6nizleme",
1689
+ download: "\u0130ndir",
1690
+ edit: "D\xFCzenle",
1691
+ save: "Kaydet",
1692
+ fullscreen: "Tam ekran",
1693
+ rename: "Yeniden adland\u0131r",
1694
+ tags: "Etiketler",
1695
+ description: "A\xE7\u0131klama",
1696
+ category: "Kategori",
1697
+ addTag: "Etiket ekle",
1698
+ fileTooLarge: "Dosya \xE7ok b\xFCy\xFCk",
1699
+ fileTooSmall: "Dosya \xE7ok k\xFC\xE7\xFCk",
1700
+ invalidType: "Dosya t\xFCr\xFCne izin verilmiyor",
1701
+ tooManyFiles: "\xC7ok fazla dosya",
1702
+ duplicateFile: "Yinelenen dosya",
1703
+ speed: "H\u0131z",
1704
+ eta: "Kalan s\xFCre",
1705
+ ofText: "/",
1706
+ filesText: "dosya",
1707
+ bytes: "B",
1708
+ kb: "KB",
1709
+ mb: "MB",
1710
+ gb: "GB"
1711
+ };
1712
+ var id = {
1713
+ dropHere: "Letakkan file di sini",
1714
+ dragOrClick: "Seret & lepas file atau klik untuk menjelajah",
1715
+ browse: "Jelajahi file",
1716
+ uploading: "Mengunggah",
1717
+ paused: "Dijeda",
1718
+ success: "Diunggah",
1719
+ error: "Gagal",
1720
+ retry: "Coba lagi",
1721
+ pause: "Jeda",
1722
+ resume: "Lanjutkan",
1723
+ cancel: "Batal",
1724
+ remove: "Hapus",
1725
+ removeAll: "Hapus semua",
1726
+ preview: "Pratinjau",
1727
+ download: "Unduh",
1728
+ edit: "Ubah",
1729
+ save: "Simpan",
1730
+ fullscreen: "Layar penuh",
1731
+ rename: "Ganti nama",
1732
+ tags: "Tag",
1733
+ description: "Deskripsi",
1734
+ category: "Kategori",
1735
+ addTag: "Tambah tag",
1736
+ fileTooLarge: "File terlalu besar",
1737
+ fileTooSmall: "File terlalu kecil",
1738
+ invalidType: "Tipe file tidak diizinkan",
1739
+ tooManyFiles: "Terlalu banyak file",
1740
+ duplicateFile: "File duplikat",
1741
+ speed: "Kecepatan",
1742
+ eta: "Sisa waktu",
1743
+ ofText: "dari",
1744
+ filesText: "file",
1745
+ bytes: "B",
1746
+ kb: "KB",
1747
+ mb: "MB",
1748
+ gb: "GB"
1749
+ };
1750
+ var bn = {
1751
+ dropHere: "\u098F\u0996\u09BE\u09A8\u09C7 \u09AB\u09BE\u0987\u09B2 \u099B\u09BE\u09A1\u09BC\u09C1\u09A8",
1752
+ dragOrClick: "\u09AB\u09BE\u0987\u09B2 \u099F\u09C7\u09A8\u09C7 \u0986\u09A8\u09C1\u09A8 \u09AC\u09BE \u09AC\u09CD\u09B0\u09BE\u0989\u099C \u0995\u09B0\u09A4\u09C7 \u0995\u09CD\u09B2\u09BF\u0995 \u0995\u09B0\u09C1\u09A8",
1753
+ browse: "\u09AB\u09BE\u0987\u09B2 \u09AC\u09CD\u09B0\u09BE\u0989\u099C \u0995\u09B0\u09C1\u09A8",
1754
+ uploading: "\u0986\u09AA\u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7",
1755
+ paused: "\u09AC\u09BF\u09B0\u09A4\u09BF",
1756
+ success: "\u0986\u09AA\u09B2\u09CB\u09A1 \u09B9\u09AF\u09BC\u09C7\u099B\u09C7",
1757
+ error: "\u09AC\u09CD\u09AF\u09B0\u09CD\u09A5",
1758
+ retry: "\u09AA\u09C1\u09A8\u09B0\u09BE\u09AF\u09BC \u099A\u09C7\u09B7\u09CD\u099F\u09BE",
1759
+ pause: "\u09AC\u09BF\u09B0\u09A4\u09BF",
1760
+ resume: "\u09AA\u09C1\u09A8\u09B0\u09BE\u09AF\u09BC \u09B6\u09C1\u09B0\u09C1",
1761
+ cancel: "\u09AC\u09BE\u09A4\u09BF\u09B2",
1762
+ remove: "\u09B8\u09B0\u09BE\u09A8",
1763
+ removeAll: "\u09B8\u09AC \u09B8\u09B0\u09BE\u09A8",
1764
+ preview: "\u09AA\u09C2\u09B0\u09CD\u09AC\u09B0\u09C2\u09AA",
1765
+ download: "\u09A1\u09BE\u0989\u09A8\u09B2\u09CB\u09A1",
1766
+ edit: "\u09B8\u09AE\u09CD\u09AA\u09BE\u09A6\u09A8\u09BE",
1767
+ save: "\u09B8\u0982\u09B0\u0995\u09CD\u09B7\u09A3",
1768
+ fullscreen: "\u09AA\u09C2\u09B0\u09CD\u09A3 \u09B8\u09CD\u0995\u09CD\u09B0\u09BF\u09A8",
1769
+ rename: "\u09A8\u09BE\u09AE \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09A8",
1770
+ tags: "\u099F\u09CD\u09AF\u09BE\u0997",
1771
+ description: "\u09AC\u09BF\u09AC\u09B0\u09A3",
1772
+ category: "\u09AC\u09BF\u09AD\u09BE\u0997",
1773
+ addTag: "\u099F\u09CD\u09AF\u09BE\u0997 \u09AF\u09CB\u0997 \u0995\u09B0\u09C1\u09A8",
1774
+ fileTooLarge: "\u09AB\u09BE\u0987\u09B2\u099F\u09BF \u0996\u09C1\u09AC \u09AC\u09A1\u09BC",
1775
+ fileTooSmall: "\u09AB\u09BE\u0987\u09B2\u099F\u09BF \u0996\u09C1\u09AC \u099B\u09CB\u099F",
1776
+ invalidType: "\u09AB\u09BE\u0987\u09B2\u09C7\u09B0 \u09AA\u09CD\u09B0\u0995\u09BE\u09B0 \u0985\u09A8\u09C1\u09AE\u09CB\u09A6\u09BF\u09A4 \u09A8\u09AF\u09BC",
1777
+ tooManyFiles: "\u0985\u09A8\u09C7\u0995 \u09AC\u09C7\u09B6\u09BF \u09AB\u09BE\u0987\u09B2",
1778
+ duplicateFile: "\u09A8\u0995\u09B2 \u09AB\u09BE\u0987\u09B2",
1779
+ speed: "\u0997\u09A4\u09BF",
1780
+ eta: "\u09AC\u09BE\u0995\u09BF \u09B8\u09AE\u09AF\u09BC",
1781
+ ofText: "\u098F\u09B0",
1782
+ filesText: "\u09AB\u09BE\u0987\u09B2",
1783
+ bytes: "B",
1784
+ kb: "KB",
1785
+ mb: "MB",
1786
+ gb: "GB"
1787
+ };
1788
+ var ur = {
1789
+ dropHere: "\u0641\u0627\u0626\u0644\u06CC\u06BA \u06CC\u06C1\u0627\u06BA \u0686\u06BE\u0648\u0691\u06CC\u06BA",
1790
+ dragOrClick: "\u0641\u0627\u0626\u0644\u06CC\u06BA \u06AF\u06BE\u0633\u06CC\u0679\u06CC\u06BA \u0627\u0648\u0631 \u0686\u06BE\u0648\u0691\u06CC\u06BA \u06CC\u0627 \u0628\u0631\u0627\u0624\u0632 \u06A9\u0631\u0646\u06D2 \u06A9\u06D2 \u0644\u06CC\u06D2 \u06A9\u0644\u06A9 \u06A9\u0631\u06CC\u06BA",
1791
+ browse: "\u0641\u0627\u0626\u0644\u06CC\u06BA \u0628\u0631\u0627\u0624\u0632 \u06A9\u0631\u06CC\u06BA",
1792
+ uploading: "\u0627\u067E \u0644\u0648\u0688 \u06C1\u0648 \u0631\u06C1\u0627 \u06C1\u06D2",
1793
+ paused: "\u0631\u0648\u06A9 \u062F\u06CC\u0627 \u06AF\u06CC\u0627",
1794
+ success: "\u0627\u067E \u0644\u0648\u0688 \u06C1\u0648 \u06AF\u06CC\u0627",
1795
+ error: "\u0646\u0627\u06A9\u0627\u0645",
1796
+ retry: "\u062F\u0648\u0628\u0627\u0631\u06C1 \u06A9\u0648\u0634\u0634",
1797
+ pause: "\u0631\u0648\u06A9\u06CC\u06BA",
1798
+ resume: "\u062C\u0627\u0631\u06CC \u0631\u06A9\u06BE\u06CC\u06BA",
1799
+ cancel: "\u0645\u0646\u0633\u0648\u062E",
1800
+ remove: "\u06C1\u0679\u0627\u0626\u06CC\u06BA",
1801
+ removeAll: "\u0633\u0628 \u06C1\u0679\u0627\u0626\u06CC\u06BA",
1802
+ preview: "\u067E\u06CC\u0634 \u0646\u0638\u0627\u0631\u06C1",
1803
+ download: "\u0688\u0627\u0624\u0646 \u0644\u0648\u0688",
1804
+ edit: "\u062A\u0631\u0645\u06CC\u0645",
1805
+ save: "\u0645\u062D\u0641\u0648\u0638 \u06A9\u0631\u06CC\u06BA",
1806
+ fullscreen: "\u067E\u0648\u0631\u06CC \u0627\u0633\u06A9\u0631\u06CC\u0646",
1807
+ rename: "\u0646\u0627\u0645 \u062A\u0628\u062F\u06CC\u0644 \u06A9\u0631\u06CC\u06BA",
1808
+ tags: "\u0679\u06CC\u06AF\u0632",
1809
+ description: "\u062A\u0641\u0635\u06CC\u0644",
1810
+ category: "\u0632\u0645\u0631\u06C1",
1811
+ addTag: "\u0679\u06CC\u06AF \u0634\u0627\u0645\u0644 \u06A9\u0631\u06CC\u06BA",
1812
+ fileTooLarge: "\u0641\u0627\u0626\u0644 \u0628\u06C1\u062A \u0628\u0691\u06CC \u06C1\u06D2",
1813
+ fileTooSmall: "\u0641\u0627\u0626\u0644 \u0628\u06C1\u062A \u0686\u06BE\u0648\u0679\u06CC \u06C1\u06D2",
1814
+ invalidType: "\u0641\u0627\u0626\u0644 \u06A9\u06CC \u0642\u0633\u0645 \u06A9\u06CC \u0627\u062C\u0627\u0632\u062A \u0646\u06C1\u06CC\u06BA",
1815
+ tooManyFiles: "\u0628\u06C1\u062A \u0632\u06CC\u0627\u062F\u06C1 \u0641\u0627\u0626\u0644\u06CC\u06BA",
1816
+ duplicateFile: "\u0688\u067E\u0644\u06CC\u06A9\u06CC\u0679 \u0641\u0627\u0626\u0644",
1817
+ speed: "\u0631\u0641\u062A\u0627\u0631",
1818
+ eta: "\u0628\u0627\u0642\u06CC \u0648\u0642\u062A",
1819
+ ofText: "\u0645\u06CC\u06BA \u0633\u06D2",
1820
+ filesText: "\u0641\u0627\u0626\u0644\u06CC\u06BA",
1821
+ bytes: "B",
1822
+ kb: "KB",
1823
+ mb: "MB",
1824
+ gb: "GB"
1825
+ };
1826
+ var nl = {
1827
+ dropHere: "Zet bestanden hier neer",
1828
+ dragOrClick: "Sleep bestanden of klik om te bladeren",
1829
+ browse: "Bladeren",
1830
+ uploading: "Uploaden",
1831
+ paused: "Gepauzeerd",
1832
+ success: "Ge\xFCpload",
1833
+ error: "Mislukt",
1834
+ retry: "Opnieuw proberen",
1835
+ pause: "Pauze",
1836
+ resume: "Hervatten",
1837
+ cancel: "Annuleren",
1838
+ remove: "Verwijderen",
1839
+ removeAll: "Alles verwijderen",
1840
+ preview: "Voorbeeld",
1841
+ download: "Downloaden",
1842
+ edit: "Bewerken",
1843
+ save: "Opslaan",
1844
+ fullscreen: "Volledig scherm",
1845
+ rename: "Hernoemen",
1846
+ tags: "Tags",
1847
+ description: "Beschrijving",
1848
+ category: "Categorie",
1849
+ addTag: "Tag toevoegen",
1850
+ fileTooLarge: "Bestand is te groot",
1851
+ fileTooSmall: "Bestand is te klein",
1852
+ invalidType: "Bestandstype niet toegestaan",
1853
+ tooManyFiles: "Te veel bestanden",
1854
+ duplicateFile: "Dubbel bestand",
1855
+ speed: "Snelheid",
1856
+ eta: "Resterend",
1857
+ ofText: "van",
1858
+ filesText: "bestanden",
1859
+ bytes: "B",
1860
+ kb: "KB",
1861
+ mb: "MB",
1862
+ gb: "GB"
1863
+ };
1864
+ var pl = {
1865
+ dropHere: "Upu\u015B\u0107 pliki tutaj",
1866
+ dragOrClick: "Przeci\u0105gnij i upu\u015B\u0107 pliki lub kliknij, aby przegl\u0105da\u0107",
1867
+ browse: "Przegl\u0105daj pliki",
1868
+ uploading: "Przesy\u0142anie",
1869
+ paused: "Wstrzymane",
1870
+ success: "Przes\u0142ano",
1871
+ error: "B\u0142\u0105d",
1872
+ retry: "Pon\xF3w",
1873
+ pause: "Wstrzymaj",
1874
+ resume: "Wzn\xF3w",
1875
+ cancel: "Anuluj",
1876
+ remove: "Usu\u0144",
1877
+ removeAll: "Usu\u0144 wszystko",
1878
+ preview: "Podgl\u0105d",
1879
+ download: "Pobierz",
1880
+ edit: "Edytuj",
1881
+ save: "Zapisz",
1882
+ fullscreen: "Pe\u0142ny ekran",
1883
+ rename: "Zmie\u0144 nazw\u0119",
1884
+ tags: "Tagi",
1885
+ description: "Opis",
1886
+ category: "Kategoria",
1887
+ addTag: "Dodaj tag",
1888
+ fileTooLarge: "Plik jest za du\u017Cy",
1889
+ fileTooSmall: "Plik jest za ma\u0142y",
1890
+ invalidType: "Typ pliku niedozwolony",
1891
+ tooManyFiles: "Za du\u017Co plik\xF3w",
1892
+ duplicateFile: "Duplikat pliku",
1893
+ speed: "Pr\u0119dko\u015B\u0107",
1894
+ eta: "Pozosta\u0142o",
1895
+ ofText: "z",
1896
+ filesText: "plik\xF3w",
1897
+ bytes: "B",
1898
+ kb: "KB",
1899
+ mb: "MB",
1900
+ gb: "GB"
1901
+ };
1902
+ var vi = {
1903
+ dropHere: "Th\u1EA3 t\u1EC7p v\xE0o \u0111\xE2y",
1904
+ dragOrClick: "K\xE9o & th\u1EA3 t\u1EC7p ho\u1EB7c nh\u1EA5p \u0111\u1EC3 duy\u1EC7t",
1905
+ browse: "Duy\u1EC7t t\u1EC7p",
1906
+ uploading: "\u0110ang t\u1EA3i l\xEAn",
1907
+ paused: "\u0110\xE3 t\u1EA1m d\u1EEBng",
1908
+ success: "\u0110\xE3 t\u1EA3i l\xEAn",
1909
+ error: "Th\u1EA5t b\u1EA1i",
1910
+ retry: "Th\u1EED l\u1EA1i",
1911
+ pause: "T\u1EA1m d\u1EEBng",
1912
+ resume: "Ti\u1EBFp t\u1EE5c",
1913
+ cancel: "H\u1EE7y",
1914
+ remove: "X\xF3a",
1915
+ removeAll: "X\xF3a t\u1EA5t c\u1EA3",
1916
+ preview: "Xem tr\u01B0\u1EDBc",
1917
+ download: "T\u1EA3i xu\u1ED1ng",
1918
+ edit: "S\u1EEDa",
1919
+ save: "L\u01B0u",
1920
+ fullscreen: "To\xE0n m\xE0n h\xECnh",
1921
+ rename: "\u0110\u1ED5i t\xEAn",
1922
+ tags: "Th\u1EBB",
1923
+ description: "M\xF4 t\u1EA3",
1924
+ category: "Danh m\u1EE5c",
1925
+ addTag: "Th\xEAm th\u1EBB",
1926
+ fileTooLarge: "T\u1EC7p qu\xE1 l\u1EDBn",
1927
+ fileTooSmall: "T\u1EC7p qu\xE1 nh\u1ECF",
1928
+ invalidType: "Lo\u1EA1i t\u1EC7p kh\xF4ng \u0111\u01B0\u1EE3c ph\xE9p",
1929
+ tooManyFiles: "Qu\xE1 nhi\u1EC1u t\u1EC7p",
1930
+ duplicateFile: "T\u1EC7p tr\xF9ng l\u1EB7p",
1931
+ speed: "T\u1ED1c \u0111\u1ED9",
1932
+ eta: "C\xF2n l\u1EA1i",
1933
+ ofText: "c\u1EE7a",
1934
+ filesText: "t\u1EC7p",
1935
+ bytes: "B",
1936
+ kb: "KB",
1937
+ mb: "MB",
1938
+ gb: "GB"
1939
+ };
1940
+ var th = {
1941
+ dropHere: "\u0E27\u0E32\u0E07\u0E44\u0E1F\u0E25\u0E4C\u0E17\u0E35\u0E48\u0E19\u0E35\u0E48",
1942
+ dragOrClick: "\u0E25\u0E32\u0E01\u0E41\u0E25\u0E30\u0E27\u0E32\u0E07\u0E44\u0E1F\u0E25\u0E4C\u0E2B\u0E23\u0E37\u0E2D\u0E04\u0E25\u0E34\u0E01\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E40\u0E23\u0E35\u0E22\u0E01\u0E14\u0E39",
1943
+ browse: "\u0E40\u0E23\u0E35\u0E22\u0E01\u0E14\u0E39\u0E44\u0E1F\u0E25\u0E4C",
1944
+ uploading: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14",
1945
+ paused: "\u0E2B\u0E22\u0E38\u0E14\u0E0A\u0E31\u0E48\u0E27\u0E04\u0E23\u0E32\u0E27",
1946
+ success: "\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14\u0E41\u0E25\u0E49\u0E27",
1947
+ error: "\u0E25\u0E49\u0E21\u0E40\u0E2B\u0E25\u0E27",
1948
+ retry: "\u0E25\u0E2D\u0E07\u0E2D\u0E35\u0E01\u0E04\u0E23\u0E31\u0E49\u0E07",
1949
+ pause: "\u0E2B\u0E22\u0E38\u0E14\u0E0A\u0E31\u0E48\u0E27\u0E04\u0E23\u0E32\u0E27",
1950
+ resume: "\u0E14\u0E33\u0E40\u0E19\u0E34\u0E19\u0E01\u0E32\u0E23\u0E15\u0E48\u0E2D",
1951
+ cancel: "\u0E22\u0E01\u0E40\u0E25\u0E34\u0E01",
1952
+ remove: "\u0E25\u0E1A",
1953
+ removeAll: "\u0E25\u0E1A\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
1954
+ preview: "\u0E14\u0E39\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07",
1955
+ download: "\u0E14\u0E32\u0E27\u0E19\u0E4C\u0E42\u0E2B\u0E25\u0E14",
1956
+ edit: "\u0E41\u0E01\u0E49\u0E44\u0E02",
1957
+ save: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01",
1958
+ fullscreen: "\u0E40\u0E15\u0E47\u0E21\u0E08\u0E2D",
1959
+ rename: "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19\u0E0A\u0E37\u0E48\u0E2D",
1960
+ tags: "\u0E41\u0E17\u0E47\u0E01",
1961
+ description: "\u0E04\u0E33\u0E2D\u0E18\u0E34\u0E1A\u0E32\u0E22",
1962
+ category: "\u0E2B\u0E21\u0E27\u0E14\u0E2B\u0E21\u0E39\u0E48",
1963
+ addTag: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E41\u0E17\u0E47\u0E01",
1964
+ fileTooLarge: "\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E35\u0E02\u0E19\u0E32\u0E14\u0E43\u0E2B\u0E0D\u0E48\u0E40\u0E01\u0E34\u0E19\u0E44\u0E1B",
1965
+ fileTooSmall: "\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E35\u0E02\u0E19\u0E32\u0E14\u0E40\u0E25\u0E47\u0E01\u0E40\u0E01\u0E34\u0E19\u0E44\u0E1B",
1966
+ invalidType: "\u0E44\u0E21\u0E48\u0E2D\u0E19\u0E38\u0E0D\u0E32\u0E15\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E44\u0E1F\u0E25\u0E4C\u0E19\u0E35\u0E49",
1967
+ tooManyFiles: "\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E01\u0E40\u0E01\u0E34\u0E19\u0E44\u0E1B",
1968
+ duplicateFile: "\u0E44\u0E1F\u0E25\u0E4C\u0E0B\u0E49\u0E33",
1969
+ speed: "\u0E04\u0E27\u0E32\u0E21\u0E40\u0E23\u0E47\u0E27",
1970
+ eta: "\u0E40\u0E27\u0E25\u0E32\u0E17\u0E35\u0E48\u0E40\u0E2B\u0E25\u0E37\u0E2D",
1971
+ ofText: "\u0E08\u0E32\u0E01",
1972
+ filesText: "\u0E44\u0E1F\u0E25\u0E4C",
1973
+ bytes: "B",
1974
+ kb: "KB",
1975
+ mb: "MB",
1976
+ gb: "GB"
1977
+ };
1978
+ var he = {
1979
+ dropHere: "\u05D2\u05E8\u05D5\u05E8 \u05E7\u05D1\u05E6\u05D9\u05DD \u05DC\u05DB\u05D0\u05DF",
1980
+ dragOrClick: "\u05D2\u05E8\u05D5\u05E8 \u05D5\u05E9\u05D7\u05E8\u05E8 \u05E7\u05D1\u05E6\u05D9\u05DD \u05D0\u05D5 \u05DC\u05D7\u05E5 \u05DC\u05E2\u05D9\u05D5\u05DF",
1981
+ browse: "\u05E2\u05D9\u05D5\u05DF \u05D1\u05E7\u05D1\u05E6\u05D9\u05DD",
1982
+ uploading: "\u05DE\u05E2\u05DC\u05D4",
1983
+ paused: "\u05DE\u05D5\u05E9\u05D4\u05D4",
1984
+ success: "\u05D4\u05D5\u05E2\u05DC\u05D4",
1985
+ error: "\u05E0\u05DB\u05E9\u05DC",
1986
+ retry: "\u05E0\u05E1\u05D4 \u05E9\u05D5\u05D1",
1987
+ pause: "\u05D4\u05E9\u05D4\u05D4",
1988
+ resume: "\u05D4\u05DE\u05E9\u05DA",
1989
+ cancel: "\u05D1\u05D8\u05DC",
1990
+ remove: "\u05D4\u05E1\u05E8",
1991
+ removeAll: "\u05D4\u05E1\u05E8 \u05D4\u05DB\u05DC",
1992
+ preview: "\u05EA\u05E6\u05D5\u05D2\u05D4 \u05DE\u05E7\u05D3\u05D9\u05DE\u05D4",
1993
+ download: "\u05D4\u05D5\u05E8\u05D3",
1994
+ edit: "\u05E2\u05E8\u05D5\u05DA",
1995
+ save: "\u05E9\u05DE\u05D5\u05E8",
1996
+ fullscreen: "\u05DE\u05E1\u05DA \u05DE\u05DC\u05D0",
1997
+ rename: "\u05E9\u05E0\u05D4 \u05E9\u05DD",
1998
+ tags: "\u05EA\u05D2\u05D9\u05D5\u05EA",
1999
+ description: "\u05EA\u05D9\u05D0\u05D5\u05E8",
2000
+ category: "\u05E7\u05D8\u05D2\u05D5\u05E8\u05D9\u05D4",
2001
+ addTag: "\u05D4\u05D5\u05E1\u05E3 \u05EA\u05D2\u05D9\u05EA",
2002
+ fileTooLarge: "\u05D4\u05E7\u05D5\u05D1\u05E5 \u05D2\u05D3\u05D5\u05DC \u05DE\u05D3\u05D9",
2003
+ fileTooSmall: "\u05D4\u05E7\u05D5\u05D1\u05E5 \u05E7\u05D8\u05DF \u05DE\u05D3\u05D9",
2004
+ invalidType: "\u05E1\u05D5\u05D2 \u05D4\u05E7\u05D5\u05D1\u05E5 \u05D0\u05D9\u05E0\u05D5 \u05DE\u05D5\u05EA\u05E8",
2005
+ tooManyFiles: "\u05D9\u05D5\u05EA\u05E8 \u05DE\u05D3\u05D9 \u05E7\u05D1\u05E6\u05D9\u05DD",
2006
+ duplicateFile: "\u05E7\u05D5\u05D1\u05E5 \u05DB\u05E4\u05D5\u05DC",
2007
+ speed: "\u05DE\u05D4\u05D9\u05E8\u05D5\u05EA",
2008
+ eta: "\u05D6\u05DE\u05DF \u05E0\u05D5\u05EA\u05E8",
2009
+ ofText: "\u05DE\u05EA\u05D5\u05DA",
2010
+ filesText: "\u05E7\u05D1\u05E6\u05D9\u05DD",
2011
+ bytes: "B",
2012
+ kb: "KB",
2013
+ mb: "MB",
2014
+ gb: "GB"
2015
+ };
2016
+ var fa = {
2017
+ dropHere: "\u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0627\u06CC\u0646\u062C\u0627 \u0631\u0647\u0627 \u06A9\u0646\u06CC\u062F",
2018
+ dragOrClick: "\u0641\u0627\u06CC\u0644\u200C\u0647\u0627 \u0631\u0627 \u0628\u06A9\u0634\u06CC\u062F \u0648 \u0631\u0647\u0627 \u06A9\u0646\u06CC\u062F \u06CC\u0627 \u0628\u0631\u0627\u06CC \u0645\u0631\u0648\u0631 \u06A9\u0644\u06CC\u06A9 \u06A9\u0646\u06CC\u062F",
2019
+ browse: "\u0645\u0631\u0648\u0631 \u0641\u0627\u06CC\u0644\u200C\u0647\u0627",
2020
+ uploading: "\u062F\u0631 \u062D\u0627\u0644 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC",
2021
+ paused: "\u0645\u062A\u0648\u0642\u0641 \u0634\u062F",
2022
+ success: "\u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u0634\u062F",
2023
+ error: "\u0646\u0627\u0645\u0648\u0641\u0642",
2024
+ retry: "\u062A\u0644\u0627\u0634 \u062F\u0648\u0628\u0627\u0631\u0647",
2025
+ pause: "\u062A\u0648\u0642\u0641",
2026
+ resume: "\u0627\u062F\u0627\u0645\u0647",
2027
+ cancel: "\u0644\u063A\u0648",
2028
+ remove: "\u062D\u0630\u0641",
2029
+ removeAll: "\u062D\u0630\u0641 \u0647\u0645\u0647",
2030
+ preview: "\u067E\u06CC\u0634\u200C\u0646\u0645\u0627\u06CC\u0634",
2031
+ download: "\u062F\u0627\u0646\u0644\u0648\u062F",
2032
+ edit: "\u0648\u06CC\u0631\u0627\u06CC\u0634",
2033
+ save: "\u0630\u062E\u06CC\u0631\u0647",
2034
+ fullscreen: "\u062A\u0645\u0627\u0645 \u0635\u0641\u062D\u0647",
2035
+ rename: "\u062A\u063A\u06CC\u06CC\u0631 \u0646\u0627\u0645",
2036
+ tags: "\u0628\u0631\u0686\u0633\u0628\u200C\u0647\u0627",
2037
+ description: "\u062A\u0648\u0636\u06CC\u062D\u0627\u062A",
2038
+ category: "\u062F\u0633\u062A\u0647\u200C\u0628\u0646\u062F\u06CC",
2039
+ addTag: "\u0627\u0641\u0632\u0648\u062F\u0646 \u0628\u0631\u0686\u0633\u0628",
2040
+ fileTooLarge: "\u0641\u0627\u06CC\u0644 \u062E\u06CC\u0644\u06CC \u0628\u0632\u0631\u06AF \u0627\u0633\u062A",
2041
+ fileTooSmall: "\u0641\u0627\u06CC\u0644 \u062E\u06CC\u0644\u06CC \u06A9\u0648\u0686\u06A9 \u0627\u0633\u062A",
2042
+ invalidType: "\u0646\u0648\u0639 \u0641\u0627\u06CC\u0644 \u0645\u062C\u0627\u0632 \u0646\u06CC\u0633\u062A",
2043
+ tooManyFiles: "\u0641\u0627\u06CC\u0644\u200C\u0647\u0627\u06CC \u0632\u06CC\u0627\u062F",
2044
+ duplicateFile: "\u0641\u0627\u06CC\u0644 \u062A\u06A9\u0631\u0627\u0631\u06CC",
2045
+ speed: "\u0633\u0631\u0639\u062A",
2046
+ eta: "\u0632\u0645\u0627\u0646 \u0628\u0627\u0642\u06CC\u200C\u0645\u0627\u0646\u062F\u0647",
2047
+ ofText: "\u0627\u0632",
2048
+ filesText: "\u0641\u0627\u06CC\u0644",
2049
+ bytes: "B",
2050
+ kb: "KB",
2051
+ mb: "MB",
2052
+ gb: "GB"
2053
+ };
2054
+ var translations = {
2055
+ en,
2056
+ hi,
2057
+ gu,
2058
+ fr,
2059
+ de,
2060
+ ar,
2061
+ zh,
2062
+ es,
2063
+ pt,
2064
+ ru,
2065
+ ja,
2066
+ ko,
2067
+ it,
2068
+ tr,
2069
+ id,
2070
+ bn,
2071
+ ur,
2072
+ nl,
2073
+ pl,
2074
+ vi,
2075
+ th,
2076
+ he,
2077
+ fa
2078
+ };
2079
+ var rtlLocales = /* @__PURE__ */ new Set(["ar", "ur", "he", "fa"]);
2080
+ var I18nContext = createContext(null);
2081
+ function I18nProvider({ locale = "en", messages, children }) {
2082
+ const value = useMemo(() => {
2083
+ const base2 = translations[locale] ?? translations.en;
2084
+ return {
2085
+ locale,
2086
+ t: { ...base2, ...messages },
2087
+ isRtl: rtlLocales.has(locale)
2088
+ };
2089
+ }, [locale, messages]);
2090
+ return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
2091
+ }
2092
+ function useI18n() {
2093
+ const ctx = useContext(I18nContext);
2094
+ if (ctx) return ctx;
2095
+ return { locale: "en", t: translations.en, isRtl: false };
2096
+ }
2097
+ var base = {
2098
+ width: 16,
2099
+ height: 16,
2100
+ viewBox: "0 0 24 24",
2101
+ fill: "none",
2102
+ stroke: "currentColor",
2103
+ strokeWidth: 2,
2104
+ strokeLinecap: "round",
2105
+ strokeLinejoin: "round"
2106
+ };
2107
+ var UploadIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2108
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
2109
+ /* @__PURE__ */ jsx("polyline", { points: "17 8 12 3 7 8" }),
2110
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
2111
+ ] });
2112
+ var FileIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2113
+ /* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
2114
+ /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })
2115
+ ] });
2116
+ var ImageIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2117
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
2118
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
2119
+ /* @__PURE__ */ jsx("path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21" })
2120
+ ] });
2121
+ var TrashIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2122
+ /* @__PURE__ */ jsx("polyline", { points: "3 6 5 6 21 6" }),
2123
+ /* @__PURE__ */ jsx("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
2124
+ ] });
2125
+ var PauseIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2126
+ /* @__PURE__ */ jsx("rect", { x: "6", y: "4", width: "4", height: "16" }),
2127
+ /* @__PURE__ */ jsx("rect", { x: "14", y: "4", width: "4", height: "16" })
2128
+ ] });
2129
+ var PlayIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("polygon", { points: "5 3 19 12 5 21 5 3" }) });
2130
+ var RetryIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2131
+ /* @__PURE__ */ jsx("polyline", { points: "23 4 23 10 17 10" }),
2132
+ /* @__PURE__ */ jsx("path", { d: "M20.49 15a9 9 0 1 1-2.12-9.36L23 10" })
2133
+ ] });
2134
+ var CheckIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" }) });
2135
+ var AlertIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2136
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
2137
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
2138
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
2139
+ ] });
2140
+ var CloseIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2141
+ /* @__PURE__ */ jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2142
+ /* @__PURE__ */ jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2143
+ ] });
2144
+ var DownloadIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2145
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
2146
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
2147
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
2148
+ ] });
2149
+ var EyeIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2150
+ /* @__PURE__ */ jsx("path", { d: "M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" }),
2151
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
2152
+ ] });
2153
+ var FolderIcon = (p) => /* @__PURE__ */ jsx("svg", { ...base, ...p, children: /* @__PURE__ */ jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" }) });
2154
+ var EditIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2155
+ /* @__PURE__ */ jsx("path", { d: "M12 20h9" }),
2156
+ /* @__PURE__ */ jsx("path", { d: "M16.5 3.5a2.121 2.121 0 1 1 3 3L7 19l-4 1 1-4z" })
2157
+ ] });
2158
+ var MaximizeIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2159
+ /* @__PURE__ */ jsx("path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }),
2160
+ /* @__PURE__ */ jsx("path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }),
2161
+ /* @__PURE__ */ jsx("path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }),
2162
+ /* @__PURE__ */ jsx("path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" })
2163
+ ] });
2164
+ var TagIcon = (p) => /* @__PURE__ */ jsxs("svg", { ...base, ...p, children: [
2165
+ /* @__PURE__ */ jsx("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }),
2166
+ /* @__PURE__ */ jsx("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })
2167
+ ] });
2168
+ var sizeClasses = {
2169
+ sm: "max-w-sm",
2170
+ md: "max-w-md",
2171
+ lg: "max-w-2xl",
2172
+ xl: "max-w-4xl",
2173
+ full: "max-w-[95vw]"
2174
+ };
2175
+ function UploadModal({
2176
+ open,
2177
+ onClose,
2178
+ title,
2179
+ description,
2180
+ children,
2181
+ size = "lg",
2182
+ hideClose,
2183
+ className
2184
+ }) {
2185
+ const { t } = useI18n();
2186
+ useEffect(() => {
2187
+ if (!open) return;
2188
+ const prev = document.body.style.overflow;
2189
+ document.body.style.overflow = "hidden";
2190
+ const handler = (e) => {
2191
+ if (e.key === "Escape") onClose();
2192
+ };
2193
+ document.addEventListener("keydown", handler);
2194
+ return () => {
2195
+ document.body.style.overflow = prev;
2196
+ document.removeEventListener("keydown", handler);
2197
+ };
2198
+ }, [open, onClose]);
2199
+ if (!open || typeof document === "undefined") return null;
2200
+ return /* @__PURE__ */ jsxs(
2201
+ "div",
2202
+ {
2203
+ role: "dialog",
2204
+ "aria-modal": "true",
2205
+ "aria-label": typeof title === "string" ? title : "Upload",
2206
+ className: "fixed inset-0 z-[1000] flex items-center justify-center p-4",
2207
+ children: [
2208
+ /* @__PURE__ */ jsx(
2209
+ "div",
2210
+ {
2211
+ className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
2212
+ onClick: onClose,
2213
+ "aria-hidden": true
2214
+ }
2215
+ ),
2216
+ /* @__PURE__ */ jsxs(
2217
+ "div",
2218
+ {
2219
+ className: cn(
2220
+ "relative w-full overflow-hidden rounded-rup border border-rup-border bg-rup-bg text-rup-fg shadow-rup",
2221
+ sizeClasses[size],
2222
+ className
2223
+ ),
2224
+ children: [
2225
+ (title || !hideClose) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-rup-border px-5 py-3", children: [
2226
+ /* @__PURE__ */ jsxs("div", { children: [
2227
+ title && /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title }),
2228
+ description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-rup-muted", children: description })
2229
+ ] }),
2230
+ !hideClose && /* @__PURE__ */ jsx(
2231
+ "button",
2232
+ {
2233
+ type: "button",
2234
+ onClick: onClose,
2235
+ "aria-label": t.cancel,
2236
+ className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-rup-muted hover:bg-rup-border/40 hover:text-rup-fg",
2237
+ children: /* @__PURE__ */ jsx(CloseIcon, { width: 16, height: 16 })
2238
+ }
2239
+ )
2240
+ ] }),
2241
+ /* @__PURE__ */ jsx("div", { className: "max-h-[80vh] overflow-y-auto p-5", children })
2242
+ ]
2243
+ }
2244
+ )
2245
+ ]
2246
+ }
2247
+ );
2248
+ }
2249
+ function FileEditModal({ file, onClose, onRename, onSave }) {
2250
+ const { t } = useI18n();
2251
+ const [name, setName] = useState("");
2252
+ const [category, setCategory] = useState("");
2253
+ const [description, setDescription] = useState("");
2254
+ const [tags, setTags] = useState([]);
2255
+ const [tagDraft, setTagDraft] = useState("");
2256
+ useEffect(() => {
2257
+ if (!file) return;
2258
+ setName(file.name);
2259
+ setCategory(typeof file.metadata.category === "string" ? file.metadata.category : "");
2260
+ setDescription(
2261
+ typeof file.metadata.description === "string" ? file.metadata.description : ""
2262
+ );
2263
+ setTags(Array.isArray(file.metadata.tags) ? [...file.metadata.tags] : []);
2264
+ setTagDraft("");
2265
+ }, [file]);
2266
+ const commitTagDraft = () => {
2267
+ const next = tagDraft.trim();
2268
+ if (!next) return;
2269
+ if (tags.includes(next)) {
2270
+ setTagDraft("");
2271
+ return;
2272
+ }
2273
+ setTags((prev) => [...prev, next]);
2274
+ setTagDraft("");
2275
+ };
2276
+ const onTagKeyDown = (e) => {
2277
+ if (e.key === "Enter" || e.key === ",") {
2278
+ e.preventDefault();
2279
+ commitTagDraft();
2280
+ } else if (e.key === "Backspace" && tagDraft === "" && tags.length > 0) {
2281
+ setTags((prev) => prev.slice(0, -1));
2282
+ }
2283
+ };
2284
+ const handleSave = () => {
2285
+ if (!file) return;
2286
+ if (name && name !== file.name) onRename?.(file, name);
2287
+ onSave?.(file, { category: category || void 0, description: description || void 0, tags });
2288
+ onClose();
2289
+ };
2290
+ return /* @__PURE__ */ jsx(UploadModal, { open: !!file, onClose, title: t.edit, size: "md", hideClose: true, children: file && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2291
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2292
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-rup-muted", children: [
2293
+ file.size > 0 ? new Intl.NumberFormat().format(file.size) + " bytes" : "",
2294
+ " ",
2295
+ "\xB7 ",
2296
+ file.type || "unknown"
2297
+ ] }),
2298
+ /* @__PURE__ */ jsx(
2299
+ "button",
2300
+ {
2301
+ type: "button",
2302
+ onClick: onClose,
2303
+ "aria-label": t.cancel,
2304
+ className: "inline-flex h-7 w-7 items-center justify-center rounded-md text-rup-muted hover:bg-rup-border/40 hover:text-rup-fg",
2305
+ children: /* @__PURE__ */ jsx(CloseIcon, { width: 14, height: 14 })
2306
+ }
2307
+ )
2308
+ ] }),
2309
+ /* @__PURE__ */ jsx(Field, { label: t.rename, children: /* @__PURE__ */ jsx(
2310
+ "input",
2311
+ {
2312
+ value: name,
2313
+ onChange: (e) => setName(e.target.value),
2314
+ className: inputCls,
2315
+ autoFocus: true
2316
+ }
2317
+ ) }),
2318
+ /* @__PURE__ */ jsx(Field, { label: t.category, children: /* @__PURE__ */ jsx(
2319
+ "input",
2320
+ {
2321
+ value: category,
2322
+ onChange: (e) => setCategory(e.target.value),
2323
+ placeholder: "e.g. invoices",
2324
+ className: inputCls
2325
+ }
2326
+ ) }),
2327
+ /* @__PURE__ */ jsx(Field, { label: t.tags, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 rounded-rup border border-rup-border bg-rup-bg p-2", children: [
2328
+ tags.map((tag) => /* @__PURE__ */ jsxs(
2329
+ "span",
2330
+ {
2331
+ className: "inline-flex items-center gap-1 rounded-full bg-rup-accent/15 px-2 py-0.5 text-xs text-rup-accent",
2332
+ children: [
2333
+ /* @__PURE__ */ jsx(TagIcon, { width: 10, height: 10 }),
2334
+ tag,
2335
+ /* @__PURE__ */ jsx(
2336
+ "button",
2337
+ {
2338
+ type: "button",
2339
+ onClick: () => setTags((prev) => prev.filter((x) => x !== tag)),
2340
+ className: "ml-0.5 text-rup-accent hover:opacity-70",
2341
+ "aria-label": `${t.remove} ${tag}`,
2342
+ children: /* @__PURE__ */ jsx(CloseIcon, { width: 10, height: 10 })
2343
+ }
2344
+ )
2345
+ ]
2346
+ },
2347
+ tag
2348
+ )),
2349
+ /* @__PURE__ */ jsx(
2350
+ "input",
2351
+ {
2352
+ value: tagDraft,
2353
+ onChange: (e) => setTagDraft(e.target.value),
2354
+ onKeyDown: onTagKeyDown,
2355
+ onBlur: commitTagDraft,
2356
+ placeholder: t.addTag,
2357
+ className: "min-w-[80px] flex-1 bg-transparent text-sm text-rup-fg outline-none placeholder:text-rup-muted"
2358
+ }
2359
+ )
2360
+ ] }) }),
2361
+ /* @__PURE__ */ jsx(Field, { label: t.description, children: /* @__PURE__ */ jsx(
2362
+ "textarea",
2363
+ {
2364
+ value: description,
2365
+ onChange: (e) => setDescription(e.target.value),
2366
+ rows: 3,
2367
+ className: cn(inputCls, "resize-none")
2368
+ }
2369
+ ) }),
2370
+ /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-end gap-2", children: [
2371
+ /* @__PURE__ */ jsx(
2372
+ "button",
2373
+ {
2374
+ type: "button",
2375
+ onClick: onClose,
2376
+ className: "rounded-md border border-rup-border bg-rup-bg px-4 py-2 text-sm text-rup-fg hover:bg-rup-border/30",
2377
+ children: t.cancel
2378
+ }
2379
+ ),
2380
+ /* @__PURE__ */ jsx(
2381
+ "button",
2382
+ {
2383
+ type: "button",
2384
+ onClick: handleSave,
2385
+ className: "rounded-md bg-rup-accent px-4 py-2 text-sm font-medium text-rup-accent-fg hover:opacity-90",
2386
+ children: t.save
2387
+ }
2388
+ )
2389
+ ] })
2390
+ ] }) });
2391
+ }
2392
+ var inputCls = "w-full rounded-rup border border-rup-border bg-rup-bg px-3 py-2 text-sm text-rup-fg placeholder:text-rup-muted focus:outline-none focus:ring-2 focus:ring-rup-accent";
2393
+ function Field({ label, children }) {
2394
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1", children: [
2395
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-rup-muted", children: label }),
2396
+ children
2397
+ ] });
2398
+ }
2399
+ function FilePreviewModal({ file, onClose, onDownload }) {
2400
+ const { t } = useI18n();
2401
+ const preview = useFilePreview(file);
2402
+ const [zoom, setZoom] = useState(1);
2403
+ const [rotate, setRotate] = useState(0);
2404
+ useEffect(() => {
2405
+ setZoom(1);
2406
+ setRotate(0);
2407
+ }, [file?.id]);
2408
+ const handleDownload = () => {
2409
+ if (!file) return;
2410
+ if (onDownload) {
2411
+ onDownload(file);
2412
+ return;
2413
+ }
2414
+ const url = URL.createObjectURL(file.file);
2415
+ const a = document.createElement("a");
2416
+ a.href = url;
2417
+ a.download = file.name;
2418
+ document.body.appendChild(a);
2419
+ a.click();
2420
+ document.body.removeChild(a);
2421
+ URL.revokeObjectURL(url);
2422
+ };
2423
+ return /* @__PURE__ */ jsx(UploadModal, { open: !!file, onClose, title: file?.name, size: "xl", hideClose: true, children: file && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
2424
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
2425
+ preview.kind === "image" && /* @__PURE__ */ jsxs(Fragment, { children: [
2426
+ /* @__PURE__ */ jsx(ZoomButton, { onClick: () => setZoom((z) => Math.max(0.2, z - 0.2)), children: "\u2212" }),
2427
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-rup-muted", children: [
2428
+ Math.round(zoom * 100),
2429
+ "%"
2430
+ ] }),
2431
+ /* @__PURE__ */ jsx(ZoomButton, { onClick: () => setZoom((z) => Math.min(5, z + 0.2)), children: "+" }),
2432
+ /* @__PURE__ */ jsx(ZoomButton, { onClick: () => setRotate((r) => (r + 90) % 360), children: "\u21BB" })
2433
+ ] }),
2434
+ /* @__PURE__ */ jsxs(
2435
+ "button",
2436
+ {
2437
+ type: "button",
2438
+ onClick: handleDownload,
2439
+ className: "inline-flex items-center gap-1 rounded-md border border-rup-border px-3 py-1.5 text-xs hover:bg-rup-border/30",
2440
+ children: [
2441
+ /* @__PURE__ */ jsx(DownloadIcon, { width: 14, height: 14 }),
2442
+ t.download
2443
+ ]
2444
+ }
2445
+ ),
2446
+ /* @__PURE__ */ jsx(
2447
+ "button",
2448
+ {
2449
+ type: "button",
2450
+ onClick: onClose,
2451
+ "aria-label": t.cancel,
2452
+ className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-rup-muted hover:bg-rup-border/40 hover:text-rup-fg",
2453
+ children: /* @__PURE__ */ jsx(CloseIcon, { width: 16, height: 16 })
2454
+ }
2455
+ )
2456
+ ] }),
2457
+ /* @__PURE__ */ jsx("div", { className: "flex min-h-[40vh] items-center justify-center overflow-auto rounded-rup bg-rup-border/20 p-4", children: renderPreview({ preview, file, zoom, rotate }) })
2458
+ ] }) });
2459
+ }
2460
+ function renderPreview({
2461
+ preview,
2462
+ file,
2463
+ zoom,
2464
+ rotate
2465
+ }) {
2466
+ if (preview.loading) {
2467
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-rup-muted", children: "Loading\u2026" });
2468
+ }
2469
+ if (preview.error) {
2470
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-rup-error", children: preview.error });
2471
+ }
2472
+ if (preview.kind === "image" && preview.url) {
2473
+ return /* @__PURE__ */ jsx(
2474
+ "img",
2475
+ {
2476
+ src: preview.url,
2477
+ alt: file.name,
2478
+ style: { transform: `scale(${zoom}) rotate(${rotate}deg)` },
2479
+ className: "max-h-[70vh] origin-center select-none transition-transform"
2480
+ }
2481
+ );
2482
+ }
2483
+ if (preview.kind === "video" && preview.url) {
2484
+ return /* @__PURE__ */ jsx("video", { src: preview.url, controls: true, className: "max-h-[70vh]" });
2485
+ }
2486
+ if (preview.kind === "audio" && preview.url) {
2487
+ return /* @__PURE__ */ jsx("audio", { src: preview.url, controls: true, className: "w-full" });
2488
+ }
2489
+ if (preview.kind === "pdf" && preview.url) {
2490
+ return /* @__PURE__ */ jsx("iframe", { src: preview.url, title: file.name, className: "h-[70vh] w-full rounded border-0" });
2491
+ }
2492
+ if (preview.kind === "text" && typeof preview.text === "string") {
2493
+ return /* @__PURE__ */ jsx("pre", { className: "max-h-[70vh] w-full overflow-auto whitespace-pre-wrap break-words rounded bg-rup-bg p-4 text-xs", children: preview.text });
2494
+ }
2495
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2 text-sm text-rup-muted", children: [
2496
+ /* @__PURE__ */ jsx("span", { children: "No inline preview available for this file type." }),
2497
+ preview.url && /* @__PURE__ */ jsx(
2498
+ "a",
2499
+ {
2500
+ href: preview.url,
2501
+ target: "_blank",
2502
+ rel: "noreferrer",
2503
+ className: "text-rup-accent underline",
2504
+ children: "Open in new tab"
2505
+ }
2506
+ )
2507
+ ] });
2508
+ }
2509
+ function ZoomButton({ children, onClick }) {
2510
+ return /* @__PURE__ */ jsx(
2511
+ "button",
2512
+ {
2513
+ type: "button",
2514
+ onClick,
2515
+ className: cn(
2516
+ "h-8 w-8 rounded-md border border-rup-border bg-rup-bg text-sm text-rup-fg hover:bg-rup-border/30"
2517
+ ),
2518
+ children
2519
+ }
2520
+ );
2521
+ }
2522
+ var SEGMENT_COUNT = 10;
2523
+ function UploadProgress({
2524
+ value,
2525
+ variant = "bar",
2526
+ showLabel = false,
2527
+ size,
2528
+ fillClassName,
2529
+ trackClassName,
2530
+ indeterminate = false,
2531
+ segments = SEGMENT_COUNT,
2532
+ className,
2533
+ ...rest
2534
+ }) {
2535
+ const pct = Math.max(0, Math.min(1, value));
2536
+ const roundedPct = Math.round(pct * 100);
2537
+ if (variant === "circle") {
2538
+ const dim = size ?? 48;
2539
+ const stroke = Math.max(3, Math.floor(dim / 12));
2540
+ const radius = (dim - stroke) / 2;
2541
+ const circumference = 2 * Math.PI * radius;
2542
+ const offset = indeterminate ? circumference * 0.75 : circumference * (1 - pct);
2543
+ return /* @__PURE__ */ jsxs(
2544
+ "div",
2545
+ {
2546
+ className: cn("relative inline-flex items-center justify-center", className),
2547
+ style: { width: dim, height: dim },
2548
+ role: "progressbar",
2549
+ "aria-valuenow": indeterminate ? void 0 : roundedPct,
2550
+ "aria-valuemin": 0,
2551
+ "aria-valuemax": 100,
2552
+ ...rest,
2553
+ children: [
2554
+ /* @__PURE__ */ jsxs("svg", { width: dim, height: dim, className: indeterminate ? "animate-spin" : "", children: [
2555
+ /* @__PURE__ */ jsx(
2556
+ "circle",
2557
+ {
2558
+ cx: dim / 2,
2559
+ cy: dim / 2,
2560
+ r: radius,
2561
+ stroke: "currentColor",
2562
+ strokeWidth: stroke,
2563
+ fill: "none",
2564
+ className: cn("text-rup-border", trackClassName)
2565
+ }
2566
+ ),
2567
+ /* @__PURE__ */ jsx(
2568
+ "circle",
2569
+ {
2570
+ cx: dim / 2,
2571
+ cy: dim / 2,
2572
+ r: radius,
2573
+ stroke: "currentColor",
2574
+ strokeWidth: stroke,
2575
+ strokeLinecap: "round",
2576
+ fill: "none",
2577
+ strokeDasharray: circumference,
2578
+ strokeDashoffset: offset,
2579
+ transform: `rotate(-90 ${dim / 2} ${dim / 2})`,
2580
+ className: cn("text-rup-accent transition-[stroke-dashoffset] duration-300", fillClassName)
2581
+ }
2582
+ )
2583
+ ] }),
2584
+ showLabel && /* @__PURE__ */ jsx("span", { className: "absolute text-xs font-medium text-rup-fg", children: formatPercent(pct) })
2585
+ ]
2586
+ }
2587
+ );
2588
+ }
2589
+ if (variant === "segmented") {
2590
+ const filled = Math.round(pct * segments);
2591
+ return /* @__PURE__ */ jsxs(
2592
+ "div",
2593
+ {
2594
+ className: cn("flex items-center gap-2", className),
2595
+ role: "progressbar",
2596
+ "aria-valuenow": roundedPct,
2597
+ "aria-valuemin": 0,
2598
+ "aria-valuemax": 100,
2599
+ ...rest,
2600
+ children: [
2601
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center gap-[3px]", children: Array.from({ length: segments }).map((_, i) => /* @__PURE__ */ jsx(
2602
+ "div",
2603
+ {
2604
+ className: cn(
2605
+ "h-2 flex-1 rounded-sm transition-colors duration-200",
2606
+ i < filled ? "bg-rup-accent" : "bg-rup-border/60",
2607
+ fillClassName && i < filled && fillClassName
2608
+ )
2609
+ },
2610
+ i
2611
+ )) }),
2612
+ showLabel && /* @__PURE__ */ jsx("span", { className: "min-w-[3ch] text-right text-xs tabular-nums text-rup-muted", children: formatPercent(pct) })
2613
+ ]
2614
+ }
2615
+ );
2616
+ }
2617
+ if (variant === "dots") {
2618
+ const filled = Math.round(pct * segments);
2619
+ return /* @__PURE__ */ jsxs(
2620
+ "div",
2621
+ {
2622
+ className: cn("flex items-center gap-2", className),
2623
+ role: "progressbar",
2624
+ "aria-valuenow": roundedPct,
2625
+ "aria-valuemin": 0,
2626
+ "aria-valuemax": 100,
2627
+ ...rest,
2628
+ children: [
2629
+ /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-between gap-1", children: Array.from({ length: segments }).map((_, i) => /* @__PURE__ */ jsx(
2630
+ "span",
2631
+ {
2632
+ className: cn(
2633
+ "h-2 w-2 rounded-full transition-all duration-200",
2634
+ i < filled ? "bg-rup-accent scale-110" : "bg-rup-border/60",
2635
+ fillClassName && i < filled && fillClassName
2636
+ )
2637
+ },
2638
+ i
2639
+ )) }),
2640
+ showLabel && /* @__PURE__ */ jsx("span", { className: "min-w-[3ch] text-right text-xs tabular-nums text-rup-muted", children: formatPercent(pct) })
2641
+ ]
2642
+ }
2643
+ );
2644
+ }
2645
+ const isMinimal = variant === "minimal";
2646
+ const isGradient = variant === "gradient";
2647
+ const isStriped = variant === "striped";
2648
+ const height = size ?? (isMinimal ? 2 : isStriped ? 10 : 6);
2649
+ return /* @__PURE__ */ jsxs(
2650
+ "div",
2651
+ {
2652
+ className: cn("flex items-center gap-2", className),
2653
+ role: "progressbar",
2654
+ "aria-valuenow": indeterminate ? void 0 : roundedPct,
2655
+ "aria-valuemin": 0,
2656
+ "aria-valuemax": 100,
2657
+ ...rest,
2658
+ children: [
2659
+ /* @__PURE__ */ jsx(
2660
+ "div",
2661
+ {
2662
+ className: cn(
2663
+ "relative w-full overflow-hidden bg-rup-border/60",
2664
+ isMinimal ? "rounded-none" : "rounded-full",
2665
+ trackClassName
2666
+ ),
2667
+ style: { height },
2668
+ children: /* @__PURE__ */ jsx(
2669
+ "div",
2670
+ {
2671
+ className: cn(
2672
+ "absolute inset-y-0 left-0 transition-[width] duration-300",
2673
+ isMinimal ? "rounded-none" : "rounded-full",
2674
+ isGradient ? "bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500" : "bg-rup-accent",
2675
+ isStriped && "bg-[linear-gradient(45deg,rgba(255,255,255,0.18)_25%,transparent_25%,transparent_50%,rgba(255,255,255,0.18)_50%,rgba(255,255,255,0.18)_75%,transparent_75%,transparent)] bg-[length:1rem_1rem] animate-[rup-shimmer_1s_linear_infinite]",
2676
+ indeterminate && "w-1/3 animate-[rup-shimmer_1.4s_linear_infinite]",
2677
+ fillClassName
2678
+ ),
2679
+ style: { width: indeterminate ? void 0 : `${pct * 100}%` }
2680
+ }
2681
+ )
2682
+ }
2683
+ ),
2684
+ showLabel && /* @__PURE__ */ jsx("span", { className: "min-w-[3ch] text-right text-xs tabular-nums text-rup-muted", children: formatPercent(pct) })
2685
+ ]
2686
+ }
2687
+ );
2688
+ }
2689
+ function UploadPreview({
2690
+ file,
2691
+ density = "comfortable",
2692
+ showStats = true,
2693
+ showThumb = true,
2694
+ progressVariant,
2695
+ progressSize,
2696
+ className,
2697
+ onRetry,
2698
+ onPause,
2699
+ onResume,
2700
+ onCancel,
2701
+ onRemove,
2702
+ onPreview,
2703
+ onDownload,
2704
+ onEdit,
2705
+ ...rest
2706
+ }) {
2707
+ const { t } = useI18n();
2708
+ const category = useMemo(() => getFileCategory(file), [file]);
2709
+ const [thumbError, setThumbError] = useState(false);
2710
+ const isImage = category === "image" && file.previewUrl && showThumb && !thumbError;
2711
+ const isVideo = category === "video" && file.previewUrl && showThumb && !thumbError;
2712
+ const isUploading = file.status === "uploading";
2713
+ const isPaused = file.status === "paused";
2714
+ const isErr = file.status === "error";
2715
+ const isOk = file.status === "success";
2716
+ const showProgress = isUploading || isPaused || file.progress > 0;
2717
+ const resolvedProgressVariant = progressVariant ?? (isPaused ? "bar" : isUploading ? "striped" : "bar");
2718
+ return /* @__PURE__ */ jsxs(
2719
+ "div",
2720
+ {
2721
+ className: cn(
2722
+ "group relative flex items-center gap-3 rounded-rup border border-rup-border bg-rup-bg p-3 text-rup-fg",
2723
+ density === "compact" && "p-2 gap-2",
2724
+ density === "card" && "flex-col items-stretch p-4",
2725
+ isErr && "border-rup-error/50",
2726
+ isOk && "border-rup-success/50",
2727
+ className
2728
+ ),
2729
+ "data-rup-file-id": file.id,
2730
+ "data-status": file.status,
2731
+ ...rest,
2732
+ children: [
2733
+ /* @__PURE__ */ jsx(
2734
+ "div",
2735
+ {
2736
+ className: cn(
2737
+ "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md bg-rup-border/30 text-rup-muted",
2738
+ density === "compact" && "h-9 w-9",
2739
+ density === "card" && "h-32 w-full"
2740
+ ),
2741
+ children: isImage ? /* @__PURE__ */ jsx(
2742
+ "img",
2743
+ {
2744
+ src: file.previewUrl,
2745
+ alt: file.name,
2746
+ className: "h-full w-full object-cover",
2747
+ onError: () => setThumbError(true)
2748
+ }
2749
+ ) : isVideo ? /* @__PURE__ */ jsx(
2750
+ "video",
2751
+ {
2752
+ src: file.previewUrl,
2753
+ className: "h-full w-full object-cover",
2754
+ muted: true,
2755
+ onError: () => setThumbError(true)
2756
+ }
2757
+ ) : category === "image" ? /* @__PURE__ */ jsx(ImageIcon, { width: 20, height: 20 }) : /* @__PURE__ */ jsx(FileIcon, { width: 20, height: 20 })
2758
+ }
2759
+ ),
2760
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2761
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2762
+ /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", title: file.name, children: file.name }),
2763
+ isOk && /* @__PURE__ */ jsx(CheckIcon, { width: 14, height: 14, className: "text-rup-success" }),
2764
+ isErr && /* @__PURE__ */ jsx(AlertIcon, { width: 14, height: 14, className: "text-rup-error" })
2765
+ ] }),
2766
+ /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex items-center gap-2 text-xs text-rup-muted", children: [
2767
+ /* @__PURE__ */ jsx("span", { children: formatBytes(file.size) }),
2768
+ isUploading && showStats && /* @__PURE__ */ jsxs(Fragment, { children: [
2769
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2770
+ /* @__PURE__ */ jsxs("span", { children: [
2771
+ t.speed,
2772
+ ": ",
2773
+ formatSpeed(file.speed)
2774
+ ] }),
2775
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2776
+ /* @__PURE__ */ jsxs("span", { children: [
2777
+ t.eta,
2778
+ ": ",
2779
+ formatEta(file.eta)
2780
+ ] })
2781
+ ] }),
2782
+ isErr && file.error && /* @__PURE__ */ jsx("span", { className: "truncate text-rup-error", title: file.error.message, children: file.error.message }),
2783
+ isPaused && /* @__PURE__ */ jsx("span", { children: t.paused })
2784
+ ] }),
2785
+ showProgress && /* @__PURE__ */ jsx(
2786
+ UploadProgress,
2787
+ {
2788
+ value: file.progress,
2789
+ variant: resolvedProgressVariant,
2790
+ size: progressSize,
2791
+ className: "mt-2",
2792
+ fillClassName: isErr ? "bg-rup-error" : isOk ? "bg-rup-success" : void 0
2793
+ }
2794
+ )
2795
+ ] }),
2796
+ /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-1", density === "card" && "justify-end"), children: [
2797
+ onPreview && /* @__PURE__ */ jsx(IconButton, { onClick: () => onPreview(file), label: t.preview, children: /* @__PURE__ */ jsx(EyeIcon, { width: 14, height: 14 }) }),
2798
+ onEdit && /* @__PURE__ */ jsx(IconButton, { onClick: () => onEdit(file), label: t.edit ?? "Edit", children: /* @__PURE__ */ jsx(EditIcon, { width: 14, height: 14 }) }),
2799
+ onDownload && isOk && /* @__PURE__ */ jsx(IconButton, { onClick: () => onDownload(file), label: t.download, children: /* @__PURE__ */ jsx(DownloadIcon, { width: 14, height: 14 }) }),
2800
+ onRetry && (isErr || file.status === "cancelled") && /* @__PURE__ */ jsx(IconButton, { onClick: () => onRetry(file), label: t.retry, children: /* @__PURE__ */ jsx(RetryIcon, { width: 14, height: 14 }) }),
2801
+ onPause && isUploading && /* @__PURE__ */ jsx(IconButton, { onClick: () => onPause(file), label: t.pause, children: /* @__PURE__ */ jsx(PauseIcon, { width: 14, height: 14 }) }),
2802
+ onResume && isPaused && /* @__PURE__ */ jsx(IconButton, { onClick: () => onResume(file), label: t.resume, children: /* @__PURE__ */ jsx(PlayIcon, { width: 14, height: 14 }) }),
2803
+ onCancel && isUploading && /* @__PURE__ */ jsx(IconButton, { onClick: () => onCancel(file), label: t.cancel, children: /* @__PURE__ */ jsx(CloseIcon, { width: 14, height: 14 }) }),
2804
+ onRemove && /* @__PURE__ */ jsx(IconButton, { onClick: () => onRemove(file), label: t.remove, danger: true, children: /* @__PURE__ */ jsx(TrashIcon, { width: 14, height: 14 }) })
2805
+ ] })
2806
+ ]
2807
+ }
2808
+ );
2809
+ }
2810
+ function IconButton({
2811
+ children,
2812
+ onClick,
2813
+ label,
2814
+ danger
2815
+ }) {
2816
+ return /* @__PURE__ */ jsx(
2817
+ "button",
2818
+ {
2819
+ type: "button",
2820
+ onClick,
2821
+ "aria-label": label,
2822
+ title: label,
2823
+ className: cn(
2824
+ "inline-flex h-7 w-7 items-center justify-center rounded-md text-rup-muted transition hover:bg-rup-border/40 hover:text-rup-fg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rup-accent",
2825
+ danger && "hover:bg-rup-error/10 hover:text-rup-error"
2826
+ ),
2827
+ children
2828
+ }
2829
+ );
2830
+ }
2831
+ function UploadGallery({
2832
+ files,
2833
+ layout = "list",
2834
+ reorderable = false,
2835
+ onReorder,
2836
+ searchable = false,
2837
+ filterable = false,
2838
+ previewable = false,
2839
+ editable = false,
2840
+ onRename,
2841
+ onMetadataChange,
2842
+ scrollAfter,
2843
+ maxHeight = "280px",
2844
+ emptyState,
2845
+ className,
2846
+ density,
2847
+ showStats,
2848
+ showThumb,
2849
+ progressVariant,
2850
+ progressSize,
2851
+ onRetry,
2852
+ onPause,
2853
+ onResume,
2854
+ onCancel,
2855
+ onRemove,
2856
+ onPreview,
2857
+ onDownload,
2858
+ onEdit,
2859
+ ...rest
2860
+ }) {
2861
+ const { t } = useI18n();
2862
+ const [query, setQuery] = useState("");
2863
+ const [filter, setFilter] = useState("all");
2864
+ const [dragIndex, setDragIndex] = useState(null);
2865
+ const [previewFile, setPreviewFile] = useState(null);
2866
+ const [editFile, setEditFile] = useState(null);
2867
+ const resolvedOnPreview = onPreview ?? (previewable ? (f) => setPreviewFile(f) : void 0);
2868
+ const resolvedOnEdit = onEdit ?? (editable ? (f) => setEditFile(f) : void 0);
2869
+ const visible = files.filter((f) => {
2870
+ if (query && !f.name.toLowerCase().includes(query.toLowerCase())) return false;
2871
+ if (filter === "uploading" && f.status !== "uploading" && f.status !== "queued") return false;
2872
+ if (filter === "success" && f.status !== "success") return false;
2873
+ if (filter === "error" && f.status !== "error" && f.status !== "cancelled") return false;
2874
+ return true;
2875
+ });
2876
+ const shouldScroll = typeof scrollAfter === "number" && scrollAfter > 0 && visible.length > scrollAfter;
2877
+ const wrapDrag = (index) => reorderable && onReorder ? {
2878
+ draggable: true,
2879
+ onDragStart: (e) => {
2880
+ e.dataTransfer.effectAllowed = "move";
2881
+ setDragIndex(index);
2882
+ },
2883
+ onDragOver: (e) => {
2884
+ e.preventDefault();
2885
+ e.dataTransfer.dropEffect = "move";
2886
+ },
2887
+ onDrop: (e) => {
2888
+ e.preventDefault();
2889
+ if (dragIndex === null || dragIndex === index) return;
2890
+ onReorder(dragIndex, index);
2891
+ setDragIndex(null);
2892
+ },
2893
+ onDragEnd: () => setDragIndex(null)
2894
+ } : {};
2895
+ if (files.length === 0 && emptyState) {
2896
+ return /* @__PURE__ */ jsx("div", { className: cn("text-sm text-rup-muted", className), children: emptyState });
2897
+ }
2898
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3", className), ...rest, children: [
2899
+ (searchable || filterable) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
2900
+ searchable && /* @__PURE__ */ jsx(
2901
+ "input",
2902
+ {
2903
+ type: "search",
2904
+ value: query,
2905
+ onChange: (e) => setQuery(e.target.value),
2906
+ placeholder: "Search\u2026",
2907
+ className: "h-9 flex-1 min-w-[180px] rounded-rup border border-rup-border bg-rup-bg px-3 text-sm text-rup-fg placeholder:text-rup-muted focus:outline-none focus:ring-2 focus:ring-rup-accent"
2908
+ }
2909
+ ),
2910
+ filterable && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: ["all", "uploading", "success", "error"].map((key) => /* @__PURE__ */ jsx(
2911
+ "button",
2912
+ {
2913
+ type: "button",
2914
+ onClick: () => setFilter(key),
2915
+ className: cn(
2916
+ "rounded-full px-3 py-1 text-xs font-medium capitalize transition",
2917
+ filter === key ? "bg-rup-accent text-rup-accent-fg" : "bg-rup-border/30 text-rup-muted hover:bg-rup-border/60"
2918
+ ),
2919
+ children: key === "all" ? "All" : key === "uploading" ? t.uploading : key === "success" ? t.success : t.error
2920
+ },
2921
+ key
2922
+ )) })
2923
+ ] }),
2924
+ /* @__PURE__ */ jsx(ScrollWrapper, { active: shouldScroll, maxHeight, children: /* @__PURE__ */ jsx(
2925
+ "div",
2926
+ {
2927
+ className: cn(
2928
+ layout === "list" && "flex flex-col gap-2",
2929
+ layout === "compact" && "flex flex-col gap-1",
2930
+ layout === "grid" && "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4",
2931
+ layout === "table" && "overflow-hidden rounded-rup border border-rup-border"
2932
+ ),
2933
+ children: visible.map((f, i) => /* @__PURE__ */ jsx("div", { ...wrapDrag(i), children: /* @__PURE__ */ jsx(
2934
+ UploadPreview,
2935
+ {
2936
+ file: f,
2937
+ density: density ?? (layout === "grid" ? "card" : layout === "compact" ? "compact" : "comfortable"),
2938
+ showStats,
2939
+ showThumb,
2940
+ progressVariant,
2941
+ progressSize,
2942
+ onRetry,
2943
+ onPause,
2944
+ onResume,
2945
+ onCancel,
2946
+ onRemove,
2947
+ onPreview: resolvedOnPreview,
2948
+ onDownload,
2949
+ onEdit: resolvedOnEdit,
2950
+ className: cn(layout === "table" && "rounded-none border-x-0 border-t-0 last:border-b-0")
2951
+ }
2952
+ ) }, f.id))
2953
+ }
2954
+ ) }),
2955
+ previewable && !onPreview && /* @__PURE__ */ jsx(FilePreviewModal, { file: previewFile, onClose: () => setPreviewFile(null) }),
2956
+ editable && !onEdit && /* @__PURE__ */ jsx(
2957
+ FileEditModal,
2958
+ {
2959
+ file: editFile,
2960
+ onClose: () => setEditFile(null),
2961
+ onRename,
2962
+ onSave: onMetadataChange
2963
+ }
2964
+ )
2965
+ ] });
2966
+ }
2967
+ function ScrollWrapper({
2968
+ active,
2969
+ maxHeight,
2970
+ children
2971
+ }) {
2972
+ if (!active) return /* @__PURE__ */ jsx(Fragment, { children });
2973
+ return /* @__PURE__ */ jsx(
2974
+ "div",
2975
+ {
2976
+ className: "rup-scrollbar overflow-y-auto pr-1",
2977
+ style: { maxHeight, scrollbarGutter: "stable" },
2978
+ children
2979
+ }
2980
+ );
2981
+ }
2982
+
2983
+ export { AlertIcon, CheckIcon, CloseIcon, DownloadIcon, EditIcon, EyeIcon, FileEditModal, FileIcon, FilePreviewModal, FolderIcon, I18nProvider, ImageIcon, MaximizeIcon, PauseIcon, PlayIcon, RetryIcon, TagIcon, TrashIcon, UploadGallery, UploadIcon, UploadModal, UploadPreview, UploadProgress, UploadQueue, cn, detectSignature, formatBytes, formatEta, formatPercent, formatSpeed, generatePreview, getFileCategory, matchesAccept, revokePreview, rtlLocales, translations, useDropzone, useFilePreview, useI18n, useUploadProgress, useUploadQueue, validateBatch, validateFile, wrapFile };
2984
+ //# sourceMappingURL=chunk-JI2V5ITY.js.map
2985
+ //# sourceMappingURL=chunk-JI2V5ITY.js.map