@scaleflex/uploader 0.1.0 → 0.2.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 (54) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +1 -1
  3. package/dist/auth/auth.service.d.ts.map +1 -1
  4. package/dist/components/actions-bar.d.ts +4 -1
  5. package/dist/components/actions-bar.d.ts.map +1 -1
  6. package/dist/components/camera-dialog.d.ts +2 -1
  7. package/dist/components/camera-dialog.d.ts.map +1 -1
  8. package/dist/components/drop-zone.d.ts +17 -0
  9. package/dist/components/drop-zone.d.ts.map +1 -1
  10. package/dist/components/file-item.d.ts.map +1 -1
  11. package/dist/components/file-list.d.ts.map +1 -1
  12. package/dist/components/provider-browser.d.ts +1 -0
  13. package/dist/components/provider-browser.d.ts.map +1 -1
  14. package/dist/components/screen-cast-dialog.d.ts +2 -1
  15. package/dist/components/screen-cast-dialog.d.ts.map +1 -1
  16. package/dist/components/shared-styles.d.ts +5 -0
  17. package/dist/components/shared-styles.d.ts.map +1 -0
  18. package/dist/components/source-pills.d.ts.map +1 -1
  19. package/dist/components/success-card.d.ts +3 -1
  20. package/dist/components/success-card.d.ts.map +1 -1
  21. package/dist/components/url-dialog.d.ts +2 -2
  22. package/dist/components/url-dialog.d.ts.map +1 -1
  23. package/dist/connectors/provider-registry.d.ts.map +1 -1
  24. package/dist/define.cjs +1 -1
  25. package/dist/define.js +1 -1
  26. package/dist/engine/upload-engine.d.ts.map +1 -1
  27. package/dist/events/public-events.d.ts +1 -3
  28. package/dist/events/public-events.d.ts.map +1 -1
  29. package/dist/index.cjs +1 -1
  30. package/dist/index.js +1 -1
  31. package/dist/{provider-browser-C-S_MPrC.js → provider-browser-CUbPlWmj.js} +430 -185
  32. package/dist/provider-browser-D7G2wcFH.cjs +820 -0
  33. package/dist/{search-provider-browser-jCOer2Y9.js → search-provider-browser-D_kyqQ2m.js} +30 -30
  34. package/dist/{search-provider-browser-DxmLznEB.cjs → search-provider-browser-uDZrkDBZ.cjs} +29 -29
  35. package/dist/sfx-uploader-Cxz9YHev.js +5387 -0
  36. package/dist/sfx-uploader-DbaSJxEf.cjs +3298 -0
  37. package/dist/sfx-uploader.d.ts +55 -1
  38. package/dist/sfx-uploader.d.ts.map +1 -1
  39. package/dist/store/store.types.d.ts +1 -1
  40. package/dist/store/store.types.d.ts.map +1 -1
  41. package/dist/types/source.types.d.ts +3 -0
  42. package/dist/types/source.types.d.ts.map +1 -1
  43. package/dist/utils/file-utils.d.ts +2 -0
  44. package/dist/utils/file-utils.d.ts.map +1 -1
  45. package/dist/utils/focus-trap.d.ts +7 -0
  46. package/dist/utils/focus-trap.d.ts.map +1 -0
  47. package/dist/utils/validate.d.ts +11 -0
  48. package/dist/utils/validate.d.ts.map +1 -1
  49. package/package.json +3 -2
  50. package/dist/events/event-bus.d.ts +0 -38
  51. package/dist/events/event-bus.d.ts.map +0 -1
  52. package/dist/provider-browser-CmCwv0ph.cjs +0 -581
  53. package/dist/sfx-uploader-BVDK-9xi.cjs +0 -2029
  54. package/dist/sfx-uploader-C2lWIRnU.js +0 -3789
@@ -1,3789 +0,0 @@
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
- };