@scaleflex/uploader 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.
Files changed (96) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/LICENSE +50 -0
  3. package/README.md +123 -0
  4. package/dist/auth/auth.service.d.ts +25 -0
  5. package/dist/auth/auth.service.d.ts.map +1 -0
  6. package/dist/auth/auth.types.d.ts +24 -0
  7. package/dist/auth/auth.types.d.ts.map +1 -0
  8. package/dist/auth/index.d.ts +3 -0
  9. package/dist/auth/index.d.ts.map +1 -0
  10. package/dist/components/actions-bar.d.ts +17 -0
  11. package/dist/components/actions-bar.d.ts.map +1 -0
  12. package/dist/components/camera-dialog.d.ts +27 -0
  13. package/dist/components/camera-dialog.d.ts.map +1 -0
  14. package/dist/components/drop-zone.d.ts +28 -0
  15. package/dist/components/drop-zone.d.ts.map +1 -0
  16. package/dist/components/file-item.d.ts +12 -0
  17. package/dist/components/file-item.d.ts.map +1 -0
  18. package/dist/components/file-list.d.ts +8 -0
  19. package/dist/components/file-list.d.ts.map +1 -0
  20. package/dist/components/import-divider.d.ts +6 -0
  21. package/dist/components/import-divider.d.ts.map +1 -0
  22. package/dist/components/index.d.ts +9 -0
  23. package/dist/components/index.d.ts.map +1 -0
  24. package/dist/components/provider-browser.d.ts +48 -0
  25. package/dist/components/provider-browser.d.ts.map +1 -0
  26. package/dist/components/screen-cast-dialog.d.ts +29 -0
  27. package/dist/components/screen-cast-dialog.d.ts.map +1 -0
  28. package/dist/components/search-provider-browser.d.ts +36 -0
  29. package/dist/components/search-provider-browser.d.ts.map +1 -0
  30. package/dist/components/source-pills.d.ts +11 -0
  31. package/dist/components/source-pills.d.ts.map +1 -0
  32. package/dist/components/success-card.d.ts +10 -0
  33. package/dist/components/success-card.d.ts.map +1 -0
  34. package/dist/components/url-dialog.d.ts +25 -0
  35. package/dist/components/url-dialog.d.ts.map +1 -0
  36. package/dist/connectors/companion-client.d.ts +58 -0
  37. package/dist/connectors/companion-client.d.ts.map +1 -0
  38. package/dist/connectors/connector.types.d.ts +56 -0
  39. package/dist/connectors/connector.types.d.ts.map +1 -0
  40. package/dist/connectors/index.d.ts +5 -0
  41. package/dist/connectors/index.d.ts.map +1 -0
  42. package/dist/connectors/provider-registry.d.ts +8 -0
  43. package/dist/connectors/provider-registry.d.ts.map +1 -0
  44. package/dist/connectors/token-store.d.ts +10 -0
  45. package/dist/connectors/token-store.d.ts.map +1 -0
  46. package/dist/controllers/store.controller.d.ts +14 -0
  47. package/dist/controllers/store.controller.d.ts.map +1 -0
  48. package/dist/define.cjs +1 -0
  49. package/dist/define.d.ts +2 -0
  50. package/dist/define.d.ts.map +1 -0
  51. package/dist/define.js +15 -0
  52. package/dist/engine/companion-upload.d.ts +23 -0
  53. package/dist/engine/companion-upload.d.ts.map +1 -0
  54. package/dist/engine/index.d.ts +2 -0
  55. package/dist/engine/index.d.ts.map +1 -0
  56. package/dist/engine/upload-engine.d.ts +56 -0
  57. package/dist/engine/upload-engine.d.ts.map +1 -0
  58. package/dist/engine/xhr-upload.d.ts +25 -0
  59. package/dist/engine/xhr-upload.d.ts.map +1 -0
  60. package/dist/events/event-bus.d.ts +38 -0
  61. package/dist/events/event-bus.d.ts.map +1 -0
  62. package/dist/events/public-events.d.ts +23 -0
  63. package/dist/events/public-events.d.ts.map +1 -0
  64. package/dist/index.cjs +1 -0
  65. package/dist/index.d.ts +17 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +21 -0
  68. package/dist/provider-browser-C-S_MPrC.js +832 -0
  69. package/dist/provider-browser-CmCwv0ph.cjs +581 -0
  70. package/dist/react.cjs +1 -0
  71. package/dist/react.d.ts +33 -0
  72. package/dist/react.d.ts.map +1 -0
  73. package/dist/react.js +125 -0
  74. package/dist/search-provider-browser-DxmLznEB.cjs +390 -0
  75. package/dist/search-provider-browser-jCOer2Y9.js +537 -0
  76. package/dist/sfx-uploader-BVDK-9xi.cjs +2029 -0
  77. package/dist/sfx-uploader-C2lWIRnU.js +3789 -0
  78. package/dist/sfx-uploader.d.ts +131 -0
  79. package/dist/sfx-uploader.d.ts.map +1 -0
  80. package/dist/store/helpers.d.ts +16 -0
  81. package/dist/store/helpers.d.ts.map +1 -0
  82. package/dist/store/index.d.ts +7 -0
  83. package/dist/store/index.d.ts.map +1 -0
  84. package/dist/store/store.d.ts +14 -0
  85. package/dist/store/store.d.ts.map +1 -0
  86. package/dist/store/store.types.d.ts +77 -0
  87. package/dist/store/store.types.d.ts.map +1 -0
  88. package/dist/test-utils.d.ts +5 -0
  89. package/dist/test-utils.d.ts.map +1 -0
  90. package/dist/types/source.types.d.ts +12 -0
  91. package/dist/types/source.types.d.ts.map +1 -0
  92. package/dist/utils/file-utils.d.ts +14 -0
  93. package/dist/utils/file-utils.d.ts.map +1 -0
  94. package/dist/utils/validate.d.ts +6 -0
  95. package/dist/utils/validate.d.ts.map +1 -0
  96. package/package.json +72 -0
