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