@@ -0,0 +1,3789 @@
1
+ import { LitElement as w, css as _, svg as fe, html as c, nothing as h } from "lit";
2
+ import { property as x, state as g, query as he } from "lit/decorators.js";
3
+ import { unsafeSVG as ge } from "lit/directives/unsafe-svg.js";
4
+ class xe {
5
+ constructor(e) {
6
+ this.listeners = /* @__PURE__ */ new Set(), this._notifying = !1, this._pendingState = null, this.state = e;
7
+ }
8
+ getState() {
9
+ return this.state;
10
+ }
11
+ setState(e) {
12
+ if (this._notifying) {
13
+ this._pendingState = { ...this._pendingState || {}, ...e };
14
+ return;
15
+ }
16
+ const t = this.state;
17
+ this.state = { ...t, ...e }, this._notifying = !0;
18
+ try {
19
+ this.listeners.forEach((r) => r(this.state, t));
20
+ } finally {
21
+ this._notifying = !1;
22
+ }
23
+ if (this._pendingState) {
24
+ const r = this._pendingState;
25
+ this._pendingState = null, this.setState(r);
26
+ }
27
+ }
28
+ subscribe(e) {
29
+ return this.listeners.add(e), () => this.listeners.delete(e);
30
+ }
31
+ destroy() {
32
+ this.listeners.clear();
33
+ }
34
+ }
35
+ function y(i, e, t) {
36
+ const r = i.getState().files, s = r.get(e);
37
+ if (!s) return;
38
+ const o = new Map(r);
39
+ o.set(e, { ...s, ...t }), i.setState({ files: o });
40
+ }
41
+ function j(i, e) {
42
+ const t = new Map(i.getState().files);
43
+ t.set(e.id, e), i.setState({ files: t });
44
+ }
45
+ function me(i, e) {
46
+ const t = i.getState().files;
47
+ if (!t.has(e)) return;
48
+ const r = new Map(t);
49
+ r.delete(e), i.setState({ files: r });
50
+ }
51
+ function ve() {
52
+ return new xe({
53
+ files: /* @__PURE__ */ new Map(),
54
+ queueConfig: {
55
+ concurrency: 3,
56
+ autoProceed: !1,
57
+ retryConfig: {
58
+ maxRetries: 0,
59
+ baseDelay: 1e3,
60
+ maxDelay: 3e4,
61
+ backoffFactor: 2
62
+ }
63
+ },
64
+ isPaused: !1,
65
+ restrictions: {
66
+ maxFileSize: null,
67
+ maxTotalFilesSize: null,
68
+ maxNumberOfFiles: null,
69
+ minNumberOfFiles: null,
70
+ allowedFileTypes: null,
71
+ blockedFileTypes: null
72
+ },
73
+ targetFolder: "/",
74
+ totalProgress: 0,
75
+ totalSpeed: 0,
76
+ totalBytesUploaded: 0,
77
+ totalBytes: 0,
78
+ isUploading: !1
79
+ });
80
+ }
81
+ class ye {
82
+ constructor(e, t) {
83
+ this.host = e, this.store = t, e.addController(this);
84
+ }
85
+ get state() {
86
+ return this.store.getState();
87
+ }
88
+ setState(e) {
89
+ this.store.setState(e);
90
+ }
91
+ hostConnected() {
92
+ this.unsubscribe = this.store.subscribe(() => {
93
+ this.host.requestUpdate();
94
+ });
95
+ }
96
+ hostDisconnected() {
97
+ var e;
98
+ (e = this.unsubscribe) == null || e.call(this);
99
+ }
100
+ }
101
+ function we(i, e) {
102
+ const t = new XMLHttpRequest();
103
+ let r = !1;
104
+ const o = `${e.apiBase.replace(/\/+$/, "")}/v4/files?folder=${encodeURIComponent(e.folder)}`;
105
+ t.open("POST", o);
106
+ for (const [a, l] of Object.entries(e.authHeaders))
107
+ t.setRequestHeader(a, l);
108
+ t.upload.addEventListener("progress", (a) => {
109
+ a.lengthComputable && !r && e.onProgress(a.loaded, a.total);
110
+ }), t.addEventListener("load", () => {
111
+ if (r) return;
112
+ let a;
113
+ try {
114
+ a = JSON.parse(t.responseText);
115
+ } catch {
116
+ e.onError(new Error(`Invalid JSON response (HTTP ${t.status})`));
117
+ return;
118
+ }
119
+ t.status >= 200 && t.status < 300 && a.status === "success" ? e.onComplete(a) : e.onError(new Error(a.msg || `Upload failed (HTTP ${t.status})`));
120
+ }), t.addEventListener("error", () => {
121
+ r || e.onError(new Error("Network error — check your connection"));
122
+ }), t.addEventListener("timeout", () => {
123
+ r || e.onError(new Error("Upload timed out"));
124
+ });
125
+ const n = new FormData();
126
+ if (i.file) {
127
+ const a = {
128
+ name: i.name,
129
+ type: i.type
130
+ };
131
+ Object.keys(i.meta).length > 0 && (a.meta = i.meta), i.tags.length > 0 && (a.tags = i.tags), n.append("info[files[]]", JSON.stringify(a)), n.append("files[]", i.file, i.name);
132
+ }
133
+ return t.timeout = 6e4, t.send(n), {
134
+ abort() {
135
+ r = !0, t.abort();
136
+ }
137
+ };
138
+ }
139
+ function _e(i, e) {
140
+ const t = new XMLHttpRequest();
141
+ let r = !1;
142
+ const o = `${e.apiBase.replace(/\/+$/, "")}/v4/files/upload_url`;
143
+ t.open("POST", o);
144
+ for (const [a, l] of Object.entries(e.authHeaders))
145
+ t.setRequestHeader(a, l);
146
+ if (t.setRequestHeader("Content-Type", "application/json"), t.addEventListener("load", () => {
147
+ if (r) return;
148
+ let a;
149
+ try {
150
+ a = JSON.parse(t.responseText);
151
+ } catch {
152
+ e.onError(new Error(`Invalid JSON response (HTTP ${t.status})`));
153
+ return;
154
+ }
155
+ t.status >= 200 && t.status < 300 && a.status === "success" ? e.onComplete(a) : e.onError(new Error(a.msg || `Upload failed (HTTP ${t.status})`));
156
+ }), t.addEventListener("error", () => {
157
+ r || e.onError(new Error("Network error — check your connection"));
158
+ }), t.addEventListener("timeout", () => {
159
+ r || e.onError(new Error("Upload timed out"));
160
+ }), !i.remoteUrl)
161
+ return e.onError(new Error("Remote URL is required for URL upload")), { abort() {
162
+ } };
163
+ const n = {
164
+ files_urls: [{ url: i.remoteUrl, name: i.name }],
165
+ dir: e.folder
166
+ };
167
+ return t.timeout = 6e4, t.send(JSON.stringify(n)), {
168
+ abort() {
169
+ r = !0, t.abort();
170
+ }
171
+ };
172
+ }
173
+ function N(i) {
174
+ return {
175
+ Accept: "application/json",
176
+ "Content-Type": "application/json",
177
+ "uppy-auth-token": i
178
+ };
179
+ }
180
+ function O(i) {
181
+ return i.replace(/\/+$/, "");
182
+ }
183
+ const ke = {
184
+ "google-drive": "drive",
185
+ dropbox: "dropbox",
186
+ onedrive: "onedrive",
187
+ box: "box",
188
+ instagram: "instagram",
189
+ facebook: "facebook",
190
+ unsplash: "unsplash"
191
+ };
192
+ function M(i) {
193
+ return ke[i] ?? i;
194
+ }
195
+ function nt(i, e) {
196
+ const t = O(i), r = btoa(JSON.stringify({ origin: window.location.origin })), s = M(e);
197
+ return `${t}/${s}/connect?state=${encodeURIComponent(r)}`;
198
+ }
199
+ async function at(i, e, t, r = "") {
200
+ const s = O(i), o = r ? `/${r}` : "", n = M(e), a = await fetch(`${s}/${n}/list${o}`, {
201
+ method: "GET",
202
+ headers: N(t),
203
+ credentials: "same-origin"
204
+ });
205
+ if (a.status === 401)
206
+ throw new Q();
207
+ if (!a.ok) {
208
+ const l = await a.json().catch(() => null);
209
+ throw new Error((l == null ? void 0 : l.message) || `Companion list failed (HTTP ${a.status})`);
210
+ }
211
+ return a.json();
212
+ }
213
+ async function lt(i, e, t) {
214
+ const r = O(i), s = await fetch(`${r}/${t}`, {
215
+ method: "GET",
216
+ headers: N(e),
217
+ credentials: "same-origin"
218
+ });
219
+ if (s.status === 401)
220
+ throw new Q();
221
+ if (!s.ok) {
222
+ const o = await s.json().catch(() => null);
223
+ throw new Error((o == null ? void 0 : o.message) || `Companion list failed (HTTP ${s.status})`);
224
+ }
225
+ return s.json();
226
+ }
227
+ async function ct(i, e, t, r) {
228
+ const s = O(i), o = M(e), n = r ? `q=${encodeURIComponent(t)}&${r}` : `q=${encodeURIComponent(t)}`, a = await fetch(`${s}/search/${o}/list?${n}`, {
229
+ method: "GET",
230
+ headers: {
231
+ Accept: "application/json",
232
+ "Content-Type": "application/json"
233
+ },
234
+ credentials: "same-origin"
235
+ });
236
+ if (!a.ok) {
237
+ const l = await a.json().catch(() => null);
238
+ throw new Error((l == null ? void 0 : l.message) || `Search failed (HTTP ${a.status})`);
239
+ }
240
+ return a.json();
241
+ }
242
+ async function Ce(i, e, t, r, s, o = !1) {
243
+ const n = O(i), a = M(e), l = o ? `${n}/search/${a}/get/${r}` : `${n}/${a}/get/${r}`, p = o ? { Accept: "application/json", "Content-Type": "application/json" } : N(t), b = await fetch(l, {
244
+ method: "POST",
245
+ headers: p,
246
+ credentials: "same-origin",
247
+ body: JSON.stringify({
248
+ ...s,
249
+ httpMethod: s.httpMethod ?? "POST",
250
+ useFormData: s.useFormData ?? !0,
251
+ fieldname: s.fieldname ?? "files[]"
252
+ })
253
+ });
254
+ if (b.status === 401)
255
+ throw new Q();
256
+ if (!b.ok) {
257
+ const u = await b.json().catch(() => null);
258
+ throw new Error((u == null ? void 0 : u.message) || `Companion upload failed (HTTP ${b.status})`);
259
+ }
260
+ return b.json();
261
+ }
262
+ async function dt(i, e, t) {
263
+ const r = O(i), s = M(e), o = await fetch(`${r}/${s}/logout`, {
264
+ method: "GET",
265
+ headers: N(t),
266
+ credentials: "same-origin"
267
+ });
268
+ return o.ok ? o.json() : { ok: !1, revoked: !1 };
269
+ }
270
+ function Ee(i) {
271
+ var s;
272
+ const t = ((s = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/i.exec(i)) == null ? void 0 : s[1]) ?? i;
273
+ return `${location.protocol === "https:" ? "wss" : "ws"}://${t}`;
274
+ }
275
+ class Q extends Error {
276
+ constructor() {
277
+ super("Authentication expired"), this.name = "AuthExpiredError";
278
+ }
279
+ }
280
+ function $e(i, e) {
281
+ const t = i.remoteInfo;
282
+ if (!t)
283
+ return e.onError(new Error("remoteInfo is required for companion upload")), { abort() {
284
+ } };
285
+ let r = !1, s = null;
286
+ const n = `${e.apiBase.replace(/\/+$/, "")}/v4/files?folder=${encodeURIComponent(e.folder)}`, a = {};
287
+ i.meta && Object.keys(i.meta).length > 0 && Object.assign(a, i.meta), i.tags && i.tags.length > 0 && (a.tags = i.tags);
288
+ const l = !t.token;
289
+ return Ce(t.companionUrl, t.provider, t.token, t.requestPath, {
290
+ fileId: t.fileId,
291
+ endpoint: n,
292
+ headers: e.authHeaders,
293
+ size: t.size,
294
+ metadata: Object.keys(a).length > 0 ? a : void 0
295
+ }, l).then((p) => {
296
+ if (r) return;
297
+ const u = `${Ee(t.companionUrl)}/api/${p.token}`;
298
+ try {
299
+ s = new WebSocket(u);
300
+ } catch {
301
+ e.onError(new Error("Failed to connect to upload progress channel"));
302
+ return;
303
+ }
304
+ s.onmessage = (d) => {
305
+ var v, E, S;
306
+ if (!r)
307
+ try {
308
+ const F = JSON.parse(d.data);
309
+ switch (F.action) {
310
+ case "progress": {
311
+ const k = F.payload, C = k.bytesUploaded ?? 0, L = k.bytesTotal ?? (t.size || 1);
312
+ e.onProgress(C, L);
313
+ break;
314
+ }
315
+ case "success": {
316
+ const k = F.payload;
317
+ if (s == null || s.close(), (v = k.response) != null && v.responseText)
318
+ try {
319
+ const C = JSON.parse(k.response.responseText);
320
+ if (C.status === "success") {
321
+ e.onComplete(C);
322
+ return;
323
+ }
324
+ e.onError(new Error(C.msg || "Upload failed"));
325
+ return;
326
+ } catch {
327
+ }
328
+ e.onError(new Error("Upload completed but no valid response received"));
329
+ break;
330
+ }
331
+ case "error": {
332
+ s == null || s.close();
333
+ const k = F.payload;
334
+ let C = ((E = k.error) == null ? void 0 : E.message) || "Upload failed";
335
+ if ((S = k.response) != null && S.responseText)
336
+ try {
337
+ const L = JSON.parse(k.response.responseText);
338
+ C = L.hint || L.msg || L.message || C;
339
+ } catch {
340
+ }
341
+ e.onError(new Error(C));
342
+ break;
343
+ }
344
+ }
345
+ } catch {
346
+ }
347
+ }, s.onerror = () => {
348
+ r || e.onError(new Error("Upload progress connection failed"));
349
+ }, s.onclose = () => {
350
+ s = null;
351
+ };
352
+ }).catch((p) => {
353
+ r || e.onError(p instanceof Error ? p : new Error(String(p)));
354
+ }), {
355
+ abort() {
356
+ if (r = !0, s) {
357
+ try {
358
+ s.send(JSON.stringify({ action: "cancel", payload: {} }));
359
+ } catch {
360
+ }
361
+ s.close(), s = null;
362
+ }
363
+ }
364
+ };
365
+ }
366
+ class Se {
367
+ constructor(e, t) {
368
+ this.activeUploads = /* @__PURE__ */ new Map(), this.retryTimers = /* @__PURE__ */ new Map(), this.unsubscribe = null, this.store = e, this.config = t;
369
+ }
370
+ /**
371
+ * Start processing the queue. Subscribes to store changes to
372
+ * automatically pick up newly queued files.
373
+ */
374
+ start() {
375
+ this.unsubscribe || (this.unsubscribe = this.store.subscribe(() => this.processQueue()), this.processQueue());
376
+ }
377
+ /**
378
+ * Upload all queued files. Transitions idle files → queued, then processes.
379
+ */
380
+ uploadAll() {
381
+ const { files: e } = this.store.getState();
382
+ let t = !1;
383
+ for (const r of e.values())
384
+ r.status === "idle" ? (y(this.store, r.id, { status: "queued" }), t = !0) : r.status === "queued" && (t = !0);
385
+ t && (this.store.setState({ isUploading: !0 }), this.processQueue());
386
+ }
387
+ /**
388
+ * Retry a single failed/errored file.
389
+ */
390
+ retryFile(e) {
391
+ const t = this.store.getState().files.get(e);
392
+ !t || t.status !== "error" && t.status !== "failed" || (y(this.store, e, {
393
+ status: "queued",
394
+ error: null,
395
+ progress: 0,
396
+ bytesUploaded: 0,
397
+ speed: 0
398
+ }), this.processQueue());
399
+ }
400
+ /**
401
+ * Retry all failed/errored files.
402
+ */
403
+ retryAll() {
404
+ const { files: e } = this.store.getState();
405
+ for (const t of e.values())
406
+ (t.status === "error" || t.status === "failed") && y(this.store, t.id, {
407
+ status: "queued",
408
+ error: null,
409
+ progress: 0,
410
+ bytesUploaded: 0,
411
+ speed: 0
412
+ });
413
+ this.processQueue();
414
+ }
415
+ /**
416
+ * Cancel a single file upload.
417
+ */
418
+ cancelFile(e) {
419
+ this.abortUpload(e), y(this.store, e, { status: "cancelled" });
420
+ }
421
+ /**
422
+ * Cancel all active/queued uploads.
423
+ */
424
+ cancelAll() {
425
+ const { files: e } = this.store.getState();
426
+ for (const t of e.values())
427
+ Ue(t.status) && (this.abortUpload(t.id), y(this.store, t.id, { status: "cancelled" }));
428
+ this.store.setState({ isUploading: !1 });
429
+ }
430
+ /**
431
+ * Update auth config (e.g. after SASS key renewal).
432
+ */
433
+ updateConfig(e) {
434
+ Object.assign(this.config, e);
435
+ }
436
+ /**
437
+ * Clean up: abort all uploads, clear timers, unsubscribe.
438
+ */
439
+ destroy() {
440
+ var e;
441
+ for (const t of this.activeUploads.keys())
442
+ this.abortUpload(t);
443
+ for (const t of this.retryTimers.values())
444
+ clearTimeout(t);
445
+ this.retryTimers.clear(), (e = this.unsubscribe) == null || e.call(this), this.unsubscribe = null;
446
+ }
447
+ // --- Private ---
448
+ processQueue() {
449
+ const e = this.store.getState();
450
+ if (e.isPaused) return;
451
+ const { concurrency: t } = e.queueConfig, r = this.activeUploads.size, s = t - r;
452
+ if (s <= 0) return;
453
+ const n = [...e.files.values()].filter((a) => a.status === "queued").sort((a, l) => a.retryCount !== l.retryCount ? l.retryCount - a.retryCount : a.addedAt - l.addedAt).slice(0, s);
454
+ for (const a of n)
455
+ this.startUpload(a);
456
+ }
457
+ startUpload(e) {
458
+ y(this.store, e.id, { status: "uploading", error: null });
459
+ let t = 0, r = Date.now();
460
+ const s = {
461
+ apiBase: this.config.apiBase,
462
+ authHeaders: this.config.authHeaders,
463
+ folder: this.store.getState().targetFolder,
464
+ onComplete: (a) => this.handleComplete(e.id, a),
465
+ onError: (a) => this.handleError(e.id, a)
466
+ }, o = (a, l) => {
467
+ const p = Date.now(), b = (p - r) / 1e3, u = b > 0 ? (a - t) / b : 0;
468
+ t = a, r = p;
469
+ const d = l > 0 ? a / l * 100 : 0;
470
+ y(this.store, e.id, { progress: d, bytesUploaded: a, speed: u }), this.updateTotalProgress();
471
+ };
472
+ let n;
473
+ e.remoteInfo ? n = $e(e, { ...s, onProgress: o }) : e.remoteUrl ? n = _e(e, s) : n = we(e, { ...s, onProgress: o }), this.activeUploads.set(e.id, n);
474
+ }
475
+ handleComplete(e, t) {
476
+ this.activeUploads.delete(e), y(this.store, e, {
477
+ status: "complete",
478
+ progress: 100,
479
+ response: t
480
+ }), this.updateTotalProgress(), this.checkAllComplete(), this.processQueue();
481
+ }
482
+ handleError(e, t) {
483
+ this.activeUploads.delete(e);
484
+ const r = this.store.getState().files.get(e);
485
+ if (!r) return;
486
+ const { retryConfig: s } = this.store.getState().queueConfig, o = r.retryCount + 1;
487
+ if (o <= s.maxRetries) {
488
+ const n = Math.min(
489
+ s.baseDelay * Math.pow(s.backoffFactor, r.retryCount),
490
+ s.maxDelay
491
+ );
492
+ y(this.store, e, {
493
+ status: "retrying",
494
+ error: t.message,
495
+ retryCount: o
496
+ });
497
+ const a = setTimeout(() => {
498
+ this.retryTimers.delete(e), y(this.store, e, { status: "queued" }), this.processQueue();
499
+ }, n);
500
+ this.retryTimers.set(e, a);
501
+ } else
502
+ y(this.store, e, {
503
+ status: "failed",
504
+ error: t.message
505
+ }), this.checkAllComplete(), this.processQueue();
506
+ }
507
+ abortUpload(e) {
508
+ var r;
509
+ (r = this.activeUploads.get(e)) == null || r.abort(), this.activeUploads.delete(e);
510
+ const t = this.retryTimers.get(e);
511
+ t && (clearTimeout(t), this.retryTimers.delete(e));
512
+ }
513
+ updateTotalProgress() {
514
+ const { files: e } = this.store.getState();
515
+ let t = 0, r = 0, s = 0;
516
+ for (const o of e.values())
517
+ (o.status === "uploading" || o.status === "complete") && (t += o.size, r += o.bytesUploaded), o.status === "uploading" && (s += o.speed);
518
+ this.store.setState({
519
+ totalBytes: t,
520
+ totalBytesUploaded: r,
521
+ totalSpeed: s,
522
+ totalProgress: t > 0 ? r / t * 100 : 0
523
+ });
524
+ }
525
+ checkAllComplete() {
526
+ const { files: e } = this.store.getState();
527
+ ![...e.values()].some(
528
+ (r) => r.status === "queued" || r.status === "uploading" || r.status === "retrying" || r.status === "preparing"
529
+ ) && this.store.getState().isUploading && this.store.setState({ isUploading: !1 });
530
+ }
531
+ }
532
+ function Ue(i) {
533
+ return i === "queued" || i === "uploading" || i === "retrying" || i === "preparing";
534
+ }
535
+ function Z(i) {
536
+ return `https://api.filerobot.com/${i}`;
537
+ }
538
+ async function Pe(i, e) {
539
+ const t = `${Z(i)}/key/${encodeURIComponent(e)}`, r = new AbortController(), s = setTimeout(() => r.abort(), 3e4);
540
+ try {
541
+ const o = await fetch(t, { signal: r.signal });
542
+ if (clearTimeout(s), !o.ok)
543
+ throw new Error(`SASS key exchange failed (HTTP ${o.status})`);
544
+ const n = await o.json();
545
+ if (n.status === "error")
546
+ throw new Error(`SASS key exchange failed: ${n.msg || "Unknown error"}`);
547
+ return n.key;
548
+ } catch (o) {
549
+ throw clearTimeout(s), o instanceof DOMException && o.name === "AbortError" ? new Error("SASS key exchange timed out") : o;
550
+ }
551
+ }
552
+ function J(i, e) {
553
+ const t = {};
554
+ switch (i.mode) {
555
+ case "security-template":
556
+ t["X-Filerobot-Key"] = e ?? i.securityTemplateId;
557
+ break;
558
+ case "sass-key":
559
+ t["X-Filerobot-Key"] = i.sassKey;
560
+ break;
561
+ case "session":
562
+ t["X-Filerobot-Session"] = i.sessionToken, i.companyToken && (t["X-Company-Token"] = i.companyToken), i.projectToken && (t["X-Project-Token"] = i.projectToken);
563
+ break;
564
+ }
565
+ return i.airboxPuid && (t["X-Filerobot-Airbox-Puid"] = i.airboxPuid), t;
566
+ }
567
+ async function ze(i) {
568
+ const e = Z(i.container);
569
+ if (i.mode === "security-template") {
570
+ const t = await Pe(i.container, i.securityTemplateId);
571
+ return { apiBase: e, headers: J(i, t), sassKey: t };
572
+ }
573
+ return { apiBase: e, headers: J(i) };
574
+ }
575
+ const f = {
576
+ FILE_ADDED: "sfx-file-added",
577
+ FILE_REMOVED: "sfx-file-removed",
578
+ FILE_REJECTED: "sfx-file-rejected",
579
+ UPLOAD_STARTED: "sfx-upload-started",
580
+ UPLOAD_PROGRESS: "sfx-upload-progress",
581
+ UPLOAD_COMPLETE: "sfx-upload-complete",
582
+ UPLOAD_ERROR: "sfx-upload-error",
583
+ UPLOAD_RETRY: "sfx-upload-retry",
584
+ ALL_COMPLETE: "sfx-all-complete",
585
+ TOTAL_PROGRESS: "sfx-total-progress",
586
+ BEFORE_UPLOAD: "sfx-before-upload",
587
+ OPEN: "sfx-open",
588
+ CLOSE: "sfx-close",
589
+ CANCEL: "sfx-cancel",
590
+ SOURCE_CHANGE: "sfx-source-change",
591
+ COMPLETE_ACTION: "sfx-complete-action",
592
+ CONNECTOR_AUTH: "sfx-connector-auth",
593
+ CONNECTOR_FILES_ADDED: "sfx-connector-files-added",
594
+ FILE_PREVIEW: "sfx-file-preview",
595
+ FILL_METADATA: "sfx-fill-metadata"
596
+ };
597
+ let Re = 0;
598
+ function B() {
599
+ return `file-${Date.now()}-${++Re}`;
600
+ }
601
+ function Ae(i) {
602
+ if (i === 0) return "0 B";
603
+ const e = ["B", "KB", "MB", "GB"], t = Math.min(Math.floor(Math.log(i) / Math.log(1024)), e.length - 1), r = i / Math.pow(1024, t);
604
+ return `${t === 0 ? r : r.toFixed(1)} ${e[t]}`;
605
+ }
606
+ function Oe(i) {
607
+ var t;
608
+ const e = ((t = i.name.split(".").pop()) == null ? void 0 : t.toLowerCase()) ?? "";
609
+ return i.type.startsWith("image/") ? "image" : i.type.startsWith("video/") || ["mp4", "mov", "avi", "webm", "mkv"].includes(e) ? "vid" : i.type === "application/pdf" || e === "pdf" ? "pdf" : ["doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "rtf", "odt"].includes(e) ? "doc" : ["zip", "rar", "7z", "tar", "gz", "bz2"].includes(e) ? "zip" : "gen";
610
+ }
611
+ function Te(i) {
612
+ const e = i.lastIndexOf(".");
613
+ return e >= 0 ? i.slice(e + 1).toUpperCase() : "";
614
+ }
615
+ const De = {
616
+ jpg: "image/jpeg",
617
+ jpeg: "image/jpeg",
618
+ png: "image/png",
619
+ gif: "image/gif",
620
+ webp: "image/webp",
621
+ svg: "image/svg+xml",
622
+ bmp: "image/bmp",
623
+ ico: "image/x-icon",
624
+ mp4: "video/mp4",
625
+ mov: "video/quicktime",
626
+ avi: "video/x-msvideo",
627
+ webm: "video/webm",
628
+ pdf: "application/pdf",
629
+ zip: "application/zip",
630
+ doc: "application/msword",
631
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
632
+ };
633
+ function Le(i) {
634
+ var t;
635
+ const e = ((t = i.split(".").pop()) == null ? void 0 : t.toLowerCase()) ?? "";
636
+ return De[e] || "";
637
+ }
638
+ function Me(i, e, t) {
639
+ var r, s;
640
+ if (e.maxFileSize != null && i.size > e.maxFileSize)
641
+ return `File exceeds ${(e.maxFileSize / 1048576).toFixed(1)} MB limit`;
642
+ if (e.maxTotalFilesSize != null) {
643
+ let o = i.size;
644
+ for (const n of t.values()) o += n.size;
645
+ if (o > e.maxTotalFilesSize)
646
+ return "Total file size limit exceeded";
647
+ }
648
+ if (e.maxNumberOfFiles != null && t.size >= e.maxNumberOfFiles)
649
+ return `Maximum ${e.maxNumberOfFiles} files allowed`;
650
+ if (e.allowedFileTypes != null) {
651
+ const o = e.allowedFileTypes, n = "." + (((r = i.name.split(".").pop()) == null ? void 0 : r.toLowerCase()) ?? "");
652
+ if (!o.some((l) => l.startsWith(".") ? n === l.toLowerCase() : l.endsWith("/*") ? i.type.startsWith(l.slice(0, -1)) : i.type === l)) return "File type not allowed";
653
+ }
654
+ if (e.blockedFileTypes != null) {
655
+ const o = e.blockedFileTypes, n = "." + (((s = i.name.split(".").pop()) == null ? void 0 : s.toLowerCase()) ?? "");
656
+ if (o.some((l) => l.startsWith(".") ? n === l.toLowerCase() : i.type === l)) return "File type is blocked";
657
+ }
658
+ return null;
659
+ }
660
+ function Ie(i) {
661
+ return i.allowedFileTypes ? i.allowedFileTypes.join(",") : "";
662
+ }
663
+ const de = {
664
+ "google-drive": {
665
+ id: "google-drive",
666
+ label: "Google Drive",
667
+ fillIcon: !0,
668
+ icon: '<path d="M7.71 3.5L1.15 15l2.98 5.16h3.46L1.15 8.66 4.13 3.5h3.58zm1.04 0L15.3 15l-2.98 5.16H8.86L15.3 8.66 12.33 3.5H8.75zm7.54 5.16L22.85 15l-2.98 5.16-6.56-11.5h6.56z" fill="currentColor"/>'
669
+ },
670
+ dropbox: {
671
+ id: "dropbox",
672
+ label: "Dropbox",
673
+ fillIcon: !0,
674
+ icon: '<path d="M12 2L6.5 5.75 12 9.5l5.5-3.75L12 2zM6.5 5.75L1 9.5l5.5 3.75L12 9.5 6.5 5.75zM17.5 5.75L12 9.5l5.5 3.75L23 9.5l-5.5-3.75zM1 9.5l5.5 3.75L12 9.5 6.5 5.75 1 9.5zm22 0l-5.5 3.75L12 9.5l5.5-3.75L23 9.5zM6.5 14.5L12 18.25l5.5-3.75L12 10.75 6.5 14.5z" fill="currentColor"/>'
675
+ },
676
+ onedrive: {
677
+ id: "onedrive",
678
+ label: "OneDrive",
679
+ fillIcon: !0,
680
+ icon: '<path d="M10.07 8.82a5.5 5.5 0 0 1 8.6 1.43A4.5 4.5 0 0 1 19.5 19H6a4 4 0 0 1-.67-7.95 5.49 5.49 0 0 1 4.74-2.23z" fill="currentColor"/>'
681
+ },
682
+ box: {
683
+ id: "box",
684
+ label: "Box",
685
+ fillIcon: !0,
686
+ icon: '<path d="M12 2L3 7v10l9 5 9-5V7l-9-5zm0 2.18L18.36 7.5 12 10.82 5.64 7.5 12 4.18zM5 8.82l6 3.33v6.67l-6-3.33V8.82zm14 0v6.67l-6 3.33v-6.67l6-3.33z" fill="currentColor"/>'
687
+ },
688
+ instagram: {
689
+ id: "instagram",
690
+ label: "Instagram",
691
+ fillIcon: !0,
692
+ icon: '<path d="M12 2.16c2.94 0 3.29.01 4.45.06 1.07.05 1.8.22 2.43.46.66.25 1.21.6 1.77 1.16.55.55.9 1.1 1.16 1.77.25.64.41 1.37.46 2.43.05 1.16.06 1.51.06 4.45s-.01 3.29-.06 4.45c-.05 1.07-.22 1.8-.46 2.43a4.9 4.9 0 0 1-1.16 1.77c-.55.55-1.1.9-1.77 1.16-.64.25-1.37.41-2.43.46-1.16.05-1.51.06-4.45.06s-3.29-.01-4.45-.06c-1.07-.05-1.8-.22-2.43-.46a4.9 4.9 0 0 1-1.77-1.16 4.9 4.9 0 0 1-1.16-1.77c-.25-.64-.41-1.37-.46-2.43C2.17 15.29 2.16 14.94 2.16 12s.01-3.29.06-4.45c.05-1.07.22-1.8.46-2.43a4.9 4.9 0 0 1 1.16-1.77A4.9 4.9 0 0 1 5.61 2.2c.64-.25 1.37-.41 2.43-.46C9.21 2.17 9.56 2.16 12 2.16zM12 0C8.97 0 8.6.01 7.43.07 6.26.12 5.45.3 4.73.58a6.9 6.9 0 0 0-2.5 1.63A6.9 6.9 0 0 0 .58 4.73C.3 5.45.12 6.26.07 7.43.01 8.6 0 8.97 0 12s.01 3.4.07 4.57c.05 1.17.23 1.98.51 2.7a6.9 6.9 0 0 0 1.63 2.5 6.9 6.9 0 0 0 2.5 1.63c.72.28 1.53.46 2.7.51C8.6 23.99 8.97 24 12 24s3.4-.01 4.57-.07c1.17-.05 1.98-.23 2.7-.51a6.9 6.9 0 0 0 2.5-1.63 6.9 6.9 0 0 0 1.63-2.5c.28-.72.46-1.53.51-2.7.06-1.17.07-1.54.07-4.57s-.01-3.4-.07-4.57c-.05-1.17-.23-1.98-.51-2.7a6.9 6.9 0 0 0-1.63-2.5A6.9 6.9 0 0 0 19.27.58C18.55.3 17.74.12 16.57.07 15.4.01 15.03 0 12 0zm0 5.84a6.16 6.16 0 1 0 0 12.32 6.16 6.16 0 0 0 0-12.32zM12 16a4 4 0 1 1 0-8 4 4 0 0 1 0 8zm6.4-11.85a1.44 1.44 0 1 0 0 2.88 1.44 1.44 0 0 0 0-2.88z" fill="currentColor"/>'
693
+ },
694
+ facebook: {
695
+ id: "facebook",
696
+ label: "Facebook",
697
+ fillIcon: !0,
698
+ icon: '<path d="M24 12.07C24 5.41 18.63 0 12 0S0 5.41 0 12.07c0 6.02 4.39 11.02 10.12 11.93v-8.44H7.08v-3.49h3.04V9.41c0-3.02 1.79-4.69 4.53-4.69 1.31 0 2.68.24 2.68.24v2.97h-1.51c-1.49 0-1.95.93-1.95 1.89v2.26h3.33l-.53 3.49h-2.8v8.44C19.61 23.09 24 18.09 24 12.07z" fill="currentColor"/>'
699
+ },
700
+ unsplash: {
701
+ id: "unsplash",
702
+ label: "Unsplash",
703
+ fillIcon: !0,
704
+ icon: '<path d="M7.5 6.75V0h9v6.75h-9zM0 24V10.5h7.5v6.75h9V10.5H24V24H0z" fill="currentColor"/>'
705
+ }
706
+ };
707
+ function Fe(i) {
708
+ return i.filter((e) => e in de).map((e) => de[e]);
709
+ }
710
+ var je = Object.defineProperty, Be = (i, e, t, r) => {
711
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
712
+ (n = i[o]) && (s = n(e, t, s) || s);
713
+ return s && je(e, t, s), s;
714
+ };
715
+ const He = '<rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/>', qe = '<path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71"/>', Ne = '<path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/><circle cx="12" cy="13" r="4"/>', Ve = '<rect x="2" y="3" width="20" height="14" rx="2"/><circle cx="12" cy="10" r="3"/><path d="M7 21h10"/>', H = [
716
+ { id: "device", label: "My Device", icon: He },
717
+ { id: "url", label: "URL link", icon: qe },
718
+ { id: "camera", label: "Camera", icon: Ne },
719
+ { id: "screen-cast", label: "Screen cast", icon: Ve }
720
+ ], ee = class ee extends w {
721
+ constructor() {
722
+ super(...arguments), this.sources = H;
723
+ }
724
+ _handleClick(e) {
725
+ this.dispatchEvent(
726
+ new CustomEvent("source-click", {
727
+ detail: { source: e.id },
728
+ bubbles: !0,
729
+ composed: !0
730
+ })
731
+ );
732
+ }
733
+ render() {
734
+ return c`
735
+ ${this.sources.map(
736
+ (e) => c`
737
+ <button @click=${() => this._handleClick(e)}>
738
+ ${fe`<svg viewBox="0 0 24 24" class=${e.fillIcon ? "fill-icon" : ""}>${ge(e.icon)}</svg>`}
739
+ ${e.label}
740
+ </button>
741
+ `
742
+ )}
743
+ `;
744
+ }
745
+ };
746
+ ee.styles = _`
747
+ :host {
748
+ display: flex;
749
+ flex-wrap: wrap;
750
+ gap: 8px;
751
+ justify-content: center;
752
+ }
753
+
754
+ button {
755
+ display: inline-flex;
756
+ align-items: center;
757
+ gap: 9px;
758
+ padding: 13px 24px;
759
+ border-radius: 50px;
760
+ border: 1.5px solid var(--sfx-up-border, #e8edf5);
761
+ background: var(--sfx-up-bg, #fff);
762
+ font-size: 14px;
763
+ font-weight: 500;
764
+ color: var(--sfx-up-text-secondary, #475569);
765
+ cursor: pointer;
766
+ transition: all 0.18s ease;
767
+ white-space: nowrap;
768
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
769
+ font-family: inherit;
770
+ }
771
+
772
+ button:hover {
773
+ border-color: var(--sfx-up-primary, #2563eb);
774
+ color: var(--sfx-up-primary, #2563eb);
775
+ background: var(--sfx-up-primary-bg, #eff6ff);
776
+ box-shadow: 0 2px 10px var(--sfx-up-primary-glow, rgba(37, 99, 235, 0.18));
777
+ transform: translateY(-1px);
778
+ }
779
+
780
+ button:active {
781
+ transform: translateY(0) scale(0.98);
782
+ }
783
+
784
+ svg {
785
+ width: 17px;
786
+ height: 17px;
787
+ flex-shrink: 0;
788
+ fill: none;
789
+ stroke: currentColor;
790
+ stroke-width: 2;
791
+ stroke-linecap: round;
792
+ }
793
+
794
+ svg.fill-icon {
795
+ fill: currentColor;
796
+ stroke: none;
797
+ stroke-width: 0;
798
+ }
799
+ `;
800
+ let W = ee;
801
+ Be([
802
+ x({ type: Array })
803
+ ], W.prototype, "sources");
804
+ var Ye = Object.defineProperty, T = (i, e, t, r) => {
805
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
806
+ (n = i[o]) && (s = n(e, t, s) || s);
807
+ return s && Ye(e, t, s), s;
808
+ };
809
+ const te = class te extends w {
810
+ constructor() {
811
+ super(...arguments), this.compact = !1, this.accept = "", this.sources = [], this._dragOver = !1, this._dragCounter = 0, this._onDragEnter = (e) => {
812
+ e.preventDefault(), this._dragCounter++, this._dragCounter === 1 && (this._dragOver = !0);
813
+ }, this._onDragOver = (e) => {
814
+ e.preventDefault();
815
+ }, this._onDragLeave = (e) => {
816
+ e.preventDefault(), this._dragCounter--, this._dragCounter <= 0 && (this._dragCounter = 0, this._dragOver = !1);
817
+ }, this._onDrop = (e) => {
818
+ var r;
819
+ e.preventDefault(), this._dragCounter = 0, this._dragOver = !1;
820
+ const t = Array.from(((r = e.dataTransfer) == null ? void 0 : r.files) ?? []);
821
+ t.length > 0 && this._emitFiles(t);
822
+ }, this._onClick = (e) => {
823
+ const t = this.shadowRoot.querySelector(".drop-zone");
824
+ if (t && this._rippleEl) {
825
+ const r = t.getBoundingClientRect();
826
+ this._rippleEl.style.left = `${e.clientX - r.left}px`, this._rippleEl.style.top = `${e.clientY - r.top}px`, this._rippleEl.classList.remove("go"), this._rippleEl.offsetWidth, this._rippleEl.classList.add("go");
827
+ }
828
+ this.browse();
829
+ }, this._onKeyDown = (e) => {
830
+ (e.key === "Enter" || e.key === " ") && (e.preventDefault(), this.browse());
831
+ }, this._onFileChange = (e) => {
832
+ const t = e.target, r = Array.from(t.files ?? []);
833
+ r.length > 0 && this._emitFiles(r), t.value = "";
834
+ }, this._onPaste = (e) => {
835
+ var s;
836
+ if (!this.isConnected || this.offsetWidth === 0) return;
837
+ const t = (s = e.clipboardData) == null ? void 0 : s.items;
838
+ if (!t) return;
839
+ const r = [];
840
+ for (const o of t)
841
+ if (o.kind === "file") {
842
+ const n = o.getAsFile();
843
+ n && r.push(n);
844
+ }
845
+ r.length > 0 && (e.preventDefault(), this._emitFiles(r));
846
+ };
847
+ }
848
+ /** Programmatically open file browser. */
849
+ browse() {
850
+ var e;
851
+ (e = this.fileInput) == null || e.click();
852
+ }
853
+ _onSourceIconClick(e) {
854
+ this.dispatchEvent(
855
+ new CustomEvent("source-click", {
856
+ detail: { source: e.id },
857
+ bubbles: !0,
858
+ composed: !0
859
+ })
860
+ );
861
+ }
862
+ _emitFiles(e) {
863
+ this.dispatchEvent(
864
+ new CustomEvent("files-selected", {
865
+ detail: { files: e },
866
+ bubbles: !0,
867
+ composed: !0
868
+ })
869
+ );
870
+ }
871
+ connectedCallback() {
872
+ super.connectedCallback(), document.addEventListener("paste", this._onPaste);
873
+ }
874
+ disconnectedCallback() {
875
+ super.disconnectedCallback(), document.removeEventListener("paste", this._onPaste);
876
+ }
877
+ render() {
878
+ const e = [
879
+ "drop-zone",
880
+ this._dragOver ? "drag-over" : "",
881
+ this.compact ? "compact" : ""
882
+ ].filter(Boolean).join(" ");
883
+ return c`
884
+ <div
885
+ class=${e}
886
+ role="button"
887
+ tabindex="0"
888
+ aria-label="Drop files here or click to browse"
889
+ @dragenter=${this._onDragEnter}
890
+ @dragover=${this._onDragOver}
891
+ @dragleave=${this._onDragLeave}
892
+ @drop=${this._onDrop}
893
+ @click=${this._onClick}
894
+ @keydown=${this._onKeyDown}
895
+ >
896
+ <div class="rings">
897
+ <div class="ring"></div>
898
+ <div class="ring"></div>
899
+ <div class="core">
900
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
901
+ <polyline points="16 16 12 12 8 16" />
902
+ <line x1="12" y1="12" x2="12" y2="21" />
903
+ <path d="M20.39 18.39A5 5 0 0018 9h-1.26A8 8 0 103 16.3" />
904
+ </svg>
905
+ </div>
906
+ </div>
907
+
908
+ <div class="title">
909
+ Drag & Drop or click to <span>browse</span>
910
+ </div>
911
+ ${this.compact ? h : c`<div class="subtitle">Drop files anywhere on this page</div>`}
912
+
913
+ ${this.compact && this.sources.length > 0 ? c`
914
+ <div class="sources-row">
915
+ <span class="src-divider"></span>
916
+ ${this.sources.map(
917
+ (t) => c`
918
+ <button
919
+ class="src-ico"
920
+ data-tip=${t.label}
921
+ @click=${(r) => {
922
+ r.stopPropagation(), this._onSourceIconClick(t);
923
+ }}
924
+ >
925
+ ${fe`<svg viewBox="0 0 24 24" class=${t.fillIcon ? "fill-icon" : ""}>${ge(t.icon)}</svg>`}
926
+ </button>
927
+ `
928
+ )}
929
+ </div>
930
+ ` : h}
931
+
932
+ <div class="ripple"></div>
933
+ <input
934
+ type="file"
935
+ multiple
936
+ accept=${this.accept || h}
937
+ @change=${this._onFileChange}
938
+ />
939
+ </div>
940
+ `;
941
+ }
942
+ };
943
+ te.styles = _`
944
+ :host {
945
+ display: block;
946
+ flex-shrink: 0;
947
+ }
948
+
949
+ .drop-zone {
950
+ border: 1.5px dashed var(--sfx-up-border, #d8d8d8);
951
+ border-radius: var(--sfx-up-radius, 16px);
952
+ background: var(--sfx-up-bg, #ffffff);
953
+ padding: 52px 40px 44px;
954
+ display: flex;
955
+ flex-direction: column;
956
+ align-items: center;
957
+ justify-content: center;
958
+ text-align: center;
959
+ cursor: pointer;
960
+ position: relative;
961
+ overflow: hidden;
962
+ transition: border-color 0.22s, background 0.22s, padding 0.35s ease;
963
+ user-select: none;
964
+ }
965
+
966
+ .drop-zone:hover {
967
+ border-color: #bbb;
968
+ background: #fafafa;
969
+ }
970
+
971
+ /* Drag over state */
972
+ .drop-zone.drag-over {
973
+ border: 2px solid var(--sfx-up-primary, #2563eb);
974
+ background: var(--sfx-up-primary-bg, #eff6ff);
975
+ box-shadow: 0 0 0 5px rgba(37, 99, 235, 0.06);
976
+ }
977
+
978
+ /* Compact state when files exist */
979
+ .drop-zone.compact {
980
+ padding: 14px 24px;
981
+ flex-direction: row;
982
+ align-items: center;
983
+ gap: 12px;
984
+ justify-content: flex-start;
985
+ overflow: visible;
986
+ }
987
+
988
+ /* --- Rings --- */
989
+ .rings {
990
+ width: 110px;
991
+ height: 110px;
992
+ position: relative;
993
+ display: flex;
994
+ align-items: center;
995
+ justify-content: center;
996
+ margin-bottom: 20px;
997
+ flex-shrink: 0;
998
+ }
999
+
1000
+ .ring {
1001
+ position: absolute;
1002
+ inset: 0;
1003
+ border-radius: 50%;
1004
+ border: 1.5px dashed #c4d5ef;
1005
+ animation: slowSpin 20s linear infinite;
1006
+ transition: border-color 0.3s;
1007
+ }
1008
+
1009
+ .ring:nth-child(2) {
1010
+ inset: 13px;
1011
+ border-color: #d8e5f5;
1012
+ border-style: dotted;
1013
+ animation-direction: reverse;
1014
+ animation-duration: 14s;
1015
+ }
1016
+
1017
+ .drag-over .ring {
1018
+ border-color: var(--sfx-up-primary, #2563eb);
1019
+ animation-duration: 3s;
1020
+ }
1021
+
1022
+ .drag-over .ring:nth-child(2) {
1023
+ border-color: rgba(37, 99, 235, 0.4);
1024
+ animation-duration: 2s;
1025
+ }
1026
+
1027
+ .compact .rings {
1028
+ width: 36px;
1029
+ height: 36px;
1030
+ margin-bottom: 0;
1031
+ }
1032
+
1033
+ .compact .ring {
1034
+ border-width: 1px;
1035
+ }
1036
+
1037
+ /* --- Core icon --- */
1038
+ .core {
1039
+ width: 54px;
1040
+ height: 54px;
1041
+ border-radius: 50%;
1042
+ background: var(--sfx-up-primary-bg, #eff6ff);
1043
+ color: var(--sfx-up-primary, #2563eb);
1044
+ display: flex;
1045
+ align-items: center;
1046
+ justify-content: center;
1047
+ z-index: 1;
1048
+ transition: all 0.28s cubic-bezier(0.34, 1.4, 0.64, 1);
1049
+ box-shadow: 0 3px 12px rgba(37, 99, 235, 0.15);
1050
+ }
1051
+
1052
+ .core svg {
1053
+ width: 24px;
1054
+ height: 24px;
1055
+ }
1056
+
1057
+ .drop-zone:hover .core {
1058
+ transform: translateY(-2px);
1059
+ box-shadow: 0 5px 18px rgba(37, 99, 235, 0.22);
1060
+ }
1061
+
1062
+ .drag-over .core {
1063
+ background: var(--sfx-up-primary, #2563eb);
1064
+ color: #fff;
1065
+ transform: scale(1.12);
1066
+ box-shadow: 0 8px 24px rgba(37, 99, 235, 0.38);
1067
+ }
1068
+
1069
+ .compact .core {
1070
+ width: 36px;
1071
+ height: 36px;
1072
+ box-shadow: none;
1073
+ }
1074
+
1075
+ .compact .core svg {
1076
+ width: 16px;
1077
+ height: 16px;
1078
+ }
1079
+
1080
+ /* --- Text --- */
1081
+ .title {
1082
+ font-size: 18px;
1083
+ font-weight: 700;
1084
+ color: var(--sfx-up-text, #1e293b);
1085
+ margin-bottom: 5px;
1086
+ transition: font-size 0.3s, margin 0.3s;
1087
+ }
1088
+
1089
+ .title span {
1090
+ color: var(--sfx-up-primary, #2563eb);
1091
+ cursor: pointer;
1092
+ }
1093
+
1094
+ .subtitle {
1095
+ font-size: 13px;
1096
+ color: var(--sfx-up-text-muted, #94a3b8);
1097
+ transition: opacity 0.2s;
1098
+ }
1099
+
1100
+ .compact .title {
1101
+ font-size: 13.5px;
1102
+ font-weight: 600;
1103
+ margin-bottom: 0;
1104
+ white-space: nowrap;
1105
+ overflow: hidden;
1106
+ text-overflow: ellipsis;
1107
+ }
1108
+
1109
+ .compact .subtitle {
1110
+ display: none;
1111
+ }
1112
+
1113
+ /* --- Source icons row (compact mode) --- */
1114
+ .sources-row {
1115
+ display: none;
1116
+ }
1117
+
1118
+ .compact .sources-row {
1119
+ display: flex;
1120
+ align-items: center;
1121
+ gap: 6px;
1122
+ margin-left: auto;
1123
+ flex-shrink: 0;
1124
+ position: relative;
1125
+ z-index: 20;
1126
+ }
1127
+
1128
+ .src-divider {
1129
+ width: 1px;
1130
+ height: 20px;
1131
+ background: #e5e7eb;
1132
+ margin-right: 2px;
1133
+ flex-shrink: 0;
1134
+ }
1135
+
1136
+ .src-ico {
1137
+ width: 28px;
1138
+ height: 28px;
1139
+ border-radius: 50%;
1140
+ border: none;
1141
+ background: #f3f4f6;
1142
+ cursor: pointer;
1143
+ display: flex;
1144
+ align-items: center;
1145
+ justify-content: center;
1146
+ transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.15s;
1147
+ position: relative;
1148
+ flex-shrink: 0;
1149
+ color: #6b7280;
1150
+ padding: 0;
1151
+ font-family: inherit;
1152
+ }
1153
+
1154
+ .src-ico svg {
1155
+ width: 14px;
1156
+ height: 14px;
1157
+ fill: none;
1158
+ stroke: currentColor;
1159
+ stroke-width: 2;
1160
+ stroke-linecap: round;
1161
+ }
1162
+
1163
+ .src-ico svg.fill-icon {
1164
+ fill: currentColor;
1165
+ stroke: none;
1166
+ stroke-width: 0;
1167
+ }
1168
+
1169
+ .src-ico:hover {
1170
+ transform: scale(1.18);
1171
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.12);
1172
+ background: #fff;
1173
+ color: #374151;
1174
+ }
1175
+
1176
+ .src-ico::after {
1177
+ content: attr(data-tip);
1178
+ position: absolute;
1179
+ bottom: -28px;
1180
+ left: 50%;
1181
+ transform: translateX(-50%);
1182
+ background: #fff;
1183
+ color: #374151;
1184
+ font-size: 10px;
1185
+ font-weight: 500;
1186
+ border: 1px solid #e5e7eb;
1187
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
1188
+ padding: 3px 8px;
1189
+ border-radius: 5px;
1190
+ white-space: nowrap;
1191
+ opacity: 0;
1192
+ visibility: hidden;
1193
+ transition: opacity 0.15s, visibility 0.15s;
1194
+ pointer-events: none;
1195
+ z-index: 50;
1196
+ font-family: inherit;
1197
+ }
1198
+
1199
+ .src-ico:hover::after {
1200
+ opacity: 1;
1201
+ visibility: visible;
1202
+ }
1203
+
1204
+ /* --- Ripple --- */
1205
+ .ripple {
1206
+ position: absolute;
1207
+ width: 10px;
1208
+ height: 10px;
1209
+ border-radius: 50%;
1210
+ background: var(--sfx-up-primary, #2563eb);
1211
+ opacity: 0;
1212
+ pointer-events: none;
1213
+ transform: translate(-50%, -50%) scale(0);
1214
+ }
1215
+
1216
+ .ripple.go {
1217
+ animation: ripple 0.55s ease-out forwards;
1218
+ }
1219
+
1220
+ /* --- Hidden input --- */
1221
+ input[type='file'] {
1222
+ display: none;
1223
+ }
1224
+
1225
+ @keyframes slowSpin {
1226
+ to {
1227
+ transform: rotate(360deg);
1228
+ }
1229
+ }
1230
+
1231
+ @keyframes ripple {
1232
+ from {
1233
+ transform: translate(-50%, -50%) scale(0);
1234
+ opacity: 0.18;
1235
+ }
1236
+ to {
1237
+ transform: translate(-50%, -50%) scale(12);
1238
+ opacity: 0;
1239
+ }
1240
+ }
1241
+
1242
+ @media (prefers-reduced-motion: reduce) {
1243
+ .ring {
1244
+ animation: none;
1245
+ }
1246
+ .ripple.go {
1247
+ animation: none;
1248
+ }
1249
+ }
1250
+ `;
1251
+ let $ = te;
1252
+ T([
1253
+ x({ type: Boolean })
1254
+ ], $.prototype, "compact");
1255
+ T([
1256
+ x({ type: String })
1257
+ ], $.prototype, "accept");
1258
+ T([
1259
+ x({ type: Array })
1260
+ ], $.prototype, "sources");
1261
+ T([
1262
+ g()
1263
+ ], $.prototype, "_dragOver");
1264
+ T([
1265
+ he(".ripple")
1266
+ ], $.prototype, "_rippleEl");
1267
+ T([
1268
+ he('input[type="file"]')
1269
+ ], $.prototype, "fileInput");
1270
+ const re = class re extends w {
1271
+ render() {
1272
+ return c`
1273
+ <div class="line"></div>
1274
+ <div class="label">or import from</div>
1275
+ <div class="line"></div>
1276
+ `;
1277
+ }
1278
+ };
1279
+ re.styles = _`
1280
+ :host {
1281
+ display: flex;
1282
+ align-items: center;
1283
+ gap: 14px;
1284
+ padding: 20px 0;
1285
+ }
1286
+
1287
+ .line {
1288
+ flex: 1;
1289
+ height: 1px;
1290
+ background: var(--sfx-up-border-light, #f1f5f9);
1291
+ }
1292
+
1293
+ .label {
1294
+ font-size: 11px;
1295
+ font-weight: 600;
1296
+ color: var(--sfx-up-text-muted, #cbd5e1);
1297
+ text-transform: uppercase;
1298
+ letter-spacing: 1px;
1299
+ white-space: nowrap;
1300
+ }
1301
+ `;
1302
+ let pe = re;
1303
+ var Ke = Object.defineProperty, Je = (i, e, t, r) => {
1304
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
1305
+ (n = i[o]) && (s = n(e, t, s) || s);
1306
+ return s && Ke(e, t, s), s;
1307
+ };
1308
+ const se = class se extends w {
1309
+ constructor() {
1310
+ super(...arguments), this.files = [];
1311
+ }
1312
+ render() {
1313
+ return c`
1314
+ <div class="grid">
1315
+ ${this.files.map(
1316
+ (e) => c`<sfx-file-item .file=${e}></sfx-file-item>`
1317
+ )}
1318
+ </div>
1319
+ `;
1320
+ }
1321
+ };
1322
+ se.styles = _`
1323
+ :host {
1324
+ display: block;
1325
+ flex: 1;
1326
+ overflow-y: auto;
1327
+ min-height: 0;
1328
+ }
1329
+
1330
+ .grid {
1331
+ display: grid;
1332
+ grid-template-columns: repeat(auto-fill, minmax(186px, 1fr));
1333
+ gap: 12px;
1334
+ }
1335
+ `;
1336
+ let G = se;
1337
+ Je([
1338
+ x({ attribute: !1 })
1339
+ ], G.prototype, "files");
1340
+ var We = Object.defineProperty, Ge = (i, e, t, r) => {
1341
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
1342
+ (n = i[o]) && (s = n(e, t, s) || s);
1343
+ return s && We(e, t, s), s;
1344
+ };
1345
+ const oe = class oe extends w {
1346
+ _remove() {
1347
+ this.dispatchEvent(
1348
+ new CustomEvent("file-remove", {
1349
+ detail: { fileId: this.file.id },
1350
+ bubbles: !0,
1351
+ composed: !0
1352
+ })
1353
+ );
1354
+ }
1355
+ _retry() {
1356
+ this.dispatchEvent(
1357
+ new CustomEvent("file-retry", {
1358
+ detail: { fileId: this.file.id },
1359
+ bubbles: !0,
1360
+ composed: !0
1361
+ })
1362
+ );
1363
+ }
1364
+ _preview(e) {
1365
+ e.stopPropagation(), this.dispatchEvent(
1366
+ new CustomEvent("file-preview", {
1367
+ detail: { fileId: this.file.id },
1368
+ bubbles: !0,
1369
+ composed: !0
1370
+ })
1371
+ );
1372
+ }
1373
+ render() {
1374
+ const e = this.file;
1375
+ if (!e) return h;
1376
+ const t = Oe(e), r = t === "image", s = e.status === "complete", o = e.status === "uploading", n = e.status === "error" || e.status === "failed", a = Te(e.name), l = [
1377
+ "tile",
1378
+ s ? "done" : "",
1379
+ o ? "uploading" : ""
1380
+ ].filter(Boolean).join(" ");
1381
+ return c`
1382
+ <div class=${l}>
1383
+ <!-- Preview area -->
1384
+ <div class="preview">
1385
+ ${r && e.previewUrl ? c`<div class="preview-bg" style="background-image:url(${e.previewUrl})"></div>` : c`
1386
+ <div class="preview-bg ${t}"></div>
1387
+ <div class="type-icon">
1388
+ <div class="type-icon-inner ${t}">
1389
+ ${this._renderTypeIcon(t)}
1390
+ ${a ? c`<div class="ext-label">${a}</div>` : h}
1391
+ </div>
1392
+ </div>
1393
+ `}
1394
+
1395
+ <!-- Preview button -->
1396
+ ${!s && !o && !n && e.status !== "rejected" ? c`
1397
+ <button class="preview-btn" @click=${this._preview} aria-label="Preview file">
1398
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
1399
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
1400
+ <circle cx="12" cy="12" r="3"/>
1401
+ </svg>
1402
+ Preview
1403
+ </button>
1404
+ ` : h}
1405
+
1406
+ <!-- Spinner overlay -->
1407
+ <div class="spinner-overlay">
1408
+ <div class="spin-ring"></div>
1409
+ </div>
1410
+
1411
+ <!-- Done overlay -->
1412
+ <div class="done-overlay">
1413
+ <div class="done-check">
1414
+ <svg viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round">
1415
+ <polyline points="20 6 9 17 4 12" />
1416
+ </svg>
1417
+ </div>
1418
+ </div>
1419
+
1420
+ <!-- Progress bar -->
1421
+ ${e.status === "uploading" || s ? c`
1422
+ <div class="progress">
1423
+ <div class="progress-fill" style="transform:scaleX(${Math.min(e.progress, 100) / 100})"></div>
1424
+ </div>
1425
+ ` : h}
1426
+
1427
+ <!-- Error badge -->
1428
+ ${n && e.error ? c`<div class="error-badge" title=${e.error}>${e.error}</div>` : h}
1429
+ </div>
1430
+
1431
+ <!-- Action buttons -->
1432
+ <div class="actions">
1433
+ ${n ? c`
1434
+ <button class="act-btn retry" @click=${this._retry} title="Retry" aria-label="Retry upload">
1435
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
1436
+ <polyline points="23 4 23 10 17 10" />
1437
+ <path d="M20.49 15a9 9 0 11-2.12-9.36L23 10" />
1438
+ </svg>
1439
+ </button>
1440
+ ` : h}
1441
+ <button class="act-btn del" @click=${this._remove} title="Remove" aria-label="Remove file">
1442
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
1443
+ <line x1="18" y1="6" x2="6" y2="18" />
1444
+ <line x1="6" y1="6" x2="18" y2="18" />
1445
+ </svg>
1446
+ </button>
1447
+ </div>
1448
+
1449
+ <!-- Info bar -->
1450
+ <div class="info">
1451
+ <div class="name" title=${e.name}>${e.name}</div>
1452
+ <div class="meta">${e.size ? Ae(e.size) : ""}${e.type ? `${e.size ? " · " : ""}${e.type}` : ""}</div>
1453
+ </div>
1454
+ </div>
1455
+ `;
1456
+ }
1457
+ _renderTypeIcon(e) {
1458
+ switch (e) {
1459
+ case "pdf":
1460
+ return c`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>`;
1461
+ case "doc":
1462
+ return c`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>`;
1463
+ case "vid":
1464
+ return c`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
1465
+ case "zip":
1466
+ return c`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 8v13H3V8"/><path d="M1 3h22v5H1z"/><path d="M10 12h4"/></svg>`;
1467
+ default:
1468
+ return c`<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/><polyline points="13 2 13 9 20 9"/></svg>`;
1469
+ }
1470
+ }
1471
+ };
1472
+ oe.styles = _`
1473
+ :host {
1474
+ display: block;
1475
+ }
1476
+
1477
+ .tile {
1478
+ border-radius: 6px;
1479
+ overflow: hidden;
1480
+ background: var(--sfx-up-bg, #fff);
1481
+ border: 1px solid #e8eaed;
1482
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
1483
+ animation: tileIn 0.6s cubic-bezier(0.34, 1.2, 0.64, 1) both;
1484
+ transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s;
1485
+ cursor: default;
1486
+ display: flex;
1487
+ flex-direction: column;
1488
+ position: relative;
1489
+ }
1490
+
1491
+ .tile:hover {
1492
+ box-shadow: 0 6px 24px rgba(0, 0, 0, 0.11);
1493
+ transform: translateY(-2px);
1494
+ border-color: #d1d5db;
1495
+ }
1496
+
1497
+ /* --- Preview area --- */
1498
+ .preview {
1499
+ position: relative;
1500
+ aspect-ratio: 16 / 10;
1501
+ overflow: hidden;
1502
+ flex-shrink: 0;
1503
+ background: #f3f4f6;
1504
+ }
1505
+
1506
+ .preview-bg {
1507
+ position: absolute;
1508
+ inset: 0;
1509
+ background-size: cover;
1510
+ background-position: center;
1511
+ transition: transform 0.4s ease;
1512
+ }
1513
+
1514
+ .tile:hover .preview-bg {
1515
+ transform: scale(1.03);
1516
+ }
1517
+
1518
+ .preview-bg.pdf { background: linear-gradient(135deg, #fef2f2, #fee2e2); }
1519
+ .preview-bg.doc { background: linear-gradient(135deg, #eff6ff, #dbeafe); }
1520
+ .preview-bg.vid { background: linear-gradient(135deg, #f5f3ff, #ede9fe); }
1521
+ .preview-bg.zip { background: linear-gradient(135deg, #fffbeb, #fef3c7); }
1522
+ .preview-bg.gen { background: linear-gradient(135deg, #f8fafc, #f1f5f9); }
1523
+
1524
+ /* --- File type icon --- */
1525
+ .type-icon {
1526
+ position: absolute;
1527
+ inset: 0;
1528
+ display: flex;
1529
+ align-items: center;
1530
+ justify-content: center;
1531
+ pointer-events: none;
1532
+ }
1533
+
1534
+ .type-icon-inner {
1535
+ width: 44px;
1536
+ height: 44px;
1537
+ border-radius: 12px;
1538
+ display: flex;
1539
+ align-items: center;
1540
+ justify-content: center;
1541
+ background: rgba(255, 255, 255, 0.9);
1542
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
1543
+ }
1544
+
1545
+ .type-icon-inner svg {
1546
+ width: 22px;
1547
+ height: 22px;
1548
+ }
1549
+
1550
+ .type-icon-inner.pdf { color: #dc2626; }
1551
+ .type-icon-inner.doc { color: #1d4ed8; }
1552
+ .type-icon-inner.vid { color: #7c3aed; }
1553
+ .type-icon-inner.zip { color: #b45309; }
1554
+ .type-icon-inner.gen { color: #64748b; }
1555
+
1556
+ .ext-label {
1557
+ font-size: 9px;
1558
+ font-weight: 800;
1559
+ text-transform: uppercase;
1560
+ margin-top: 2px;
1561
+ }
1562
+
1563
+ /* --- Info bar --- */
1564
+ .info {
1565
+ padding: 9px 11px 10px;
1566
+ background: var(--sfx-up-bg, #fff);
1567
+ min-width: 0;
1568
+ }
1569
+
1570
+ .name {
1571
+ font-size: 12.5px;
1572
+ font-weight: 600;
1573
+ color: #111827;
1574
+ white-space: nowrap;
1575
+ overflow: hidden;
1576
+ text-overflow: ellipsis;
1577
+ margin-bottom: 2px;
1578
+ }
1579
+
1580
+ .meta {
1581
+ font-size: 11px;
1582
+ font-weight: 400;
1583
+ color: #9ca3af;
1584
+ white-space: nowrap;
1585
+ overflow: hidden;
1586
+ text-overflow: ellipsis;
1587
+ }
1588
+
1589
+ .tile.done .info {
1590
+ opacity: 0.5;
1591
+ }
1592
+
1593
+ /* --- Action buttons --- */
1594
+ .actions {
1595
+ position: absolute;
1596
+ top: 6px;
1597
+ right: 6px;
1598
+ display: flex;
1599
+ gap: 3px;
1600
+ opacity: 0;
1601
+ transition: opacity 0.15s;
1602
+ z-index: 10;
1603
+ }
1604
+
1605
+ .tile:hover .actions {
1606
+ opacity: 1;
1607
+ }
1608
+
1609
+ .act-btn {
1610
+ width: 26px;
1611
+ height: 26px;
1612
+ border-radius: 6px;
1613
+ border: none;
1614
+ background: #fff;
1615
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
1616
+ cursor: pointer;
1617
+ display: flex;
1618
+ align-items: center;
1619
+ justify-content: center;
1620
+ transition: background 0.15s, transform 0.12s;
1621
+ color: #374151;
1622
+ padding: 0;
1623
+ }
1624
+
1625
+ .act-btn:hover {
1626
+ background: #f3f4f6;
1627
+ transform: scale(1.08);
1628
+ }
1629
+
1630
+ .act-btn.del:hover {
1631
+ background: #fee2e2;
1632
+ color: #dc2626;
1633
+ }
1634
+
1635
+ .act-btn.retry:hover {
1636
+ background: #eff6ff;
1637
+ color: var(--sfx-up-primary, #2563eb);
1638
+ }
1639
+
1640
+ .act-btn svg {
1641
+ width: 11px;
1642
+ height: 11px;
1643
+ }
1644
+
1645
+ /* --- Preview button --- */
1646
+ .preview-btn {
1647
+ position: absolute;
1648
+ bottom: 50%;
1649
+ left: 50%;
1650
+ transform: translate(-50%, 50%);
1651
+ padding: 6px 16px;
1652
+ border-radius: 6px;
1653
+ border: 1.5px solid var(--sfx-up-primary, #2563eb);
1654
+ background: #fff;
1655
+ cursor: pointer;
1656
+ display: flex;
1657
+ align-items: center;
1658
+ gap: 5px;
1659
+ opacity: 0;
1660
+ transition: all 0.15s ease;
1661
+ color: var(--sfx-up-primary, #2563eb);
1662
+ font-family: inherit;
1663
+ font-size: 11px;
1664
+ font-weight: 600;
1665
+ white-space: nowrap;
1666
+ z-index: 5;
1667
+ }
1668
+
1669
+ .tile:hover .preview-btn {
1670
+ opacity: 1;
1671
+ }
1672
+
1673
+ .preview-btn:hover {
1674
+ background: var(--sfx-up-primary, #2563eb);
1675
+ color: #fff;
1676
+ }
1677
+
1678
+ .preview-btn:hover svg {
1679
+ stroke: #fff;
1680
+ }
1681
+
1682
+ .preview-btn svg {
1683
+ width: 13px;
1684
+ height: 13px;
1685
+ }
1686
+
1687
+ /* --- Progress bar --- */
1688
+ .progress {
1689
+ position: absolute;
1690
+ bottom: 0;
1691
+ left: 0;
1692
+ right: 0;
1693
+ height: 2px;
1694
+ background: rgba(0, 0, 0, 0.06);
1695
+ }
1696
+
1697
+ .progress-fill {
1698
+ height: 100%;
1699
+ background: var(--sfx-up-primary, #2563eb);
1700
+ transform-origin: left;
1701
+ transition: transform 0.32s ease;
1702
+ }
1703
+
1704
+ .tile.done .progress-fill {
1705
+ background: var(--sfx-up-success, #16a34a);
1706
+ }
1707
+
1708
+ /* --- Uploading spinner overlay --- */
1709
+ .spinner-overlay {
1710
+ position: absolute;
1711
+ inset: 0;
1712
+ display: flex;
1713
+ align-items: center;
1714
+ justify-content: center;
1715
+ background: rgba(0, 0, 0, 0.22);
1716
+ opacity: 0;
1717
+ transition: opacity 0.2s;
1718
+ pointer-events: none;
1719
+ }
1720
+
1721
+ .tile.uploading .spinner-overlay {
1722
+ opacity: 1;
1723
+ }
1724
+
1725
+ .spin-ring {
1726
+ width: 28px;
1727
+ height: 28px;
1728
+ border: 2.5px solid rgba(255, 255, 255, 0.22);
1729
+ border-top-color: #fff;
1730
+ border-radius: 50%;
1731
+ animation: spinRing 0.7s linear infinite;
1732
+ }
1733
+
1734
+ /* --- Done overlay --- */
1735
+ .done-overlay {
1736
+ position: absolute;
1737
+ inset: 0;
1738
+ background: rgba(5, 150, 105, 0.72);
1739
+ opacity: 0;
1740
+ transition: opacity 0.45s cubic-bezier(0.4, 0, 0.2, 1);
1741
+ pointer-events: none;
1742
+ display: flex;
1743
+ align-items: center;
1744
+ justify-content: center;
1745
+ }
1746
+
1747
+ .tile.done .done-overlay {
1748
+ opacity: 1;
1749
+ pointer-events: auto;
1750
+ }
1751
+
1752
+ .done-check {
1753
+ animation: popBounce 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s both;
1754
+ }
1755
+
1756
+ .done-check svg {
1757
+ width: 28px;
1758
+ height: 28px;
1759
+ filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.2));
1760
+ }
1761
+
1762
+ /* --- Error state --- */
1763
+ .error-badge {
1764
+ position: absolute;
1765
+ bottom: 28px;
1766
+ left: 6px;
1767
+ right: 6px;
1768
+ font-size: 10px;
1769
+ font-weight: 600;
1770
+ color: #fff;
1771
+ background: rgba(220, 38, 38, 0.88);
1772
+ border-radius: 4px;
1773
+ padding: 3px 6px;
1774
+ text-align: center;
1775
+ white-space: nowrap;
1776
+ overflow: hidden;
1777
+ text-overflow: ellipsis;
1778
+ }
1779
+
1780
+ @keyframes tileIn {
1781
+ 0% {
1782
+ opacity: 0;
1783
+ transform: scale(0.8) translateY(22px);
1784
+ filter: blur(10px);
1785
+ }
1786
+ 55% {
1787
+ opacity: 1;
1788
+ filter: blur(0);
1789
+ }
1790
+ 80% {
1791
+ transform: scale(1.03) translateY(-3px);
1792
+ }
1793
+ 100% {
1794
+ opacity: 1;
1795
+ transform: scale(1) translateY(0);
1796
+ filter: blur(0);
1797
+ }
1798
+ }
1799
+
1800
+ @keyframes popBounce {
1801
+ 0% { transform: scale(0); opacity: 0; }
1802
+ 55% { transform: scale(1.2); opacity: 1; }
1803
+ 75% { transform: scale(0.94); }
1804
+ 100% { transform: scale(1); }
1805
+ }
1806
+
1807
+ @keyframes spinRing {
1808
+ to { transform: rotate(360deg); }
1809
+ }
1810
+
1811
+ @media (prefers-reduced-motion: reduce) {
1812
+ .tile { animation: none; }
1813
+ .done-check { animation: none; }
1814
+ .spin-ring { animation: none; }
1815
+ }
1816
+ `;
1817
+ let X = oe;
1818
+ Ge([
1819
+ x({ attribute: !1 })
1820
+ ], X.prototype, "file");
1821
+ var Xe = Object.defineProperty, be = (i, e, t, r) => {
1822
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
1823
+ (n = i[o]) && (s = n(e, t, s) || s);
1824
+ return s && Xe(e, t, s), s;
1825
+ };
1826
+ const ie = class ie extends w {
1827
+ constructor() {
1828
+ super(...arguments), this.fileCount = 0, this.primaryLabel = "Done";
1829
+ }
1830
+ _uploadMore() {
1831
+ this.dispatchEvent(
1832
+ new CustomEvent("upload-more", { bubbles: !0, composed: !0 })
1833
+ );
1834
+ }
1835
+ _primaryAction() {
1836
+ this.dispatchEvent(
1837
+ new CustomEvent("primary-action", { bubbles: !0, composed: !0 })
1838
+ );
1839
+ }
1840
+ render() {
1841
+ const e = this.fileCount === 1 ? "file has" : "files have";
1842
+ return c`
1843
+ <div class="card">
1844
+ <div class="icon">
1845
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
1846
+ <polyline points="20 6 9 17 4 12" />
1847
+ </svg>
1848
+ </div>
1849
+ <div class="title">Uploaded successfully!</div>
1850
+ <div class="subtitle">
1851
+ All ${this.fileCount} ${e} been uploaded and are ready for review.
1852
+ </div>
1853
+ <div class="actions">
1854
+ <button class="btn-ghost" @click=${this._uploadMore}>Upload more</button>
1855
+ <button class="btn-primary" @click=${this._primaryAction}>${this.primaryLabel}</button>
1856
+ </div>
1857
+ </div>
1858
+ `;
1859
+ }
1860
+ };
1861
+ ie.styles = _`
1862
+ :host {
1863
+ display: flex;
1864
+ flex: 1;
1865
+ justify-content: center;
1866
+ align-items: center;
1867
+ }
1868
+
1869
+ .card {
1870
+ background: var(--sfx-up-bg, #fff);
1871
+ border-radius: 20px;
1872
+ border: 1.5px solid #bbf7d0;
1873
+ padding: 64px 48px;
1874
+ display: flex;
1875
+ flex-direction: column;
1876
+ align-items: center;
1877
+ text-align: center;
1878
+ box-shadow: 0 8px 40px rgba(34, 197, 94, 0.08);
1879
+ animation: fadeUp 0.4s ease both;
1880
+ }
1881
+
1882
+ .icon {
1883
+ width: 64px;
1884
+ height: 64px;
1885
+ border-radius: 50%;
1886
+ background: linear-gradient(135deg, #dcfce7, #bbf7d0);
1887
+ display: flex;
1888
+ align-items: center;
1889
+ justify-content: center;
1890
+ margin-bottom: 18px;
1891
+ color: var(--sfx-up-success, #16a34a);
1892
+ box-shadow: 0 4px 18px rgba(22, 163, 74, 0.16);
1893
+ animation: popBounce 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) 0.1s both;
1894
+ }
1895
+
1896
+ .icon svg {
1897
+ width: 30px;
1898
+ height: 30px;
1899
+ }
1900
+
1901
+ .title {
1902
+ font-size: 21px;
1903
+ font-weight: 800;
1904
+ color: #0f172a;
1905
+ letter-spacing: -0.4px;
1906
+ margin-bottom: 7px;
1907
+ }
1908
+
1909
+ .subtitle {
1910
+ font-size: 13.5px;
1911
+ color: var(--sfx-up-text-muted, #94a3b8);
1912
+ line-height: 1.6;
1913
+ max-width: 320px;
1914
+ margin-bottom: 22px;
1915
+ }
1916
+
1917
+ .actions {
1918
+ display: flex;
1919
+ gap: 9px;
1920
+ }
1921
+
1922
+ button {
1923
+ height: 36px;
1924
+ padding: 0 17px;
1925
+ border-radius: 9px;
1926
+ border: none;
1927
+ font-family: inherit;
1928
+ font-size: 13px;
1929
+ font-weight: 600;
1930
+ cursor: pointer;
1931
+ display: inline-flex;
1932
+ align-items: center;
1933
+ justify-content: center;
1934
+ gap: 6px;
1935
+ transition: all 0.18s ease;
1936
+ white-space: nowrap;
1937
+ }
1938
+
1939
+ .btn-ghost {
1940
+ background: none;
1941
+ color: var(--sfx-up-text-muted, #94a3b8);
1942
+ border: 1.5px solid var(--sfx-up-border, #e8edf5);
1943
+ }
1944
+
1945
+ .btn-ghost:hover {
1946
+ background: #f8faff;
1947
+ color: #64748b;
1948
+ border-color: #d1dff0;
1949
+ }
1950
+
1951
+ .btn-primary {
1952
+ background: linear-gradient(135deg, var(--sfx-up-primary, #2563eb), var(--sfx-up-primary-mid, #3b82f6));
1953
+ color: #fff;
1954
+ box-shadow: 0 2px 10px rgba(37, 99, 235, 0.28);
1955
+ }
1956
+
1957
+ .btn-primary:hover {
1958
+ background: linear-gradient(135deg, var(--sfx-up-primary-hover, #1d4ed8), var(--sfx-up-primary, #2563eb));
1959
+ box-shadow: 0 4px 16px rgba(37, 99, 235, 0.38);
1960
+ transform: translateY(-1px);
1961
+ }
1962
+
1963
+ @keyframes fadeUp {
1964
+ from {
1965
+ opacity: 0;
1966
+ transform: translateY(12px);
1967
+ }
1968
+ to {
1969
+ opacity: 1;
1970
+ transform: translateY(0);
1971
+ }
1972
+ }
1973
+
1974
+ @keyframes popBounce {
1975
+ 0% { transform: scale(0); opacity: 0; }
1976
+ 55% { transform: scale(1.2); opacity: 1; }
1977
+ 75% { transform: scale(0.94); }
1978
+ 100% { transform: scale(1); }
1979
+ }
1980
+
1981
+ @media (prefers-reduced-motion: reduce) {
1982
+ .card { animation: none; }
1983
+ .icon { animation: none; }
1984
+ }
1985
+ `;
1986
+ let q = ie;
1987
+ be([
1988
+ x({ type: Number })
1989
+ ], q.prototype, "fileCount");
1990
+ be([
1991
+ x({ type: String })
1992
+ ], q.prototype, "primaryLabel");
1993
+ var Qe = Object.defineProperty, V = (i, e, t, r) => {
1994
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
1995
+ (n = i[o]) && (s = n(e, t, s) || s);
1996
+ return s && Qe(e, t, s), s;
1997
+ };
1998
+ const ne = class ne extends w {
1999
+ constructor() {
2000
+ super(...arguments), this.uploadState = "idle", this.fileCount = 0, this.failedCount = 0, this.showFillMetadata = !1;
2001
+ }
2002
+ _clear() {
2003
+ this.dispatchEvent(new CustomEvent("clear-all", { bubbles: !0, composed: !0 }));
2004
+ }
2005
+ _addMore() {
2006
+ this.dispatchEvent(new CustomEvent("add-more", { bubbles: !0, composed: !0 }));
2007
+ }
2008
+ _fillMetadata() {
2009
+ this.dispatchEvent(new CustomEvent("fill-metadata", { bubbles: !0, composed: !0 }));
2010
+ }
2011
+ _upload() {
2012
+ this.dispatchEvent(new CustomEvent("upload-start", { bubbles: !0, composed: !0 }));
2013
+ }
2014
+ _retryAll() {
2015
+ this.dispatchEvent(new CustomEvent("retry-all", { bubbles: !0, composed: !0 }));
2016
+ }
2017
+ render() {
2018
+ return c`
2019
+ <div class="left">
2020
+ ${this.showFillMetadata && this.uploadState === "idle" ? c`
2021
+ <button class="btn-sec" @click=${this._fillMetadata}>
2022
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2023
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
2024
+ <polyline points="14 2 14 8 20 8"/>
2025
+ <line x1="16" y1="13" x2="8" y2="13"/>
2026
+ <line x1="16" y1="17" x2="8" y2="17"/>
2027
+ <line x1="10" y1="9" x2="8" y2="9"/>
2028
+ </svg>
2029
+ Fill Metadata
2030
+ </button>
2031
+ ` : h}
2032
+ <div class="count">
2033
+ ${this.fileCount} <span>${this.fileCount === 1 ? "file" : "files"}</span>
2034
+ </div>
2035
+ </div>
2036
+ <div class="right">
2037
+ <button class="btn-ghost" @click=${this._clear}>
2038
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
2039
+ <polyline points="3 6 5 6 21 6" />
2040
+ <path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6" />
2041
+ </svg>
2042
+ Clear
2043
+ </button>
2044
+ <button class="btn-sec" @click=${this._addMore}>
2045
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
2046
+ <line x1="12" y1="5" x2="12" y2="19" />
2047
+ <line x1="5" y1="12" x2="19" y2="12" />
2048
+ </svg>
2049
+ Add more
2050
+ </button>
2051
+ ${this.failedCount > 0 ? c`
2052
+ <button class="btn-retry" @click=${this._retryAll}>
2053
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
2054
+ <polyline points="23 4 23 10 17 10" />
2055
+ <path d="M20.49 15a9 9 0 11-2.12-9.36L23 10" />
2056
+ </svg>
2057
+ Retry all (${this.failedCount})
2058
+ </button>
2059
+ ` : h}
2060
+ ${this._renderUploadButton()}
2061
+ </div>
2062
+ `;
2063
+ }
2064
+ _renderUploadButton() {
2065
+ const e = this.uploadState === "uploading", t = this.uploadState === "done", r = ["btn-primary", t ? "done-state" : ""].filter(Boolean).join(" ");
2066
+ return c`
2067
+ <button
2068
+ class=${r}
2069
+ @click=${this._upload}
2070
+ ?disabled=${e}
2071
+ >
2072
+ ${e ? c`<span class="btn-spin"></span> Uploading\u2026` : t ? c`
2073
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
2074
+ <polyline points="20 6 9 17 4 12" />
2075
+ </svg>
2076
+ Done!
2077
+ ` : c`
2078
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
2079
+ <polyline points="16 16 12 12 8 16" />
2080
+ <line x1="12" y1="12" x2="12" y2="21" />
2081
+ <path d="M20.39 18.39A5 5 0 0018 9h-1.26A8 8 0 103 16.3" />
2082
+ </svg>
2083
+ Upload
2084
+ `}
2085
+ </button>
2086
+ `;
2087
+ }
2088
+ };
2089
+ ne.styles = _`
2090
+ :host {
2091
+ display: flex;
2092
+ align-items: center;
2093
+ justify-content: space-between;
2094
+ background: var(--sfx-up-bg, #ffffff);
2095
+ border-top: 1px solid #ebebeb;
2096
+ padding: 14px 24px;
2097
+ flex-shrink: 0;
2098
+ box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.04);
2099
+ }
2100
+
2101
+ .left {
2102
+ display: flex;
2103
+ align-items: center;
2104
+ gap: 9px;
2105
+ }
2106
+
2107
+ .right {
2108
+ display: flex;
2109
+ align-items: center;
2110
+ gap: 9px;
2111
+ }
2112
+
2113
+ /* --- Buttons --- */
2114
+ button {
2115
+ height: 36px;
2116
+ padding: 0 17px;
2117
+ border-radius: 9px;
2118
+ border: none;
2119
+ font-family: inherit;
2120
+ font-size: 13px;
2121
+ font-weight: 600;
2122
+ cursor: pointer;
2123
+ display: inline-flex;
2124
+ align-items: center;
2125
+ justify-content: center;
2126
+ gap: 6px;
2127
+ transition: all 0.18s ease;
2128
+ white-space: nowrap;
2129
+ }
2130
+
2131
+ button svg {
2132
+ width: 14px;
2133
+ height: 14px;
2134
+ }
2135
+
2136
+ .btn-ghost {
2137
+ background: none;
2138
+ color: var(--sfx-up-text-muted, #94a3b8);
2139
+ border: 1.5px solid var(--sfx-up-border, #e8edf5);
2140
+ }
2141
+
2142
+ .btn-ghost:hover {
2143
+ background: #f8faff;
2144
+ color: #64748b;
2145
+ border-color: #d1dff0;
2146
+ }
2147
+
2148
+ .btn-sec {
2149
+ background: var(--sfx-up-primary-bg, #eff6ff);
2150
+ color: var(--sfx-up-primary, #2563eb);
2151
+ border: 1.5px solid rgba(37, 99, 235, 0.15);
2152
+ }
2153
+
2154
+ .btn-sec:hover {
2155
+ background: #dbeafe;
2156
+ }
2157
+
2158
+ .btn-retry {
2159
+ background: #fef2f2;
2160
+ color: #dc2626;
2161
+ border: 1.5px solid rgba(220, 38, 38, 0.2);
2162
+ }
2163
+
2164
+ .btn-retry:hover {
2165
+ background: #fee2e2;
2166
+ color: #b91c1c;
2167
+ border-color: rgba(220, 38, 38, 0.35);
2168
+ }
2169
+
2170
+ .btn-primary {
2171
+ background: linear-gradient(135deg, var(--sfx-up-primary, #2563eb), var(--sfx-up-primary-mid, #3b82f6));
2172
+ color: #fff;
2173
+ box-shadow: 0 2px 10px rgba(37, 99, 235, 0.28);
2174
+ min-width: 110px;
2175
+ position: relative;
2176
+ overflow: hidden;
2177
+ }
2178
+
2179
+ .btn-primary:hover:not(:disabled) {
2180
+ background: linear-gradient(135deg, var(--sfx-up-primary-hover, #1d4ed8), var(--sfx-up-primary, #2563eb));
2181
+ box-shadow: 0 4px 16px rgba(37, 99, 235, 0.38);
2182
+ transform: translateY(-1px);
2183
+ }
2184
+
2185
+ .btn-primary:active {
2186
+ transform: translateY(0);
2187
+ }
2188
+
2189
+ .btn-primary:disabled {
2190
+ opacity: 0.55;
2191
+ cursor: not-allowed;
2192
+ }
2193
+
2194
+ .btn-primary.done-state {
2195
+ background: linear-gradient(135deg, var(--sfx-up-success, #16a34a), #22c55e);
2196
+ box-shadow: 0 2px 10px rgba(22, 163, 74, 0.28);
2197
+ }
2198
+
2199
+ /* --- Spinner --- */
2200
+ .btn-spin {
2201
+ width: 14px;
2202
+ height: 14px;
2203
+ border: 2px solid rgba(255, 255, 255, 0.3);
2204
+ border-top-color: #fff;
2205
+ border-radius: 50%;
2206
+ animation: spinRing 0.7s linear infinite;
2207
+ }
2208
+
2209
+ /* --- Count --- */
2210
+ .count {
2211
+ font-size: 13.5px;
2212
+ font-weight: 700;
2213
+ color: var(--sfx-up-text, #1e293b);
2214
+ }
2215
+
2216
+ .count span {
2217
+ font-weight: 400;
2218
+ color: var(--sfx-up-text-muted, #94a3b8);
2219
+ }
2220
+
2221
+ @keyframes spinRing {
2222
+ to { transform: rotate(360deg); }
2223
+ }
2224
+
2225
+ @media (prefers-reduced-motion: reduce) {
2226
+ .btn-spin { animation: none; }
2227
+ }
2228
+ `;
2229
+ let z = ne;
2230
+ V([
2231
+ x({ type: String })
2232
+ ], z.prototype, "uploadState");
2233
+ V([
2234
+ x({ type: Number })
2235
+ ], z.prototype, "fileCount");
2236
+ V([
2237
+ x({ type: Number })
2238
+ ], z.prototype, "failedCount");
2239
+ V([
2240
+ x({ type: Boolean })
2241
+ ], z.prototype, "showFillMetadata");
2242
+ var Ze = Object.defineProperty, Y = (i, e, t, r) => {
2243
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
2244
+ (n = i[o]) && (s = n(e, t, s) || s);
2245
+ return s && Ze(e, t, s), s;
2246
+ };
2247
+ const ae = class ae extends w {
2248
+ constructor() {
2249
+ super(...arguments), this._url = "", this._name = "", this._error = "", this._loading = !1, this._onBackdropClick = (e) => {
2250
+ e.target === e.currentTarget && this._cancel();
2251
+ }, this._onUrlInput = (e) => {
2252
+ this._url = e.target.value, this._error = "", this._autoName();
2253
+ }, this._onNameInput = (e) => {
2254
+ this._name = e.target.value;
2255
+ }, this._onKeyDown = (e) => {
2256
+ e.key === "Escape" && this._cancel(), e.key === "Enter" && !this._loading && this._submit();
2257
+ };
2258
+ }
2259
+ _autoName() {
2260
+ var e;
2261
+ if (!this._name)
2262
+ try {
2263
+ const t = new URL(this._url).pathname.split("/"), r = t[t.length - 1];
2264
+ if (r) {
2265
+ const s = (e = this.shadowRoot) == null ? void 0 : e.querySelector("#nameInput");
2266
+ s && (s.placeholder = r);
2267
+ }
2268
+ } catch {
2269
+ }
2270
+ }
2271
+ _cancel() {
2272
+ this.dispatchEvent(new CustomEvent("url-cancel", { bubbles: !0, composed: !0 }));
2273
+ }
2274
+ _submit() {
2275
+ const e = this._url.trim();
2276
+ if (!e) {
2277
+ this._error = "Please enter a URL";
2278
+ return;
2279
+ }
2280
+ try {
2281
+ new URL(e);
2282
+ } catch {
2283
+ this._error = "Please enter a valid URL";
2284
+ return;
2285
+ }
2286
+ this._error = "";
2287
+ let t = this._name.trim();
2288
+ if (!t)
2289
+ try {
2290
+ const r = new URL(e).pathname.split("/");
2291
+ t = r[r.length - 1] || "imported-file";
2292
+ } catch {
2293
+ t = "imported-file";
2294
+ }
2295
+ this.dispatchEvent(
2296
+ new CustomEvent("url-submit", {
2297
+ detail: { url: e, name: t },
2298
+ bubbles: !0,
2299
+ composed: !0
2300
+ })
2301
+ );
2302
+ }
2303
+ connectedCallback() {
2304
+ super.connectedCallback(), this.updateComplete.then(() => {
2305
+ var e, t;
2306
+ (t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("#urlInput")) == null || t.focus();
2307
+ });
2308
+ }
2309
+ render() {
2310
+ return c`
2311
+ <div class="backdrop" @click=${this._onBackdropClick} @keydown=${this._onKeyDown}>
2312
+ <div class="card">
2313
+ <div class="head">
2314
+ <div class="head-icon">
2315
+ <svg viewBox="0 0 24 24">
2316
+ <path d="M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71" />
2317
+ <path d="M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71" />
2318
+ </svg>
2319
+ </div>
2320
+ <div class="title">Import from URL</div>
2321
+ <button class="close-btn" @click=${this._cancel}>\u2715</button>
2322
+ </div>
2323
+ <div class="body">
2324
+ <div class="field">
2325
+ <label>File URL</label>
2326
+ <input
2327
+ id="urlInput"
2328
+ type="url"
2329
+ placeholder="https://example.com/file.pdf"
2330
+ .value=${this._url}
2331
+ @input=${this._onUrlInput}
2332
+ />
2333
+ </div>
2334
+ <div class="field">
2335
+ <label>File name <span class="optional">(optional)</span></label>
2336
+ <input
2337
+ id="nameInput"
2338
+ type="text"
2339
+ placeholder="document.pdf"
2340
+ .value=${this._name}
2341
+ @input=${this._onNameInput}
2342
+ />
2343
+ </div>
2344
+ ${this._error ? c`<div class="error">${this._error}</div>` : ""}
2345
+ <div class="actions">
2346
+ <button class="btn btn-ghost" @click=${this._cancel}>Cancel</button>
2347
+ <button class="btn btn-primary" ?disabled=${this._loading} @click=${this._submit}>
2348
+ Import file
2349
+ </button>
2350
+ </div>
2351
+ </div>
2352
+ </div>
2353
+ </div>
2354
+ `;
2355
+ }
2356
+ };
2357
+ ae.styles = _`
2358
+ :host {
2359
+ display: block;
2360
+ }
2361
+
2362
+ .backdrop {
2363
+ position: fixed;
2364
+ inset: 0;
2365
+ z-index: 1000;
2366
+ background: rgba(0, 0, 0, 0.4);
2367
+ backdrop-filter: blur(6px);
2368
+ display: flex;
2369
+ align-items: center;
2370
+ justify-content: center;
2371
+ padding: 20px;
2372
+ animation: fadeIn 0.18s ease both;
2373
+ }
2374
+
2375
+ .card {
2376
+ background: var(--sfx-up-bg, #fff);
2377
+ border-radius: 20px;
2378
+ box-shadow: 0 28px 80px rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.06);
2379
+ width: 100%;
2380
+ max-width: 480px;
2381
+ overflow: hidden;
2382
+ display: flex;
2383
+ flex-direction: column;
2384
+ transform: translateY(18px) scale(0.97);
2385
+ animation: slideUp 0.28s cubic-bezier(0.34, 1.2, 0.64, 1) forwards;
2386
+ }
2387
+
2388
+ .head {
2389
+ display: flex;
2390
+ align-items: center;
2391
+ gap: 10px;
2392
+ padding: 18px 20px 0;
2393
+ }
2394
+
2395
+ .head-icon {
2396
+ width: 34px;
2397
+ height: 34px;
2398
+ border-radius: 10px;
2399
+ background: var(--sfx-up-primary-bg, #f5f5f7);
2400
+ display: flex;
2401
+ align-items: center;
2402
+ justify-content: center;
2403
+ flex-shrink: 0;
2404
+ color: var(--sfx-up-primary, #2563eb);
2405
+ }
2406
+
2407
+ .head-icon svg {
2408
+ width: 18px;
2409
+ height: 18px;
2410
+ fill: none;
2411
+ stroke: currentColor;
2412
+ stroke-width: 2;
2413
+ stroke-linecap: round;
2414
+ }
2415
+
2416
+ .title {
2417
+ font-size: 15px;
2418
+ font-weight: 700;
2419
+ color: var(--sfx-up-text, #1a1a1a);
2420
+ flex: 1;
2421
+ }
2422
+
2423
+ .close-btn {
2424
+ width: 28px;
2425
+ height: 28px;
2426
+ border-radius: 8px;
2427
+ border: none;
2428
+ background: #f0f0f0;
2429
+ color: #888;
2430
+ font-size: 14px;
2431
+ cursor: pointer;
2432
+ display: flex;
2433
+ align-items: center;
2434
+ justify-content: center;
2435
+ transition: background 0.15s, color 0.15s;
2436
+ flex-shrink: 0;
2437
+ line-height: 1;
2438
+ }
2439
+
2440
+ .close-btn:hover {
2441
+ background: #e4e4e4;
2442
+ color: #333;
2443
+ }
2444
+
2445
+ .body {
2446
+ padding: 18px 20px 20px;
2447
+ }
2448
+
2449
+ .field {
2450
+ margin-bottom: 14px;
2451
+ }
2452
+
2453
+ label {
2454
+ display: block;
2455
+ font-size: 11px;
2456
+ font-weight: 600;
2457
+ color: #aaa;
2458
+ margin-bottom: 5px;
2459
+ text-transform: uppercase;
2460
+ letter-spacing: 0.7px;
2461
+ }
2462
+
2463
+ label .optional {
2464
+ color: #ccc;
2465
+ font-weight: 400;
2466
+ text-transform: none;
2467
+ letter-spacing: 0;
2468
+ }
2469
+
2470
+ input {
2471
+ width: 100%;
2472
+ height: 42px;
2473
+ border: 1.5px solid #ebebeb;
2474
+ border-radius: 10px;
2475
+ padding: 0 14px;
2476
+ font-size: 14px;
2477
+ font-family: inherit;
2478
+ color: #1a1a1a;
2479
+ background: #fafafa;
2480
+ transition: border-color 0.15s, background 0.15s;
2481
+ outline: none;
2482
+ box-sizing: border-box;
2483
+ }
2484
+
2485
+ input:focus {
2486
+ border-color: var(--sfx-up-primary, #2563eb);
2487
+ background: #fff;
2488
+ }
2489
+
2490
+ input::placeholder {
2491
+ color: #ccc;
2492
+ }
2493
+
2494
+ .error {
2495
+ font-size: 12px;
2496
+ color: #dc2626;
2497
+ margin-top: -6px;
2498
+ margin-bottom: 8px;
2499
+ }
2500
+
2501
+ .actions {
2502
+ display: flex;
2503
+ gap: 9px;
2504
+ justify-content: flex-end;
2505
+ margin-top: 18px;
2506
+ }
2507
+
2508
+ button.btn {
2509
+ height: 36px;
2510
+ padding: 0 17px;
2511
+ border-radius: 9px;
2512
+ border: none;
2513
+ font-family: inherit;
2514
+ font-size: 13px;
2515
+ font-weight: 600;
2516
+ cursor: pointer;
2517
+ display: inline-flex;
2518
+ align-items: center;
2519
+ justify-content: center;
2520
+ gap: 6px;
2521
+ transition: all 0.18s ease;
2522
+ white-space: nowrap;
2523
+ }
2524
+
2525
+ .btn-ghost {
2526
+ background: none;
2527
+ color: #94a3b8;
2528
+ border: 1.5px solid #e8edf5;
2529
+ }
2530
+
2531
+ .btn-ghost:hover {
2532
+ background: #f8faff;
2533
+ color: #64748b;
2534
+ border-color: #d1dff0;
2535
+ }
2536
+
2537
+ .btn-primary {
2538
+ background: linear-gradient(135deg, var(--sfx-up-primary, #2563eb), #3b82f6);
2539
+ color: #fff;
2540
+ box-shadow: 0 2px 10px rgba(37, 99, 235, 0.28);
2541
+ position: relative;
2542
+ overflow: hidden;
2543
+ }
2544
+
2545
+ .btn-primary:hover:not(:disabled) {
2546
+ background: linear-gradient(135deg, #1d4ed8, var(--sfx-up-primary, #2563eb));
2547
+ box-shadow: 0 4px 16px rgba(37, 99, 235, 0.38);
2548
+ transform: translateY(-1px);
2549
+ }
2550
+
2551
+ .btn-primary:disabled {
2552
+ opacity: 0.55;
2553
+ cursor: not-allowed;
2554
+ }
2555
+
2556
+ .btn-spin {
2557
+ width: 14px;
2558
+ height: 14px;
2559
+ border: 2px solid rgba(255, 255, 255, 0.3);
2560
+ border-top-color: #fff;
2561
+ border-radius: 50%;
2562
+ animation: spin 0.7s linear infinite;
2563
+ }
2564
+
2565
+ @keyframes fadeIn {
2566
+ from { opacity: 0; }
2567
+ to { opacity: 1; }
2568
+ }
2569
+
2570
+ @keyframes slideUp {
2571
+ from { transform: translateY(18px) scale(0.97); }
2572
+ to { transform: translateY(0) scale(1); }
2573
+ }
2574
+
2575
+ @keyframes spin {
2576
+ to { transform: rotate(360deg); }
2577
+ }
2578
+ `;
2579
+ let R = ae;
2580
+ Y([
2581
+ g()
2582
+ ], R.prototype, "_url");
2583
+ Y([
2584
+ g()
2585
+ ], R.prototype, "_name");
2586
+ Y([
2587
+ g()
2588
+ ], R.prototype, "_error");
2589
+ Y([
2590
+ g()
2591
+ ], R.prototype, "_loading");
2592
+ var et = Object.defineProperty, K = (i, e, t, r) => {
2593
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
2594
+ (n = i[o]) && (s = n(e, t, s) || s);
2595
+ return s && et(e, t, s), s;
2596
+ };
2597
+ const le = class le extends w {
2598
+ constructor() {
2599
+ super(...arguments), this._stream = null, this._error = "", this._captured = null, this._previewUrl = "", this._onBackdropClick = (e) => {
2600
+ e.target === e.currentTarget && this._cancel();
2601
+ }, this._onKeyDown = (e) => {
2602
+ e.key === "Escape" && this._cancel();
2603
+ }, this._capture = () => {
2604
+ var s, o;
2605
+ const e = (s = this.shadowRoot) == null ? void 0 : s.querySelector("video"), t = (o = this.shadowRoot) == null ? void 0 : o.querySelector("canvas");
2606
+ if (!e || !t) return;
2607
+ t.width = e.videoWidth, t.height = e.videoHeight, t.getContext("2d").drawImage(e, 0, 0), t.toBlob((n) => {
2608
+ n && (this._captured = n, this._previewUrl = URL.createObjectURL(n), this._stopStream());
2609
+ }, "image/jpeg", 0.92);
2610
+ }, this._retake = () => {
2611
+ this._previewUrl && URL.revokeObjectURL(this._previewUrl), this._captured = null, this._previewUrl = "", this._startCamera();
2612
+ }, this._usePhoto = () => {
2613
+ if (!this._captured) return;
2614
+ const e = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = new File([this._captured], `camera-${e}.jpg`, { type: "image/jpeg" });
2615
+ this.dispatchEvent(new CustomEvent("camera-capture", { detail: { file: t }, bubbles: !0, composed: !0 }));
2616
+ };
2617
+ }
2618
+ connectedCallback() {
2619
+ super.connectedCallback(), this._startCamera();
2620
+ }
2621
+ disconnectedCallback() {
2622
+ super.disconnectedCallback(), this._stopStream(), this._previewUrl && URL.revokeObjectURL(this._previewUrl);
2623
+ }
2624
+ async _startCamera() {
2625
+ var e;
2626
+ try {
2627
+ this._stream = await navigator.mediaDevices.getUserMedia({ video: !0, audio: !1 }), await this.updateComplete;
2628
+ const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video");
2629
+ t && (t.srcObject = this._stream);
2630
+ } catch {
2631
+ this._error = "Could not access camera. Please check your permissions.";
2632
+ }
2633
+ }
2634
+ _stopStream() {
2635
+ var e;
2636
+ (e = this._stream) == null || e.getTracks().forEach((t) => t.stop()), this._stream = null;
2637
+ }
2638
+ _cancel() {
2639
+ this._stopStream(), this.dispatchEvent(new CustomEvent("camera-cancel", { bubbles: !0, composed: !0 }));
2640
+ }
2641
+ render() {
2642
+ return c`
2643
+ <div class="backdrop" @click=${this._onBackdropClick} @keydown=${this._onKeyDown}>
2644
+ <div class="card">
2645
+ <div class="head">
2646
+ <div class="head-icon">
2647
+ <svg viewBox="0 0 24 24">
2648
+ <path d="M23 19a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2h4l2-3h6l2 3h4a2 2 0 012 2z"/>
2649
+ <circle cx="12" cy="13" r="4"/>
2650
+ </svg>
2651
+ </div>
2652
+ <div class="title">Camera</div>
2653
+ <button class="close-btn" @click=${this._cancel}>\u2715</button>
2654
+ </div>
2655
+ <div class="body">
2656
+ ${this._error ? c`<div class="error">${this._error}</div>` : this._captured ? c`
2657
+ <img class="preview-img" src=${this._previewUrl} alt="Captured photo" />
2658
+ <div class="actions">
2659
+ <button class="btn btn-ghost" @click=${this._retake}>Retake</button>
2660
+ <button class="btn btn-primary" @click=${this._usePhoto}>Use photo</button>
2661
+ </div>
2662
+ ` : c`
2663
+ <video autoplay playsinline muted></video>
2664
+ <canvas></canvas>
2665
+ <div class="actions">
2666
+ <button class="btn-capture" @click=${this._capture}></button>
2667
+ </div>
2668
+ `}
2669
+ </div>
2670
+ </div>
2671
+ </div>
2672
+ `;
2673
+ }
2674
+ };
2675
+ le.styles = _`
2676
+ :host { display: block; }
2677
+
2678
+ .backdrop {
2679
+ position: fixed; inset: 0; z-index: 1000;
2680
+ background: rgba(0, 0, 0, 0.4);
2681
+ backdrop-filter: blur(6px);
2682
+ display: flex; align-items: center; justify-content: center;
2683
+ padding: 20px;
2684
+ animation: fadeIn 0.18s ease both;
2685
+ }
2686
+
2687
+ .card {
2688
+ background: var(--sfx-up-bg, #fff);
2689
+ border-radius: 20px;
2690
+ box-shadow: 0 28px 80px rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.06);
2691
+ width: 100%; max-width: 520px;
2692
+ height: 520px;
2693
+ overflow: hidden; display: flex; flex-direction: column;
2694
+ animation: slideUp 0.28s cubic-bezier(0.34, 1.2, 0.64, 1) forwards;
2695
+ }
2696
+
2697
+ .head {
2698
+ display: flex; align-items: center; gap: 10px;
2699
+ padding: 18px 20px 0;
2700
+ }
2701
+
2702
+ .head-icon {
2703
+ width: 34px; height: 34px; border-radius: 10px;
2704
+ background: var(--sfx-up-primary-bg, #f5f5f7);
2705
+ display: flex; align-items: center; justify-content: center;
2706
+ flex-shrink: 0; color: var(--sfx-up-primary, #2563eb);
2707
+ }
2708
+
2709
+ .head-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
2710
+
2711
+ .title { font-size: 15px; font-weight: 700; color: var(--sfx-up-text, #1a1a1a); flex: 1; }
2712
+
2713
+ .close-btn {
2714
+ width: 28px; height: 28px; border-radius: 8px; border: none;
2715
+ background: #f0f0f0; color: #888; font-size: 14px; cursor: pointer;
2716
+ display: flex; align-items: center; justify-content: center;
2717
+ transition: background 0.15s, color 0.15s; flex-shrink: 0; line-height: 1;
2718
+ }
2719
+ .close-btn:hover { background: #e4e4e4; color: #333; }
2720
+
2721
+ .body { padding: 18px 20px 20px; display: flex; flex-direction: column; align-items: center; gap: 16px; flex: 1; min-height: 0; justify-content: center; }
2722
+
2723
+ video, canvas {
2724
+ width: 100%; flex: 1; min-height: 0; border-radius: 12px;
2725
+ background: #000; object-fit: cover;
2726
+ }
2727
+
2728
+ canvas { display: none; }
2729
+
2730
+ .preview-img {
2731
+ width: 100%; max-height: 320px; border-radius: 12px;
2732
+ object-fit: contain; background: #000;
2733
+ }
2734
+
2735
+ .error { font-size: 13px; color: #dc2626; text-align: center; padding: 40px 20px; }
2736
+
2737
+ .actions { display: flex; gap: 9px; justify-content: center; width: 100%; }
2738
+
2739
+ button.btn {
2740
+ height: 36px; padding: 0 17px; border-radius: 9px; border: none;
2741
+ font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
2742
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
2743
+ transition: all 0.18s ease; white-space: nowrap;
2744
+ }
2745
+
2746
+ .btn-ghost { background: none; color: #94a3b8; border: 1.5px solid #e8edf5; }
2747
+ .btn-ghost:hover { background: #f8faff; color: #64748b; border-color: #d1dff0; }
2748
+
2749
+ .btn-primary {
2750
+ background: linear-gradient(135deg, var(--sfx-up-primary, #2563eb), #3b82f6);
2751
+ color: #fff; box-shadow: 0 2px 10px rgba(37, 99, 235, 0.28);
2752
+ }
2753
+ .btn-primary:hover { background: linear-gradient(135deg, #1d4ed8, var(--sfx-up-primary, #2563eb)); box-shadow: 0 4px 16px rgba(37, 99, 235, 0.38); transform: translateY(-1px); }
2754
+
2755
+ .btn-capture {
2756
+ width: 52px; height: 52px; border-radius: 50%; padding: 0;
2757
+ background: #dc2626; border: 4px solid #fff;
2758
+ box-shadow: 0 0 0 2px #dc2626, 0 4px 12px rgba(220, 38, 38, 0.3);
2759
+ cursor: pointer; transition: all 0.15s;
2760
+ }
2761
+ .btn-capture:hover { background: #b91c1c; transform: scale(1.05); }
2762
+
2763
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
2764
+ @keyframes slideUp { from { transform: translateY(18px) scale(0.97); } to { transform: translateY(0) scale(1); } }
2765
+ `;
2766
+ let A = le;
2767
+ K([
2768
+ g()
2769
+ ], A.prototype, "_stream");
2770
+ K([
2771
+ g()
2772
+ ], A.prototype, "_error");
2773
+ K([
2774
+ g()
2775
+ ], A.prototype, "_captured");
2776
+ K([
2777
+ g()
2778
+ ], A.prototype, "_previewUrl");
2779
+ var tt = Object.defineProperty, I = (i, e, t, r) => {
2780
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
2781
+ (n = i[o]) && (s = n(e, t, s) || s);
2782
+ return s && tt(e, t, s), s;
2783
+ };
2784
+ const ce = class ce extends w {
2785
+ constructor() {
2786
+ super(...arguments), this._stream = null, this._recording = !1, this._error = "", this._recordedBlob = null, this._previewUrl = "", this._recorder = null, this._chunks = [], this._onBackdropClick = (e) => {
2787
+ e.target === e.currentTarget && this._cancel();
2788
+ }, this._onKeyDown = (e) => {
2789
+ e.key === "Escape" && this._cancel();
2790
+ }, this._startRecording = async () => {
2791
+ var e;
2792
+ try {
2793
+ this._stream = await navigator.mediaDevices.getDisplayMedia({
2794
+ video: { width: 1280, height: 720, frameRate: 5 },
2795
+ audio: !0
2796
+ }), this._stream.getVideoTracks()[0].addEventListener("ended", () => {
2797
+ this._stopRecording();
2798
+ }), this._recording = !0, await this.updateComplete;
2799
+ const t = (e = this.shadowRoot) == null ? void 0 : e.querySelector("video");
2800
+ t && (t.srcObject = this._stream), this._chunks = [];
2801
+ const r = MediaRecorder.isTypeSupported("video/webm;codecs=vp9") ? "video/webm;codecs=vp9" : "video/webm";
2802
+ this._recorder = new MediaRecorder(this._stream, { mimeType: r }), this._recorder.ondataavailable = (s) => {
2803
+ s.data.size > 0 && this._chunks.push(s.data);
2804
+ }, this._recorder.onstop = () => {
2805
+ var o;
2806
+ const s = new Blob(this._chunks, { type: "video/webm" });
2807
+ this._recordedBlob = s, this._previewUrl = URL.createObjectURL(s), (o = this._stream) == null || o.getTracks().forEach((n) => n.stop()), this._stream = null;
2808
+ }, this._recorder.start();
2809
+ } catch {
2810
+ this._error = "Could not start screen capture. Please check your permissions.";
2811
+ }
2812
+ }, this._stopRecording = () => {
2813
+ var e;
2814
+ this._recording = !1, (e = this._recorder) == null || e.stop(), this._recorder = null;
2815
+ }, this._useRecording = () => {
2816
+ if (!this._recordedBlob) return;
2817
+ const e = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = new File([this._recordedBlob], `screencap-${e}.webm`, { type: "video/webm" });
2818
+ this.dispatchEvent(new CustomEvent("screencast-capture", { detail: { file: t }, bubbles: !0, composed: !0 }));
2819
+ }, this._discard = () => {
2820
+ this._previewUrl && URL.revokeObjectURL(this._previewUrl), this._recordedBlob = null, this._previewUrl = "";
2821
+ };
2822
+ }
2823
+ disconnectedCallback() {
2824
+ super.disconnectedCallback(), this._stopAll(), this._previewUrl && URL.revokeObjectURL(this._previewUrl);
2825
+ }
2826
+ _stopAll() {
2827
+ var e, t;
2828
+ (e = this._recorder) == null || e.stop(), this._recorder = null, (t = this._stream) == null || t.getTracks().forEach((r) => r.stop()), this._stream = null;
2829
+ }
2830
+ _cancel() {
2831
+ this._stopAll(), this.dispatchEvent(new CustomEvent("screencast-cancel", { bubbles: !0, composed: !0 }));
2832
+ }
2833
+ render() {
2834
+ return c`
2835
+ <div class="backdrop" @click=${this._onBackdropClick} @keydown=${this._onKeyDown}>
2836
+ <div class="card">
2837
+ <div class="head">
2838
+ <div class="head-icon">
2839
+ <svg viewBox="0 0 24 24">
2840
+ <rect x="2" y="3" width="20" height="14" rx="2"/>
2841
+ <circle cx="12" cy="10" r="3"/>
2842
+ <path d="M7 21h10"/>
2843
+ </svg>
2844
+ </div>
2845
+ <div class="title">Screen cast</div>
2846
+ <button class="close-btn" @click=${this._cancel}>\u2715</button>
2847
+ </div>
2848
+ <div class="body">
2849
+ ${this._error ? c`<div class="error">${this._error}</div>` : this._recordedBlob ? c`
2850
+ <video src=${this._previewUrl} controls></video>
2851
+ <div class="actions">
2852
+ <button class="btn btn-ghost" @click=${this._discard}>Discard</button>
2853
+ <button class="btn btn-primary" @click=${this._useRecording}>Use recording</button>
2854
+ </div>
2855
+ ` : this._recording ? c`
2856
+ <video autoplay playsinline muted></video>
2857
+ <div class="status"><div class="rec-dot"></div> Recording...</div>
2858
+ <div class="actions">
2859
+ <button class="btn btn-danger" @click=${this._stopRecording}>Stop recording</button>
2860
+ </div>
2861
+ ` : c`
2862
+ <div class="start-view">
2863
+ <div class="start-icon">
2864
+ <svg viewBox="0 0 24 24">
2865
+ <rect x="2" y="3" width="20" height="14" rx="2"/>
2866
+ <circle cx="12" cy="10" r="3"/>
2867
+ <path d="M7 21h10"/>
2868
+ </svg>
2869
+ </div>
2870
+ <div class="start-text">Share your screen to record a video that will be added to your uploads.</div>
2871
+ <div class="actions">
2872
+ <button class="btn btn-ghost" @click=${this._cancel}>Cancel</button>
2873
+ <button class="btn btn-primary" @click=${this._startRecording}>Start recording</button>
2874
+ </div>
2875
+ </div>
2876
+ `}
2877
+ </div>
2878
+ </div>
2879
+ </div>
2880
+ `;
2881
+ }
2882
+ };
2883
+ ce.styles = _`
2884
+ :host { display: block; }
2885
+
2886
+ .backdrop {
2887
+ position: fixed; inset: 0; z-index: 1000;
2888
+ background: rgba(0, 0, 0, 0.4);
2889
+ backdrop-filter: blur(6px);
2890
+ display: flex; align-items: center; justify-content: center;
2891
+ padding: 20px;
2892
+ animation: fadeIn 0.18s ease both;
2893
+ }
2894
+
2895
+ .card {
2896
+ background: var(--sfx-up-bg, #fff);
2897
+ border-radius: 20px;
2898
+ box-shadow: 0 28px 80px rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.06);
2899
+ width: 100%; max-width: 560px;
2900
+ overflow: hidden; display: flex; flex-direction: column;
2901
+ animation: slideUp 0.28s cubic-bezier(0.34, 1.2, 0.64, 1) forwards;
2902
+ }
2903
+
2904
+ .head {
2905
+ display: flex; align-items: center; gap: 10px;
2906
+ padding: 18px 20px 0;
2907
+ }
2908
+
2909
+ .head-icon {
2910
+ width: 34px; height: 34px; border-radius: 10px;
2911
+ background: var(--sfx-up-primary-bg, #f5f5f7);
2912
+ display: flex; align-items: center; justify-content: center;
2913
+ flex-shrink: 0; color: var(--sfx-up-primary, #2563eb);
2914
+ }
2915
+
2916
+ .head-icon svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
2917
+
2918
+ .title { font-size: 15px; font-weight: 700; color: var(--sfx-up-text, #1a1a1a); flex: 1; }
2919
+
2920
+ .close-btn {
2921
+ width: 28px; height: 28px; border-radius: 8px; border: none;
2922
+ background: #f0f0f0; color: #888; font-size: 14px; cursor: pointer;
2923
+ display: flex; align-items: center; justify-content: center;
2924
+ transition: background 0.15s, color 0.15s; flex-shrink: 0; line-height: 1;
2925
+ }
2926
+ .close-btn:hover { background: #e4e4e4; color: #333; }
2927
+
2928
+ .body { padding: 18px 20px 20px; display: flex; flex-direction: column; align-items: center; gap: 16px; }
2929
+
2930
+ video {
2931
+ width: 100%; max-height: 320px; border-radius: 12px;
2932
+ background: #000; object-fit: contain;
2933
+ }
2934
+
2935
+ .error { font-size: 13px; color: #dc2626; text-align: center; padding: 40px 20px; }
2936
+
2937
+ .status {
2938
+ font-size: 13px; color: var(--sfx-up-text-secondary, #475569);
2939
+ display: flex; align-items: center; gap: 8px;
2940
+ }
2941
+
2942
+ .rec-dot {
2943
+ width: 10px; height: 10px; border-radius: 50%;
2944
+ background: #dc2626; animation: pulse 1s ease-in-out infinite;
2945
+ }
2946
+
2947
+ .actions { display: flex; gap: 9px; justify-content: center; width: 100%; }
2948
+
2949
+ button.btn {
2950
+ height: 36px; padding: 0 17px; border-radius: 9px; border: none;
2951
+ font-family: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
2952
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
2953
+ transition: all 0.18s ease; white-space: nowrap;
2954
+ }
2955
+
2956
+ .btn-ghost { background: none; color: #94a3b8; border: 1.5px solid #e8edf5; }
2957
+ .btn-ghost:hover { background: #f8faff; color: #64748b; border-color: #d1dff0; }
2958
+
2959
+ .btn-primary {
2960
+ background: linear-gradient(135deg, var(--sfx-up-primary, #2563eb), #3b82f6);
2961
+ color: #fff; box-shadow: 0 2px 10px rgba(37, 99, 235, 0.28);
2962
+ }
2963
+ .btn-primary:hover { background: linear-gradient(135deg, #1d4ed8, var(--sfx-up-primary, #2563eb)); box-shadow: 0 4px 16px rgba(37, 99, 235, 0.38); transform: translateY(-1px); }
2964
+
2965
+ .btn-danger {
2966
+ background: #dc2626; color: #fff;
2967
+ box-shadow: 0 2px 10px rgba(220, 38, 38, 0.28);
2968
+ }
2969
+ .btn-danger:hover { background: #b91c1c; }
2970
+
2971
+ .start-view {
2972
+ display: flex; flex-direction: column; align-items: center; gap: 16px;
2973
+ padding: 30px 20px; text-align: center;
2974
+ }
2975
+
2976
+ .start-icon {
2977
+ width: 56px; height: 56px; border-radius: 16px;
2978
+ background: var(--sfx-up-primary-bg, #eff6ff);
2979
+ color: var(--sfx-up-primary, #2563eb);
2980
+ display: flex; align-items: center; justify-content: center;
2981
+ }
2982
+
2983
+ .start-icon svg { width: 28px; height: 28px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
2984
+
2985
+ .start-text {
2986
+ font-size: 14px; color: var(--sfx-up-text-secondary, #475569); max-width: 300px;
2987
+ }
2988
+
2989
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
2990
+ @keyframes slideUp { from { transform: translateY(18px) scale(0.97); } to { transform: translateY(0) scale(1); } }
2991
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
2992
+ `;
2993
+ let U = ce;
2994
+ I([
2995
+ g()
2996
+ ], U.prototype, "_stream");
2997
+ I([
2998
+ g()
2999
+ ], U.prototype, "_recording");
3000
+ I([
3001
+ g()
3002
+ ], U.prototype, "_error");
3003
+ I([
3004
+ g()
3005
+ ], U.prototype, "_recordedBlob");
3006
+ I([
3007
+ g()
3008
+ ], U.prototype, "_previewUrl");
3009
+ var rt = Object.defineProperty, D = (i, e, t, r) => {
3010
+ for (var s = void 0, o = i.length - 1, n; o >= 0; o--)
3011
+ (n = i[o]) && (s = n(e, t, s) || s);
3012
+ return s && rt(e, t, s), s;
3013
+ };
3014
+ const ue = /* @__PURE__ */ new Set(["unsplash"]);
3015
+ var m;
3016
+ const P = (m = class extends w {
3017
+ constructor() {
3018
+ super(), this.config = null, this._isOpen = !1, this._activeConnector = null, this._showUrlDialog = !1, this._showCameraDialog = !1, this._showScreenCastDialog = !1, this._engine = null, this._cachedSources = H, this._cachedSourcesConfig = void 0, this._apiBase = null, this._authHeaders = null, this._authResolveId = 0, this._prevStoreState = null, this._unsubStoreEvents = null, this._onFilesSelected = (e) => {
3019
+ this._processIncomingFiles(e.detail.files);
3020
+ }, this._onSourceClick = async (e) => {
3021
+ var o, n;
3022
+ const t = e.detail.source, r = this._mergedSources.find((a) => a.id === t);
3023
+ if (r != null && r.onActivate) {
3024
+ try {
3025
+ r.onActivate(this);
3026
+ } catch (a) {
3027
+ console.error(`[sfx-uploader] onActivate for custom source "${t}" threw:`, a);
3028
+ }
3029
+ return;
3030
+ }
3031
+ if (t === "device") {
3032
+ const a = this.shadowRoot.querySelector("sfx-drop-zone");
3033
+ a == null || a.browse();
3034
+ return;
3035
+ }
3036
+ if (t === "url") {
3037
+ this._showUrlDialog = !0;
3038
+ return;
3039
+ }
3040
+ if (t === "camera") {
3041
+ this._showCameraDialog = !0;
3042
+ return;
3043
+ }
3044
+ if (t === "screen-cast") {
3045
+ this._showScreenCastDialog = !0;
3046
+ return;
3047
+ }
3048
+ if ((((n = (o = this.config) == null ? void 0 : o.connectors) == null ? void 0 : n.providers) ?? []).includes(t)) {
3049
+ if (ue.has(t)) {
3050
+ if (!customElements.get("sfx-search-provider-browser")) {
3051
+ const { SfxSearchProviderBrowser: l } = await import("./search-provider-browser-jCOer2Y9.js");
3052
+ customElements.define("sfx-search-provider-browser", l);
3053
+ }
3054
+ } else if (!customElements.get("sfx-provider-browser")) {
3055
+ const { SfxProviderBrowser: l } = await import("./provider-browser-C-S_MPrC.js");
3056
+ customElements.define("sfx-provider-browser", l);
3057
+ }
3058
+ this._activeConnector = t;
3059
+ }
3060
+ }, this._onUrlSubmit = (e) => {
3061
+ var a, l, p;
3062
+ this._showUrlDialog = !1;
3063
+ const { url: t, name: r } = e.detail, s = Le(r), o = s.startsWith("image/"), n = {
3064
+ id: B(),
3065
+ status: "idle",
3066
+ file: null,
3067
+ remoteUrl: t,
3068
+ name: r,
3069
+ size: 0,
3070
+ type: s,
3071
+ previewUrl: o ? t : null,
3072
+ progress: 0,
3073
+ speed: 0,
3074
+ bytesUploaded: 0,
3075
+ error: null,
3076
+ retryCount: 0,
3077
+ response: null,
3078
+ addedAt: Date.now(),
3079
+ meta: {},
3080
+ tags: [],
3081
+ remoteInfo: null
3082
+ };
3083
+ j(this._store, n), this._dispatchPublic(f.FILE_ADDED, { file: n }), (p = (l = (a = this.config) == null ? void 0 : a.callbacks) == null ? void 0 : l.onFileAdded) == null || p.call(l, n), this._store.getState().queueConfig.autoProceed && this.upload();
3084
+ }, this._onUrlCancel = () => {
3085
+ this._showUrlDialog = !1;
3086
+ }, this._onCameraCapture = (e) => {
3087
+ this._showCameraDialog = !1, this._processIncomingFiles([e.detail.file]);
3088
+ }, this._onCameraCancel = () => {
3089
+ this._showCameraDialog = !1;
3090
+ }, this._onScreenCastCapture = (e) => {
3091
+ this._showScreenCastDialog = !1, this._processIncomingFiles([e.detail.file]);
3092
+ }, this._onScreenCastCancel = () => {
3093
+ this._showScreenCastDialog = !1;
3094
+ }, this._onFileRemove = (e) => {
3095
+ var r, s, o, n;
3096
+ const t = this._store.getState().files.get(e.detail.fileId);
3097
+ t != null && t.previewUrl && URL.revokeObjectURL(t.previewUrl), t && (t.status === "uploading" || t.status === "queued") && ((r = this._engine) == null || r.cancelFile(e.detail.fileId)), me(this._store, e.detail.fileId), t && (this._dispatchPublic(f.FILE_REMOVED, { file: t }), (n = (o = (s = this.config) == null ? void 0 : s.callbacks) == null ? void 0 : o.onFileRemoved) == null || n.call(o, t));
3098
+ }, this._onFilePreview = (e) => {
3099
+ var r, s, o;
3100
+ const t = this._store.getState().files.get(e.detail.fileId);
3101
+ t && (this._dispatchPublic(f.FILE_PREVIEW, { file: t }), (o = (s = (r = this.config) == null ? void 0 : r.callbacks) == null ? void 0 : s.onFilePreview) == null || o.call(s, t));
3102
+ }, this._onFillMetadata = () => {
3103
+ var t, r, s;
3104
+ const e = [...this._store.getState().files.values()].filter(
3105
+ (o) => m._MODIFIABLE_STATUSES.has(o.status)
3106
+ );
3107
+ this._dispatchPublic(f.FILL_METADATA, { files: e }), (s = (r = (t = this.config) == null ? void 0 : t.callbacks) == null ? void 0 : r.onFillMetadata) == null || s.call(r, e);
3108
+ }, this._onFileRetry = (e) => {
3109
+ var t;
3110
+ this._ensureEngine(), (t = this._engine) == null || t.retryFile(e.detail.fileId);
3111
+ }, this._onRetryAll = () => {
3112
+ var e;
3113
+ this._ensureEngine(), (e = this._engine) == null || e.retryAll();
3114
+ }, this._onClearAll = () => {
3115
+ var t, r, s;
3116
+ const e = (t = this.config) == null ? void 0 : t.callbacks;
3117
+ for (const o of this._store.getState().files.values())
3118
+ o.previewUrl && URL.revokeObjectURL(o.previewUrl), this._dispatchPublic(f.FILE_REMOVED, { file: o }), (r = e == null ? void 0 : e.onFileRemoved) == null || r.call(e, o);
3119
+ (s = this._engine) == null || s.cancelAll(), this._store.setState({
3120
+ files: /* @__PURE__ */ new Map(),
3121
+ isUploading: !1,
3122
+ totalProgress: 0,
3123
+ totalSpeed: 0,
3124
+ totalBytesUploaded: 0,
3125
+ totalBytes: 0
3126
+ });
3127
+ }, this._onAddMore = () => {
3128
+ const e = this.shadowRoot.querySelector("sfx-drop-zone");
3129
+ e == null || e.browse();
3130
+ }, this._onUploadStart = () => {
3131
+ if (this._phase === "complete") {
3132
+ this._onClearAll();
3133
+ return;
3134
+ }
3135
+ this.upload();
3136
+ }, this._onUploadMore = () => {
3137
+ this._onClearAll();
3138
+ }, this._onConnectorFilesSelected = (e) => {
3139
+ var r, s;
3140
+ const t = (r = this.config) == null ? void 0 : r.callbacks;
3141
+ for (const o of e.detail.files) {
3142
+ const n = {
3143
+ id: B(),
3144
+ status: "idle",
3145
+ file: null,
3146
+ remoteUrl: null,
3147
+ name: o.name,
3148
+ size: o.size,
3149
+ type: o.mimeType,
3150
+ previewUrl: o.thumbnail,
3151
+ progress: 0,
3152
+ speed: 0,
3153
+ bytesUploaded: 0,
3154
+ error: null,
3155
+ retryCount: 0,
3156
+ response: null,
3157
+ addedAt: Date.now(),
3158
+ meta: {},
3159
+ tags: [],
3160
+ remoteInfo: o
3161
+ };
3162
+ j(this._store, n), this._dispatchPublic(f.FILE_ADDED, { file: n }), (s = t == null ? void 0 : t.onFileAdded) == null || s.call(t, n);
3163
+ }
3164
+ this._activeConnector = null, this._store.getState().queueConfig.autoProceed && this.upload();
3165
+ }, this._onConnectorClose = () => {
3166
+ this._activeConnector = null;
3167
+ }, this._onConnectorBackdropClick = (e) => {
3168
+ e.target === e.currentTarget && (this._activeConnector = null);
3169
+ }, this._onPrimaryAction = () => {
3170
+ var e;
3171
+ this._dispatchPublic(f.COMPLETE_ACTION, {}), ((e = this.config) == null ? void 0 : e.mode) === "modal" && this.close();
3172
+ }, this._onModalDismiss = () => {
3173
+ var e, t, r;
3174
+ (r = (t = (e = this.config) == null ? void 0 : e.callbacks) == null ? void 0 : t.onCancel) == null || r.call(t), this._dispatchPublic(f.CANCEL, {}), this.close();
3175
+ }, this._onModalBackdropClick = (e) => {
3176
+ e.target === e.currentTarget && this._onModalDismiss();
3177
+ }, this._onKeyDown = (e) => {
3178
+ var t;
3179
+ e.key === "Escape" && this._isOpen && ((t = this.config) == null ? void 0 : t.mode) === "modal" && this._onModalDismiss();
3180
+ }, this._store = ve(), this._storeCtrl = new ye(this, this._store);
3181
+ }
3182
+ // --- Public API ---
3183
+ /** Open the uploader (modal mode). */
3184
+ open() {
3185
+ var e, t, r;
3186
+ this._isOpen || (this._isOpen = !0, (r = (t = (e = this.config) == null ? void 0 : e.callbacks) == null ? void 0 : t.onOpen) == null || r.call(t), this._dispatchPublic(f.OPEN, {}), this.requestUpdate());
3187
+ }
3188
+ /** Close the uploader (modal mode). */
3189
+ close() {
3190
+ var e, t, r;
3191
+ this._isOpen && (this._isOpen = !1, (r = (t = (e = this.config) == null ? void 0 : e.callbacks) == null ? void 0 : t.onClose) == null || r.call(t), this._dispatchPublic(f.CLOSE, {}), this.requestUpdate());
3192
+ }
3193
+ /** Start uploading all queued files. */
3194
+ upload() {
3195
+ var s, o, n, a, l;
3196
+ if (this._ensureEngine(), !this._engine) {
3197
+ console.warn("[sfx-uploader] Cannot upload: auth not resolved yet");
3198
+ return;
3199
+ }
3200
+ const e = [...this._store.getState().files.values()].filter(
3201
+ (p) => p.status === "idle" || p.status === "queued"
3202
+ );
3203
+ if ((o = (s = this.config) == null ? void 0 : s.callbacks) != null && o.onBeforeUpload && this.config.callbacks.onBeforeUpload(e) === !1)
3204
+ return;
3205
+ const t = new CustomEvent(f.BEFORE_UPLOAD, {
3206
+ bubbles: !0,
3207
+ composed: !0,
3208
+ cancelable: !0,
3209
+ detail: { files: e }
3210
+ });
3211
+ this.dispatchEvent(t) && (this._dispatchPublic(f.UPLOAD_STARTED, { files: e }), (l = (a = (n = this.config) == null ? void 0 : n.callbacks) == null ? void 0 : a.onUploadStarted) == null || l.call(a, e), this._engine.uploadAll());
3212
+ }
3213
+ /** Programmatically add files. */
3214
+ addFiles(e) {
3215
+ this._processIncomingFiles(e);
3216
+ }
3217
+ /** Resume a paused upload (spec §13.2). */
3218
+ resumeUpload(e) {
3219
+ var t;
3220
+ if (e && e.length > 0) {
3221
+ const r = this._store.getState().files, s = new Map(r);
3222
+ let o = !1;
3223
+ for (const n of e) {
3224
+ const a = r.get(n.id);
3225
+ a && (s.set(n.id, { ...a, ...n }), o = !0);
3226
+ }
3227
+ o && this._store.setState({ files: s });
3228
+ }
3229
+ this._ensureEngine(), (t = this._engine) == null || t.uploadAll();
3230
+ }
3231
+ /** Cancel a paused upload (spec §13.2). */
3232
+ cancelUpload() {
3233
+ var e;
3234
+ (e = this._engine) == null || e.cancelAll();
3235
+ }
3236
+ /** Get a snapshot of all current files. */
3237
+ getFiles() {
3238
+ return [...this._store.getState().files.values()];
3239
+ }
3240
+ /** Get a single file by ID. */
3241
+ getFile(e) {
3242
+ return this._store.getState().files.get(e);
3243
+ }
3244
+ /** Update metadata and/or tags for a single file. */
3245
+ updateFileMeta(e, t, r) {
3246
+ const s = this._store.getState().files, o = s.get(e);
3247
+ if (!o || !m._MODIFIABLE_STATUSES.has(o.status)) return;
3248
+ const n = new Map(s);
3249
+ n.set(e, {
3250
+ ...o,
3251
+ meta: t != null ? { ...o.meta, ...t } : o.meta,
3252
+ tags: r ?? o.tags
3253
+ }), this._store.setState({ files: n });
3254
+ }
3255
+ /** Batch-update metadata and/or tags for multiple files. */
3256
+ updateFilesMeta(e) {
3257
+ const t = this._store.getState().files, r = new Map(t);
3258
+ let s = !1;
3259
+ for (const { fileId: o, meta: n, tags: a } of e) {
3260
+ const l = t.get(o);
3261
+ !l || !m._MODIFIABLE_STATUSES.has(l.status) || (r.set(o, {
3262
+ ...l,
3263
+ meta: n != null ? { ...l.meta, ...n } : l.meta,
3264
+ tags: a ?? l.tags
3265
+ }), s = !0);
3266
+ }
3267
+ s && this._store.setState({ files: r });
3268
+ }
3269
+ // --- Lifecycle ---
3270
+ updated(e) {
3271
+ e.has("config") && this.config && this._applyConfig(this.config);
3272
+ }
3273
+ connectedCallback() {
3274
+ super.connectedCallback(), document.addEventListener("keydown", this._onKeyDown), this._unsubStoreEvents = this._store.subscribe(() => this._onStoreChange());
3275
+ }
3276
+ disconnectedCallback() {
3277
+ var e, t;
3278
+ super.disconnectedCallback(), document.removeEventListener("keydown", this._onKeyDown), (e = this._unsubStoreEvents) == null || e.call(this), this._unsubStoreEvents = null, this._prevStoreState = null, (t = this._engine) == null || t.destroy(), this._engine = null;
3279
+ }
3280
+ // --- Config ---
3281
+ _applyConfig(e) {
3282
+ const t = {};
3283
+ if (e.targetFolder && (t.targetFolder = e.targetFolder), e.restrictions && (t.restrictions = {
3284
+ ...this._store.getState().restrictions,
3285
+ ...e.restrictions
3286
+ }), e.concurrency) {
3287
+ const r = this._store.getState().queueConfig;
3288
+ t.queueConfig = { ...r, concurrency: e.concurrency };
3289
+ }
3290
+ if (e.autoProceed != null) {
3291
+ const r = t.queueConfig ?? this._store.getState().queueConfig;
3292
+ t.queueConfig = { ...r, autoProceed: e.autoProceed };
3293
+ }
3294
+ Object.keys(t).length > 0 && this._store.setState(t), this._resolveAuthAndEngine(e), (e.mode === "inline" || !e.mode) && (this._isOpen = !0);
3295
+ }
3296
+ async _resolveAuthAndEngine(e) {
3297
+ const t = e.auth;
3298
+ if (t.mode === "sass-key" || t.mode === "session") {
3299
+ this._apiBase = Z(t.container), this._authHeaders = J(t), this._ensureEngine(), this._engine.updateConfig({
3300
+ apiBase: this._apiBase,
3301
+ authHeaders: this._authHeaders
3302
+ });
3303
+ return;
3304
+ }
3305
+ const r = ++this._authResolveId;
3306
+ try {
3307
+ const s = await ze(t);
3308
+ if (r !== this._authResolveId) return;
3309
+ this._apiBase = s.apiBase, this._authHeaders = s.headers, this._ensureEngine(), this._engine.updateConfig({
3310
+ apiBase: this._apiBase,
3311
+ authHeaders: this._authHeaders
3312
+ });
3313
+ } catch (s) {
3314
+ if (r !== this._authResolveId) return;
3315
+ console.error("[sfx-uploader] Auth resolution failed:", s);
3316
+ }
3317
+ }
3318
+ _ensureEngine() {
3319
+ !this._engine && this._apiBase && this._authHeaders && (this._engine = new Se(this._store, {
3320
+ apiBase: this._apiBase,
3321
+ authHeaders: this._authHeaders
3322
+ }), this._engine.start());
3323
+ }
3324
+ // --- Public event dispatching (spec §13.1) ---
3325
+ _dispatchPublic(e, t) {
3326
+ this.dispatchEvent(
3327
+ new CustomEvent(e, { bubbles: !0, composed: !0, detail: t })
3328
+ );
3329
+ }
3330
+ /**
3331
+ * React to store changes and dispatch public events + callbacks
3332
+ * for file status transitions.
3333
+ */
3334
+ _onStoreChange() {
3335
+ var s, o, n, a, l, p, b;
3336
+ const e = this._store.getState(), t = this._prevStoreState;
3337
+ if (this._prevStoreState = e, !t) return;
3338
+ const r = (s = this.config) == null ? void 0 : s.callbacks;
3339
+ for (const [u, d] of e.files) {
3340
+ const v = t.files.get(u);
3341
+ if (v) {
3342
+ if (v.status !== d.status)
3343
+ switch (d.status) {
3344
+ case "uploading":
3345
+ break;
3346
+ case "complete":
3347
+ d.response && (this._dispatchPublic(f.UPLOAD_COMPLETE, { file: d, response: d.response }), (o = r == null ? void 0 : r.onUploadComplete) == null || o.call(r, d, d.response));
3348
+ break;
3349
+ case "error":
3350
+ case "failed": {
3351
+ const E = new Error(d.error ?? "Upload failed");
3352
+ this._dispatchPublic(f.UPLOAD_ERROR, { file: d, error: E }), (n = r == null ? void 0 : r.onUploadError) == null || n.call(r, d, E);
3353
+ break;
3354
+ }
3355
+ case "retrying":
3356
+ this._dispatchPublic(f.UPLOAD_RETRY, { file: d, attempt: d.retryCount }), (a = r == null ? void 0 : r.onUploadRetry) == null || a.call(r, d, d.retryCount);
3357
+ break;
3358
+ }
3359
+ d.status === "uploading" && v.progress !== d.progress && (this._dispatchPublic(f.UPLOAD_PROGRESS, { file: d, progress: d.progress, speed: d.speed }), (l = r == null ? void 0 : r.onUploadProgress) == null || l.call(r, d, d.progress, d.speed));
3360
+ }
3361
+ }
3362
+ if (e.totalProgress !== t.totalProgress || e.totalSpeed !== t.totalSpeed) {
3363
+ const u = e.totalSpeed > 0 ? (e.totalBytes - e.totalBytesUploaded) / e.totalSpeed : 0;
3364
+ this._dispatchPublic(f.TOTAL_PROGRESS, {
3365
+ percentage: e.totalProgress,
3366
+ speed: e.totalSpeed,
3367
+ eta: u
3368
+ }), (p = r == null ? void 0 : r.onTotalProgress) == null || p.call(r, e.totalProgress, e.totalSpeed, u);
3369
+ }
3370
+ if (t.isUploading && !e.isUploading) {
3371
+ const u = [...e.files.values()];
3372
+ if (!u.some((v) => v.status === "cancelled")) {
3373
+ const v = u.filter((S) => S.status === "complete"), E = u.filter((S) => S.status === "failed" || S.status === "error");
3374
+ this._dispatchPublic(f.ALL_COMPLETE, { successful: v, failed: E }), (b = r == null ? void 0 : r.onAllComplete) == null || b.call(r, v, E);
3375
+ }
3376
+ }
3377
+ }
3378
+ get _mergedSources() {
3379
+ var n;
3380
+ const e = (n = this.config) == null ? void 0 : n.connectors;
3381
+ if (e === this._cachedSourcesConfig) return this._cachedSources;
3382
+ if (this._cachedSourcesConfig = e, !e)
3383
+ return this._cachedSources = H, this._cachedSources;
3384
+ const t = e.providers.length > 0 ? Fe(e.providers) : [], r = e.customSources ?? [], s = /* @__PURE__ */ new Set(), o = [];
3385
+ for (const a of [...H, ...t, ...r])
3386
+ if (!s.has(a.id)) {
3387
+ if (m._RESERVED_IDS.has(a.id) && a.onActivate) {
3388
+ console.warn(`[sfx-uploader] Custom source id "${a.id}" conflicts with a built-in source and was skipped.`);
3389
+ continue;
3390
+ }
3391
+ s.add(a.id), o.push(a);
3392
+ }
3393
+ return this._cachedSources = o, this._cachedSources;
3394
+ }
3395
+ // --- Phase computation ---
3396
+ get _phase() {
3397
+ const e = this._storeCtrl.state, t = [...e.files.values()];
3398
+ if (t.length === 0) return "empty";
3399
+ if (e.isUploading) return "uploading";
3400
+ const r = /* @__PURE__ */ new Set(["complete", "rejected", "cancelled", "failed"]);
3401
+ return t.every((s) => r.has(s.status)) && t.some((s) => s.status === "complete") ? "complete" : "ready";
3402
+ }
3403
+ // --- File handling ---
3404
+ _processIncomingFiles(e) {
3405
+ var r, s, o;
3406
+ const t = (r = this.config) == null ? void 0 : r.callbacks;
3407
+ for (const n of e) {
3408
+ const a = this._store.getState(), l = Me(n, a.restrictions, a.files);
3409
+ if (l) {
3410
+ const u = {
3411
+ id: B(),
3412
+ status: "rejected",
3413
+ file: n,
3414
+ remoteUrl: null,
3415
+ name: n.name,
3416
+ size: n.size,
3417
+ type: n.type,
3418
+ previewUrl: null,
3419
+ progress: 0,
3420
+ speed: 0,
3421
+ bytesUploaded: 0,
3422
+ error: l,
3423
+ retryCount: 0,
3424
+ response: null,
3425
+ addedAt: Date.now(),
3426
+ meta: {},
3427
+ tags: [],
3428
+ remoteInfo: null
3429
+ };
3430
+ j(this._store, u), this._dispatchPublic(f.FILE_REJECTED, { file: u, reason: l }), (s = t == null ? void 0 : t.onFileRejected) == null || s.call(t, u, l);
3431
+ continue;
3432
+ }
3433
+ let p = null;
3434
+ n.type.startsWith("image/") && (p = URL.createObjectURL(n));
3435
+ const b = {
3436
+ id: B(),
3437
+ status: "idle",
3438
+ file: n,
3439
+ remoteUrl: null,
3440
+ name: n.name,
3441
+ size: n.size,
3442
+ type: n.type,
3443
+ previewUrl: p,
3444
+ progress: 0,
3445
+ speed: 0,
3446
+ bytesUploaded: 0,
3447
+ error: null,
3448
+ retryCount: 0,
3449
+ response: null,
3450
+ addedAt: Date.now(),
3451
+ meta: {},
3452
+ tags: [],
3453
+ remoteInfo: null
3454
+ };
3455
+ j(this._store, b), this._dispatchPublic(f.FILE_ADDED, { file: b }), (o = t == null ? void 0 : t.onFileAdded) == null || o.call(t, b);
3456
+ }
3457
+ this._store.getState().queueConfig.autoProceed && this.upload();
3458
+ }
3459
+ // --- Render ---
3460
+ render() {
3461
+ var t;
3462
+ return (((t = this.config) == null ? void 0 : t.mode) ?? "modal") === "modal" ? this._isOpen ? c`
3463
+ <div class="modal-backdrop" @click=${this._onModalBackdropClick}>
3464
+ <div class="modal-card">
3465
+ ${this._renderHeader()}
3466
+ ${this._renderBody()}
3467
+ </div>
3468
+ </div>
3469
+ ` : h : c`
3470
+ <div class="inline">
3471
+ ${this._renderBody()}
3472
+ </div>
3473
+ `;
3474
+ }
3475
+ _renderHeader() {
3476
+ return c`
3477
+ <div class="header">
3478
+ <div class="header-title">Upload files</div>
3479
+ <button class="close-btn" @click=${this._onModalDismiss}>
3480
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
3481
+ <line x1="18" y1="6" x2="6" y2="18" />
3482
+ <line x1="6" y1="6" x2="18" y2="18" />
3483
+ </svg>
3484
+ </button>
3485
+ </div>
3486
+ `;
3487
+ }
3488
+ _renderBody() {
3489
+ var n, a;
3490
+ const e = this._storeCtrl.state, t = [...e.files.values()], r = this._phase, s = Ie(e.restrictions), o = t.length > 0;
3491
+ return c`
3492
+ <div class="content"
3493
+ @files-selected=${this._onFilesSelected}
3494
+ @source-click=${this._onSourceClick}
3495
+ @file-remove=${this._onFileRemove}
3496
+ @file-preview=${this._onFilePreview}
3497
+ @file-retry=${this._onFileRetry}
3498
+ @fill-metadata=${this._onFillMetadata}
3499
+ @retry-all=${this._onRetryAll}
3500
+ @clear-all=${this._onClearAll}
3501
+ @add-more=${this._onAddMore}
3502
+ @upload-start=${this._onUploadStart}
3503
+ @upload-more=${this._onUploadMore}
3504
+ @primary-action=${this._onPrimaryAction}
3505
+ @connector-files-selected=${this._onConnectorFilesSelected}
3506
+ @connector-close=${this._onConnectorClose}
3507
+ @url-submit=${this._onUrlSubmit}
3508
+ @url-cancel=${this._onUrlCancel}
3509
+ @camera-capture=${this._onCameraCapture}
3510
+ @camera-cancel=${this._onCameraCancel}
3511
+ @screencast-capture=${this._onScreenCastCapture}
3512
+ @screencast-cancel=${this._onScreenCastCancel}
3513
+ >
3514
+ <div class="body">
3515
+ ${r === "complete" ? c`
3516
+ <sfx-success-card
3517
+ .fileCount=${t.length}
3518
+ ></sfx-success-card>
3519
+ ` : c`
3520
+ <sfx-drop-zone
3521
+ .compact=${o}
3522
+ .accept=${s}
3523
+ .sources=${this._mergedSources}
3524
+ ></sfx-drop-zone>
3525
+
3526
+ ${o ? h : c`
3527
+ <sfx-import-divider></sfx-import-divider>
3528
+ <sfx-source-pills .sources=${this._mergedSources}></sfx-source-pills>
3529
+ `}
3530
+
3531
+ ${o ? c`
3532
+ <sfx-file-list .files=${t}></sfx-file-list>
3533
+ ` : h}
3534
+ `}
3535
+ </div>
3536
+
3537
+ ${o ? c`
3538
+ <sfx-actions-bar
3539
+ .uploadState=${r === "complete" ? "done" : r === "uploading" ? "uploading" : "idle"}
3540
+ .fileCount=${t.length}
3541
+ .failedCount=${t.filter((l) => l.status === "failed" || l.status === "error").length}
3542
+ .showFillMetadata=${!!((n = this.config) != null && n.showFillMetadata)}
3543
+ ></sfx-actions-bar>
3544
+ ` : h}
3545
+
3546
+ ${this._showUrlDialog ? c`<sfx-url-dialog></sfx-url-dialog>` : h}
3547
+ ${this._showCameraDialog ? c`<sfx-camera-dialog></sfx-camera-dialog>` : h}
3548
+ ${this._showScreenCastDialog ? c`<sfx-screen-cast-dialog></sfx-screen-cast-dialog>` : h}
3549
+
3550
+ ${this._activeConnector && ((a = this.config) != null && a.connectors) ? c`
3551
+ <div class="connector-modal-backdrop" @click=${this._onConnectorBackdropClick}>
3552
+ <div class="connector-modal">
3553
+ ${ue.has(this._activeConnector) ? c`
3554
+ <sfx-search-provider-browser
3555
+ .provider=${this._activeConnector}
3556
+ .companionUrl=${this.config.connectors.companionUrl}
3557
+ ></sfx-search-provider-browser>
3558
+ ` : c`
3559
+ <sfx-provider-browser
3560
+ .provider=${this._activeConnector}
3561
+ .companionUrl=${this.config.connectors.companionUrl}
3562
+ ></sfx-provider-browser>
3563
+ `}
3564
+ </div>
3565
+ </div>
3566
+ ` : h}
3567
+ </div>
3568
+ `;
3569
+ }
3570
+ }, m.styles = _`
3571
+ :host {
3572
+ display: block;
3573
+ font-family: var(--sfx-up-font, 'Inter', system-ui, -apple-system, sans-serif);
3574
+ color: var(--sfx-up-text, #1e293b);
3575
+ --sfx-up-primary: #2563eb;
3576
+ --sfx-up-primary-hover: #1d4ed8;
3577
+ --sfx-up-primary-mid: #3b82f6;
3578
+ --sfx-up-primary-bg: #eff6ff;
3579
+ --sfx-up-primary-glow: rgba(37, 99, 235, 0.18);
3580
+ --sfx-up-success: #16a34a;
3581
+ --sfx-up-error: #dc2626;
3582
+ --sfx-up-text: #1e293b;
3583
+ --sfx-up-text-secondary: #475569;
3584
+ --sfx-up-text-muted: #94a3b8;
3585
+ --sfx-up-border: #e8edf5;
3586
+ --sfx-up-border-light: #f1f5f9;
3587
+ --sfx-up-bg: #ffffff;
3588
+ --sfx-up-radius: 16px;
3589
+ --sfx-up-font: 'Inter', system-ui, -apple-system, sans-serif;
3590
+ }
3591
+
3592
+ /* --- Modal overlay --- */
3593
+ .modal-backdrop {
3594
+ position: fixed;
3595
+ inset: 0;
3596
+ background: rgba(0, 0, 0, 0.45);
3597
+ display: flex;
3598
+ align-items: center;
3599
+ justify-content: center;
3600
+ z-index: 9999;
3601
+ animation: fadeIn 0.2s ease;
3602
+ }
3603
+
3604
+ .modal-card {
3605
+ background: var(--sfx-up-bg, #fff);
3606
+ border-radius: 20px;
3607
+ box-shadow: 0 25px 60px rgba(0, 0, 0, 0.2);
3608
+ width: 90vw;
3609
+ max-width: 680px;
3610
+ max-height: 85vh;
3611
+ display: flex;
3612
+ flex-direction: column;
3613
+ overflow: hidden;
3614
+ animation: modalIn 0.25s cubic-bezier(0.34, 1.2, 0.64, 1);
3615
+ }
3616
+
3617
+ /* --- Header --- */
3618
+ .header {
3619
+ display: flex;
3620
+ align-items: center;
3621
+ justify-content: space-between;
3622
+ padding: 18px 24px;
3623
+ border-bottom: 1px solid var(--sfx-up-border-light, #f1f5f9);
3624
+ }
3625
+
3626
+ .header-title {
3627
+ font-size: 16px;
3628
+ font-weight: 700;
3629
+ color: var(--sfx-up-text, #1e293b);
3630
+ }
3631
+
3632
+ .close-btn {
3633
+ width: 32px;
3634
+ height: 32px;
3635
+ border-radius: 8px;
3636
+ border: none;
3637
+ background: none;
3638
+ cursor: pointer;
3639
+ display: flex;
3640
+ align-items: center;
3641
+ justify-content: center;
3642
+ color: var(--sfx-up-text-muted, #94a3b8);
3643
+ transition: all 0.15s;
3644
+ }
3645
+
3646
+ .close-btn:hover {
3647
+ background: #f1f5f9;
3648
+ color: var(--sfx-up-text, #1e293b);
3649
+ }
3650
+
3651
+ .close-btn svg {
3652
+ width: 18px;
3653
+ height: 18px;
3654
+ }
3655
+
3656
+ /* --- Content wrapper (holds body + actions bar) --- */
3657
+ .content {
3658
+ flex: 1;
3659
+ display: flex;
3660
+ flex-direction: column;
3661
+ min-height: 0;
3662
+ }
3663
+
3664
+ /* --- Body --- */
3665
+ .body {
3666
+ flex: 1;
3667
+ overflow-y: auto;
3668
+ padding: 24px;
3669
+ display: flex;
3670
+ flex-direction: column;
3671
+ gap: 4px;
3672
+ min-height: 0;
3673
+ }
3674
+
3675
+ .body sfx-drop-zone {
3676
+ position: relative;
3677
+ z-index: 1;
3678
+ }
3679
+
3680
+ /* --- Inline mode --- */
3681
+ .inline {
3682
+ border: 1px solid var(--sfx-up-border, #e8edf5);
3683
+ border-radius: var(--sfx-up-radius, 16px);
3684
+ display: flex;
3685
+ flex-direction: column;
3686
+ overflow: hidden;
3687
+ }
3688
+
3689
+ /* --- Connector modal overlay --- */
3690
+ .connector-modal-backdrop {
3691
+ position: fixed;
3692
+ inset: 0;
3693
+ z-index: 1000;
3694
+ background: rgba(0, 0, 0, 0.4);
3695
+ backdrop-filter: blur(6px);
3696
+ display: flex;
3697
+ align-items: center;
3698
+ justify-content: center;
3699
+ padding: 20px;
3700
+ animation: fadeIn 0.18s ease both;
3701
+ }
3702
+
3703
+ .connector-modal {
3704
+ background: var(--sfx-up-bg, #fff);
3705
+ border-radius: 20px;
3706
+ box-shadow: 0 28px 80px rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.06);
3707
+ width: 100%;
3708
+ max-width: 540px;
3709
+ height: 80vh;
3710
+ overflow: hidden;
3711
+ display: flex;
3712
+ flex-direction: column;
3713
+ animation: modalIn 0.28s cubic-bezier(0.34, 1.2, 0.64, 1) forwards;
3714
+ }
3715
+
3716
+ @keyframes fadeIn {
3717
+ from { opacity: 0; }
3718
+ to { opacity: 1; }
3719
+ }
3720
+
3721
+ @keyframes modalIn {
3722
+ from {
3723
+ opacity: 0;
3724
+ transform: scale(0.92) translateY(10px);
3725
+ }
3726
+ to {
3727
+ opacity: 1;
3728
+ transform: scale(1) translateY(0);
3729
+ }
3730
+ }
3731
+
3732
+ @media (prefers-reduced-motion: reduce) {
3733
+ .modal-backdrop { animation: none; }
3734
+ .modal-card { animation: none; }
3735
+ }
3736
+ `, m._MODIFIABLE_STATUSES = /* @__PURE__ */ new Set([
3737
+ "idle",
3738
+ "queued",
3739
+ "validating",
3740
+ "rejected"
3741
+ ]), m._RESERVED_IDS = /* @__PURE__ */ new Set(["device", "camera", "screen-cast", "url"]), m);
3742
+ D([
3743
+ x({ attribute: !1 })
3744
+ ], P.prototype, "config");
3745
+ D([
3746
+ g()
3747
+ ], P.prototype, "_isOpen");
3748
+ D([
3749
+ g()
3750
+ ], P.prototype, "_activeConnector");
3751
+ D([
3752
+ g()
3753
+ ], P.prototype, "_showUrlDialog");
3754
+ D([
3755
+ g()
3756
+ ], P.prototype, "_showCameraDialog");
3757
+ D([
3758
+ g()
3759
+ ], P.prototype, "_showScreenCastDialog");
3760
+ let pt = P;
3761
+ export {
3762
+ Q as A,
3763
+ H as C,
3764
+ f as P,
3765
+ z as S,
3766
+ Se as U,
3767
+ $ as a,
3768
+ X as b,
3769
+ G as c,
3770
+ pe as d,
3771
+ W as e,
3772
+ q as f,
3773
+ pt as g,
3774
+ xe as h,
3775
+ J as i,
3776
+ ve as j,
3777
+ Pe as k,
3778
+ Z as l,
3779
+ Fe as m,
3780
+ R as n,
3781
+ A as o,
3782
+ U as p,
3783
+ nt as q,
3784
+ ze as r,
3785
+ ct as s,
3786
+ dt as t,
3787
+ at as u,
3788
+ lt as v
3789
+ };