@masters-union/union-stack 0.1.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/loader.v1.global.js +2 -2
- package/dist/cdn/loader.v1.global.js.map +1 -1
- package/dist/{chunk-DETAGOYQ.cjs → chunk-FFNVWHSR.cjs} +36 -5
- package/dist/chunk-FFNVWHSR.cjs.map +1 -0
- package/dist/{chunk-6Z46YEXF.js → chunk-JHANJOHS.js} +36 -5
- package/dist/chunk-JHANJOHS.js.map +1 -0
- package/dist/{client-BKCHbspL.d.cts → client-C3JZYYpA.d.cts} +6 -0
- package/dist/{client-BKCHbspL.d.ts → client-C3JZYYpA.d.ts} +6 -0
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/picker.d.cts +2 -2
- package/dist/picker.d.ts +2 -2
- package/dist/react.cjs +2 -2
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-6Z46YEXF.js.map +0 -1
- package/dist/chunk-DETAGOYQ.cjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/picker/styles.ts","../../src/picker/imageEditor.ts","../../src/picker/picker.ts","../../src/picker/index.ts","../../src/loader/index.ts","../../src/errors.ts","../../src/uploader.ts","../../src/client.ts","../../src/index.ts"],"sourcesContent":["import type { PickerTheme } from './types.js';\n\nconst STYLE_ID = 'unionstack-picker-styles';\nconst FONT_ID = 'unionstack-picker-fonts';\n\n// Kinetic Sync uses Inter for UI and JetBrains Mono for technical data.\n// Loaded from Google Fonts with display=swap — system stacks below render\n// instantly and the webfont swaps in when ready. No bundled font assets.\nconst FONT_HREF =\n 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap';\n\nfunction ensureFonts(): void {\n if (document.getElementById(FONT_ID)) return;\n const preconnect = (href: string, cross?: boolean) => {\n const l = document.createElement('link');\n l.rel = 'preconnect';\n l.href = href;\n if (cross) l.crossOrigin = 'anonymous';\n document.head.appendChild(l);\n };\n preconnect('https://fonts.googleapis.com');\n preconnect('https://fonts.gstatic.com', true);\n const link = document.createElement('link');\n link.id = FONT_ID;\n link.rel = 'stylesheet';\n link.href = FONT_HREF;\n document.head.appendChild(link);\n}\n\n/** Inject the picker stylesheet (and webfonts) once. Idempotent — safe to call on every open. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n ensureFonts();\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ID;\n el.textContent = BASE_CSS;\n document.head.appendChild(el);\n}\n\n/** Resolve a (possibly partial) theme into the CSS variables the picker reads. */\nexport function themeToCssVars(theme: PickerTheme | undefined): Record<string, string> {\n const mode = theme?.mode || 'light';\n const defaults = mode === 'dark' ? DARK_DEFAULTS : LIGHT_DEFAULTS;\n return {\n '--us-primary': theme?.primary ?? defaults.primary,\n '--us-on-primary': defaults.onPrimary,\n '--us-bg': theme?.background ?? defaults.background,\n '--us-fg': theme?.foreground ?? defaults.foreground,\n '--us-muted': defaults.muted,\n '--us-subtle': defaults.subtle,\n '--us-border': theme?.border ?? defaults.border,\n '--us-border-strong': defaults.borderStrong,\n '--us-elevated': defaults.elevated,\n '--us-overlay': defaults.overlay,\n '--us-raised': defaults.raised,\n '--us-accent': defaults.accent,\n '--us-success': defaults.success,\n '--us-danger': defaults.danger,\n '--us-radius': theme?.radius ?? '12px',\n };\n}\n\n// ─── Kinetic Sync — light scheme ────────────────────────────────────\n// Derived light counterpart of the dark palette below: same violet\n// family, inverse tonal ramp. Depth comes from tonal layering + 1px\n// borders, not shadows.\nconst LIGHT_DEFAULTS = {\n primary: '#494bd6', // inverse-primary of the dark scheme\n onPrimary: '#ffffff',\n background: '#fdfbff', // surface — near-white with a violet hint\n foreground: '#1b1b21', // on-surface\n muted: '#5e5c6e', // on-surface-variant\n subtle: '#f3f1fa', // surface-container — chips, icon wells, tab rail\n border: '#e4e1ee', // outline-variant (soft)\n borderStrong: '#c8c5d4', // outline — drag-over emphasis\n elevated: '#ffffff', // cards, one step above the floor\n overlay: '#eceaf4', // instant hover state\n raised: '#ffffff', // active tab / popover layer\n accent: '#b35a00', // tertiary — \"edited\" markers\n success: '#2c9a5b', // desaturated green\n danger: '#ba1a1a',\n};\n\n// ─── Kinetic Sync — dark scheme ─────────────────────────────────────\n// Tokens straight from the design system. Periwinkle primary on a deep\n// midnight-navy tonal ramp; no blue.\nconst DARK_DEFAULTS = {\n primary: '#c0c1ff', // primary\n onPrimary: '#1000a9', // on-primary — dark ink on periwinkle\n background: '#0c1324', // surface (floor)\n foreground: '#dce1fb', // on-surface\n muted: '#908fa0', // outline\n subtle: '#191f31', // surface-container\n border: '#2e3447', // surface-container-highest\n borderStrong: '#464554', // outline-variant\n elevated: '#151b2d', // surface-container-low — cards, one step up\n overlay: '#23293c', // surface-container-high — instant hover\n raised: '#2e3447', // active tab / popover layer\n accent: '#ffb783', // tertiary — \"edited\" markers\n success: '#7ad08e', // desaturated green\n danger: '#ffb4ab', // error\n};\n\n// Everything is namespaced under `.us-picker` and uses CSS variables so the\n// host page's styles never leak in, and theme overrides flow cleanly.\nconst BASE_CSS = `\n.us-picker-backdrop {\n --us-font: \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n --us-mono: \"JetBrains Mono\", ui-monospace, \"SF Mono\", Menlo, monospace;\n position: fixed; inset: 0; z-index: 2147483000;\n background: rgba(2, 6, 23, 0.4);\n -webkit-backdrop-filter: blur(8px);\n backdrop-filter: blur(8px);\n display: flex; align-items: center; justify-content: center;\n padding: 16px;\n font-family: var(--us-font);\n font-feature-settings: \"cv02\", \"cv11\";\n animation: us-fade 140ms ease-out;\n}\n@keyframes us-fade { from { opacity: 0; } to { opacity: 1; } }\n\n.us-picker {\n background: var(--us-bg); color: var(--us-fg);\n border: 1px solid var(--us-border);\n border-radius: var(--us-radius);\n width: 100%; max-width: 480px;\n max-height: min(calc(100dvh - 32px), 680px);\n display: flex; flex-direction: column;\n position: relative;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);\n overflow: hidden;\n animation: us-rise 240ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n@keyframes us-rise {\n from { opacity: 0; transform: translateY(8px) scale(0.985); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n.us-picker * { box-sizing: border-box; }\n\n/* ──────────────────── header ──────────────────── */\n.us-picker-header {\n display: flex; align-items: center; gap: 12px;\n padding: 14px 16px;\n border-bottom: 1px solid var(--us-border);\n}\n.us-picker-header-logo {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; flex-shrink: 0;\n border-radius: 8px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n color: var(--us-primary);\n}\n.us-picker-header-logo svg { width: 15px; height: 15px; }\n.us-picker-header-logo img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }\n.us-picker-title { font-weight: 600; font-size: 15px; letter-spacing: -0.02em; line-height: 1.2; flex: 1; }\n.us-picker-close {\n background: none; border: 0; cursor: pointer;\n width: 32px; height: 32px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted); border-radius: 8px;\n transition: color 100ms, background 0ms;\n}\n.us-picker-close:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-picker-close:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }\n.us-picker-close svg { width: 16px; height: 16px; }\n\n/* ──────────────────── body / dropzone ──────────────────── */\n.us-picker-body { padding: 16px; overflow-y: auto; }\n\n.us-dropzone {\n position: relative;\n border: 1px dashed var(--us-border-strong);\n border-radius: 8px;\n padding: 28px 20px;\n text-align: center;\n cursor: pointer;\n transition: border-color 120ms, background 0ms;\n background: var(--us-subtle);\n}\n.us-dropzone:hover {\n border-color: var(--us-primary);\n background: color-mix(in srgb, var(--us-primary) 6%, var(--us-subtle));\n}\n.us-dropzone:focus-visible {\n outline: 2px solid var(--us-primary); outline-offset: 2px;\n}\n.us-dropzone[data-drag=\"over\"] {\n border-style: solid;\n border-color: var(--us-primary);\n background: color-mix(in srgb, var(--us-primary) 10%, var(--us-subtle));\n}\n.us-dropzone-icon {\n width: 44px; height: 44px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--us-primary) 12%, var(--us-bg));\n border: 1px solid color-mix(in srgb, var(--us-primary) 24%, transparent);\n color: var(--us-primary);\n display: inline-flex; align-items: center; justify-content: center;\n margin-bottom: 12px;\n transition: transform 240ms cubic-bezier(0.16, 1.4, 0.3, 1);\n}\n.us-dropzone:hover .us-dropzone-icon { transform: translateY(-2px); }\n.us-dropzone[data-drag=\"over\"] .us-dropzone-icon {\n transform: translateY(-3px) scale(1.06);\n}\n.us-dropzone-icon svg { width: 22px; height: 22px; }\n.us-dropzone-title {\n font-size: 14px; font-weight: 600; letter-spacing: -0.011em;\n margin-bottom: 2px;\n}\n.us-dropzone-hint { color: var(--us-muted); font-size: 12.5px; letter-spacing: -0.006em; }\n.us-dropzone-constraints {\n margin-top: 12px;\n font-size: 11px; color: var(--us-muted);\n font-family: var(--us-mono);\n letter-spacing: 0;\n}\n\n.us-dropzone--compact {\n padding: 12px 16px;\n display: flex; align-items: center; gap: 12px;\n text-align: left;\n}\n.us-dropzone--compact .us-dropzone-icon {\n width: 32px; height: 32px; border-radius: 8px; margin-bottom: 0;\n}\n.us-dropzone--compact .us-dropzone-icon svg { width: 16px; height: 16px; }\n.us-dropzone--compact .us-dropzone-title { font-size: 13px; margin: 0; }\n.us-dropzone--compact .us-dropzone-hint { display: none; }\n.us-dropzone--compact .us-dropzone-constraints { display: none; }\n\n/* ──────────────────── file list ──────────────────── */\n.us-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }\n\n.us-file {\n display: flex; align-items: center; gap: 12px;\n padding: 10px 12px;\n background: var(--us-elevated);\n border: 1px solid var(--us-border);\n border-radius: 8px;\n transition: border-color 120ms;\n animation: us-row-in 280ms cubic-bezier(0.16, 1, 0.3, 1) backwards;\n position: relative;\n}\n@keyframes us-row-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.us-file[data-state=\"done\"] { border-color: color-mix(in srgb, var(--us-success) 35%, var(--us-border)); }\n.us-file[data-state=\"failed\"] { border-color: color-mix(in srgb, var(--us-danger) 35%, var(--us-border)); }\n\n.us-file-thumb {\n width: 40px; height: 40px; flex-shrink: 0;\n border-radius: 4px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n background-size: cover; background-position: center;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted);\n overflow: hidden;\n}\n.us-file-thumb[data-image=\"true\"] { color: transparent; border-color: transparent; }\n.us-file-thumb svg { width: 18px; height: 18px; }\n\n.us-file-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }\n.us-file-row1 { display: flex; align-items: center; gap: 8px; }\n.us-file-name {\n font-size: 13px; font-weight: 500;\n flex: 1; min-width: 0;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n letter-spacing: -0.006em;\n}\n.us-file-meta {\n color: var(--us-muted); font-size: 11px; flex-shrink: 0;\n font-family: var(--us-mono); letter-spacing: 0;\n}\n\n.us-file-progress {\n height: 3px; background: var(--us-border); border-radius: 999px; overflow: hidden;\n position: relative;\n}\n.us-file-progress-bar {\n height: 100%; width: 0%;\n background: var(--us-primary);\n border-radius: inherit;\n transition: width 260ms cubic-bezier(0.4, 0.0, 0.2, 1);\n position: relative;\n}\n/* Shimmer overlay while uploading. Stops on terminal states. */\n.us-file[data-state=\"uploading\"] .us-file-progress-bar::after {\n content: \"\"; position: absolute; inset: 0;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, #fff 35%, transparent) 50%,\n transparent 100%\n );\n animation: us-shimmer 1.4s linear infinite;\n}\n@keyframes us-shimmer {\n from { transform: translateX(-100%); }\n to { transform: translateX(100%); }\n}\n.us-file[data-state=\"done\"] .us-file-progress-bar { width: 100% !important; background: var(--us-success); }\n.us-file[data-state=\"failed\"] .us-file-progress-bar { background: var(--us-danger); }\n\n.us-file-status {\n flex-shrink: 0;\n display: inline-flex; align-items: center; justify-content: center;\n width: 24px; height: 24px;\n color: var(--us-muted);\n}\n.us-file-status svg { width: 18px; height: 18px; }\n.us-file[data-state=\"done\"] .us-file-status { color: var(--us-success); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }\n.us-file[data-state=\"failed\"] .us-file-status { color: var(--us-danger); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }\n.us-file[data-state=\"uploading\"] .us-file-status { animation: us-spin 0.9s linear infinite; }\n@keyframes us-pop {\n from { transform: scale(0.5); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n@keyframes us-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n/* ──────────────────── actions / footer ──────────────────── */\n.us-actions {\n display: flex; gap: 8px; justify-content: space-between; align-items: center;\n padding: 12px 16px;\n border-top: 1px solid var(--us-border);\n}\n.us-actions-summary {\n font-size: 11px; color: var(--us-muted);\n font-family: var(--us-mono); letter-spacing: 0;\n}\n.us-actions-buttons { display: flex; gap: 8px; }\n\n.us-btn {\n appearance: none;\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n padding: 8px 14px; min-height: 36px;\n border-radius: 8px; border: 1px solid var(--us-border-strong);\n background: transparent; color: var(--us-fg);\n cursor: pointer; font-size: 13px; font-weight: 500;\n font-family: inherit; letter-spacing: 0.01em;\n transition: background 0ms, border-color 120ms, transform 80ms ease-out;\n}\n.us-btn:hover { background: var(--us-overlay); }\n.us-btn:active { transform: scale(0.98); }\n.us-btn:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 2px; }\n.us-btn-primary {\n background: var(--us-primary); color: var(--us-on-primary);\n border-color: var(--us-primary);\n}\n.us-btn-primary:hover { filter: brightness(1.06); background: var(--us-primary); }\n.us-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.us-btn[disabled]:hover { transform: none; }\n.us-btn svg { width: 14px; height: 14px; }\n\n.us-footer {\n padding: 8px 16px 12px;\n font-size: 11px; color: var(--us-muted); text-align: center;\n letter-spacing: 0.02em;\n display: flex; align-items: center; justify-content: center; gap: 6px;\n}\n.us-footer svg { width: 11px; height: 11px; opacity: 0.7; }\n.us-footer a { color: var(--us-muted); text-decoration: none; font-weight: 500; }\n.us-footer a:hover { color: var(--us-fg); }\n\n/* ──────────────────── source tabs (Device / URL) ──────────────────── */\n.us-source-tabs {\n display: inline-flex; gap: 2px; padding: 3px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n border-radius: 8px;\n margin-bottom: 14px;\n}\n.us-source-tab {\n appearance: none; background: transparent; border: 1px solid transparent;\n font: inherit; cursor: pointer;\n padding: 6px 14px; min-height: 30px;\n border-radius: 6px;\n font-size: 12px; font-weight: 500; letter-spacing: 0.02em;\n color: var(--us-muted);\n transition: color 100ms, background 0ms;\n display: inline-flex; align-items: center; gap: 6px;\n}\n.us-source-tab svg { width: 13px; height: 13px; }\n.us-source-tab:hover { color: var(--us-fg); }\n.us-source-tab[data-active=\"true\"] {\n background: var(--us-raised);\n border-color: var(--us-border);\n color: var(--us-fg);\n}\n.us-source-tab:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 2px; }\n\n/* ──────────────────── URL source ──────────────────── */\n.us-url-source { display: none; }\n.us-url-source[data-active=\"true\"] { display: block; }\n.us-url-form {\n display: flex; gap: 8px;\n padding: 16px;\n border: 1px dashed var(--us-border-strong);\n border-radius: 8px;\n background: var(--us-subtle);\n}\n.us-url-input {\n appearance: none;\n flex: 1; min-width: 0;\n padding: 9px 12px; min-height: 36px;\n border-radius: 8px; border: 1px solid var(--us-border-strong);\n background: var(--us-bg); color: var(--us-fg);\n font: inherit; font-size: 13px; letter-spacing: -0.006em;\n transition: border-color 120ms, box-shadow 120ms;\n}\n.us-url-input::placeholder { color: var(--us-muted); }\n.us-url-input:focus {\n outline: none;\n border-color: var(--us-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--us-primary) 25%, transparent);\n}\n.us-url-hint {\n margin-top: 8px;\n font-size: 11.5px; color: var(--us-muted); letter-spacing: -0.006em;\n}\n.us-url-hint[data-error=\"true\"] { color: var(--us-danger); }\n\n/* ──────────────────── per-row edit button ──────────────────── */\n.us-file-actions { display: inline-flex; gap: 4px; }\n.us-file-action {\n appearance: none; background: transparent; border: 0; cursor: pointer;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted); border-radius: 6px;\n transition: color 100ms, background 0ms;\n}\n.us-file-action:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-file-action:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }\n.us-file-action svg { width: 15px; height: 15px; }\n.us-file-action[data-edited=\"true\"] { color: var(--us-accent); }\n.us-file:not([data-state=\"queued\"]) .us-file-action { display: none; }\n\n/* ──────────────────── image editor overlay ──────────────────── */\n.us-editor {\n position: absolute; inset: 0; z-index: 2;\n display: flex; flex-direction: column;\n background: var(--us-bg);\n animation: us-fade 140ms ease-out;\n}\n.us-editor-header {\n display: flex; align-items: center; gap: 12px;\n padding: 14px 16px;\n border-bottom: 1px solid var(--us-border);\n}\n.us-editor-title { font-weight: 600; font-size: 15px; flex: 1; letter-spacing: -0.02em; }\n.us-editor-back {\n appearance: none; background: transparent; border: 0; cursor: pointer;\n width: 32px; height: 32px; border-radius: 8px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted);\n transition: color 100ms, background 0ms;\n}\n.us-editor-back:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-editor-back svg { width: 16px; height: 16px; }\n\n.us-editor-canvas-wrap {\n flex: 1; min-height: 0;\n background: var(--us-subtle);\n display: flex; align-items: center; justify-content: center;\n padding: 16px;\n position: relative;\n overflow: hidden;\n}\n.us-editor-canvas {\n max-width: 100%; max-height: 100%;\n border-radius: 4px;\n display: block;\n /* Checkerboard for transparency awareness (visible only in circle mode). */\n background-image:\n linear-gradient(45deg, var(--us-border) 25%, transparent 25%, transparent 75%, var(--us-border) 75%),\n linear-gradient(45deg, var(--us-border) 25%, transparent 25%, transparent 75%, var(--us-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 6px 6px;\n}\n.us-editor-overlay {\n position: absolute; inset: 0;\n pointer-events: none;\n}\n\n.us-editor-toolbar {\n display: flex; gap: 6px; flex-wrap: wrap;\n padding: 10px 16px;\n border-top: 1px solid var(--us-border);\n}\n.us-tool {\n appearance: none; background: transparent; border: 1px solid var(--us-border-strong);\n font: inherit; cursor: pointer;\n padding: 6px 11px; min-height: 32px;\n border-radius: 8px;\n font-size: 12px; font-weight: 500; letter-spacing: 0.02em;\n color: var(--us-fg);\n display: inline-flex; align-items: center; gap: 6px;\n transition: border-color 120ms, color 100ms, background 0ms;\n}\n.us-tool:hover { background: var(--us-overlay); }\n.us-tool[data-active=\"true\"] {\n background: color-mix(in srgb, var(--us-primary) 12%, var(--us-bg));\n border-color: var(--us-primary);\n color: var(--us-primary);\n}\n.us-tool[disabled] { opacity: 0.45; cursor: not-allowed; }\n.us-tool svg { width: 13px; height: 13px; }\n.us-tool-spacer { flex: 1; }\n\n.us-editor-footer {\n display: flex; gap: 8px; justify-content: flex-end;\n padding: 12px 16px;\n border-top: 1px solid var(--us-border);\n}\n\n/* Crop drag handles. Drawn inside .us-editor-canvas-wrap, positioned over canvas. */\n.us-crop-box {\n position: absolute;\n border: 1.5px solid var(--us-primary);\n box-shadow: 0 0 0 9999px rgba(0,0,0,0.45);\n pointer-events: auto;\n cursor: move;\n}\n.us-crop-handle {\n position: absolute;\n width: 12px; height: 12px;\n background: var(--us-bg);\n border: 1.5px solid var(--us-primary);\n border-radius: 2px;\n pointer-events: auto;\n}\n.us-crop-handle[data-pos=\"nw\"] { top: -6px; left: -6px; cursor: nwse-resize; }\n.us-crop-handle[data-pos=\"ne\"] { top: -6px; right: -6px; cursor: nesw-resize; }\n.us-crop-handle[data-pos=\"sw\"] { bottom: -6px; left: -6px; cursor: nesw-resize; }\n.us-crop-handle[data-pos=\"se\"] { bottom: -6px; right: -6px; cursor: nwse-resize; }\n\n/* ──────────────────── mobile (bottom sheet) ──────────────────── */\n@media (max-width: 600px) {\n .us-picker-backdrop {\n padding: 0;\n align-items: flex-end;\n }\n .us-picker {\n max-width: none;\n max-height: calc(100dvh - 40px);\n border-radius: 16px 16px 0 0;\n border-left: 0; border-right: 0; border-bottom: 0;\n animation: us-sheet-up 300ms cubic-bezier(0.32, 0.72, 0, 1);\n }\n /* Grab handle */\n .us-picker::before {\n content: \"\";\n flex-shrink: 0;\n width: 36px; height: 4px;\n border-radius: 999px;\n background: var(--us-border-strong);\n margin: 8px auto 0;\n }\n .us-picker-header { padding: 10px 16px 14px; }\n .us-dropzone { padding: 24px 16px; }\n .us-btn { min-height: 44px; padding: 10px 16px; }\n .us-actions {\n flex-direction: column; align-items: stretch; gap: 10px;\n padding-bottom: max(12px, env(safe-area-inset-bottom));\n }\n .us-actions-summary { text-align: center; order: 2; }\n .us-actions-summary:empty { display: none; }\n .us-actions-buttons { width: 100%; }\n .us-actions-buttons .us-btn { flex: 1; }\n .us-source-tabs { display: flex; width: 100%; }\n .us-source-tab { flex: 1; justify-content: center; min-height: 38px; }\n .us-url-form { flex-direction: column; }\n .us-url-input { min-height: 44px; }\n .us-file { padding: 12px; }\n .us-file-action, .us-picker-close { width: 36px; height: 36px; }\n .us-editor-toolbar { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }\n .us-tool { flex-shrink: 0; min-height: 40px; }\n .us-editor-footer { padding-bottom: max(12px, env(safe-area-inset-bottom)); }\n .us-editor-footer .us-btn { flex: 1; }\n .us-footer { padding-bottom: max(12px, env(safe-area-inset-bottom)); }\n}\n@keyframes us-sheet-up {\n from { opacity: 0.6; transform: translateY(32px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ──────────────────── reduced motion ──────────────────── */\n@media (prefers-reduced-motion: reduce) {\n .us-picker-backdrop,\n .us-picker,\n .us-file,\n .us-dropzone,\n .us-dropzone-icon,\n .us-btn,\n .us-file-status,\n .us-file-progress-bar,\n .us-editor,\n .us-source-tab,\n .us-tool { animation: none !important; transition: none !important; }\n .us-file[data-state=\"uploading\"] .us-file-progress-bar::after { animation: none; opacity: 0; }\n}\n\n/* ──────────────────── empty state niceties ──────────────────── */\n.us-file-list:empty { display: none; }\n`;\n","// Lightweight client-side image editor for the picker. Supports rotate (90°),\n// crop with draggable handles, circle (centered square + circular alpha mask),\n// and revert to original. All operations stay in the browser — we hand the\n// edited blob back to the picker, which uploads it like any other file.\n//\n// Design notes:\n// - There are two canvases. The \"working\" canvas is the source of truth in\n// image pixels and gets mutated by each operation (rotate, crop, circle).\n// The \"display\" canvas is what the user sees, sized to fit the editor\n// box; we render the working canvas into it on every draw().\n// - Crop drag math is done entirely in display-canvas pixel coordinates,\n// then scaled to image coords only when committing the crop. This keeps\n// the handle math straightforward (no scale juggling per mouse-move).\n// - Apply exports the working canvas via toBlob() and wraps it in a File so\n// the picker can swap it for the original without any other change. The\n// output mime is PNG when the image has transparency (after a circle\n// op), JPEG otherwise — keeps file size sane for photos.\n\ntype EditorMode = 'none' | 'crop';\n\ninterface Rect { x: number; y: number; w: number; h: number; }\n\nconst MAX_DISPLAY = 720; // px — keeps the working canvas reasonable on huge inputs\n\n// Inline SVG icons. Same Lucide flavor as picker.ts.\nconst ICON = {\n back: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>`,\n crop: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 2v14a2 2 0 0 0 2 2h14\"/><path d=\"M18 22V8a2 2 0 0 0-2-2H2\"/></svg>`,\n circle: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/></svg>`,\n rotate: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-9-9c2.52 0 4.82.93 6.58 2.46L21 8\"/><path d=\"M21 3v5h-5\"/></svg>`,\n undo: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 7v6h6\"/><path d=\"M21 17a9 9 0 0 0-15-6.7L3 13\"/></svg>`,\n check: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`,\n};\n\nexport interface ImageEditorOptions {\n /** Element to mount the editor inside (usually the picker panel). */\n host: HTMLElement;\n /** The source image to load into the editor on open. */\n file: File;\n /**\n * The unedited original. Revert restores to this. Defaults to `file` —\n * pass it explicitly when the user has already applied edits and you want\n * Revert to undo *all* the way back, not just the current session.\n */\n originalFile?: File;\n /** Title shown in the editor header (typically the filename). */\n title: string;\n /** Called with the edited File when the user clicks Apply. */\n onApply: (file: File) => void;\n /** Called when the user backs out without applying. */\n onCancel: () => void;\n}\n\nexport class ImageEditor {\n private root!: HTMLElement;\n private canvasWrap!: HTMLElement;\n private displayCanvas!: HTMLCanvasElement;\n private cropBox: HTMLElement | null = null;\n private cropTool!: HTMLButtonElement;\n private circleTool!: HTMLButtonElement;\n private rotateTool!: HTMLButtonElement;\n private revertTool!: HTMLButtonElement;\n private applyBtn!: HTMLButtonElement;\n\n // Source of truth, mutated by each op. Always in image pixels.\n private working!: HTMLCanvasElement;\n // What's currently rendered to the user, sized to fit MAX_DISPLAY.\n private displayScale = 1;\n // Tracks whether the working canvas has transparency (post-circle), so\n // export() can pick PNG vs JPEG correctly.\n private hasAlpha = false;\n\n private mode: EditorMode = 'none';\n private cropRect: Rect | null = null; // display-canvas coords\n\n // Drag state for crop interaction.\n private dragKind: 'move' | 'nw' | 'ne' | 'sw' | 'se' | null = null;\n private dragStart: { px: number; py: number; rect: Rect } | null = null;\n\n constructor(private opts: ImageEditorOptions) {}\n\n async open(): Promise<void> {\n this.mount();\n try {\n await this.loadOriginalIntoWorking();\n // If the caller passed a distinct originalFile, the loaded image is\n // already edited compared to the true original — enable Revert.\n if (this.opts.originalFile && this.opts.originalFile !== this.opts.file) {\n this.markEdited(true);\n }\n this.draw();\n } catch (err) {\n // Image failed to decode — surface it in the canvas wrap and let the\n // user back out. Don't throw, the picker is still alive.\n this.canvasWrap.textContent = `Couldn't load image: ${(err as Error).message}`;\n }\n }\n\n close(): void {\n document.removeEventListener('pointermove', this.onPointerMove);\n document.removeEventListener('pointerup', this.onPointerUp);\n this.root.remove();\n }\n\n // ---- mount / dom --------------------------------------------------------\n\n private mount(): void {\n this.root = el('div', 'us-editor');\n\n const header = el('div', 'us-editor-header');\n const back = document.createElement('button');\n back.type = 'button';\n back.className = 'us-editor-back';\n back.setAttribute('aria-label', 'Back');\n back.innerHTML = ICON.back;\n back.onclick = () => { this.opts.onCancel(); this.close(); };\n header.appendChild(back);\n header.appendChild(el('div', 'us-editor-title', this.opts.title));\n this.root.appendChild(header);\n\n this.canvasWrap = el('div', 'us-editor-canvas-wrap');\n this.displayCanvas = document.createElement('canvas');\n this.displayCanvas.className = 'us-editor-canvas';\n this.canvasWrap.appendChild(this.displayCanvas);\n this.root.appendChild(this.canvasWrap);\n\n // Toolbar\n const toolbar = el('div', 'us-editor-toolbar');\n this.cropTool = makeTool('Crop', ICON.crop);\n this.cropTool.onclick = () => this.toggleCropMode();\n this.circleTool = makeTool('Circle', ICON.circle);\n this.circleTool.onclick = () => this.applyCircle();\n this.rotateTool = makeTool('Rotate 90°', ICON.rotate);\n this.rotateTool.onclick = () => this.applyRotate();\n this.revertTool = makeTool('Revert', ICON.undo);\n this.revertTool.onclick = () => this.revert();\n this.revertTool.disabled = true; // enabled after first edit\n\n toolbar.appendChild(this.cropTool);\n toolbar.appendChild(this.circleTool);\n toolbar.appendChild(this.rotateTool);\n toolbar.appendChild(el('div', 'us-tool-spacer'));\n toolbar.appendChild(this.revertTool);\n this.root.appendChild(toolbar);\n\n // Footer (Cancel / Apply)\n const footer = el('div', 'us-editor-footer');\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'us-btn';\n cancel.textContent = 'Cancel';\n cancel.onclick = () => { this.opts.onCancel(); this.close(); };\n\n this.applyBtn = document.createElement('button');\n this.applyBtn.type = 'button';\n this.applyBtn.className = 'us-btn us-btn-primary';\n this.applyBtn.innerHTML = `${ICON.check} <span>Apply</span>`;\n this.applyBtn.onclick = () => this.applyAndClose();\n\n footer.appendChild(cancel);\n footer.appendChild(this.applyBtn);\n this.root.appendChild(footer);\n\n this.opts.host.appendChild(this.root);\n }\n\n // ---- image loading ------------------------------------------------------\n\n private async loadOriginalIntoWorking(): Promise<void> {\n const img = await loadImage(this.opts.file);\n const c = document.createElement('canvas');\n c.width = img.naturalWidth;\n c.height = img.naturalHeight;\n const ctx = c.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n ctx.drawImage(img, 0, 0);\n this.working = c;\n this.hasAlpha = looksLikePngMime(this.opts.file.type);\n this.markEdited(false);\n }\n\n // For Revert. Loads the true original (passed-in `originalFile`, or `file`\n // if the caller didn't distinguish).\n private async loadTrueOriginalIntoWorking(): Promise<void> {\n const source = this.opts.originalFile ?? this.opts.file;\n const img = await loadImage(source);\n const c = document.createElement('canvas');\n c.width = img.naturalWidth;\n c.height = img.naturalHeight;\n const ctx = c.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n ctx.drawImage(img, 0, 0);\n this.working = c;\n this.hasAlpha = looksLikePngMime(source.type);\n this.markEdited(false);\n }\n\n // ---- rendering ----------------------------------------------------------\n\n private draw(): void {\n // Fit the working canvas inside MAX_DISPLAY while preserving aspect.\n const { width: ww, height: wh } = this.working;\n const scale = Math.min(1, MAX_DISPLAY / Math.max(ww, wh));\n const dw = Math.max(1, Math.round(ww * scale));\n const dh = Math.max(1, Math.round(wh * scale));\n this.displayCanvas.width = dw;\n this.displayCanvas.height = dh;\n this.displayScale = scale;\n\n const ctx = this.displayCanvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, dw, dh);\n ctx.drawImage(this.working, 0, 0, dw, dh);\n\n this.updateCropBox();\n }\n\n // ---- mode: crop ---------------------------------------------------------\n\n private toggleCropMode(): void {\n if (this.mode === 'crop') {\n this.applyCrop();\n } else {\n this.enterCropMode();\n }\n }\n\n private enterCropMode(): void {\n this.mode = 'crop';\n this.cropTool.dataset.active = 'true';\n this.cropTool.innerHTML = `${ICON.check} <span>Apply crop</span>`;\n // Default inset: 10% on each side.\n const dw = this.displayCanvas.width;\n const dh = this.displayCanvas.height;\n const inset = Math.round(Math.min(dw, dh) * 0.1);\n this.cropRect = { x: inset, y: inset, w: dw - inset * 2, h: dh - inset * 2 };\n this.renderCropBox();\n }\n\n private exitCropMode(): void {\n this.mode = 'none';\n this.cropTool.removeAttribute('data-active');\n this.cropTool.innerHTML = `${ICON.crop} <span>Crop</span>`;\n this.cropRect = null;\n if (this.cropBox) { this.cropBox.remove(); this.cropBox = null; }\n }\n\n private renderCropBox(): void {\n if (!this.cropRect) return;\n if (this.cropBox) this.cropBox.remove();\n\n const box = el('div', 'us-crop-box');\n for (const pos of ['nw', 'ne', 'sw', 'se'] as const) {\n const h = el('div', 'us-crop-handle');\n h.dataset.pos = pos;\n h.addEventListener('pointerdown', (e) => this.beginDrag(e as PointerEvent, pos));\n box.appendChild(h);\n }\n box.addEventListener('pointerdown', (e) => {\n const target = e.target as HTMLElement;\n if (target.classList.contains('us-crop-handle')) return; // handled above\n this.beginDrag(e as PointerEvent, 'move');\n });\n\n // Position the box relative to the displayed canvas. The canvas may have\n // empty padding around it inside .us-editor-canvas-wrap (due to flex\n // centering), so we calculate the offset of the canvas inside the wrap.\n this.canvasWrap.appendChild(box);\n this.cropBox = box;\n this.updateCropBox();\n }\n\n private updateCropBox(): void {\n if (!this.cropBox || !this.cropRect) return;\n const canvasRect = this.displayCanvas.getBoundingClientRect();\n const wrapRect = this.canvasWrap.getBoundingClientRect();\n const left = canvasRect.left - wrapRect.left + this.cropRect.x;\n const top = canvasRect.top - wrapRect.top + this.cropRect.y;\n this.cropBox.style.left = `${left}px`;\n this.cropBox.style.top = `${top}px`;\n this.cropBox.style.width = `${this.cropRect.w}px`;\n this.cropBox.style.height = `${this.cropRect.h}px`;\n }\n\n // ---- drag handlers (crop) ----------------------------------------------\n\n private beginDrag(e: PointerEvent, kind: 'move' | 'nw' | 'ne' | 'sw' | 'se'): void {\n if (!this.cropRect) return;\n e.preventDefault();\n this.dragKind = kind;\n this.dragStart = { px: e.clientX, py: e.clientY, rect: { ...this.cropRect } };\n document.addEventListener('pointermove', this.onPointerMove);\n document.addEventListener('pointerup', this.onPointerUp);\n }\n\n private onPointerMove = (e: PointerEvent): void => {\n if (!this.dragKind || !this.dragStart || !this.cropRect) return;\n const dx = e.clientX - this.dragStart.px;\n const dy = e.clientY - this.dragStart.py;\n const start = this.dragStart.rect;\n const maxW = this.displayCanvas.width;\n const maxH = this.displayCanvas.height;\n const min = 24; // px — minimum crop size so handles stay reachable\n\n let { x, y, w, h } = start;\n switch (this.dragKind) {\n case 'move':\n x = clamp(start.x + dx, 0, maxW - start.w);\n y = clamp(start.y + dy, 0, maxH - start.h);\n break;\n case 'nw': {\n const nx = clamp(start.x + dx, 0, start.x + start.w - min);\n const ny = clamp(start.y + dy, 0, start.y + start.h - min);\n w = start.w + (start.x - nx);\n h = start.h + (start.y - ny);\n x = nx; y = ny;\n break;\n }\n case 'ne': {\n const nw = clamp(start.w + dx, min, maxW - start.x);\n const ny = clamp(start.y + dy, 0, start.y + start.h - min);\n h = start.h + (start.y - ny);\n w = nw; y = ny;\n break;\n }\n case 'sw': {\n const nx = clamp(start.x + dx, 0, start.x + start.w - min);\n const nh = clamp(start.h + dy, min, maxH - start.y);\n w = start.w + (start.x - nx);\n h = nh; x = nx;\n break;\n }\n case 'se':\n w = clamp(start.w + dx, min, maxW - start.x);\n h = clamp(start.h + dy, min, maxH - start.y);\n break;\n }\n this.cropRect = { x, y, w, h };\n this.updateCropBox();\n };\n\n private onPointerUp = (): void => {\n this.dragKind = null;\n this.dragStart = null;\n document.removeEventListener('pointermove', this.onPointerMove);\n document.removeEventListener('pointerup', this.onPointerUp);\n };\n\n // ---- operations (mutate working canvas) --------------------------------\n\n private applyCrop(): void {\n if (!this.cropRect) { this.exitCropMode(); return; }\n // Display-coords → image-coords.\n const s = this.displayScale;\n const sx = Math.round(this.cropRect.x / s);\n const sy = Math.round(this.cropRect.y / s);\n const sw = Math.round(this.cropRect.w / s);\n const sh = Math.round(this.cropRect.h / s);\n\n const next = document.createElement('canvas');\n next.width = sw;\n next.height = sh;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.drawImage(this.working, sx, sy, sw, sh, 0, 0, sw, sh);\n this.working = next;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private applyRotate(): void {\n const { width: w, height: h } = this.working;\n const next = document.createElement('canvas');\n next.width = h;\n next.height = w;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.translate(h, 0);\n ctx.rotate(Math.PI / 2);\n ctx.drawImage(this.working, 0, 0);\n this.working = next;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private applyCircle(): void {\n // Center-crop to a square, then mask with a circle. Output is PNG so the\n // transparent corners survive.\n const { width: w, height: h } = this.working;\n const size = Math.min(w, h);\n const sx = Math.round((w - size) / 2);\n const sy = Math.round((h - size) / 2);\n\n const next = document.createElement('canvas');\n next.width = size;\n next.height = size;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.save();\n ctx.beginPath();\n ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);\n ctx.closePath();\n ctx.clip();\n ctx.drawImage(this.working, sx, sy, size, size, 0, 0, size, size);\n ctx.restore();\n\n this.working = next;\n this.hasAlpha = true;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private async revert(): Promise<void> {\n await this.loadTrueOriginalIntoWorking();\n this.exitCropMode();\n this.draw();\n }\n\n private markEdited(edited: boolean): void {\n this.revertTool.disabled = !edited;\n }\n\n // ---- apply --------------------------------------------------------------\n\n private async applyAndClose(): Promise<void> {\n this.applyBtn.disabled = true;\n try {\n const file = await this.exportFile();\n this.opts.onApply(file);\n this.close();\n } catch (err) {\n // Re-enable so the user can retry or cancel. Keep error message terse.\n this.applyBtn.disabled = false;\n console.error('[union-stack] image export failed', err);\n }\n }\n\n private exportFile(): Promise<File> {\n return new Promise((resolve, reject) => {\n const mime = this.hasAlpha ? 'image/png' : 'image/jpeg';\n const quality = mime === 'image/jpeg' ? 0.92 : undefined;\n this.working.toBlob(\n (blob) => {\n if (!blob) return reject(new Error('toBlob returned null'));\n const baseName = this.opts.file.name.replace(/\\.[^.]+$/, '');\n const ext = mime === 'image/png' ? 'png' : 'jpg';\n resolve(new File([blob], `${baseName}-edited.${ext}`, { type: mime }));\n },\n mime,\n quality,\n );\n });\n }\n}\n\n// ---- helpers --------------------------------------------------------------\n\nfunction el(tag: string, className: string, text?: string): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction makeTool(label: string, iconHtml: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'us-tool';\n btn.innerHTML = `${iconHtml} <span>${escapeText(label)}</span>`;\n return btn;\n}\n\nfunction escapeText(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ({\n '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''',\n })[c] as string);\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, v));\n}\n\nfunction loadImage(file: File): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => { URL.revokeObjectURL(url); resolve(img); };\n img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('decode failed')); };\n img.src = url;\n });\n}\n\nfunction looksLikePngMime(mime: string): boolean {\n return /^image\\/(png|webp|gif)$/.test(mime);\n}\n","import type { UnionStackClient } from '../client.js';\nimport type { PickerConfig, UploadError } from '../types.js';\nimport type {\n PickResponse,\n PickedFile,\n PickerHandle,\n PickerOptions,\n PickerSource,\n UploadedFile,\n} from './types.js';\nimport { ensureStyles, themeToCssVars } from './styles.js';\nimport { ImageEditor } from './imageEditor.js';\n\n// Merge server-managed picker config with runtime options. Runtime options\n// always win — they're the explicit dev escape hatch.\nfunction mergeConfig(server: PickerConfig, runtime: PickerOptions): PickerOptions {\n const merged: PickerOptions = { ...runtime };\n\n // Branding: shallow merge, runtime wins per-field.\n const runtimeBranding = runtime.branding || {};\n merged.branding = {\n logoUrl: runtimeBranding.logoUrl ?? server.branding.logoUrl ?? undefined,\n title: runtimeBranding.title ?? server.branding.title ?? undefined,\n hideFooter: runtimeBranding.hideFooter ?? server.branding.hideFooter,\n // PickerBranding has no footerText today — keep server's value internal.\n };\n\n // Theme: shallow merge.\n const runtimeTheme = runtime.theme || {};\n merged.theme = {\n primary: runtimeTheme.primary ?? server.theme.primary ?? undefined,\n background: runtimeTheme.background ?? server.theme.background ?? undefined,\n foreground: runtimeTheme.foreground ?? server.theme.foreground ?? undefined,\n border: runtimeTheme.border ?? server.theme.border ?? undefined,\n radius: runtimeTheme.radius ?? server.theme.radius ?? undefined,\n mode: runtimeTheme.mode ?? server.theme.mode ?? undefined,\n };\n\n // Constraints inform the dropzone (accept attr, size cap).\n merged.maxFileSize = runtime.maxFileSize ?? server.constraints.maxFileSizeBytes;\n merged.maxFiles = runtime.maxFiles ?? server.constraints.maxFilesPerUpload;\n if (!runtime.accept && server.constraints.allowedMimeTypes?.length) {\n const types = server.constraints.allowedMimeTypes.filter(t => t !== '*/*');\n if (types.length > 0) merged.accept = types.join(',');\n }\n return merged;\n}\n\ntype FileItemState = 'queued' | 'uploading' | 'done' | 'failed' | 'cancelled';\n\ninterface FileItem {\n uploadId: string; // matches PickedFile.uploadId once described\n file: File; // current file (may be edited)\n originalFile: File; // immutable — used by editor Revert\n edited: boolean; // true after image edit; toggles the Edit btn color\n state: FileItemState;\n progress: number; // 0-100\n error?: string;\n uploaded?: UploadedFile;\n objectUrl?: string; // for image thumbnails (revoked on unmount/reswap)\n // DOM refs we mutate on progress.\n $row?: HTMLElement;\n $bar?: HTMLElement;\n $status?: HTMLElement;\n $meta?: HTMLElement;\n $thumb?: HTMLElement;\n $edit?: HTMLButtonElement;\n}\n\nconst DEFAULT_TITLE = 'Upload files';\nconst FOOTER_LINK = 'https://unionstack.link';\n\n// ──────────────────────────────────────────────────────────────────\n// Inline SVG icons. Single source — Lucide-style, 1.5px stroke, 24px\n// viewBox. Keeping them as string templates so the picker bundle has\n// no asset/image dependencies.\n// ──────────────────────────────────────────────────────────────────\nconst ICON = {\n upload: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg>`,\n fileUp: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><path d=\"M12 18v-6\"/><path d=\"m9 15 3-3 3 3\"/></svg>`,\n close: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`,\n check: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`,\n alert: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>`,\n spinner: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" opacity=\"0.2\"/><path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\"/></svg>`,\n image: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>`,\n video: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\"/></svg>`,\n audio: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/></svg>`,\n pdf: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"9\" y1=\"13\" x2=\"15\" y2=\"13\"/><line x1=\"9\" y1=\"17\" x2=\"13\" y2=\"17\"/></svg>`,\n archive: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"5\" rx=\"2\"/><path d=\"M4 9v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9\"/><line x1=\"10\" y1=\"13\" x2=\"14\" y2=\"13\"/></svg>`,\n file: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/></svg>`,\n zap: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>`,\n device: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><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\"/></svg>`,\n link: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72\"/></svg>`,\n pencil: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>`,\n};\n\nfunction iconForMime(mime: string): string {\n if (!mime) return ICON.file;\n if (mime.startsWith('image/')) return ICON.image;\n if (mime.startsWith('video/')) return ICON.video;\n if (mime.startsWith('audio/')) return ICON.audio;\n if (mime === 'application/pdf') return ICON.pdf;\n if (\n mime.startsWith('application/zip') ||\n mime.includes('compressed') ||\n mime === 'application/x-tar' ||\n mime === 'application/gzip'\n ) return ICON.archive;\n return ICON.file;\n}\n\n/**\n * Single-shot file picker modal. Built imperatively (no framework) so it can\n * be lazy-loaded by either the vanilla SDK or the React wrapper.\n */\nexport class Picker {\n private $backdrop: HTMLElement | null = null;\n private $panel: HTMLElement | null = null;\n private $list: HTMLElement | null = null;\n private $confirm: HTMLButtonElement | null = null;\n private $cancel: HTMLButtonElement | null = null;\n private $closeBtn: HTMLButtonElement | null = null;\n private $input: HTMLInputElement | null = null;\n\n // Source-tab DOM (Device vs URL). Only created when both sources are enabled.\n private $deviceSource: HTMLElement | null = null;\n private $urlSource: HTMLElement | null = null;\n private $urlInput: HTMLInputElement | null = null;\n private $urlHint: HTMLElement | null = null;\n private $urlAddBtn: HTMLButtonElement | null = null;\n\n private items: FileItem[] = [];\n private editor: ImageEditor | null = null;\n private abortCtrl = new AbortController();\n private uploadStarted = false;\n private resolved = false;\n private resolvePromise!: (r: PickResponse) => void;\n private donePromise: Promise<PickResponse>;\n\n constructor(\n private client: UnionStackClient,\n private opts: PickerOptions,\n ) {\n this.donePromise = new Promise<PickResponse>(res => { this.resolvePromise = res; });\n }\n\n // ---- public api ---------------------------------------------------------\n\n async open(): Promise<PickResponse> {\n if (typeof document === 'undefined') {\n throw new Error('[union-stack] Picker requires a browser environment.');\n }\n // Pull server-side branding/theme defaults before painting the modal.\n // Runtime opts win over server config — see mergeConfig below.\n try {\n const serverConfig = await this.client.pickerConfigPromise;\n if (serverConfig) this.opts = mergeConfig(serverConfig, this.opts);\n } catch { /* fall through with whatever opts the caller passed */ }\n\n ensureStyles();\n this.mount();\n this.opts.onOpen?.();\n return this.donePromise;\n }\n\n close(): void {\n this.unmount();\n // Resolve with whatever was collected so callers awaiting open() never hang.\n if (!this.resolved) this.resolveResult();\n }\n\n cancel(): void {\n this.abortCtrl.abort();\n this.opts.onCancel?.();\n this.close();\n }\n\n // ---- mount / dom --------------------------------------------------------\n\n private $summary: HTMLElement | null = null;\n\n private mount() {\n const root = document.createElement('div');\n root.className = 'us-picker-backdrop';\n Object.entries(themeToCssVars(this.opts.theme)).forEach(([k, v]) => {\n root.style.setProperty(k, v);\n });\n root.addEventListener('click', e => {\n if (e.target === root && !this.uploadStarted) this.cancel();\n });\n\n const panel = el('div', 'us-picker');\n\n // ─── Header ────────────────────────────────────────────────────\n const header = el('div', 'us-picker-header');\n const logoWrap = el('div', 'us-picker-header-logo');\n if (this.opts.branding?.logoUrl) {\n const logo = document.createElement('img');\n logo.src = this.opts.branding.logoUrl;\n logo.alt = '';\n logoWrap.appendChild(logo);\n } else {\n logoWrap.innerHTML = ICON.zap;\n }\n header.appendChild(logoWrap);\n\n const title = el('div', 'us-picker-title', this.opts.branding?.title ?? DEFAULT_TITLE);\n header.appendChild(title);\n\n this.$closeBtn = document.createElement('button');\n this.$closeBtn.type = 'button';\n this.$closeBtn.className = 'us-picker-close';\n this.$closeBtn.setAttribute('aria-label', 'Close');\n this.$closeBtn.innerHTML = ICON.close;\n this.$closeBtn.onclick = () => this.cancel();\n header.appendChild(this.$closeBtn);\n panel.appendChild(header);\n\n // ─── Body ──────────────────────────────────────────────────────\n const body = el('div', 'us-picker-body');\n const sources = this.resolvedSources();\n\n // Tabs only render when there's more than one source. Single-source flows\n // keep the original lean look.\n if (sources.length > 1) {\n body.appendChild(this.renderSourceTabs(sources));\n }\n\n if (sources.includes('device')) {\n this.$deviceSource = this.renderDropzone();\n body.appendChild(this.$deviceSource);\n }\n if (sources.includes('url')) {\n this.$urlSource = this.renderUrlSource();\n body.appendChild(this.$urlSource);\n }\n this.activateSource(sources[0] ?? 'device');\n\n this.$list = el('div', 'us-file-list');\n body.appendChild(this.$list);\n panel.appendChild(body);\n\n // ─── Actions ───────────────────────────────────────────────────\n const autoUpload = this.opts.autoUpload === true;\n const actions = el('div', 'us-actions');\n\n this.$summary = el('div', 'us-actions-summary', '');\n actions.appendChild(this.$summary);\n\n const buttons = el('div', 'us-actions-buttons');\n this.$cancel = document.createElement('button');\n this.$cancel.type = 'button';\n this.$cancel.className = 'us-btn';\n this.$cancel.textContent = 'Cancel';\n this.$cancel.onclick = () => this.cancel();\n buttons.appendChild(this.$cancel);\n\n if (!autoUpload) {\n this.$confirm = document.createElement('button');\n this.$confirm.type = 'button';\n this.$confirm.className = 'us-btn us-btn-primary';\n this.$confirm.innerHTML = `${ICON.upload} <span>Upload</span>`;\n this.$confirm.disabled = true;\n this.$confirm.onclick = () => this.startUpload();\n buttons.appendChild(this.$confirm);\n }\n actions.appendChild(buttons);\n panel.appendChild(actions);\n\n // ─── Footer ────────────────────────────────────────────────────\n if (!this.opts.branding?.hideFooter) {\n const footer = el('div', 'us-footer');\n footer.innerHTML =\n `${ICON.zap} <span>Powered by <a href=\"${FOOTER_LINK}\" target=\"_blank\" rel=\"noopener\">UnionStack</a></span>`;\n panel.appendChild(footer);\n }\n\n root.appendChild(panel);\n (this.opts.container ?? document.body).appendChild(root);\n this.$backdrop = root;\n this.$panel = panel;\n }\n\n // Resolves which sources to show, honoring opts.fromSources, defaulting to\n // both. Empty arrays fall back to ['device'] — we never want to leave a\n // picker with no way to add files.\n private resolvedSources(): PickerSource[] {\n const raw = this.opts.fromSources;\n if (!raw || raw.length === 0) return ['device', 'url'];\n return raw;\n }\n\n private renderSourceTabs(sources: PickerSource[]): HTMLElement {\n const tabs = el('div', 'us-source-tabs');\n tabs.setAttribute('role', 'tablist');\n for (const src of sources) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'us-source-tab';\n btn.setAttribute('role', 'tab');\n btn.dataset.source = src;\n btn.innerHTML = src === 'device'\n ? `${ICON.device} <span>My Device</span>`\n : `${ICON.link} <span>Link</span>`;\n btn.onclick = () => this.activateSource(src);\n tabs.appendChild(btn);\n }\n return tabs;\n }\n\n private activateSource(source: PickerSource): void {\n if (this.$deviceSource) {\n const active = source === 'device';\n this.$deviceSource.style.display = active ? '' : 'none';\n }\n if (this.$urlSource) {\n this.$urlSource.dataset.active = source === 'url' ? 'true' : 'false';\n }\n // Tab visuals.\n const tabs = this.$panel?.querySelectorAll<HTMLElement>('.us-source-tab');\n tabs?.forEach((t) => {\n t.dataset.active = t.dataset.source === source ? 'true' : 'false';\n });\n }\n\n private renderUrlSource(): HTMLElement {\n const wrap = el('div', 'us-url-source');\n const form = el('div', 'us-url-form');\n\n const input = document.createElement('input');\n input.type = 'url';\n input.className = 'us-url-input';\n input.placeholder = 'https://example.com/photo.jpg';\n input.setAttribute('aria-label', 'File URL');\n input.onkeydown = (e) => {\n if (e.key === 'Enter') { e.preventDefault(); this.handleUrlAdd(); }\n };\n this.$urlInput = input;\n\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'us-btn us-btn-primary';\n add.textContent = 'Add file';\n add.onclick = () => this.handleUrlAdd();\n this.$urlAddBtn = add;\n\n form.appendChild(input);\n form.appendChild(add);\n wrap.appendChild(form);\n\n const hint = el('div', 'us-url-hint', 'Paste a direct file URL. The host must allow CORS so we can fetch it from the browser.');\n this.$urlHint = hint;\n wrap.appendChild(hint);\n\n return wrap;\n }\n\n private async handleUrlAdd(): Promise<void> {\n if (!this.$urlInput || !this.$urlAddBtn || !this.$urlHint) return;\n const url = this.$urlInput.value.trim();\n if (!url) { this.setUrlHint('Enter a URL first.', true); return; }\n if (!/^https?:\\/\\//i.test(url)) {\n this.setUrlHint('URL must start with http:// or https://', true);\n return;\n }\n this.$urlAddBtn.disabled = true;\n const prevLabel = this.$urlAddBtn.textContent;\n this.$urlAddBtn.textContent = 'Fetching…';\n this.setUrlHint('Downloading…', false);\n try {\n const file = await fetchUrlAsFile(url);\n this.addFiles([file]);\n this.$urlInput.value = '';\n this.setUrlHint('Paste a direct file URL. The host must allow CORS so we can fetch it from the browser.', false);\n } catch (err) {\n const msg = (err as Error).message || 'Failed to fetch URL';\n this.setUrlHint(msg, true);\n } finally {\n this.$urlAddBtn.disabled = false;\n this.$urlAddBtn.textContent = prevLabel || 'Add file';\n }\n }\n\n private setUrlHint(text: string, isError: boolean): void {\n if (!this.$urlHint) return;\n this.$urlHint.textContent = text;\n this.$urlHint.dataset.error = isError ? 'true' : 'false';\n }\n\n private renderDropzone(): HTMLElement {\n const dz = el('div', 'us-dropzone');\n dz.setAttribute('role', 'button');\n dz.setAttribute('tabindex', '0');\n dz.setAttribute('aria-label', 'Drop files here or click to browse');\n\n const icon = el('div', 'us-dropzone-icon');\n icon.innerHTML = ICON.fileUp;\n dz.appendChild(icon);\n\n dz.appendChild(el('div', 'us-dropzone-title', 'Drop files to upload'));\n dz.appendChild(el('div', 'us-dropzone-hint', 'or click to browse from your device'));\n\n // Constraints line — surfaces max file size + accepted types so users\n // know the rules before they pick something that gets rejected.\n const constraintBits: string[] = [];\n if (this.opts.maxFileSize) constraintBits.push(`max ${formatBytes(this.opts.maxFileSize)}`);\n if (this.opts.accept) {\n const types = this.opts.accept\n .split(',')\n .map(s => s.trim())\n .filter(s => s && s !== '*/*')\n .map(s => s.replace('/*', ''));\n if (types.length > 0 && types.length <= 4) {\n constraintBits.push(types.join(' · '));\n }\n }\n if (constraintBits.length > 0) {\n dz.appendChild(el('div', 'us-dropzone-constraints', constraintBits.join(' · ')));\n }\n\n const input = document.createElement('input');\n input.type = 'file';\n input.multiple = (this.opts.maxFiles ?? 10) > 1;\n if (this.opts.accept) input.accept = this.opts.accept;\n input.style.display = 'none';\n input.onchange = () => {\n if (input.files) this.addFiles(Array.from(input.files));\n input.value = '';\n };\n this.$input = input;\n dz.appendChild(input);\n\n dz.onclick = () => input.click();\n dz.onkeydown = e => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); input.click(); }\n };\n\n dz.addEventListener('dragover', e => {\n e.preventDefault();\n dz.setAttribute('data-drag', 'over');\n });\n dz.addEventListener('dragleave', () => dz.removeAttribute('data-drag'));\n dz.addEventListener('drop', e => {\n e.preventDefault();\n dz.removeAttribute('data-drag');\n const dropped = e.dataTransfer?.files;\n if (dropped) this.addFiles(Array.from(dropped));\n });\n\n return dz;\n }\n\n private unmount() {\n // Close the editor first so its document-level pointer listeners get\n // removed before we drop the DOM tree they were drawing on.\n if (this.editor) { this.editor.close(); this.editor = null; }\n if (this.$backdrop?.parentNode) this.$backdrop.parentNode.removeChild(this.$backdrop);\n this.$backdrop = null;\n this.$panel = null;\n // Release any image-thumbnail object URLs we created.\n for (const item of this.items) {\n if (item.objectUrl) URL.revokeObjectURL(item.objectUrl);\n }\n this.opts.onClose?.();\n }\n\n // ---- file selection -----------------------------------------------------\n\n private addFiles(files: File[]) {\n const cap = this.opts.maxFiles ?? Infinity;\n const remaining = cap - this.items.length;\n if (remaining <= 0) return;\n const chosen = files.slice(0, remaining);\n\n for (const file of chosen) {\n if (this.opts.maxFileSize && file.size > this.opts.maxFileSize) {\n // Skip the file but surface a row in the list so the user sees why.\n const item: FileItem = {\n uploadId: cryptoId(),\n file, originalFile: file, edited: false,\n state: 'failed', progress: 0,\n error: `File exceeds ${formatBytes(this.opts.maxFileSize)} limit`,\n };\n this.items.push(item);\n this.renderItem(item);\n continue;\n }\n\n const item: FileItem = {\n uploadId: cryptoId(),\n file, originalFile: file, edited: false,\n state: 'queued', progress: 0,\n };\n this.items.push(item);\n this.renderItem(item);\n }\n\n this.refreshConfirm();\n\n // Auto-upload: kick off as soon as the selection is complete. Deferred a\n // frame so the just-added rows render before \"uploading…\" status flips on.\n const autoUpload = this.opts.autoUpload === true;\n const hasQueued = this.items.some(i => i.state === 'queued');\n if (autoUpload && hasQueued && !this.uploadStarted) {\n requestAnimationFrame(() => {\n if (!this.uploadStarted) this.startUpload();\n });\n }\n }\n\n private renderItem(item: FileItem) {\n if (!this.$list) return;\n\n const row = el('div', 'us-file');\n row.dataset.state = item.state;\n row.dataset.uploadId = item.uploadId;\n // Stagger entrance so multiple files don't all pop in at once. Cap delay\n // so a 50-file drop doesn't take forever to fully render.\n const idx = Math.min(this.items.length - 1, 8);\n row.style.animationDelay = `${idx * 35}ms`;\n\n // Thumbnail: image preview for image MIMEs, type-specific icon otherwise.\n const thumb = el('div', 'us-file-thumb');\n if (item.file.type.startsWith('image/') && typeof URL !== 'undefined' && URL.createObjectURL) {\n try {\n const objectUrl = URL.createObjectURL(item.file);\n item.objectUrl = objectUrl;\n thumb.style.backgroundImage = `url(\"${objectUrl}\")`;\n thumb.dataset.image = 'true';\n } catch {\n thumb.innerHTML = iconForMime(item.file.type);\n }\n } else {\n thumb.innerHTML = iconForMime(item.file.type);\n }\n row.appendChild(thumb);\n\n // Main column: name + meta (with progress bar tucked under)\n const main = el('div', 'us-file-main');\n\n const row1 = el('div', 'us-file-row1');\n row1.appendChild(el('div', 'us-file-name', item.file.name));\n const meta = el('div', 'us-file-meta', formatBytes(item.file.size));\n row1.appendChild(meta);\n main.appendChild(row1);\n\n const progress = el('div', 'us-file-progress');\n const bar = el('div', 'us-file-progress-bar');\n progress.appendChild(bar);\n main.appendChild(progress);\n\n row.appendChild(main);\n\n // Edit button — only for image files, only while editable is enabled, only\n // when the file is queued (pre-upload). We append BEFORE the status icon so\n // the visual order is name → edit → status.\n const isImage = item.file.type.startsWith('image/');\n const editingEnabled = this.opts.imageEditing !== false;\n if (isImage && editingEnabled && item.state === 'queued') {\n const edit = document.createElement('button');\n edit.type = 'button';\n edit.className = 'us-file-action';\n edit.setAttribute('aria-label', 'Edit image');\n edit.title = 'Edit image';\n edit.innerHTML = ICON.pencil;\n edit.dataset.edited = item.edited ? 'true' : 'false';\n edit.onclick = () => this.openEditor(item);\n row.appendChild(edit);\n item.$edit = edit;\n }\n\n // Status icon (spinner / check / alert)\n const status = el('div', 'us-file-status');\n status.setAttribute('aria-label', this.statusLabel(item));\n status.innerHTML = this.statusIcon(item.state);\n row.appendChild(status);\n\n item.$row = row;\n item.$bar = bar;\n item.$status = status;\n item.$meta = meta;\n item.$thumb = thumb;\n\n this.$list.appendChild(row);\n this.updateSummary();\n }\n\n // Swap a queued item's file with an edited version. Refreshes thumbnail and\n // meta in place so the row identity is preserved (uploadId, position).\n private replaceItemFile(item: FileItem, nextFile: File, edited: boolean): void {\n item.file = nextFile;\n item.edited = edited;\n\n if (item.objectUrl) URL.revokeObjectURL(item.objectUrl);\n item.objectUrl = undefined;\n\n if (item.$thumb) {\n item.$thumb.style.backgroundImage = '';\n item.$thumb.removeAttribute('data-image');\n if (nextFile.type.startsWith('image/') && typeof URL !== 'undefined' && URL.createObjectURL) {\n try {\n const objectUrl = URL.createObjectURL(nextFile);\n item.objectUrl = objectUrl;\n item.$thumb.style.backgroundImage = `url(\"${objectUrl}\")`;\n item.$thumb.dataset.image = 'true';\n item.$thumb.innerHTML = '';\n } catch {\n item.$thumb.innerHTML = iconForMime(nextFile.type);\n }\n } else {\n item.$thumb.innerHTML = iconForMime(nextFile.type);\n }\n }\n if (item.$meta) item.$meta.textContent = formatBytes(nextFile.size);\n if (item.$row) {\n // Filename may have shifted (revert vs edit). Re-query the name node.\n const nameEl = item.$row.querySelector('.us-file-name');\n if (nameEl) nameEl.textContent = nextFile.name;\n }\n if (item.$edit) item.$edit.dataset.edited = edited ? 'true' : 'false';\n }\n\n // Mount the image editor overlay on top of the picker panel for the given\n // queued item. The editor calls back with either an edited File (Apply) or\n // nothing (Cancel/Back).\n private openEditor(item: FileItem): void {\n if (!this.$panel || this.editor || item.state !== 'queued') return;\n this.editor = new ImageEditor({\n host: this.$panel,\n file: item.file,\n originalFile: item.originalFile,\n title: item.originalFile.name,\n onApply: (edited) => {\n // If the applied file is byte-identical to the original (rare — would\n // require a no-op Apply), don't mark as edited.\n const wasEdited = edited !== item.originalFile;\n this.replaceItemFile(item, edited, wasEdited);\n this.editor = null;\n },\n onCancel: () => { this.editor = null; },\n });\n this.editor.open();\n }\n\n private statusIcon(state: FileItemState): string {\n switch (state) {\n case 'uploading': return ICON.spinner;\n case 'done': return ICON.check;\n case 'failed': return ICON.alert;\n case 'cancelled': return ICON.alert;\n default: return ICON.spinner; // queued — also spinner (waiting)\n }\n }\n\n private statusLabel(item: FileItem): string {\n switch (item.state) {\n case 'queued': return 'Waiting to upload';\n case 'uploading': return `Uploading ${Math.round(item.progress)} percent`;\n case 'done': return 'Upload complete';\n case 'failed': return item.error ? `Failed: ${item.error}` : 'Upload failed';\n case 'cancelled': return 'Cancelled';\n }\n }\n\n private setItemState(item: FileItem, state: FileItemState, progress?: number) {\n item.state = state;\n if (progress !== undefined) item.progress = progress;\n if (item.$row) item.$row.dataset.state = state;\n if (item.$bar) item.$bar.style.width = `${item.progress}%`;\n if (item.$status) {\n item.$status.innerHTML = this.statusIcon(state);\n item.$status.setAttribute('aria-label', this.statusLabel(item));\n }\n if (item.$meta) {\n // Augment the meta line with live state — keeps the eye on a single spot\n // for \"what's happening\" without adding another text row.\n const size = formatBytes(item.file.size);\n switch (state) {\n case 'uploading':\n item.$meta.textContent = `${size} · ${Math.round(item.progress)}%`;\n break;\n case 'done':\n item.$meta.textContent = size;\n break;\n case 'failed':\n item.$meta.textContent = item.error || 'Failed';\n break;\n default:\n item.$meta.textContent = size;\n }\n }\n this.updateSummary();\n }\n\n private updateSummary() {\n if (!this.$summary) return;\n const total = this.items.length;\n if (total === 0) { this.$summary.textContent = ''; return; }\n const done = this.items.filter(i => i.state === 'done').length;\n const failed = this.items.filter(i => i.state === 'failed').length;\n const active = this.items.filter(i => i.state === 'uploading' || i.state === 'queued').length;\n if (!this.uploadStarted && active > 0) {\n const ready = this.items.filter(i => i.state === 'queued').length;\n this.$summary.textContent = `${ready} file${ready === 1 ? '' : 's'} ready`;\n } else if (active > 0) {\n this.$summary.textContent = `Uploading ${done + 1} of ${total}`;\n } else if (failed > 0) {\n this.$summary.textContent = `${done} of ${total} uploaded · ${failed} failed`;\n } else if (done === total) {\n this.$summary.textContent = `${total} file${total === 1 ? '' : 's'} uploaded`;\n } else {\n this.$summary.textContent = `${total} file${total === 1 ? '' : 's'} ready`;\n }\n }\n\n private refreshConfirm() {\n if (!this.$confirm) return;\n const queued = this.items.filter(i => i.state === 'queued').length;\n this.$confirm.disabled = queued === 0 || this.uploadStarted;\n }\n\n // ---- upload -------------------------------------------------------------\n\n private async startUpload() {\n if (this.uploadStarted) return;\n const queued = this.items.filter(i => i.state === 'queued');\n if (queued.length === 0) return;\n\n this.uploadStarted = true;\n if (this.$confirm) {\n this.$confirm.disabled = true;\n this.$confirm.textContent = 'Uploading…';\n }\n if (this.$cancel) this.$cancel.textContent = 'Stop';\n if (this.$closeBtn) this.$closeBtn.disabled = true;\n if (this.$input) this.$input.disabled = true;\n\n // Map PickedFile.uploadId → FileItem so callbacks update the right row.\n const itemByUploadId = new Map<string, FileItem>();\n const filesToUpload = queued.map(i => i.file);\n\n try {\n await this.client.uploadMany(filesToUpload, {\n ...this.opts,\n signal: this.abortCtrl.signal,\n onUploadStarted: (pickedFiles: PickedFile[]) => {\n // Pair each PickedFile to the queued FileItem in order.\n pickedFiles.forEach((p, idx) => {\n const item = queued[idx];\n if (item) {\n item.uploadId = p.uploadId;\n if (item.$row) item.$row.dataset.uploadId = p.uploadId;\n itemByUploadId.set(p.uploadId, item);\n }\n });\n this.opts.onUploadStarted?.(pickedFiles);\n },\n onFileUploadStarted: p => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', 0);\n this.opts.onFileUploadStarted?.(p);\n },\n onFileUploadProgress: (p, ev) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', ev.totalPercent);\n this.opts.onFileUploadProgress?.(p, ev);\n },\n onFileUploadFinished: f => {\n const item = itemByUploadId.get(f.uploadId);\n if (item) {\n item.uploaded = f;\n this.setItemState(item, 'done', 100);\n }\n this.opts.onFileUploadFinished?.(f);\n },\n onFileUploadFailed: (p, err) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) {\n item.error = err.message;\n this.setItemState(item, 'failed');\n }\n this.opts.onFileUploadFailed?.(p, err);\n },\n onUploadDone: r => {\n this.opts.onUploadDone?.(r);\n this.resolveResult(r);\n this.unmount();\n },\n onError: (err: UploadError) => {\n this.opts.onError?.(err);\n this.resolveResult();\n this.unmount();\n },\n });\n } catch {\n // Errors already surfaced via onError / onFileUploadFailed.\n if (!this.resolved) {\n this.resolveResult();\n this.unmount();\n }\n }\n }\n\n private resolveResult(result?: PickResponse) {\n if (this.resolved) return;\n this.resolved = true;\n const fallback: PickResponse = result ?? {\n filesUploaded: this.items.filter(i => i.uploaded).map(i => i.uploaded!),\n filesFailed: this.items\n .filter(i => i.state === 'failed')\n .map(i => ({\n file: {\n uploadId: i.uploadId,\n filename: i.file.name,\n mimetype: i.file.type || 'application/octet-stream',\n size: i.file.size,\n source: 'local',\n },\n error: {\n code: 'VALIDATION',\n message: i.error || 'failed',\n retryable: false,\n },\n })),\n };\n this.resolvePromise(fallback);\n }\n}\n\n// ---- helpers --------------------------------------------------------------\n\nfunction el(tag: string, className: string, text?: string): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;\n return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;\n}\n\nfunction cryptoId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n// Fetch a remote URL as a File. The host MUST send CORS headers — there's no\n// way around that from a browser-only SDK. We surface a clear message in the\n// dropzone hint when the fetch fails so the user understands why.\nasync function fetchUrlAsFile(url: string): Promise<File> {\n let res: Response;\n try {\n res = await fetch(url, { mode: 'cors', credentials: 'omit' });\n } catch {\n throw new Error(`Could not reach ${shortHost(url)} — check the URL or CORS.`);\n }\n if (!res.ok) throw new Error(`Server returned ${res.status} — file unavailable.`);\n const blob = await res.blob();\n const filename = filenameFromUrl(url) || 'download';\n const type = blob.type || guessMimeFromExt(filename);\n return new File([blob], filename, { type });\n}\n\nfunction shortHost(url: string): string {\n try { return new URL(url).host; } catch { return 'that host'; }\n}\n\nfunction filenameFromUrl(url: string): string {\n try {\n const u = new URL(url);\n const last = u.pathname.split('/').filter(Boolean).pop();\n return last ? decodeURIComponent(last) : '';\n } catch {\n return '';\n }\n}\n\nfunction guessMimeFromExt(filename: string): string {\n const ext = filename.toLowerCase().split('.').pop() || '';\n const map: Record<string, string> = {\n jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png',\n gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml',\n pdf: 'application/pdf', mp4: 'video/mp4', mov: 'video/quicktime',\n mp3: 'audio/mpeg', wav: 'audio/wav', zip: 'application/zip',\n json: 'application/json', txt: 'text/plain', csv: 'text/csv',\n };\n return map[ext] || 'application/octet-stream';\n}\n\nexport function openPicker(client: UnionStackClient, opts: PickerOptions): PickerHandle {\n const picker = new Picker(client, opts);\n return {\n open: () => picker.open(),\n close: () => picker.close(),\n cancel: () => picker.cancel(),\n };\n}\n","export { Picker, openPicker } from './picker.js';\nexport type {\n PickerOptions,\n PickerHandle,\n PickerTheme,\n PickerBranding,\n} from './types.js';\n","// IIFE loader entry — bundled as a single drop-in <script>.\n//\n// <script src=\"https://sdk.unionstack.link/v1/loader.js\"></script>\n// <script>\n// const client = UnionStack.init({ apiKey: 'unionstack_live_…', apiBase: '…' });\n// client.picker({ onUploadDone: r => console.log(r) }).open();\n// </script>\n//\n// Everything (core + picker UI) ships in this single file. The /react entry\n// is intentionally not included here — React users install from npm.\n\nimport { UnionStack, UnionStackClient } from '../index.js';\nimport { Picker, openPicker } from '../picker/index.js';\nimport type { ClientConfig } from '../types.js';\n\n// tsup's IIFE wrapper attaches every named export as a property on the\n// global `UnionStack`. To make `UnionStack.init({...})` work directly (rather\n// than `UnionStack.UnionStack.init(...)`), we flatten `init` to a top-level\n// export and keep the constructor classes available for advanced users.\nconst init = UnionStack.init.bind(UnionStack);\nexport { init, UnionStackClient, Picker, openPicker };\n\nexport type { ClientConfig };\n\n// Auto-init via data attribute — wire the loader without any inline script:\n//\n// <script src=\".../loader.js\" data-unionstack-key=\"unionstack_live_…\"></script>\n//\n// The resulting client lands on `window.UnionStack.client`.\ndeclare global {\n interface Window {\n UnionStack?: {\n init: typeof UnionStack.init;\n client?: UnionStackClient;\n };\n }\n}\n\n(function autoInit() {\n if (typeof document === 'undefined') return;\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return;\n const apiKey = script.getAttribute('data-unionstack-key');\n if (!apiKey) return;\n try {\n const client = UnionStack.init({ apiKey });\n queueMicrotask(() => {\n if (window.UnionStack) window.UnionStack.client = client;\n });\n } catch (err) {\n console.error('[UnionStack] auto-init failed:', err);\n }\n})();\n","import type { UploadError, UploadErrorCode } from './types.js';\n\nexport function makeError(\n code: UploadErrorCode,\n message: string,\n opts: { status?: number; retryable?: boolean; cause?: unknown } = {}\n): UploadError {\n return {\n code,\n message,\n status: opts.status,\n retryable: opts.retryable ?? defaultRetryable(code, opts.status),\n cause: opts.cause,\n };\n}\n\nfunction defaultRetryable(code: UploadErrorCode, status?: number): boolean {\n if (code === 'NETWORK' || code === 'PART_FAILED') return true;\n if (code === 'SERVER' && status && status >= 500) return true;\n return false;\n}\n\n/** Map an HTTP error response from the cdn-be API to an UploadError. */\nexport function fromApiResponse(status: number, body: unknown): UploadError {\n const err = (body as { error?: { code?: string; message?: string } } | null)?.error;\n const msg = err?.message || `Request failed with status ${status}`;\n const code = (err?.code || '').toUpperCase();\n\n if (status === 401) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 403) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 413) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 415) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 429) {\n const isQuota = code === 'QUOTA_EXCEEDED';\n return makeError(isQuota ? 'QUOTA' : 'NETWORK', msg, { status, retryable: !isQuota });\n }\n if (status >= 500) {\n return makeError('SERVER', msg, { status, retryable: true });\n }\n return makeError('SERVER', msg, { status, retryable: false });\n}\n","import type {\n ResolvedClientConfig,\n PickedFile,\n PickerConfig,\n UploadedFile,\n UploadOptions,\n ProgressEvent as USProgressEvent,\n} from './types.js';\nimport { fromApiResponse, makeError } from './errors.js';\n\ninterface InitResponse {\n sessionId: string;\n fileId: string;\n chunkSize: number;\n totalParts: number;\n partUrls: Array<{ partNumber: number; url: string }>;\n expiresAt: string;\n}\n\ninterface CompleteResponse {\n handle: string;\n fileId: string;\n url: string;\n filename: string;\n mimetype: string;\n size: number;\n key: string;\n container: string;\n status: 'Stored';\n etag?: string;\n metadata?: Record<string, unknown>;\n}\n\ninterface PartResult {\n partNumber: number;\n etag: string;\n size: number;\n}\n\nconst DEFAULT_CONCURRENCY = 3;\nconst DEFAULT_MAX_RETRIES_PER_PART = 3;\n// Backoff schedule between part retries. Capped — past this we surrender.\nconst BACKOFF_MS = [400, 1200, 3600];\n\nexport class Uploader {\n constructor(private cfg: ResolvedClientConfig) {}\n\n /** Returns a stable PickedFile descriptor for the input blob/file. */\n describe(file: File | Blob, opts: { filename?: string; mimeType?: string } = {}): PickedFile {\n const isFile = typeof File !== 'undefined' && file instanceof File;\n const filename =\n opts.filename ||\n (isFile ? (file as File).name : 'untitled');\n const mimetype =\n opts.mimeType || (file as Blob).type || 'application/octet-stream';\n return {\n uploadId: cryptoRandomId(),\n filename,\n mimetype,\n size: file.size,\n source: 'local',\n };\n }\n\n async upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n const picked = this.describe(file, { filename: opts.filename, mimeType: opts.mimeType });\n opts.onFileUploadStarted?.(picked);\n\n try {\n const init = await this.initUpload(picked, opts);\n const parts = await this.uploadAllParts(file, init, picked, opts);\n const completed = await this.completeUpload(init.sessionId, parts, opts);\n\n const uploaded: UploadedFile = {\n ...picked,\n handle: completed.handle,\n fileId: completed.fileId,\n url: completed.url,\n size: completed.size,\n mimetype: completed.mimetype,\n filename: completed.filename,\n key: completed.key,\n container: completed.container,\n status: 'Stored',\n etag: completed.etag,\n metadata: completed.metadata,\n };\n opts.onFileUploadFinished?.(uploaded);\n return uploaded;\n } catch (rawErr) {\n const err = normalizeError(rawErr);\n opts.onFileUploadFailed?.(picked, err);\n // Best effort: tell the server to clean up the multipart, but ignore failures.\n if ((rawErr as { sessionId?: string })?.sessionId) {\n this.abortSilently((rawErr as { sessionId: string }).sessionId).catch(() => {});\n }\n throw err;\n }\n }\n\n /** Cancel an in-flight session server-side. */\n async abort(sessionId: string): Promise<void> {\n await this.api('POST', '/sdk/v1/uploads/abort', { sessionId });\n }\n\n /** Fetch the server-managed picker config (branding, theme, constraints). */\n async fetchPickerConfig(): Promise<PickerConfig> {\n return this.api<PickerConfig>('GET', '/sdk/v1/picker-config');\n }\n\n private async initUpload(picked: PickedFile, opts: UploadOptions): Promise<InitResponse> {\n return this.api<InitResponse>('POST', '/sdk/v1/uploads/init', {\n filename: picked.filename,\n mimeType: picked.mimetype,\n size: picked.size,\n metadata: opts.metadata,\n }, opts.signal);\n }\n\n private async uploadAllParts(\n file: File | Blob,\n init: InitResponse,\n picked: PickedFile,\n opts: UploadOptions,\n ): Promise<PartResult[]> {\n const totalParts = init.totalParts;\n const chunkSize = init.chunkSize;\n const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);\n const maxRetries = opts.maxRetriesPerPart ?? DEFAULT_MAX_RETRIES_PER_PART;\n\n // Quick lookup: partNumber → presigned URL.\n const urlByPart = new Map(init.partUrls.map(p => [p.partNumber, p.url] as const));\n\n const results: PartResult[] = new Array(totalParts);\n const loadedPerPart: number[] = new Array(totalParts).fill(0);\n let cursor = 1;\n\n const totalBytes = file.size;\n\n const reportProgress = () => {\n const loaded = loadedPerPart.reduce((a, b) => a + b, 0);\n const totalPercent = totalBytes > 0 ? Math.min(100, Math.round((loaded / totalBytes) * 100)) : 100;\n const ev: USProgressEvent = { totalBytes, loaded, totalPercent };\n opts.onFileUploadProgress?.(picked, ev);\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n const partNumber = cursor++;\n if (partNumber > totalParts) return;\n\n const start = (partNumber - 1) * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const chunk = file.slice(start, end);\n\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= maxRetries) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n try {\n let url = urlByPart.get(partNumber);\n if (!url) {\n const refreshed = await this.api<{ url: string }>(\n 'POST', '/sdk/v1/uploads/sign-part',\n { sessionId: init.sessionId, partNumber }, opts.signal,\n );\n url = refreshed.url;\n urlByPart.set(partNumber, url);\n }\n\n const res = await this.cfg.fetch?.(url, {\n method: 'PUT',\n body: chunk,\n signal: opts.signal,\n }) ?? await fetch(url, { method: 'PUT', body: chunk, signal: opts.signal });\n\n if (!res.ok) {\n // If R2 rejects with 403 the URL likely expired — drop it so the\n // next attempt re-signs.\n if (res.status === 403 || res.status === 401) urlByPart.delete(partNumber);\n throw makeError('PART_FAILED', `Part ${partNumber} PUT failed (HTTP ${res.status})`, { status: res.status });\n }\n const etag = res.headers.get('etag');\n if (!etag) {\n throw makeError('PART_FAILED', `Part ${partNumber}: R2 did not return an ETag.`);\n }\n\n results[partNumber - 1] = { partNumber, etag, size: chunk.size };\n loadedPerPart[partNumber - 1] = chunk.size;\n reportProgress();\n break;\n } catch (err) {\n lastErr = err;\n attempt++;\n if (attempt > maxRetries) break;\n await sleep(BACKOFF_MS[Math.min(attempt - 1, BACKOFF_MS.length - 1)]);\n }\n }\n if (!results[partNumber - 1]) {\n throw withSessionId(\n normalizeError(lastErr ?? makeError('PART_FAILED', `Part ${partNumber} failed after ${maxRetries} retries.`)),\n init.sessionId,\n );\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, totalParts) }, worker);\n await Promise.all(workers);\n return results;\n }\n\n private async completeUpload(\n sessionId: string,\n parts: PartResult[],\n opts: UploadOptions,\n ): Promise<CompleteResponse> {\n return this.api<CompleteResponse>('POST', '/sdk/v1/uploads/complete', {\n sessionId,\n parts: parts.map(p => ({ partNumber: p.partNumber, etag: p.etag })),\n }, opts.signal);\n }\n\n private async abortSilently(sessionId: string): Promise<void> {\n try { await this.abort(sessionId); } catch { /* ignore */ }\n }\n\n private async api<T>(\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const fetchImpl = this.cfg.fetch ?? fetch;\n const url = `${this.cfg.apiBase}${path}`;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.cfg.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (cause) {\n if ((cause as Error)?.name === 'AbortError') {\n throw makeError('ABORTED', 'Request aborted.', { retryable: false, cause });\n }\n throw makeError('NETWORK', 'Network request failed.', { retryable: true, cause });\n }\n let parsed: unknown = null;\n try { parsed = await res.json(); } catch { /* tolerate empty body */ }\n if (!res.ok) throw fromApiResponse(res.status, parsed);\n return parsed as T;\n }\n}\n\nfunction cryptoRandomId(): string {\n // Cheap unique id for client-side uploadId. Not security-sensitive.\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction normalizeError(err: unknown): ReturnType<typeof makeError> {\n if (err && typeof err === 'object' && 'code' in err && 'message' in err && 'retryable' in err) {\n return err as ReturnType<typeof makeError>;\n }\n const msg = (err as Error)?.message || 'Upload failed.';\n return makeError('NETWORK', msg, { cause: err });\n}\n\nfunction withSessionId<T>(err: T, sessionId: string): T {\n if (err && typeof err === 'object') (err as { sessionId?: string }).sessionId = sessionId;\n return err;\n}\n","import type {\n ClientConfig,\n ResolvedClientConfig,\n UploadOptions,\n BatchUploadOptions,\n UploadedFile,\n PickResponse,\n PickedFile,\n PickerConfig,\n UploadError,\n} from './types.js';\nimport { Uploader } from './uploader.js';\nimport { makeError } from './errors.js';\n\n/**\n * UnionStack API URL baked into the SDK. Consumers never see or set this —\n * we ship a new SDK version if the URL ever changes.\n *\n * For local development against a staging backend, change this constant in\n * src/ and rebuild — no consumer-facing override exists by design.\n */\nconst API_BASE = 'https://api.unionstack.link/v1';\n\n// Forward-declared minimal handle so client.ts doesn't pull in the picker bundle.\nexport interface PickerHandleLike {\n open(): Promise<PickResponse>;\n close(): void;\n cancel(): void;\n}\n\nconst PRELOADER_STYLE_ID = 'unionstack-preloader-styles';\n\n// Instant spinner shown between picker.open() and the modal actually mounting\n// (covers the lazy chunk download + the server config fetch). Self-contained —\n// must not depend on the picker bundle, which is what we're waiting for. The\n// backdrop matches the picker's exactly so the handoff is seamless.\nfunction showPickerPreloader(): () => void {\n if (typeof document === 'undefined') return () => {};\n if (!document.getElementById(PRELOADER_STYLE_ID)) {\n const style = document.createElement('style');\n style.id = PRELOADER_STYLE_ID;\n style.textContent =\n '@keyframes us-preload-spin{to{transform:rotate(360deg)}}' +\n '@keyframes us-preload-fade{from{opacity:0}to{opacity:1}}';\n document.head.appendChild(style);\n }\n const backdrop = document.createElement('div');\n backdrop.setAttribute('role', 'status');\n backdrop.setAttribute('aria-label', 'Loading file picker');\n backdrop.style.cssText =\n 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;' +\n 'background:rgba(2,6,23,0.4);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);' +\n 'animation:us-preload-fade 140ms ease-out;';\n const spinner = document.createElement('div');\n spinner.style.cssText =\n 'width:36px;height:36px;border-radius:50%;' +\n 'border:3px solid rgba(220,225,251,0.25);border-top-color:#c0c1ff;' +\n 'animation:us-preload-spin 0.8s linear infinite;';\n backdrop.appendChild(spinner);\n document.body.appendChild(backdrop);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n backdrop.remove();\n };\n}\n\nexport class UnionStackClient {\n private uploader: Uploader;\n private resolvedCfg: ResolvedClientConfig;\n /**\n * Promise that resolves to the server-managed picker config. Pre-fetched on\n * construction (unless `skipConfigPrefetch: true`) so the picker opens\n * without a network hit. Fails silently — picker falls back to defaults.\n */\n pickerConfigPromise: Promise<PickerConfig | null>;\n\n constructor(cfg: ClientConfig) {\n if (!cfg.apiKey) throw makeError('CONFIG', 'apiKey is required.', { retryable: false });\n this.resolvedCfg = { ...cfg, apiBase: API_BASE };\n this.uploader = new Uploader(this.resolvedCfg);\n this.pickerConfigPromise = this.resolvedCfg.skipConfigPrefetch\n ? Promise.resolve(null)\n : this.uploader.fetchPickerConfig().catch(() => null);\n }\n\n upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n return this.uploader.upload(file, opts);\n }\n\n async uploadMany(\n files: (File | Blob)[],\n opts: BatchUploadOptions = {},\n ): Promise<PickResponse> {\n if (!Array.isArray(files) || files.length === 0) {\n const err = makeError('VALIDATION', 'uploadMany requires a non-empty array of files.', { retryable: false });\n opts.onError?.(err);\n throw err;\n }\n\n const picked: PickedFile[] = files.map(f =>\n this.uploader.describe(f, { filename: opts.filename, mimeType: opts.mimeType })\n );\n opts.onUploadStarted?.(picked);\n\n const filesUploaded: UploadedFile[] = [];\n const filesFailed: Array<{ file: PickedFile; error: UploadError }> = [];\n\n await Promise.all(files.map(async (f, i) => {\n try {\n const uploaded = await this.uploader.upload(f, opts);\n // Sync the uploadId so callers can correlate picked → uploaded.\n uploaded.uploadId = picked[i].uploadId;\n filesUploaded.push(uploaded);\n } catch (err) {\n filesFailed.push({ file: picked[i], error: err as UploadError });\n }\n }));\n\n const result: PickResponse = { filesUploaded, filesFailed };\n opts.onUploadDone?.(result);\n return result;\n }\n \n picker(opts: import('./picker/types.js').PickerOptions = {}): PickerHandleLike {\n let real: PickerHandleLike | null = null;\n let opened = false;\n let pendingResolve!: (r: PickResponse) => void;\n let pendingReject!: (e: UploadError) => void;\n const donePromise = new Promise<PickResponse>((res, rej) => {\n pendingResolve = res; pendingReject = rej;\n });\n\n return {\n open: async () => {\n if (opened) return donePromise;\n opened = true;\n const hidePreloader = showPickerPreloader();\n try {\n const mod = await import('./picker/index.js');\n real = mod.openPicker(this, {\n ...opts,\n // Picker fires onOpen right after the modal mounts — that's the\n // moment the preloader hands off to the real UI.\n onOpen: () => { hidePreloader(); opts.onOpen?.(); },\n });\n const result = await real.open();\n hidePreloader();\n pendingResolve(result);\n return result;\n } catch (err) {\n hidePreloader();\n const e = (err as UploadError)?.code\n ? (err as UploadError)\n : makeError('CONFIG', 'Failed to load picker.', { retryable: false, cause: err });\n pendingReject(e);\n throw e;\n }\n },\n close: () => { real?.close(); },\n cancel: () => { real?.cancel(); },\n };\n }\n\n /** Read-only accessor used by the React/picker packages. */\n get config(): Readonly<ResolvedClientConfig> { return this.resolvedCfg; }\n}\n","import { UnionStackClient } from './client.js';\nimport type { ClientConfig } from './types.js';\n\nexport const UnionStack = {\n init(cfg: ClientConfig): UnionStackClient {\n return new UnionStackClient(cfg);\n },\n};\n\nexport { UnionStackClient } from './client.js';\nexport type {\n ClientConfig,\n UploadOptions,\n BatchUploadOptions,\n PickedFile,\n UploadedFile,\n ProgressEvent,\n PickResponse,\n UploadError,\n UploadErrorCode,\n Source,\n} from './types.js';\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["ensureFonts","FONT_ID","preconnect","href","cross","l","link","FONT_HREF","ensureStyles","STYLE_ID","el","BASE_CSS","themeToCssVars","theme","_a","_b","_c","_d","_e","defaults","DARK_DEFAULTS","LIGHT_DEFAULTS","init_styles","__esmMin","el","tag","className","text","node","makeTool","label","iconHtml","btn","escapeText","s","c","clamp","v","lo","hi","loadImage","file","resolve","reject","url","img","looksLikePngMime","mime","ICON","ImageEditor","init_imageEditor","__esmMin","opts","dx","dy","start","maxW","maxH","min","x","y","w","h","nx","ny","nw","nh","err","header","back","toolbar","footer","cancel","ctx","_a","source","ww","wh","scale","dw","dh","inset","box","pos","e","canvasRect","wrapRect","left","top","kind","__spreadValues","sx","sy","sw","sh","next","size","edited","quality","blob","baseName","ext","mergeConfig","server","runtime","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","merged","__spreadValues","runtimeBranding","runtimeTheme","types","t","iconForMime","mime","ICON","el","tag","className","text","node","formatBytes","n","cryptoId","fetchUrlAsFile","url","res","e","shortHost","blob","filename","filenameFromUrl","type","guessMimeFromExt","last","ext","openPicker","client","opts","picker","Picker","DEFAULT_TITLE","FOOTER_LINK","init_picker","__esmMin","init_styles","init_imageEditor","serverConfig","ensureStyles","root","themeToCssVars","k","v","panel","header","logoWrap","logo","title","body","sources","autoUpload","actions","buttons","footer","raw","tabs","src","btn","source","active","wrap","form","input","add","hint","prevLabel","file","err","msg","isError","dz","icon","constraintBits","dropped","item","files","remaining","chosen","hasQueued","i","row","idx","thumb","objectUrl","main","row1","meta","progress","bar","isImage","editingEnabled","edit","status","nextFile","edited","nameEl","ImageEditor","wasEdited","state","size","total","done","failed","ready","queued","itemByUploadId","filesToUpload","__spreadProps","pickedFiles","p","ev","f","result","fallback","picker_exports","__export","Picker","openPicker","init_picker","__esmMin","loader_exports","__export","Picker","UnionStackClient","init","openPicker","makeError","code","message","opts","_a","defaultRetryable","status","fromApiResponse","body","err","msg","isQuota","DEFAULT_CONCURRENCY","DEFAULT_MAX_RETRIES_PER_PART","BACKOFF_MS","Uploader","cfg","file","opts","isFile","filename","mimetype","cryptoRandomId","_a","_b","_c","picked","init","parts","completed","uploaded","__spreadProps","__spreadValues","rawErr","err","normalizeError","sessionId","totalParts","chunkSize","concurrency","maxRetries","urlByPart","p","results","loadedPerPart","cursor","totalBytes","reportProgress","loaded","a","b","totalPercent","ev","worker","_d","_e","makeError","partNumber","start","end","chunk","attempt","lastErr","url","res","etag","sleep","withSessionId","workers","e","method","path","body","signal","fetchImpl","cause","parsed","fromApiResponse","ms","r","msg","API_BASE","PRELOADER_STYLE_ID","showPickerPreloader","style","backdrop","spinner","removed","UnionStackClient","cfg","makeError","__spreadProps","__spreadValues","Uploader","file","opts","files","_a","_b","_c","err","picked","f","filesUploaded","filesFailed","i","uploaded","result","real","opened","pendingResolve","pendingReject","donePromise","res","rej","hidePreloader","e","UnionStack","cfg","UnionStackClient","init_picker","init","UnionStack","script","apiKey","client","UnionStack","err"]}
|
|
1
|
+
{"version":3,"sources":["../../src/picker/styles.ts","../../src/picker/imageEditor.ts","../../src/picker/picker.ts","../../src/picker/index.ts","../../src/loader/index.ts","../../src/errors.ts","../../src/version.ts","../../src/uploader.ts","../../src/client.ts","../../src/index.ts"],"sourcesContent":["import type { PickerTheme } from './types.js';\n\nconst STYLE_ID = 'unionstack-picker-styles';\nconst FONT_ID = 'unionstack-picker-fonts';\n\n// Kinetic Sync uses Inter for UI and JetBrains Mono for technical data.\n// Loaded from Google Fonts with display=swap — system stacks below render\n// instantly and the webfont swaps in when ready. No bundled font assets.\nconst FONT_HREF =\n 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap';\n\nfunction ensureFonts(): void {\n if (document.getElementById(FONT_ID)) return;\n const preconnect = (href: string, cross?: boolean) => {\n const l = document.createElement('link');\n l.rel = 'preconnect';\n l.href = href;\n if (cross) l.crossOrigin = 'anonymous';\n document.head.appendChild(l);\n };\n preconnect('https://fonts.googleapis.com');\n preconnect('https://fonts.gstatic.com', true);\n const link = document.createElement('link');\n link.id = FONT_ID;\n link.rel = 'stylesheet';\n link.href = FONT_HREF;\n document.head.appendChild(link);\n}\n\n/** Inject the picker stylesheet (and webfonts) once. Idempotent — safe to call on every open. */\nexport function ensureStyles(): void {\n if (typeof document === 'undefined') return;\n ensureFonts();\n if (document.getElementById(STYLE_ID)) return;\n const el = document.createElement('style');\n el.id = STYLE_ID;\n el.textContent = BASE_CSS;\n document.head.appendChild(el);\n}\n\n/** Resolve a (possibly partial) theme into the CSS variables the picker reads. */\nexport function themeToCssVars(theme: PickerTheme | undefined): Record<string, string> {\n const mode = theme?.mode || 'light';\n const defaults = mode === 'dark' ? DARK_DEFAULTS : LIGHT_DEFAULTS;\n return {\n '--us-primary': theme?.primary ?? defaults.primary,\n '--us-on-primary': defaults.onPrimary,\n '--us-bg': theme?.background ?? defaults.background,\n '--us-fg': theme?.foreground ?? defaults.foreground,\n '--us-muted': defaults.muted,\n '--us-subtle': defaults.subtle,\n '--us-border': theme?.border ?? defaults.border,\n '--us-border-strong': defaults.borderStrong,\n '--us-elevated': defaults.elevated,\n '--us-overlay': defaults.overlay,\n '--us-raised': defaults.raised,\n '--us-accent': defaults.accent,\n '--us-success': defaults.success,\n '--us-danger': defaults.danger,\n '--us-radius': theme?.radius ?? '12px',\n };\n}\n\n// ─── Kinetic Sync — light scheme ────────────────────────────────────\n// Derived light counterpart of the dark palette below: same violet\n// family, inverse tonal ramp. Depth comes from tonal layering + 1px\n// borders, not shadows.\nconst LIGHT_DEFAULTS = {\n primary: '#494bd6', // inverse-primary of the dark scheme\n onPrimary: '#ffffff',\n background: '#fdfbff', // surface — near-white with a violet hint\n foreground: '#1b1b21', // on-surface\n muted: '#5e5c6e', // on-surface-variant\n subtle: '#f3f1fa', // surface-container — chips, icon wells, tab rail\n border: '#e4e1ee', // outline-variant (soft)\n borderStrong: '#c8c5d4', // outline — drag-over emphasis\n elevated: '#ffffff', // cards, one step above the floor\n overlay: '#eceaf4', // instant hover state\n raised: '#ffffff', // active tab / popover layer\n accent: '#b35a00', // tertiary — \"edited\" markers\n success: '#2c9a5b', // desaturated green\n danger: '#ba1a1a',\n};\n\n// ─── Kinetic Sync — dark scheme ─────────────────────────────────────\n// Tokens straight from the design system. Periwinkle primary on a deep\n// midnight-navy tonal ramp; no blue.\nconst DARK_DEFAULTS = {\n primary: '#c0c1ff', // primary\n onPrimary: '#1000a9', // on-primary — dark ink on periwinkle\n background: '#0c1324', // surface (floor)\n foreground: '#dce1fb', // on-surface\n muted: '#908fa0', // outline\n subtle: '#191f31', // surface-container\n border: '#2e3447', // surface-container-highest\n borderStrong: '#464554', // outline-variant\n elevated: '#151b2d', // surface-container-low — cards, one step up\n overlay: '#23293c', // surface-container-high — instant hover\n raised: '#2e3447', // active tab / popover layer\n accent: '#ffb783', // tertiary — \"edited\" markers\n success: '#7ad08e', // desaturated green\n danger: '#ffb4ab', // error\n};\n\n// Everything is namespaced under `.us-picker` and uses CSS variables so the\n// host page's styles never leak in, and theme overrides flow cleanly.\nconst BASE_CSS = `\n.us-picker-backdrop {\n --us-font: \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n --us-mono: \"JetBrains Mono\", ui-monospace, \"SF Mono\", Menlo, monospace;\n position: fixed; inset: 0; z-index: 2147483000;\n background: rgba(2, 6, 23, 0.4);\n -webkit-backdrop-filter: blur(8px);\n backdrop-filter: blur(8px);\n display: flex; align-items: center; justify-content: center;\n padding: 16px;\n font-family: var(--us-font);\n font-feature-settings: \"cv02\", \"cv11\";\n animation: us-fade 140ms ease-out;\n}\n@keyframes us-fade { from { opacity: 0; } to { opacity: 1; } }\n\n.us-picker {\n background: var(--us-bg); color: var(--us-fg);\n border: 1px solid var(--us-border);\n border-radius: var(--us-radius);\n width: 100%; max-width: 480px;\n max-height: min(calc(100dvh - 32px), 680px);\n display: flex; flex-direction: column;\n position: relative;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.5);\n overflow: hidden;\n animation: us-rise 240ms cubic-bezier(0.16, 1, 0.3, 1);\n}\n@keyframes us-rise {\n from { opacity: 0; transform: translateY(8px) scale(0.985); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n}\n.us-picker * { box-sizing: border-box; }\n\n/* ──────────────────── header ──────────────────── */\n.us-picker-header {\n display: flex; align-items: center; gap: 12px;\n padding: 14px 16px;\n border-bottom: 1px solid var(--us-border);\n}\n.us-picker-header-logo {\n display: inline-flex; align-items: center; justify-content: center;\n width: 28px; height: 28px; flex-shrink: 0;\n border-radius: 8px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n color: var(--us-primary);\n}\n.us-picker-header-logo svg { width: 15px; height: 15px; }\n.us-picker-header-logo img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }\n.us-picker-title { font-weight: 600; font-size: 15px; letter-spacing: -0.02em; line-height: 1.2; flex: 1; }\n.us-picker-close {\n background: none; border: 0; cursor: pointer;\n width: 32px; height: 32px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted); border-radius: 8px;\n transition: color 100ms, background 0ms;\n}\n.us-picker-close:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-picker-close:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }\n.us-picker-close svg { width: 16px; height: 16px; }\n\n/* ──────────────────── body / dropzone ──────────────────── */\n.us-picker-body { padding: 16px; overflow-y: auto; }\n\n.us-dropzone {\n position: relative;\n border: 1px dashed var(--us-border-strong);\n border-radius: 8px;\n padding: 28px 20px;\n text-align: center;\n cursor: pointer;\n transition: border-color 120ms, background 0ms;\n background: var(--us-subtle);\n}\n.us-dropzone:hover {\n border-color: var(--us-primary);\n background: color-mix(in srgb, var(--us-primary) 6%, var(--us-subtle));\n}\n.us-dropzone:focus-visible {\n outline: 2px solid var(--us-primary); outline-offset: 2px;\n}\n.us-dropzone[data-drag=\"over\"] {\n border-style: solid;\n border-color: var(--us-primary);\n background: color-mix(in srgb, var(--us-primary) 10%, var(--us-subtle));\n}\n.us-dropzone-icon {\n width: 44px; height: 44px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--us-primary) 12%, var(--us-bg));\n border: 1px solid color-mix(in srgb, var(--us-primary) 24%, transparent);\n color: var(--us-primary);\n display: inline-flex; align-items: center; justify-content: center;\n margin-bottom: 12px;\n transition: transform 240ms cubic-bezier(0.16, 1.4, 0.3, 1);\n}\n.us-dropzone:hover .us-dropzone-icon { transform: translateY(-2px); }\n.us-dropzone[data-drag=\"over\"] .us-dropzone-icon {\n transform: translateY(-3px) scale(1.06);\n}\n.us-dropzone-icon svg { width: 22px; height: 22px; }\n.us-dropzone-title {\n font-size: 14px; font-weight: 600; letter-spacing: -0.011em;\n margin-bottom: 2px;\n}\n.us-dropzone-hint { color: var(--us-muted); font-size: 12.5px; letter-spacing: -0.006em; }\n.us-dropzone-constraints {\n margin-top: 12px;\n font-size: 11px; color: var(--us-muted);\n font-family: var(--us-mono);\n letter-spacing: 0;\n}\n\n.us-dropzone--compact {\n padding: 12px 16px;\n display: flex; align-items: center; gap: 12px;\n text-align: left;\n}\n.us-dropzone--compact .us-dropzone-icon {\n width: 32px; height: 32px; border-radius: 8px; margin-bottom: 0;\n}\n.us-dropzone--compact .us-dropzone-icon svg { width: 16px; height: 16px; }\n.us-dropzone--compact .us-dropzone-title { font-size: 13px; margin: 0; }\n.us-dropzone--compact .us-dropzone-hint { display: none; }\n.us-dropzone--compact .us-dropzone-constraints { display: none; }\n\n/* ──────────────────── file list ──────────────────── */\n.us-file-list { display: flex; flex-direction: column; gap: 8px; margin-top: 14px; }\n\n.us-file {\n display: flex; align-items: center; gap: 12px;\n padding: 10px 12px;\n background: var(--us-elevated);\n border: 1px solid var(--us-border);\n border-radius: 8px;\n transition: border-color 120ms;\n animation: us-row-in 280ms cubic-bezier(0.16, 1, 0.3, 1) backwards;\n position: relative;\n}\n@keyframes us-row-in {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n.us-file[data-state=\"done\"] { border-color: color-mix(in srgb, var(--us-success) 35%, var(--us-border)); }\n.us-file[data-state=\"failed\"] { border-color: color-mix(in srgb, var(--us-danger) 35%, var(--us-border)); }\n\n.us-file-thumb {\n width: 40px; height: 40px; flex-shrink: 0;\n border-radius: 4px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n background-size: cover; background-position: center;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted);\n overflow: hidden;\n}\n.us-file-thumb[data-image=\"true\"] { color: transparent; border-color: transparent; }\n.us-file-thumb svg { width: 18px; height: 18px; }\n\n.us-file-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }\n.us-file-row1 { display: flex; align-items: center; gap: 8px; }\n.us-file-name {\n font-size: 13px; font-weight: 500;\n flex: 1; min-width: 0;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n letter-spacing: -0.006em;\n}\n.us-file-meta {\n color: var(--us-muted); font-size: 11px; flex-shrink: 0;\n font-family: var(--us-mono); letter-spacing: 0;\n}\n\n.us-file-progress {\n height: 3px; background: var(--us-border); border-radius: 999px; overflow: hidden;\n position: relative;\n}\n.us-file-progress-bar {\n height: 100%; width: 0%;\n background: var(--us-primary);\n border-radius: inherit;\n transition: width 260ms cubic-bezier(0.4, 0.0, 0.2, 1);\n position: relative;\n}\n/* Shimmer overlay while uploading. Stops on terminal states. */\n.us-file[data-state=\"uploading\"] .us-file-progress-bar::after {\n content: \"\"; position: absolute; inset: 0;\n background: linear-gradient(\n 90deg,\n transparent 0%,\n color-mix(in srgb, #fff 35%, transparent) 50%,\n transparent 100%\n );\n animation: us-shimmer 1.4s linear infinite;\n}\n@keyframes us-shimmer {\n from { transform: translateX(-100%); }\n to { transform: translateX(100%); }\n}\n.us-file[data-state=\"done\"] .us-file-progress-bar { width: 100% !important; background: var(--us-success); }\n.us-file[data-state=\"failed\"] .us-file-progress-bar { background: var(--us-danger); }\n\n.us-file-status {\n flex-shrink: 0;\n display: inline-flex; align-items: center; justify-content: center;\n width: 24px; height: 24px;\n color: var(--us-muted);\n}\n.us-file-status svg { width: 18px; height: 18px; }\n.us-file[data-state=\"done\"] .us-file-status { color: var(--us-success); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }\n.us-file[data-state=\"failed\"] .us-file-status { color: var(--us-danger); animation: us-pop 320ms cubic-bezier(0.16, 1.4, 0.3, 1); }\n.us-file[data-state=\"uploading\"] .us-file-status { animation: us-spin 0.9s linear infinite; }\n@keyframes us-pop {\n from { transform: scale(0.5); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n}\n@keyframes us-spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n\n/* ──────────────────── actions / footer ──────────────────── */\n.us-actions {\n display: flex; gap: 8px; justify-content: space-between; align-items: center;\n padding: 12px 16px;\n border-top: 1px solid var(--us-border);\n}\n.us-actions-summary {\n font-size: 11px; color: var(--us-muted);\n font-family: var(--us-mono); letter-spacing: 0;\n}\n.us-actions-buttons { display: flex; gap: 8px; }\n\n.us-btn {\n appearance: none;\n display: inline-flex; align-items: center; justify-content: center; gap: 6px;\n padding: 8px 14px; min-height: 36px;\n border-radius: 8px; border: 1px solid var(--us-border-strong);\n background: transparent; color: var(--us-fg);\n cursor: pointer; font-size: 13px; font-weight: 500;\n font-family: inherit; letter-spacing: 0.01em;\n transition: background 0ms, border-color 120ms, transform 80ms ease-out;\n}\n.us-btn:hover { background: var(--us-overlay); }\n.us-btn:active { transform: scale(0.98); }\n.us-btn:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 2px; }\n.us-btn-primary {\n background: var(--us-primary); color: var(--us-on-primary);\n border-color: var(--us-primary);\n}\n.us-btn-primary:hover { filter: brightness(1.06); background: var(--us-primary); }\n.us-btn[disabled] { opacity: 0.5; cursor: not-allowed; }\n.us-btn[disabled]:hover { transform: none; }\n.us-btn svg { width: 14px; height: 14px; }\n\n.us-footer {\n padding: 8px 16px 12px;\n font-size: 11px; color: var(--us-muted); text-align: center;\n letter-spacing: 0.02em;\n display: flex; align-items: center; justify-content: center; gap: 6px;\n}\n.us-footer svg { width: 11px; height: 11px; opacity: 0.7; }\n.us-footer a { color: var(--us-muted); text-decoration: none; font-weight: 500; }\n.us-footer a:hover { color: var(--us-fg); }\n\n/* ──────────────────── source tabs (Device / URL) ──────────────────── */\n.us-source-tabs {\n display: inline-flex; gap: 2px; padding: 3px;\n background: var(--us-subtle);\n border: 1px solid var(--us-border);\n border-radius: 8px;\n margin-bottom: 14px;\n}\n.us-source-tab {\n appearance: none; background: transparent; border: 1px solid transparent;\n font: inherit; cursor: pointer;\n padding: 6px 14px; min-height: 30px;\n border-radius: 6px;\n font-size: 12px; font-weight: 500; letter-spacing: 0.02em;\n color: var(--us-muted);\n transition: color 100ms, background 0ms;\n display: inline-flex; align-items: center; gap: 6px;\n}\n.us-source-tab svg { width: 13px; height: 13px; }\n.us-source-tab:hover { color: var(--us-fg); }\n.us-source-tab[data-active=\"true\"] {\n background: var(--us-raised);\n border-color: var(--us-border);\n color: var(--us-fg);\n}\n.us-source-tab:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 2px; }\n\n/* ──────────────────── URL source ──────────────────── */\n.us-url-source { display: none; }\n.us-url-source[data-active=\"true\"] { display: block; }\n.us-url-form {\n display: flex; gap: 8px;\n padding: 16px;\n border: 1px dashed var(--us-border-strong);\n border-radius: 8px;\n background: var(--us-subtle);\n}\n.us-url-input {\n appearance: none;\n flex: 1; min-width: 0;\n padding: 9px 12px; min-height: 36px;\n border-radius: 8px; border: 1px solid var(--us-border-strong);\n background: var(--us-bg); color: var(--us-fg);\n font: inherit; font-size: 13px; letter-spacing: -0.006em;\n transition: border-color 120ms, box-shadow 120ms;\n}\n.us-url-input::placeholder { color: var(--us-muted); }\n.us-url-input:focus {\n outline: none;\n border-color: var(--us-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--us-primary) 25%, transparent);\n}\n.us-url-hint {\n margin-top: 8px;\n font-size: 11.5px; color: var(--us-muted); letter-spacing: -0.006em;\n}\n.us-url-hint[data-error=\"true\"] { color: var(--us-danger); }\n\n/* ──────────────────── per-row edit button ──────────────────── */\n.us-file-actions { display: inline-flex; gap: 4px; }\n.us-file-action {\n appearance: none; background: transparent; border: 0; cursor: pointer;\n width: 28px; height: 28px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted); border-radius: 6px;\n transition: color 100ms, background 0ms;\n}\n.us-file-action:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-file-action:focus-visible { outline: 2px solid var(--us-primary); outline-offset: 1px; }\n.us-file-action svg { width: 15px; height: 15px; }\n.us-file-action[data-edited=\"true\"] { color: var(--us-accent); }\n.us-file:not([data-state=\"queued\"]) .us-file-action { display: none; }\n\n/* ──────────────────── image editor overlay ──────────────────── */\n.us-editor {\n position: absolute; inset: 0; z-index: 2;\n display: flex; flex-direction: column;\n background: var(--us-bg);\n animation: us-fade 140ms ease-out;\n}\n.us-editor-header {\n display: flex; align-items: center; gap: 12px;\n padding: 14px 16px;\n border-bottom: 1px solid var(--us-border);\n}\n.us-editor-title { font-weight: 600; font-size: 15px; flex: 1; letter-spacing: -0.02em; }\n.us-editor-back {\n appearance: none; background: transparent; border: 0; cursor: pointer;\n width: 32px; height: 32px; border-radius: 8px;\n display: inline-flex; align-items: center; justify-content: center;\n color: var(--us-muted);\n transition: color 100ms, background 0ms;\n}\n.us-editor-back:hover { background: var(--us-overlay); color: var(--us-fg); }\n.us-editor-back svg { width: 16px; height: 16px; }\n\n.us-editor-canvas-wrap {\n flex: 1; min-height: 0;\n background: var(--us-subtle);\n display: flex; align-items: center; justify-content: center;\n padding: 16px;\n position: relative;\n overflow: hidden;\n}\n.us-editor-canvas {\n max-width: 100%; max-height: 100%;\n border-radius: 4px;\n display: block;\n /* Checkerboard for transparency awareness (visible only in circle mode). */\n background-image:\n linear-gradient(45deg, var(--us-border) 25%, transparent 25%, transparent 75%, var(--us-border) 75%),\n linear-gradient(45deg, var(--us-border) 25%, transparent 25%, transparent 75%, var(--us-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 6px 6px;\n}\n.us-editor-overlay {\n position: absolute; inset: 0;\n pointer-events: none;\n}\n\n.us-editor-toolbar {\n display: flex; gap: 6px; flex-wrap: wrap;\n padding: 10px 16px;\n border-top: 1px solid var(--us-border);\n}\n.us-tool {\n appearance: none; background: transparent; border: 1px solid var(--us-border-strong);\n font: inherit; cursor: pointer;\n padding: 6px 11px; min-height: 32px;\n border-radius: 8px;\n font-size: 12px; font-weight: 500; letter-spacing: 0.02em;\n color: var(--us-fg);\n display: inline-flex; align-items: center; gap: 6px;\n transition: border-color 120ms, color 100ms, background 0ms;\n}\n.us-tool:hover { background: var(--us-overlay); }\n.us-tool[data-active=\"true\"] {\n background: color-mix(in srgb, var(--us-primary) 12%, var(--us-bg));\n border-color: var(--us-primary);\n color: var(--us-primary);\n}\n.us-tool[disabled] { opacity: 0.45; cursor: not-allowed; }\n.us-tool svg { width: 13px; height: 13px; }\n.us-tool-spacer { flex: 1; }\n\n.us-editor-footer {\n display: flex; gap: 8px; justify-content: flex-end;\n padding: 12px 16px;\n border-top: 1px solid var(--us-border);\n}\n\n/* Crop drag handles. Drawn inside .us-editor-canvas-wrap, positioned over canvas. */\n.us-crop-box {\n position: absolute;\n border: 1.5px solid var(--us-primary);\n box-shadow: 0 0 0 9999px rgba(0,0,0,0.45);\n pointer-events: auto;\n cursor: move;\n}\n.us-crop-handle {\n position: absolute;\n width: 12px; height: 12px;\n background: var(--us-bg);\n border: 1.5px solid var(--us-primary);\n border-radius: 2px;\n pointer-events: auto;\n}\n.us-crop-handle[data-pos=\"nw\"] { top: -6px; left: -6px; cursor: nwse-resize; }\n.us-crop-handle[data-pos=\"ne\"] { top: -6px; right: -6px; cursor: nesw-resize; }\n.us-crop-handle[data-pos=\"sw\"] { bottom: -6px; left: -6px; cursor: nesw-resize; }\n.us-crop-handle[data-pos=\"se\"] { bottom: -6px; right: -6px; cursor: nwse-resize; }\n\n/* ──────────────────── mobile (bottom sheet) ──────────────────── */\n@media (max-width: 600px) {\n .us-picker-backdrop {\n padding: 0;\n align-items: flex-end;\n }\n .us-picker {\n max-width: none;\n max-height: calc(100dvh - 40px);\n border-radius: 16px 16px 0 0;\n border-left: 0; border-right: 0; border-bottom: 0;\n animation: us-sheet-up 300ms cubic-bezier(0.32, 0.72, 0, 1);\n }\n /* Grab handle */\n .us-picker::before {\n content: \"\";\n flex-shrink: 0;\n width: 36px; height: 4px;\n border-radius: 999px;\n background: var(--us-border-strong);\n margin: 8px auto 0;\n }\n .us-picker-header { padding: 10px 16px 14px; }\n .us-dropzone { padding: 24px 16px; }\n .us-btn { min-height: 44px; padding: 10px 16px; }\n .us-actions {\n flex-direction: column; align-items: stretch; gap: 10px;\n padding-bottom: max(12px, env(safe-area-inset-bottom));\n }\n .us-actions-summary { text-align: center; order: 2; }\n .us-actions-summary:empty { display: none; }\n .us-actions-buttons { width: 100%; }\n .us-actions-buttons .us-btn { flex: 1; }\n .us-source-tabs { display: flex; width: 100%; }\n .us-source-tab { flex: 1; justify-content: center; min-height: 38px; }\n .us-url-form { flex-direction: column; }\n .us-url-input { min-height: 44px; }\n .us-file { padding: 12px; }\n .us-file-action, .us-picker-close { width: 36px; height: 36px; }\n .us-editor-toolbar { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; }\n .us-tool { flex-shrink: 0; min-height: 40px; }\n .us-editor-footer { padding-bottom: max(12px, env(safe-area-inset-bottom)); }\n .us-editor-footer .us-btn { flex: 1; }\n .us-footer { padding-bottom: max(12px, env(safe-area-inset-bottom)); }\n}\n@keyframes us-sheet-up {\n from { opacity: 0.6; transform: translateY(32px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* ──────────────────── reduced motion ──────────────────── */\n@media (prefers-reduced-motion: reduce) {\n .us-picker-backdrop,\n .us-picker,\n .us-file,\n .us-dropzone,\n .us-dropzone-icon,\n .us-btn,\n .us-file-status,\n .us-file-progress-bar,\n .us-editor,\n .us-source-tab,\n .us-tool { animation: none !important; transition: none !important; }\n .us-file[data-state=\"uploading\"] .us-file-progress-bar::after { animation: none; opacity: 0; }\n}\n\n/* ──────────────────── empty state niceties ──────────────────── */\n.us-file-list:empty { display: none; }\n`;\n","// Lightweight client-side image editor for the picker. Supports rotate (90°),\n// crop with draggable handles, circle (centered square + circular alpha mask),\n// and revert to original. All operations stay in the browser — we hand the\n// edited blob back to the picker, which uploads it like any other file.\n//\n// Design notes:\n// - There are two canvases. The \"working\" canvas is the source of truth in\n// image pixels and gets mutated by each operation (rotate, crop, circle).\n// The \"display\" canvas is what the user sees, sized to fit the editor\n// box; we render the working canvas into it on every draw().\n// - Crop drag math is done entirely in display-canvas pixel coordinates,\n// then scaled to image coords only when committing the crop. This keeps\n// the handle math straightforward (no scale juggling per mouse-move).\n// - Apply exports the working canvas via toBlob() and wraps it in a File so\n// the picker can swap it for the original without any other change. The\n// output mime is PNG when the image has transparency (after a circle\n// op), JPEG otherwise — keeps file size sane for photos.\n\ntype EditorMode = 'none' | 'crop';\n\ninterface Rect { x: number; y: number; w: number; h: number; }\n\nconst MAX_DISPLAY = 720; // px — keeps the working canvas reasonable on huge inputs\n\n// Inline SVG icons. Same Lucide flavor as picker.ts.\nconst ICON = {\n back: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"15 18 9 12 15 6\"/></svg>`,\n crop: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6 2v14a2 2 0 0 0 2 2h14\"/><path d=\"M18 22V8a2 2 0 0 0-2-2H2\"/></svg>`,\n circle: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\"/></svg>`,\n rotate: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 12a9 9 0 1 1-9-9c2.52 0 4.82.93 6.58 2.46L21 8\"/><path d=\"M21 3v5h-5\"/></svg>`,\n undo: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 7v6h6\"/><path d=\"M21 17a9 9 0 0 0-15-6.7L3 13\"/></svg>`,\n check: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`,\n};\n\nexport interface ImageEditorOptions {\n /** Element to mount the editor inside (usually the picker panel). */\n host: HTMLElement;\n /** The source image to load into the editor on open. */\n file: File;\n /**\n * The unedited original. Revert restores to this. Defaults to `file` —\n * pass it explicitly when the user has already applied edits and you want\n * Revert to undo *all* the way back, not just the current session.\n */\n originalFile?: File;\n /** Title shown in the editor header (typically the filename). */\n title: string;\n /** Called with the edited File when the user clicks Apply. */\n onApply: (file: File) => void;\n /** Called when the user backs out without applying. */\n onCancel: () => void;\n}\n\nexport class ImageEditor {\n private root!: HTMLElement;\n private canvasWrap!: HTMLElement;\n private displayCanvas!: HTMLCanvasElement;\n private cropBox: HTMLElement | null = null;\n private cropTool!: HTMLButtonElement;\n private circleTool!: HTMLButtonElement;\n private rotateTool!: HTMLButtonElement;\n private revertTool!: HTMLButtonElement;\n private applyBtn!: HTMLButtonElement;\n\n // Source of truth, mutated by each op. Always in image pixels.\n private working!: HTMLCanvasElement;\n // What's currently rendered to the user, sized to fit MAX_DISPLAY.\n private displayScale = 1;\n // Tracks whether the working canvas has transparency (post-circle), so\n // export() can pick PNG vs JPEG correctly.\n private hasAlpha = false;\n\n private mode: EditorMode = 'none';\n private cropRect: Rect | null = null; // display-canvas coords\n\n // Drag state for crop interaction.\n private dragKind: 'move' | 'nw' | 'ne' | 'sw' | 'se' | null = null;\n private dragStart: { px: number; py: number; rect: Rect } | null = null;\n\n constructor(private opts: ImageEditorOptions) {}\n\n async open(): Promise<void> {\n this.mount();\n try {\n await this.loadOriginalIntoWorking();\n // If the caller passed a distinct originalFile, the loaded image is\n // already edited compared to the true original — enable Revert.\n if (this.opts.originalFile && this.opts.originalFile !== this.opts.file) {\n this.markEdited(true);\n }\n this.draw();\n } catch (err) {\n // Image failed to decode — surface it in the canvas wrap and let the\n // user back out. Don't throw, the picker is still alive.\n this.canvasWrap.textContent = `Couldn't load image: ${(err as Error).message}`;\n }\n }\n\n close(): void {\n document.removeEventListener('pointermove', this.onPointerMove);\n document.removeEventListener('pointerup', this.onPointerUp);\n this.root.remove();\n }\n\n // ---- mount / dom --------------------------------------------------------\n\n private mount(): void {\n this.root = el('div', 'us-editor');\n\n const header = el('div', 'us-editor-header');\n const back = document.createElement('button');\n back.type = 'button';\n back.className = 'us-editor-back';\n back.setAttribute('aria-label', 'Back');\n back.innerHTML = ICON.back;\n back.onclick = () => { this.opts.onCancel(); this.close(); };\n header.appendChild(back);\n header.appendChild(el('div', 'us-editor-title', this.opts.title));\n this.root.appendChild(header);\n\n this.canvasWrap = el('div', 'us-editor-canvas-wrap');\n this.displayCanvas = document.createElement('canvas');\n this.displayCanvas.className = 'us-editor-canvas';\n this.canvasWrap.appendChild(this.displayCanvas);\n this.root.appendChild(this.canvasWrap);\n\n // Toolbar\n const toolbar = el('div', 'us-editor-toolbar');\n this.cropTool = makeTool('Crop', ICON.crop);\n this.cropTool.onclick = () => this.toggleCropMode();\n this.circleTool = makeTool('Circle', ICON.circle);\n this.circleTool.onclick = () => this.applyCircle();\n this.rotateTool = makeTool('Rotate 90°', ICON.rotate);\n this.rotateTool.onclick = () => this.applyRotate();\n this.revertTool = makeTool('Revert', ICON.undo);\n this.revertTool.onclick = () => this.revert();\n this.revertTool.disabled = true; // enabled after first edit\n\n toolbar.appendChild(this.cropTool);\n toolbar.appendChild(this.circleTool);\n toolbar.appendChild(this.rotateTool);\n toolbar.appendChild(el('div', 'us-tool-spacer'));\n toolbar.appendChild(this.revertTool);\n this.root.appendChild(toolbar);\n\n // Footer (Cancel / Apply)\n const footer = el('div', 'us-editor-footer');\n const cancel = document.createElement('button');\n cancel.type = 'button';\n cancel.className = 'us-btn';\n cancel.textContent = 'Cancel';\n cancel.onclick = () => { this.opts.onCancel(); this.close(); };\n\n this.applyBtn = document.createElement('button');\n this.applyBtn.type = 'button';\n this.applyBtn.className = 'us-btn us-btn-primary';\n this.applyBtn.innerHTML = `${ICON.check} <span>Apply</span>`;\n this.applyBtn.onclick = () => this.applyAndClose();\n\n footer.appendChild(cancel);\n footer.appendChild(this.applyBtn);\n this.root.appendChild(footer);\n\n this.opts.host.appendChild(this.root);\n }\n\n // ---- image loading ------------------------------------------------------\n\n private async loadOriginalIntoWorking(): Promise<void> {\n const img = await loadImage(this.opts.file);\n const c = document.createElement('canvas');\n c.width = img.naturalWidth;\n c.height = img.naturalHeight;\n const ctx = c.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n ctx.drawImage(img, 0, 0);\n this.working = c;\n this.hasAlpha = looksLikePngMime(this.opts.file.type);\n this.markEdited(false);\n }\n\n // For Revert. Loads the true original (passed-in `originalFile`, or `file`\n // if the caller didn't distinguish).\n private async loadTrueOriginalIntoWorking(): Promise<void> {\n const source = this.opts.originalFile ?? this.opts.file;\n const img = await loadImage(source);\n const c = document.createElement('canvas');\n c.width = img.naturalWidth;\n c.height = img.naturalHeight;\n const ctx = c.getContext('2d');\n if (!ctx) throw new Error('Canvas 2D context unavailable');\n ctx.drawImage(img, 0, 0);\n this.working = c;\n this.hasAlpha = looksLikePngMime(source.type);\n this.markEdited(false);\n }\n\n // ---- rendering ----------------------------------------------------------\n\n private draw(): void {\n // Fit the working canvas inside MAX_DISPLAY while preserving aspect.\n const { width: ww, height: wh } = this.working;\n const scale = Math.min(1, MAX_DISPLAY / Math.max(ww, wh));\n const dw = Math.max(1, Math.round(ww * scale));\n const dh = Math.max(1, Math.round(wh * scale));\n this.displayCanvas.width = dw;\n this.displayCanvas.height = dh;\n this.displayScale = scale;\n\n const ctx = this.displayCanvas.getContext('2d');\n if (!ctx) return;\n ctx.clearRect(0, 0, dw, dh);\n ctx.drawImage(this.working, 0, 0, dw, dh);\n\n this.updateCropBox();\n }\n\n // ---- mode: crop ---------------------------------------------------------\n\n private toggleCropMode(): void {\n if (this.mode === 'crop') {\n this.applyCrop();\n } else {\n this.enterCropMode();\n }\n }\n\n private enterCropMode(): void {\n this.mode = 'crop';\n this.cropTool.dataset.active = 'true';\n this.cropTool.innerHTML = `${ICON.check} <span>Apply crop</span>`;\n // Default inset: 10% on each side.\n const dw = this.displayCanvas.width;\n const dh = this.displayCanvas.height;\n const inset = Math.round(Math.min(dw, dh) * 0.1);\n this.cropRect = { x: inset, y: inset, w: dw - inset * 2, h: dh - inset * 2 };\n this.renderCropBox();\n }\n\n private exitCropMode(): void {\n this.mode = 'none';\n this.cropTool.removeAttribute('data-active');\n this.cropTool.innerHTML = `${ICON.crop} <span>Crop</span>`;\n this.cropRect = null;\n if (this.cropBox) { this.cropBox.remove(); this.cropBox = null; }\n }\n\n private renderCropBox(): void {\n if (!this.cropRect) return;\n if (this.cropBox) this.cropBox.remove();\n\n const box = el('div', 'us-crop-box');\n for (const pos of ['nw', 'ne', 'sw', 'se'] as const) {\n const h = el('div', 'us-crop-handle');\n h.dataset.pos = pos;\n h.addEventListener('pointerdown', (e) => this.beginDrag(e as PointerEvent, pos));\n box.appendChild(h);\n }\n box.addEventListener('pointerdown', (e) => {\n const target = e.target as HTMLElement;\n if (target.classList.contains('us-crop-handle')) return; // handled above\n this.beginDrag(e as PointerEvent, 'move');\n });\n\n // Position the box relative to the displayed canvas. The canvas may have\n // empty padding around it inside .us-editor-canvas-wrap (due to flex\n // centering), so we calculate the offset of the canvas inside the wrap.\n this.canvasWrap.appendChild(box);\n this.cropBox = box;\n this.updateCropBox();\n }\n\n private updateCropBox(): void {\n if (!this.cropBox || !this.cropRect) return;\n const canvasRect = this.displayCanvas.getBoundingClientRect();\n const wrapRect = this.canvasWrap.getBoundingClientRect();\n const left = canvasRect.left - wrapRect.left + this.cropRect.x;\n const top = canvasRect.top - wrapRect.top + this.cropRect.y;\n this.cropBox.style.left = `${left}px`;\n this.cropBox.style.top = `${top}px`;\n this.cropBox.style.width = `${this.cropRect.w}px`;\n this.cropBox.style.height = `${this.cropRect.h}px`;\n }\n\n // ---- drag handlers (crop) ----------------------------------------------\n\n private beginDrag(e: PointerEvent, kind: 'move' | 'nw' | 'ne' | 'sw' | 'se'): void {\n if (!this.cropRect) return;\n e.preventDefault();\n this.dragKind = kind;\n this.dragStart = { px: e.clientX, py: e.clientY, rect: { ...this.cropRect } };\n document.addEventListener('pointermove', this.onPointerMove);\n document.addEventListener('pointerup', this.onPointerUp);\n }\n\n private onPointerMove = (e: PointerEvent): void => {\n if (!this.dragKind || !this.dragStart || !this.cropRect) return;\n const dx = e.clientX - this.dragStart.px;\n const dy = e.clientY - this.dragStart.py;\n const start = this.dragStart.rect;\n const maxW = this.displayCanvas.width;\n const maxH = this.displayCanvas.height;\n const min = 24; // px — minimum crop size so handles stay reachable\n\n let { x, y, w, h } = start;\n switch (this.dragKind) {\n case 'move':\n x = clamp(start.x + dx, 0, maxW - start.w);\n y = clamp(start.y + dy, 0, maxH - start.h);\n break;\n case 'nw': {\n const nx = clamp(start.x + dx, 0, start.x + start.w - min);\n const ny = clamp(start.y + dy, 0, start.y + start.h - min);\n w = start.w + (start.x - nx);\n h = start.h + (start.y - ny);\n x = nx; y = ny;\n break;\n }\n case 'ne': {\n const nw = clamp(start.w + dx, min, maxW - start.x);\n const ny = clamp(start.y + dy, 0, start.y + start.h - min);\n h = start.h + (start.y - ny);\n w = nw; y = ny;\n break;\n }\n case 'sw': {\n const nx = clamp(start.x + dx, 0, start.x + start.w - min);\n const nh = clamp(start.h + dy, min, maxH - start.y);\n w = start.w + (start.x - nx);\n h = nh; x = nx;\n break;\n }\n case 'se':\n w = clamp(start.w + dx, min, maxW - start.x);\n h = clamp(start.h + dy, min, maxH - start.y);\n break;\n }\n this.cropRect = { x, y, w, h };\n this.updateCropBox();\n };\n\n private onPointerUp = (): void => {\n this.dragKind = null;\n this.dragStart = null;\n document.removeEventListener('pointermove', this.onPointerMove);\n document.removeEventListener('pointerup', this.onPointerUp);\n };\n\n // ---- operations (mutate working canvas) --------------------------------\n\n private applyCrop(): void {\n if (!this.cropRect) { this.exitCropMode(); return; }\n // Display-coords → image-coords.\n const s = this.displayScale;\n const sx = Math.round(this.cropRect.x / s);\n const sy = Math.round(this.cropRect.y / s);\n const sw = Math.round(this.cropRect.w / s);\n const sh = Math.round(this.cropRect.h / s);\n\n const next = document.createElement('canvas');\n next.width = sw;\n next.height = sh;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.drawImage(this.working, sx, sy, sw, sh, 0, 0, sw, sh);\n this.working = next;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private applyRotate(): void {\n const { width: w, height: h } = this.working;\n const next = document.createElement('canvas');\n next.width = h;\n next.height = w;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.translate(h, 0);\n ctx.rotate(Math.PI / 2);\n ctx.drawImage(this.working, 0, 0);\n this.working = next;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private applyCircle(): void {\n // Center-crop to a square, then mask with a circle. Output is PNG so the\n // transparent corners survive.\n const { width: w, height: h } = this.working;\n const size = Math.min(w, h);\n const sx = Math.round((w - size) / 2);\n const sy = Math.round((h - size) / 2);\n\n const next = document.createElement('canvas');\n next.width = size;\n next.height = size;\n const ctx = next.getContext('2d');\n if (!ctx) return;\n ctx.save();\n ctx.beginPath();\n ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);\n ctx.closePath();\n ctx.clip();\n ctx.drawImage(this.working, sx, sy, size, size, 0, 0, size, size);\n ctx.restore();\n\n this.working = next;\n this.hasAlpha = true;\n this.exitCropMode();\n this.markEdited(true);\n this.draw();\n }\n\n private async revert(): Promise<void> {\n await this.loadTrueOriginalIntoWorking();\n this.exitCropMode();\n this.draw();\n }\n\n private markEdited(edited: boolean): void {\n this.revertTool.disabled = !edited;\n }\n\n // ---- apply --------------------------------------------------------------\n\n private async applyAndClose(): Promise<void> {\n this.applyBtn.disabled = true;\n try {\n const file = await this.exportFile();\n this.opts.onApply(file);\n this.close();\n } catch (err) {\n // Re-enable so the user can retry or cancel. Keep error message terse.\n this.applyBtn.disabled = false;\n console.error('[union-stack] image export failed', err);\n }\n }\n\n private exportFile(): Promise<File> {\n return new Promise((resolve, reject) => {\n const mime = this.hasAlpha ? 'image/png' : 'image/jpeg';\n const quality = mime === 'image/jpeg' ? 0.92 : undefined;\n this.working.toBlob(\n (blob) => {\n if (!blob) return reject(new Error('toBlob returned null'));\n const baseName = this.opts.file.name.replace(/\\.[^.]+$/, '');\n const ext = mime === 'image/png' ? 'png' : 'jpg';\n resolve(new File([blob], `${baseName}-edited.${ext}`, { type: mime }));\n },\n mime,\n quality,\n );\n });\n }\n}\n\n// ---- helpers --------------------------------------------------------------\n\nfunction el(tag: string, className: string, text?: string): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction makeTool(label: string, iconHtml: string): HTMLButtonElement {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'us-tool';\n btn.innerHTML = `${iconHtml} <span>${escapeText(label)}</span>`;\n return btn;\n}\n\nfunction escapeText(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => ({\n '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''',\n })[c] as string);\n}\n\nfunction clamp(v: number, lo: number, hi: number): number {\n return Math.max(lo, Math.min(hi, v));\n}\n\nfunction loadImage(file: File): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const url = URL.createObjectURL(file);\n const img = new Image();\n img.onload = () => { URL.revokeObjectURL(url); resolve(img); };\n img.onerror = () => { URL.revokeObjectURL(url); reject(new Error('decode failed')); };\n img.src = url;\n });\n}\n\nfunction looksLikePngMime(mime: string): boolean {\n return /^image\\/(png|webp|gif)$/.test(mime);\n}\n","import type { UnionStackClient } from '../client.js';\nimport type { PickerConfig, UploadError } from '../types.js';\nimport type {\n PickResponse,\n PickedFile,\n PickerHandle,\n PickerOptions,\n PickerSource,\n UploadedFile,\n} from './types.js';\nimport { ensureStyles, themeToCssVars } from './styles.js';\nimport { ImageEditor } from './imageEditor.js';\n\n// Merge server-managed picker config with runtime options. Runtime options\n// always win — they're the explicit dev escape hatch.\nfunction mergeConfig(server: PickerConfig, runtime: PickerOptions): PickerOptions {\n const merged: PickerOptions = { ...runtime };\n\n // Branding: shallow merge, runtime wins per-field.\n const runtimeBranding = runtime.branding || {};\n merged.branding = {\n logoUrl: runtimeBranding.logoUrl ?? server.branding.logoUrl ?? undefined,\n title: runtimeBranding.title ?? server.branding.title ?? undefined,\n hideFooter: runtimeBranding.hideFooter ?? server.branding.hideFooter,\n // PickerBranding has no footerText today — keep server's value internal.\n };\n\n // Theme: shallow merge.\n const runtimeTheme = runtime.theme || {};\n merged.theme = {\n primary: runtimeTheme.primary ?? server.theme.primary ?? undefined,\n background: runtimeTheme.background ?? server.theme.background ?? undefined,\n foreground: runtimeTheme.foreground ?? server.theme.foreground ?? undefined,\n border: runtimeTheme.border ?? server.theme.border ?? undefined,\n radius: runtimeTheme.radius ?? server.theme.radius ?? undefined,\n mode: runtimeTheme.mode ?? server.theme.mode ?? undefined,\n };\n\n // Constraints inform the dropzone (accept attr, size cap).\n merged.maxFileSize = runtime.maxFileSize ?? server.constraints.maxFileSizeBytes;\n merged.maxFiles = runtime.maxFiles ?? server.constraints.maxFilesPerUpload;\n if (!runtime.accept && server.constraints.allowedMimeTypes?.length) {\n const types = server.constraints.allowedMimeTypes.filter(t => t !== '*/*');\n if (types.length > 0) merged.accept = types.join(',');\n }\n return merged;\n}\n\ntype FileItemState = 'queued' | 'uploading' | 'done' | 'failed' | 'cancelled';\n\ninterface FileItem {\n uploadId: string; // matches PickedFile.uploadId once described\n file: File; // current file (may be edited)\n originalFile: File; // immutable — used by editor Revert\n edited: boolean; // true after image edit; toggles the Edit btn color\n state: FileItemState;\n progress: number; // 0-100\n error?: string;\n uploaded?: UploadedFile;\n objectUrl?: string; // for image thumbnails (revoked on unmount/reswap)\n // DOM refs we mutate on progress.\n $row?: HTMLElement;\n $bar?: HTMLElement;\n $status?: HTMLElement;\n $meta?: HTMLElement;\n $thumb?: HTMLElement;\n $edit?: HTMLButtonElement;\n}\n\nconst DEFAULT_TITLE = 'Upload files';\nconst FOOTER_LINK = 'https://unionstack.link';\n\n// ──────────────────────────────────────────────────────────────────\n// Inline SVG icons. Single source — Lucide-style, 1.5px stroke, 24px\n// viewBox. Keeping them as string templates so the picker bundle has\n// no asset/image dependencies.\n// ──────────────────────────────────────────────────────────────────\nconst ICON = {\n upload: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg>`,\n fileUp: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><path d=\"M12 18v-6\"/><path d=\"m9 15 3-3 3 3\"/></svg>`,\n close: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/></svg>`,\n check: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`,\n alert: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>`,\n spinner: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke-width=\"2\" stroke-linecap=\"round\"><circle cx=\"12\" cy=\"12\" r=\"9\" stroke=\"currentColor\" opacity=\"0.2\"/><path d=\"M21 12a9 9 0 0 0-9-9\" stroke=\"currentColor\"/></svg>`,\n image: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\"/><polyline points=\"21 15 16 10 5 21\"/></svg>`,\n video: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"23 7 16 12 23 17 23 7\"/><rect x=\"1\" y=\"5\" width=\"15\" height=\"14\" rx=\"2\"/></svg>`,\n audio: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 18V5l12-2v13\"/><circle cx=\"6\" cy=\"18\" r=\"3\"/><circle cx=\"18\" cy=\"16\" r=\"3\"/></svg>`,\n pdf: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/><line x1=\"9\" y1=\"13\" x2=\"15\" y2=\"13\"/><line x1=\"9\" y1=\"17\" x2=\"13\" y2=\"17\"/></svg>`,\n archive: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"4\" width=\"20\" height=\"5\" rx=\"2\"/><path d=\"M4 9v9a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9\"/><line x1=\"10\" y1=\"13\" x2=\"14\" y2=\"13\"/></svg>`,\n file: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"/><polyline points=\"14 2 14 8 20 8\"/></svg>`,\n zap: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>`,\n device: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><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\"/></svg>`,\n link: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72\"/><path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72\"/></svg>`,\n pencil: `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/></svg>`,\n};\n\nfunction iconForMime(mime: string): string {\n if (!mime) return ICON.file;\n if (mime.startsWith('image/')) return ICON.image;\n if (mime.startsWith('video/')) return ICON.video;\n if (mime.startsWith('audio/')) return ICON.audio;\n if (mime === 'application/pdf') return ICON.pdf;\n if (\n mime.startsWith('application/zip') ||\n mime.includes('compressed') ||\n mime === 'application/x-tar' ||\n mime === 'application/gzip'\n ) return ICON.archive;\n return ICON.file;\n}\n\n/**\n * Single-shot file picker modal. Built imperatively (no framework) so it can\n * be lazy-loaded by either the vanilla SDK or the React wrapper.\n */\nexport class Picker {\n private $backdrop: HTMLElement | null = null;\n private $panel: HTMLElement | null = null;\n private $list: HTMLElement | null = null;\n private $confirm: HTMLButtonElement | null = null;\n private $cancel: HTMLButtonElement | null = null;\n private $closeBtn: HTMLButtonElement | null = null;\n private $input: HTMLInputElement | null = null;\n\n // Source-tab DOM (Device vs URL). Only created when both sources are enabled.\n private $deviceSource: HTMLElement | null = null;\n private $urlSource: HTMLElement | null = null;\n private $urlInput: HTMLInputElement | null = null;\n private $urlHint: HTMLElement | null = null;\n private $urlAddBtn: HTMLButtonElement | null = null;\n\n private items: FileItem[] = [];\n private editor: ImageEditor | null = null;\n private abortCtrl = new AbortController();\n private uploadStarted = false;\n private resolved = false;\n private resolvePromise!: (r: PickResponse) => void;\n private donePromise: Promise<PickResponse>;\n\n constructor(\n private client: UnionStackClient,\n private opts: PickerOptions,\n ) {\n this.donePromise = new Promise<PickResponse>(res => { this.resolvePromise = res; });\n }\n\n // ---- public api ---------------------------------------------------------\n\n async open(): Promise<PickResponse> {\n if (typeof document === 'undefined') {\n throw new Error('[union-stack] Picker requires a browser environment.');\n }\n // Pull server-side branding/theme defaults before painting the modal.\n // Runtime opts win over server config — see mergeConfig below.\n try {\n const serverConfig = await this.client.pickerConfigPromise;\n if (serverConfig) this.opts = mergeConfig(serverConfig, this.opts);\n } catch { /* fall through with whatever opts the caller passed */ }\n\n ensureStyles();\n this.mount();\n this.opts.onOpen?.();\n return this.donePromise;\n }\n\n close(): void {\n this.unmount();\n // Resolve with whatever was collected so callers awaiting open() never hang.\n if (!this.resolved) this.resolveResult();\n }\n\n cancel(): void {\n this.abortCtrl.abort();\n this.opts.onCancel?.();\n this.close();\n }\n\n // ---- mount / dom --------------------------------------------------------\n\n private $summary: HTMLElement | null = null;\n\n private mount() {\n const root = document.createElement('div');\n root.className = 'us-picker-backdrop';\n Object.entries(themeToCssVars(this.opts.theme)).forEach(([k, v]) => {\n root.style.setProperty(k, v);\n });\n root.addEventListener('click', e => {\n if (e.target === root && !this.uploadStarted) this.cancel();\n });\n\n const panel = el('div', 'us-picker');\n\n // ─── Header ────────────────────────────────────────────────────\n const header = el('div', 'us-picker-header');\n const logoWrap = el('div', 'us-picker-header-logo');\n if (this.opts.branding?.logoUrl) {\n const logo = document.createElement('img');\n logo.src = this.opts.branding.logoUrl;\n logo.alt = '';\n logoWrap.appendChild(logo);\n } else {\n logoWrap.innerHTML = ICON.zap;\n }\n header.appendChild(logoWrap);\n\n const title = el('div', 'us-picker-title', this.opts.branding?.title ?? DEFAULT_TITLE);\n header.appendChild(title);\n\n this.$closeBtn = document.createElement('button');\n this.$closeBtn.type = 'button';\n this.$closeBtn.className = 'us-picker-close';\n this.$closeBtn.setAttribute('aria-label', 'Close');\n this.$closeBtn.innerHTML = ICON.close;\n this.$closeBtn.onclick = () => this.cancel();\n header.appendChild(this.$closeBtn);\n panel.appendChild(header);\n\n // ─── Body ──────────────────────────────────────────────────────\n const body = el('div', 'us-picker-body');\n const sources = this.resolvedSources();\n\n // Tabs only render when there's more than one source. Single-source flows\n // keep the original lean look.\n if (sources.length > 1) {\n body.appendChild(this.renderSourceTabs(sources));\n }\n\n if (sources.includes('device')) {\n this.$deviceSource = this.renderDropzone();\n body.appendChild(this.$deviceSource);\n }\n if (sources.includes('url')) {\n this.$urlSource = this.renderUrlSource();\n body.appendChild(this.$urlSource);\n }\n this.activateSource(sources[0] ?? 'device');\n\n this.$list = el('div', 'us-file-list');\n body.appendChild(this.$list);\n panel.appendChild(body);\n\n // ─── Actions ───────────────────────────────────────────────────\n const autoUpload = this.opts.autoUpload === true;\n const actions = el('div', 'us-actions');\n\n this.$summary = el('div', 'us-actions-summary', '');\n actions.appendChild(this.$summary);\n\n const buttons = el('div', 'us-actions-buttons');\n this.$cancel = document.createElement('button');\n this.$cancel.type = 'button';\n this.$cancel.className = 'us-btn';\n this.$cancel.textContent = 'Cancel';\n this.$cancel.onclick = () => this.cancel();\n buttons.appendChild(this.$cancel);\n\n if (!autoUpload) {\n this.$confirm = document.createElement('button');\n this.$confirm.type = 'button';\n this.$confirm.className = 'us-btn us-btn-primary';\n this.$confirm.innerHTML = `${ICON.upload} <span>Upload</span>`;\n this.$confirm.disabled = true;\n this.$confirm.onclick = () => this.startUpload();\n buttons.appendChild(this.$confirm);\n }\n actions.appendChild(buttons);\n panel.appendChild(actions);\n\n // ─── Footer ────────────────────────────────────────────────────\n if (!this.opts.branding?.hideFooter) {\n const footer = el('div', 'us-footer');\n footer.innerHTML =\n `${ICON.zap} <span>Powered by <a href=\"${FOOTER_LINK}\" target=\"_blank\" rel=\"noopener\">UnionStack</a></span>`;\n panel.appendChild(footer);\n }\n\n root.appendChild(panel);\n (this.opts.container ?? document.body).appendChild(root);\n this.$backdrop = root;\n this.$panel = panel;\n }\n\n // Resolves which sources to show, honoring opts.fromSources, defaulting to\n // both. Empty arrays fall back to ['device'] — we never want to leave a\n // picker with no way to add files.\n private resolvedSources(): PickerSource[] {\n const raw = this.opts.fromSources;\n if (!raw || raw.length === 0) return ['device', 'url'];\n return raw;\n }\n\n private renderSourceTabs(sources: PickerSource[]): HTMLElement {\n const tabs = el('div', 'us-source-tabs');\n tabs.setAttribute('role', 'tablist');\n for (const src of sources) {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'us-source-tab';\n btn.setAttribute('role', 'tab');\n btn.dataset.source = src;\n btn.innerHTML = src === 'device'\n ? `${ICON.device} <span>My Device</span>`\n : `${ICON.link} <span>Link</span>`;\n btn.onclick = () => this.activateSource(src);\n tabs.appendChild(btn);\n }\n return tabs;\n }\n\n private activateSource(source: PickerSource): void {\n if (this.$deviceSource) {\n const active = source === 'device';\n this.$deviceSource.style.display = active ? '' : 'none';\n }\n if (this.$urlSource) {\n this.$urlSource.dataset.active = source === 'url' ? 'true' : 'false';\n }\n // Tab visuals.\n const tabs = this.$panel?.querySelectorAll<HTMLElement>('.us-source-tab');\n tabs?.forEach((t) => {\n t.dataset.active = t.dataset.source === source ? 'true' : 'false';\n });\n }\n\n private renderUrlSource(): HTMLElement {\n const wrap = el('div', 'us-url-source');\n const form = el('div', 'us-url-form');\n\n const input = document.createElement('input');\n input.type = 'url';\n input.className = 'us-url-input';\n input.placeholder = 'https://example.com/photo.jpg';\n input.setAttribute('aria-label', 'File URL');\n input.onkeydown = (e) => {\n if (e.key === 'Enter') { e.preventDefault(); this.handleUrlAdd(); }\n };\n this.$urlInput = input;\n\n const add = document.createElement('button');\n add.type = 'button';\n add.className = 'us-btn us-btn-primary';\n add.textContent = 'Add file';\n add.onclick = () => this.handleUrlAdd();\n this.$urlAddBtn = add;\n\n form.appendChild(input);\n form.appendChild(add);\n wrap.appendChild(form);\n\n const hint = el('div', 'us-url-hint', 'Paste a direct file URL. The host must allow CORS so we can fetch it from the browser.');\n this.$urlHint = hint;\n wrap.appendChild(hint);\n\n return wrap;\n }\n\n private async handleUrlAdd(): Promise<void> {\n if (!this.$urlInput || !this.$urlAddBtn || !this.$urlHint) return;\n const url = this.$urlInput.value.trim();\n if (!url) { this.setUrlHint('Enter a URL first.', true); return; }\n if (!/^https?:\\/\\//i.test(url)) {\n this.setUrlHint('URL must start with http:// or https://', true);\n return;\n }\n this.$urlAddBtn.disabled = true;\n const prevLabel = this.$urlAddBtn.textContent;\n this.$urlAddBtn.textContent = 'Fetching…';\n this.setUrlHint('Downloading…', false);\n try {\n const file = await fetchUrlAsFile(url);\n this.addFiles([file]);\n this.$urlInput.value = '';\n this.setUrlHint('Paste a direct file URL. The host must allow CORS so we can fetch it from the browser.', false);\n } catch (err) {\n const msg = (err as Error).message || 'Failed to fetch URL';\n this.setUrlHint(msg, true);\n } finally {\n this.$urlAddBtn.disabled = false;\n this.$urlAddBtn.textContent = prevLabel || 'Add file';\n }\n }\n\n private setUrlHint(text: string, isError: boolean): void {\n if (!this.$urlHint) return;\n this.$urlHint.textContent = text;\n this.$urlHint.dataset.error = isError ? 'true' : 'false';\n }\n\n private renderDropzone(): HTMLElement {\n const dz = el('div', 'us-dropzone');\n dz.setAttribute('role', 'button');\n dz.setAttribute('tabindex', '0');\n dz.setAttribute('aria-label', 'Drop files here or click to browse');\n\n const icon = el('div', 'us-dropzone-icon');\n icon.innerHTML = ICON.fileUp;\n dz.appendChild(icon);\n\n dz.appendChild(el('div', 'us-dropzone-title', 'Drop files to upload'));\n dz.appendChild(el('div', 'us-dropzone-hint', 'or click to browse from your device'));\n\n // Constraints line — surfaces max file size + accepted types so users\n // know the rules before they pick something that gets rejected.\n const constraintBits: string[] = [];\n if (this.opts.maxFileSize) constraintBits.push(`max ${formatBytes(this.opts.maxFileSize)}`);\n if (this.opts.accept) {\n const types = this.opts.accept\n .split(',')\n .map(s => s.trim())\n .filter(s => s && s !== '*/*')\n .map(s => s.replace('/*', ''));\n if (types.length > 0 && types.length <= 4) {\n constraintBits.push(types.join(' · '));\n }\n }\n if (constraintBits.length > 0) {\n dz.appendChild(el('div', 'us-dropzone-constraints', constraintBits.join(' · ')));\n }\n\n const input = document.createElement('input');\n input.type = 'file';\n input.multiple = (this.opts.maxFiles ?? 10) > 1;\n if (this.opts.accept) input.accept = this.opts.accept;\n input.style.display = 'none';\n input.onchange = () => {\n if (input.files) this.addFiles(Array.from(input.files));\n input.value = '';\n };\n this.$input = input;\n dz.appendChild(input);\n\n dz.onclick = () => input.click();\n dz.onkeydown = e => {\n if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); input.click(); }\n };\n\n dz.addEventListener('dragover', e => {\n e.preventDefault();\n dz.setAttribute('data-drag', 'over');\n });\n dz.addEventListener('dragleave', () => dz.removeAttribute('data-drag'));\n dz.addEventListener('drop', e => {\n e.preventDefault();\n dz.removeAttribute('data-drag');\n const dropped = e.dataTransfer?.files;\n if (dropped) this.addFiles(Array.from(dropped));\n });\n\n return dz;\n }\n\n private unmount() {\n // Close the editor first so its document-level pointer listeners get\n // removed before we drop the DOM tree they were drawing on.\n if (this.editor) { this.editor.close(); this.editor = null; }\n if (this.$backdrop?.parentNode) this.$backdrop.parentNode.removeChild(this.$backdrop);\n this.$backdrop = null;\n this.$panel = null;\n // Release any image-thumbnail object URLs we created.\n for (const item of this.items) {\n if (item.objectUrl) URL.revokeObjectURL(item.objectUrl);\n }\n this.opts.onClose?.();\n }\n\n // ---- file selection -----------------------------------------------------\n\n private addFiles(files: File[]) {\n const cap = this.opts.maxFiles ?? Infinity;\n const remaining = cap - this.items.length;\n if (remaining <= 0) return;\n const chosen = files.slice(0, remaining);\n\n for (const file of chosen) {\n if (this.opts.maxFileSize && file.size > this.opts.maxFileSize) {\n // Skip the file but surface a row in the list so the user sees why.\n const item: FileItem = {\n uploadId: cryptoId(),\n file, originalFile: file, edited: false,\n state: 'failed', progress: 0,\n error: `File exceeds ${formatBytes(this.opts.maxFileSize)} limit`,\n };\n this.items.push(item);\n this.renderItem(item);\n continue;\n }\n\n const item: FileItem = {\n uploadId: cryptoId(),\n file, originalFile: file, edited: false,\n state: 'queued', progress: 0,\n };\n this.items.push(item);\n this.renderItem(item);\n }\n\n this.refreshConfirm();\n\n // Auto-upload: kick off as soon as the selection is complete. Deferred a\n // frame so the just-added rows render before \"uploading…\" status flips on.\n const autoUpload = this.opts.autoUpload === true;\n const hasQueued = this.items.some(i => i.state === 'queued');\n if (autoUpload && hasQueued && !this.uploadStarted) {\n requestAnimationFrame(() => {\n if (!this.uploadStarted) this.startUpload();\n });\n }\n }\n\n private renderItem(item: FileItem) {\n if (!this.$list) return;\n\n const row = el('div', 'us-file');\n row.dataset.state = item.state;\n row.dataset.uploadId = item.uploadId;\n // Stagger entrance so multiple files don't all pop in at once. Cap delay\n // so a 50-file drop doesn't take forever to fully render.\n const idx = Math.min(this.items.length - 1, 8);\n row.style.animationDelay = `${idx * 35}ms`;\n\n // Thumbnail: image preview for image MIMEs, type-specific icon otherwise.\n const thumb = el('div', 'us-file-thumb');\n if (item.file.type.startsWith('image/') && typeof URL !== 'undefined' && URL.createObjectURL) {\n try {\n const objectUrl = URL.createObjectURL(item.file);\n item.objectUrl = objectUrl;\n thumb.style.backgroundImage = `url(\"${objectUrl}\")`;\n thumb.dataset.image = 'true';\n } catch {\n thumb.innerHTML = iconForMime(item.file.type);\n }\n } else {\n thumb.innerHTML = iconForMime(item.file.type);\n }\n row.appendChild(thumb);\n\n // Main column: name + meta (with progress bar tucked under)\n const main = el('div', 'us-file-main');\n\n const row1 = el('div', 'us-file-row1');\n row1.appendChild(el('div', 'us-file-name', item.file.name));\n const meta = el('div', 'us-file-meta', formatBytes(item.file.size));\n row1.appendChild(meta);\n main.appendChild(row1);\n\n const progress = el('div', 'us-file-progress');\n const bar = el('div', 'us-file-progress-bar');\n progress.appendChild(bar);\n main.appendChild(progress);\n\n row.appendChild(main);\n\n // Edit button — only for image files, only while editable is enabled, only\n // when the file is queued (pre-upload). We append BEFORE the status icon so\n // the visual order is name → edit → status.\n const isImage = item.file.type.startsWith('image/');\n const editingEnabled = this.opts.imageEditing !== false;\n if (isImage && editingEnabled && item.state === 'queued') {\n const edit = document.createElement('button');\n edit.type = 'button';\n edit.className = 'us-file-action';\n edit.setAttribute('aria-label', 'Edit image');\n edit.title = 'Edit image';\n edit.innerHTML = ICON.pencil;\n edit.dataset.edited = item.edited ? 'true' : 'false';\n edit.onclick = () => this.openEditor(item);\n row.appendChild(edit);\n item.$edit = edit;\n }\n\n // Status icon (spinner / check / alert)\n const status = el('div', 'us-file-status');\n status.setAttribute('aria-label', this.statusLabel(item));\n status.innerHTML = this.statusIcon(item.state);\n row.appendChild(status);\n\n item.$row = row;\n item.$bar = bar;\n item.$status = status;\n item.$meta = meta;\n item.$thumb = thumb;\n\n this.$list.appendChild(row);\n this.updateSummary();\n }\n\n // Swap a queued item's file with an edited version. Refreshes thumbnail and\n // meta in place so the row identity is preserved (uploadId, position).\n private replaceItemFile(item: FileItem, nextFile: File, edited: boolean): void {\n item.file = nextFile;\n item.edited = edited;\n\n if (item.objectUrl) URL.revokeObjectURL(item.objectUrl);\n item.objectUrl = undefined;\n\n if (item.$thumb) {\n item.$thumb.style.backgroundImage = '';\n item.$thumb.removeAttribute('data-image');\n if (nextFile.type.startsWith('image/') && typeof URL !== 'undefined' && URL.createObjectURL) {\n try {\n const objectUrl = URL.createObjectURL(nextFile);\n item.objectUrl = objectUrl;\n item.$thumb.style.backgroundImage = `url(\"${objectUrl}\")`;\n item.$thumb.dataset.image = 'true';\n item.$thumb.innerHTML = '';\n } catch {\n item.$thumb.innerHTML = iconForMime(nextFile.type);\n }\n } else {\n item.$thumb.innerHTML = iconForMime(nextFile.type);\n }\n }\n if (item.$meta) item.$meta.textContent = formatBytes(nextFile.size);\n if (item.$row) {\n // Filename may have shifted (revert vs edit). Re-query the name node.\n const nameEl = item.$row.querySelector('.us-file-name');\n if (nameEl) nameEl.textContent = nextFile.name;\n }\n if (item.$edit) item.$edit.dataset.edited = edited ? 'true' : 'false';\n }\n\n // Mount the image editor overlay on top of the picker panel for the given\n // queued item. The editor calls back with either an edited File (Apply) or\n // nothing (Cancel/Back).\n private openEditor(item: FileItem): void {\n if (!this.$panel || this.editor || item.state !== 'queued') return;\n this.editor = new ImageEditor({\n host: this.$panel,\n file: item.file,\n originalFile: item.originalFile,\n title: item.originalFile.name,\n onApply: (edited) => {\n // If the applied file is byte-identical to the original (rare — would\n // require a no-op Apply), don't mark as edited.\n const wasEdited = edited !== item.originalFile;\n this.replaceItemFile(item, edited, wasEdited);\n this.editor = null;\n },\n onCancel: () => { this.editor = null; },\n });\n this.editor.open();\n }\n\n private statusIcon(state: FileItemState): string {\n switch (state) {\n case 'uploading': return ICON.spinner;\n case 'done': return ICON.check;\n case 'failed': return ICON.alert;\n case 'cancelled': return ICON.alert;\n default: return ICON.spinner; // queued — also spinner (waiting)\n }\n }\n\n private statusLabel(item: FileItem): string {\n switch (item.state) {\n case 'queued': return 'Waiting to upload';\n case 'uploading': return `Uploading ${Math.round(item.progress)} percent`;\n case 'done': return 'Upload complete';\n case 'failed': return item.error ? `Failed: ${item.error}` : 'Upload failed';\n case 'cancelled': return 'Cancelled';\n }\n }\n\n private setItemState(item: FileItem, state: FileItemState, progress?: number) {\n item.state = state;\n if (progress !== undefined) item.progress = progress;\n if (item.$row) item.$row.dataset.state = state;\n if (item.$bar) item.$bar.style.width = `${item.progress}%`;\n if (item.$status) {\n item.$status.innerHTML = this.statusIcon(state);\n item.$status.setAttribute('aria-label', this.statusLabel(item));\n }\n if (item.$meta) {\n // Augment the meta line with live state — keeps the eye on a single spot\n // for \"what's happening\" without adding another text row.\n const size = formatBytes(item.file.size);\n switch (state) {\n case 'uploading':\n item.$meta.textContent = `${size} · ${Math.round(item.progress)}%`;\n break;\n case 'done':\n item.$meta.textContent = size;\n break;\n case 'failed':\n item.$meta.textContent = item.error || 'Failed';\n break;\n default:\n item.$meta.textContent = size;\n }\n }\n this.updateSummary();\n }\n\n private updateSummary() {\n if (!this.$summary) return;\n const total = this.items.length;\n if (total === 0) { this.$summary.textContent = ''; return; }\n const done = this.items.filter(i => i.state === 'done').length;\n const failed = this.items.filter(i => i.state === 'failed').length;\n const active = this.items.filter(i => i.state === 'uploading' || i.state === 'queued').length;\n if (!this.uploadStarted && active > 0) {\n const ready = this.items.filter(i => i.state === 'queued').length;\n this.$summary.textContent = `${ready} file${ready === 1 ? '' : 's'} ready`;\n } else if (active > 0) {\n this.$summary.textContent = `Uploading ${done + 1} of ${total}`;\n } else if (failed > 0) {\n this.$summary.textContent = `${done} of ${total} uploaded · ${failed} failed`;\n } else if (done === total) {\n this.$summary.textContent = `${total} file${total === 1 ? '' : 's'} uploaded`;\n } else {\n this.$summary.textContent = `${total} file${total === 1 ? '' : 's'} ready`;\n }\n }\n\n private refreshConfirm() {\n if (!this.$confirm) return;\n const queued = this.items.filter(i => i.state === 'queued').length;\n this.$confirm.disabled = queued === 0 || this.uploadStarted;\n }\n\n // ---- upload -------------------------------------------------------------\n\n private async startUpload() {\n if (this.uploadStarted) return;\n const queued = this.items.filter(i => i.state === 'queued');\n if (queued.length === 0) return;\n\n this.uploadStarted = true;\n if (this.$confirm) {\n this.$confirm.disabled = true;\n this.$confirm.textContent = 'Uploading…';\n }\n if (this.$cancel) this.$cancel.textContent = 'Stop';\n if (this.$closeBtn) this.$closeBtn.disabled = true;\n if (this.$input) this.$input.disabled = true;\n\n // Map PickedFile.uploadId → FileItem so callbacks update the right row.\n const itemByUploadId = new Map<string, FileItem>();\n const filesToUpload = queued.map(i => i.file);\n\n try {\n await this.client.uploadMany(filesToUpload, {\n ...this.opts,\n signal: this.abortCtrl.signal,\n onUploadStarted: (pickedFiles: PickedFile[]) => {\n // Pair each PickedFile to the queued FileItem in order.\n pickedFiles.forEach((p, idx) => {\n const item = queued[idx];\n if (item) {\n item.uploadId = p.uploadId;\n if (item.$row) item.$row.dataset.uploadId = p.uploadId;\n itemByUploadId.set(p.uploadId, item);\n }\n });\n this.opts.onUploadStarted?.(pickedFiles);\n },\n onFileUploadStarted: p => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', 0);\n this.opts.onFileUploadStarted?.(p);\n },\n onFileUploadProgress: (p, ev) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) this.setItemState(item, 'uploading', ev.totalPercent);\n this.opts.onFileUploadProgress?.(p, ev);\n },\n onFileUploadFinished: f => {\n const item = itemByUploadId.get(f.uploadId);\n if (item) {\n item.uploaded = f;\n this.setItemState(item, 'done', 100);\n }\n this.opts.onFileUploadFinished?.(f);\n },\n onFileUploadFailed: (p, err) => {\n const item = itemByUploadId.get(p.uploadId);\n if (item) {\n item.error = err.message;\n this.setItemState(item, 'failed');\n }\n this.opts.onFileUploadFailed?.(p, err);\n },\n onUploadDone: r => {\n this.opts.onUploadDone?.(r);\n this.resolveResult(r);\n this.unmount();\n },\n onError: (err: UploadError) => {\n this.opts.onError?.(err);\n this.resolveResult();\n this.unmount();\n },\n });\n } catch {\n // Errors already surfaced via onError / onFileUploadFailed.\n if (!this.resolved) {\n this.resolveResult();\n this.unmount();\n }\n }\n }\n\n private resolveResult(result?: PickResponse) {\n if (this.resolved) return;\n this.resolved = true;\n const fallback: PickResponse = result ?? {\n filesUploaded: this.items.filter(i => i.uploaded).map(i => i.uploaded!),\n filesFailed: this.items\n .filter(i => i.state === 'failed')\n .map(i => ({\n file: {\n uploadId: i.uploadId,\n filename: i.file.name,\n mimetype: i.file.type || 'application/octet-stream',\n size: i.file.size,\n source: 'local',\n },\n error: {\n code: 'VALIDATION',\n message: i.error || 'failed',\n retryable: false,\n },\n })),\n };\n this.resolvePromise(fallback);\n }\n}\n\n// ---- helpers --------------------------------------------------------------\n\nfunction el(tag: string, className: string, text?: string): HTMLElement {\n const node = document.createElement(tag);\n if (className) node.className = className;\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction formatBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;\n return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;\n}\n\nfunction cryptoId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\n// Fetch a remote URL as a File. The host MUST send CORS headers — there's no\n// way around that from a browser-only SDK. We surface a clear message in the\n// dropzone hint when the fetch fails so the user understands why.\nasync function fetchUrlAsFile(url: string): Promise<File> {\n let res: Response;\n try {\n res = await fetch(url, { mode: 'cors', credentials: 'omit' });\n } catch {\n throw new Error(`Could not reach ${shortHost(url)} — check the URL or CORS.`);\n }\n if (!res.ok) throw new Error(`Server returned ${res.status} — file unavailable.`);\n const blob = await res.blob();\n const filename = filenameFromUrl(url) || 'download';\n const type = blob.type || guessMimeFromExt(filename);\n return new File([blob], filename, { type });\n}\n\nfunction shortHost(url: string): string {\n try { return new URL(url).host; } catch { return 'that host'; }\n}\n\nfunction filenameFromUrl(url: string): string {\n try {\n const u = new URL(url);\n const last = u.pathname.split('/').filter(Boolean).pop();\n return last ? decodeURIComponent(last) : '';\n } catch {\n return '';\n }\n}\n\nfunction guessMimeFromExt(filename: string): string {\n const ext = filename.toLowerCase().split('.').pop() || '';\n const map: Record<string, string> = {\n jpg: 'image/jpeg', jpeg: 'image/jpeg', png: 'image/png',\n gif: 'image/gif', webp: 'image/webp', svg: 'image/svg+xml',\n pdf: 'application/pdf', mp4: 'video/mp4', mov: 'video/quicktime',\n mp3: 'audio/mpeg', wav: 'audio/wav', zip: 'application/zip',\n json: 'application/json', txt: 'text/plain', csv: 'text/csv',\n };\n return map[ext] || 'application/octet-stream';\n}\n\nexport function openPicker(client: UnionStackClient, opts: PickerOptions): PickerHandle {\n const picker = new Picker(client, opts);\n return {\n open: () => picker.open(),\n close: () => picker.close(),\n cancel: () => picker.cancel(),\n };\n}\n","export { Picker, openPicker } from './picker.js';\nexport type {\n PickerOptions,\n PickerHandle,\n PickerTheme,\n PickerBranding,\n} from './types.js';\n","// IIFE loader entry — bundled as a single drop-in <script>.\n//\n// <script src=\"https://sdk.unionstack.link/v1/loader.js\"></script>\n// <script>\n// const client = UnionStack.init({ apiKey: 'unionstack_live_…', apiBase: '…' });\n// client.picker({ onUploadDone: r => console.log(r) }).open();\n// </script>\n//\n// Everything (core + picker UI) ships in this single file. The /react entry\n// is intentionally not included here — React users install from npm.\n\nimport { UnionStack, UnionStackClient } from '../index.js';\nimport { Picker, openPicker } from '../picker/index.js';\nimport type { ClientConfig } from '../types.js';\n\n// tsup's IIFE wrapper attaches every named export as a property on the\n// global `UnionStack`. To make `UnionStack.init({...})` work directly (rather\n// than `UnionStack.UnionStack.init(...)`), we flatten `init` to a top-level\n// export and keep the constructor classes available for advanced users.\nconst init = UnionStack.init.bind(UnionStack);\nexport { init, UnionStackClient, Picker, openPicker };\n\nexport type { ClientConfig };\n\n// Auto-init via data attribute — wire the loader without any inline script:\n//\n// <script src=\".../loader.js\" data-unionstack-key=\"unionstack_live_…\"></script>\n//\n// The resulting client lands on `window.UnionStack.client`.\ndeclare global {\n interface Window {\n UnionStack?: {\n init: typeof UnionStack.init;\n client?: UnionStackClient;\n };\n }\n}\n\n(function autoInit() {\n if (typeof document === 'undefined') return;\n const script = document.currentScript as HTMLScriptElement | null;\n if (!script) return;\n const apiKey = script.getAttribute('data-unionstack-key');\n if (!apiKey) return;\n try {\n const client = UnionStack.init({ apiKey });\n queueMicrotask(() => {\n if (window.UnionStack) window.UnionStack.client = client;\n });\n } catch (err) {\n console.error('[UnionStack] auto-init failed:', err);\n }\n})();\n","import type { UploadError, UploadErrorCode } from './types.js';\n\nexport function makeError(\n code: UploadErrorCode,\n message: string,\n opts: { status?: number; retryable?: boolean; cause?: unknown } = {}\n): UploadError {\n return {\n code,\n message,\n status: opts.status,\n retryable: opts.retryable ?? defaultRetryable(code, opts.status),\n cause: opts.cause,\n };\n}\n\nfunction defaultRetryable(code: UploadErrorCode, status?: number): boolean {\n if (code === 'NETWORK' || code === 'PART_FAILED') return true;\n if (code === 'SERVER' && status && status >= 500) return true;\n return false;\n}\n\n/** Map an HTTP error response from the cdn-be API to an UploadError. */\nexport function fromApiResponse(status: number, body: unknown): UploadError {\n const err = (body as { error?: { code?: string; message?: string } } | null)?.error;\n const msg = err?.message || `Request failed with status ${status}`;\n const code = (err?.code || '').toUpperCase();\n\n if (status === 401) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 403) {\n return makeError('AUTH', msg, { status, retryable: false });\n }\n if (status === 413) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 415) {\n return makeError('VALIDATION', msg, { status, retryable: false });\n }\n if (status === 426) {\n // Server-enforced minimum SDK version. Not retryable — the integrator\n // must upgrade the package (or reload, for script-loader users).\n return makeError('CONFIG', msg, { status, retryable: false });\n }\n if (status === 429) {\n const isQuota = code === 'QUOTA_EXCEEDED';\n return makeError(isQuota ? 'QUOTA' : 'NETWORK', msg, { status, retryable: !isQuota });\n }\n if (status >= 500) {\n return makeError('SERVER', msg, { status, retryable: true });\n }\n return makeError('SERVER', msg, { status, retryable: false });\n}\n","// __SDK_VERSION__ is replaced at build time by tsup's `define` with the\n// package.json version. The typeof guard keeps tsc/tests working unreplaced.\ndeclare const __SDK_VERSION__: string | undefined;\n\nexport const SDK_VERSION: string =\n typeof __SDK_VERSION__ === 'string' ? __SDK_VERSION__ : '0.0.0-dev';\n\n/** Wire format sent to cdn-be on every API call, e.g. \"js/0.1.9\". */\nexport const SDK_VERSION_HEADER = `js/${SDK_VERSION}`;\n","import type {\n ResolvedClientConfig,\n PickedFile,\n PickerConfig,\n UploadedFile,\n UploadOptions,\n ProgressEvent as USProgressEvent,\n} from './types.js';\nimport { fromApiResponse, makeError } from './errors.js';\nimport { SDK_VERSION_HEADER } from './version.js';\n\ninterface InitResponse {\n sessionId: string;\n fileId: string;\n chunkSize: number;\n totalParts: number;\n partUrls: Array<{ partNumber: number; url: string }>;\n expiresAt: string;\n}\n\ninterface CompleteResponse {\n handle: string;\n fileId: string;\n url: string;\n filename: string;\n mimetype: string;\n size: number;\n key: string;\n container: string;\n status: 'Stored';\n etag?: string;\n metadata?: Record<string, unknown>;\n}\n\ninterface PartResult {\n partNumber: number;\n etag: string;\n size: number;\n}\n\nconst DEFAULT_CONCURRENCY = 3;\nconst DEFAULT_MAX_RETRIES_PER_PART = 3;\n// Backoff schedule between part retries. Capped — past this we surrender.\nconst BACKOFF_MS = [400, 1200, 3600];\n\nexport class Uploader {\n constructor(private cfg: ResolvedClientConfig) {}\n\n /** Returns a stable PickedFile descriptor for the input blob/file. */\n describe(file: File | Blob, opts: { filename?: string; mimeType?: string } = {}): PickedFile {\n const isFile = typeof File !== 'undefined' && file instanceof File;\n const filename =\n opts.filename ||\n (isFile ? (file as File).name : 'untitled');\n const mimetype =\n opts.mimeType || (file as Blob).type || 'application/octet-stream';\n return {\n uploadId: cryptoRandomId(),\n filename,\n mimetype,\n size: file.size,\n source: 'local',\n };\n }\n\n async upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n const picked = this.describe(file, { filename: opts.filename, mimeType: opts.mimeType });\n opts.onFileUploadStarted?.(picked);\n\n try {\n const init = await this.initUpload(picked, opts);\n const parts = await this.uploadAllParts(file, init, picked, opts);\n const completed = await this.completeUpload(init.sessionId, parts, opts);\n\n const uploaded: UploadedFile = {\n ...picked,\n handle: completed.handle,\n fileId: completed.fileId,\n url: completed.url,\n size: completed.size,\n mimetype: completed.mimetype,\n filename: completed.filename,\n key: completed.key,\n container: completed.container,\n status: 'Stored',\n etag: completed.etag,\n metadata: completed.metadata,\n };\n opts.onFileUploadFinished?.(uploaded);\n return uploaded;\n } catch (rawErr) {\n const err = normalizeError(rawErr);\n opts.onFileUploadFailed?.(picked, err);\n // Best effort: tell the server to clean up the multipart, but ignore failures.\n if ((rawErr as { sessionId?: string })?.sessionId) {\n this.abortSilently((rawErr as { sessionId: string }).sessionId).catch(() => {});\n }\n throw err;\n }\n }\n\n /** Cancel an in-flight session server-side. */\n async abort(sessionId: string): Promise<void> {\n await this.api('POST', '/sdk/v1/uploads/abort', { sessionId });\n }\n\n /** Fetch the server-managed picker config (branding, theme, constraints). */\n async fetchPickerConfig(): Promise<PickerConfig> {\n return this.api<PickerConfig>('GET', '/sdk/v1/picker-config');\n }\n\n private async initUpload(picked: PickedFile, opts: UploadOptions): Promise<InitResponse> {\n return this.api<InitResponse>('POST', '/sdk/v1/uploads/init', {\n filename: picked.filename,\n mimeType: picked.mimetype,\n size: picked.size,\n metadata: opts.metadata,\n }, opts.signal);\n }\n\n private async uploadAllParts(\n file: File | Blob,\n init: InitResponse,\n picked: PickedFile,\n opts: UploadOptions,\n ): Promise<PartResult[]> {\n const totalParts = init.totalParts;\n const chunkSize = init.chunkSize;\n const concurrency = Math.max(1, opts.concurrency ?? DEFAULT_CONCURRENCY);\n const maxRetries = opts.maxRetriesPerPart ?? DEFAULT_MAX_RETRIES_PER_PART;\n\n // Quick lookup: partNumber → presigned URL.\n const urlByPart = new Map(init.partUrls.map(p => [p.partNumber, p.url] as const));\n\n const results: PartResult[] = new Array(totalParts);\n const loadedPerPart: number[] = new Array(totalParts).fill(0);\n let cursor = 1;\n\n const totalBytes = file.size;\n\n const reportProgress = () => {\n const loaded = loadedPerPart.reduce((a, b) => a + b, 0);\n const totalPercent = totalBytes > 0 ? Math.min(100, Math.round((loaded / totalBytes) * 100)) : 100;\n const ev: USProgressEvent = { totalBytes, loaded, totalPercent };\n opts.onFileUploadProgress?.(picked, ev);\n };\n\n const worker = async (): Promise<void> => {\n while (true) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n const partNumber = cursor++;\n if (partNumber > totalParts) return;\n\n const start = (partNumber - 1) * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n const chunk = file.slice(start, end);\n\n let attempt = 0;\n let lastErr: unknown;\n while (attempt <= maxRetries) {\n if (opts.signal?.aborted) {\n throw makeError('ABORTED', 'Upload aborted by caller.', { retryable: false });\n }\n try {\n let url = urlByPart.get(partNumber);\n if (!url) {\n const refreshed = await this.api<{ url: string }>(\n 'POST', '/sdk/v1/uploads/sign-part',\n { sessionId: init.sessionId, partNumber }, opts.signal,\n );\n url = refreshed.url;\n urlByPart.set(partNumber, url);\n }\n\n const res = await this.cfg.fetch?.(url, {\n method: 'PUT',\n body: chunk,\n signal: opts.signal,\n }) ?? await fetch(url, { method: 'PUT', body: chunk, signal: opts.signal });\n\n if (!res.ok) {\n // If R2 rejects with 403 the URL likely expired — drop it so the\n // next attempt re-signs.\n if (res.status === 403 || res.status === 401) urlByPart.delete(partNumber);\n throw makeError('PART_FAILED', `Part ${partNumber} PUT failed (HTTP ${res.status})`, { status: res.status });\n }\n const etag = res.headers.get('etag');\n if (!etag) {\n throw makeError('PART_FAILED', `Part ${partNumber}: R2 did not return an ETag.`);\n }\n\n results[partNumber - 1] = { partNumber, etag, size: chunk.size };\n loadedPerPart[partNumber - 1] = chunk.size;\n reportProgress();\n break;\n } catch (err) {\n lastErr = err;\n attempt++;\n if (attempt > maxRetries) break;\n await sleep(BACKOFF_MS[Math.min(attempt - 1, BACKOFF_MS.length - 1)]);\n }\n }\n if (!results[partNumber - 1]) {\n throw withSessionId(\n normalizeError(lastErr ?? makeError('PART_FAILED', `Part ${partNumber} failed after ${maxRetries} retries.`)),\n init.sessionId,\n );\n }\n }\n };\n\n const workers = Array.from({ length: Math.min(concurrency, totalParts) }, worker);\n await Promise.all(workers);\n return results;\n }\n\n private async completeUpload(\n sessionId: string,\n parts: PartResult[],\n opts: UploadOptions,\n ): Promise<CompleteResponse> {\n return this.api<CompleteResponse>('POST', '/sdk/v1/uploads/complete', {\n sessionId,\n parts: parts.map(p => ({ partNumber: p.partNumber, etag: p.etag })),\n }, opts.signal);\n }\n\n private async abortSilently(sessionId: string): Promise<void> {\n try { await this.abort(sessionId); } catch { /* ignore */ }\n }\n\n private async api<T>(\n method: 'GET' | 'POST' | 'DELETE',\n path: string,\n body?: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const fetchImpl = this.cfg.fetch ?? fetch;\n const url = `${this.cfg.apiBase}${path}`;\n let res: Response;\n try {\n res = await fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.cfg.apiKey}`,\n 'Content-Type': 'application/json',\n 'X-UnionStack-SDK': SDK_VERSION_HEADER,\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal,\n });\n } catch (cause) {\n if ((cause as Error)?.name === 'AbortError') {\n throw makeError('ABORTED', 'Request aborted.', { retryable: false, cause });\n }\n throw makeError('NETWORK', 'Network request failed.', { retryable: true, cause });\n }\n let parsed: unknown = null;\n try { parsed = await res.json(); } catch { /* tolerate empty body */ }\n if (!res.ok) throw fromApiResponse(res.status, parsed);\n return parsed as T;\n }\n}\n\nfunction cryptoRandomId(): string {\n // Cheap unique id for client-side uploadId. Not security-sensitive.\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {\n return (crypto as { randomUUID: () => string }).randomUUID().replace(/-/g, '');\n }\n return Math.random().toString(36).slice(2) + Date.now().toString(36);\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(r => setTimeout(r, ms));\n}\n\nfunction normalizeError(err: unknown): ReturnType<typeof makeError> {\n if (err && typeof err === 'object' && 'code' in err && 'message' in err && 'retryable' in err) {\n return err as ReturnType<typeof makeError>;\n }\n const msg = (err as Error)?.message || 'Upload failed.';\n return makeError('NETWORK', msg, { cause: err });\n}\n\nfunction withSessionId<T>(err: T, sessionId: string): T {\n if (err && typeof err === 'object') (err as { sessionId?: string }).sessionId = sessionId;\n return err;\n}\n","import type {\n ClientConfig,\n ResolvedClientConfig,\n UploadOptions,\n BatchUploadOptions,\n UploadedFile,\n PickResponse,\n PickedFile,\n PickerConfig,\n UploadError,\n} from './types.js';\nimport { Uploader } from './uploader.js';\nimport { makeError } from './errors.js';\n\n/**\n * UnionStack API URL baked into the SDK. Consumers never see or set this —\n * we ship a new SDK version if the URL ever changes.\n *\n * For local development against a staging backend, change this constant in\n * src/ and rebuild — no consumer-facing override exists by design.\n */\nconst API_BASE = 'https://api.unionstack.link/v1';\n\n// Forward-declared minimal handle so client.ts doesn't pull in the picker bundle.\nexport interface PickerHandleLike {\n open(): Promise<PickResponse>;\n close(): void;\n cancel(): void;\n}\n\nconst PRELOADER_STYLE_ID = 'unionstack-preloader-styles';\n\n// Instant spinner shown between picker.open() and the modal actually mounting\n// (covers the lazy chunk download + the server config fetch). Self-contained —\n// must not depend on the picker bundle, which is what we're waiting for. The\n// backdrop matches the picker's exactly so the handoff is seamless.\nfunction showPickerPreloader(): () => void {\n if (typeof document === 'undefined') return () => {};\n if (!document.getElementById(PRELOADER_STYLE_ID)) {\n const style = document.createElement('style');\n style.id = PRELOADER_STYLE_ID;\n style.textContent =\n '@keyframes us-preload-spin{to{transform:rotate(360deg)}}' +\n '@keyframes us-preload-fade{from{opacity:0}to{opacity:1}}';\n document.head.appendChild(style);\n }\n const backdrop = document.createElement('div');\n backdrop.setAttribute('role', 'status');\n backdrop.setAttribute('aria-label', 'Loading file picker');\n backdrop.style.cssText =\n 'position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;' +\n 'background:rgba(2,6,23,0.4);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);' +\n 'animation:us-preload-fade 140ms ease-out;';\n const spinner = document.createElement('div');\n spinner.style.cssText =\n 'width:36px;height:36px;border-radius:50%;' +\n 'border:3px solid rgba(220,225,251,0.25);border-top-color:#c0c1ff;' +\n 'animation:us-preload-spin 0.8s linear infinite;';\n backdrop.appendChild(spinner);\n document.body.appendChild(backdrop);\n let removed = false;\n return () => {\n if (removed) return;\n removed = true;\n backdrop.remove();\n };\n}\n\nexport class UnionStackClient {\n private uploader: Uploader;\n private resolvedCfg: ResolvedClientConfig;\n /**\n * Promise that resolves to the server-managed picker config. Pre-fetched on\n * construction (unless `skipConfigPrefetch: true`) so the picker opens\n * without a network hit. Fails silently — picker falls back to defaults.\n */\n pickerConfigPromise: Promise<PickerConfig | null>;\n\n constructor(cfg: ClientConfig) {\n // Don't throw on a missing apiKey: the React provider constructs the\n // client during render, and a render-time throw crashes the host app.\n // Log instead, and let every operation reject with a CONFIG error.\n if (!cfg.apiKey) {\n console.error(\n '[UnionStack] apiKey is not set. Uploads and the picker are disabled until a valid apiKey is provided.',\n );\n }\n this.resolvedCfg = { ...cfg, apiBase: API_BASE };\n this.uploader = new Uploader(this.resolvedCfg);\n this.pickerConfigPromise = (!cfg.apiKey || this.resolvedCfg.skipConfigPrefetch)\n ? Promise.resolve(null)\n : this.uploader.fetchPickerConfig()\n .then(cfg => {\n if (cfg?.sdkWarning) console.warn(`[UnionStack] ${cfg.sdkWarning}`);\n return cfg;\n })\n .catch(() => null);\n }\n\n private missingKeyError(): UploadError | null {\n if (this.resolvedCfg.apiKey) return null;\n return makeError('CONFIG', 'apiKey is required.', { retryable: false });\n }\n\n upload(file: File | Blob, opts: UploadOptions = {}): Promise<UploadedFile> {\n const keyErr = this.missingKeyError();\n if (keyErr) return Promise.reject(keyErr);\n return this.uploader.upload(file, opts);\n }\n\n async uploadMany(\n files: (File | Blob)[],\n opts: BatchUploadOptions = {},\n ): Promise<PickResponse> {\n const keyErr = this.missingKeyError();\n if (keyErr) {\n opts.onError?.(keyErr);\n throw keyErr;\n }\n if (!Array.isArray(files) || files.length === 0) {\n const err = makeError('VALIDATION', 'uploadMany requires a non-empty array of files.', { retryable: false });\n opts.onError?.(err);\n throw err;\n }\n\n const picked: PickedFile[] = files.map(f =>\n this.uploader.describe(f, { filename: opts.filename, mimeType: opts.mimeType })\n );\n opts.onUploadStarted?.(picked);\n\n const filesUploaded: UploadedFile[] = [];\n const filesFailed: Array<{ file: PickedFile; error: UploadError }> = [];\n\n await Promise.all(files.map(async (f, i) => {\n try {\n const uploaded = await this.uploader.upload(f, opts);\n // Sync the uploadId so callers can correlate picked → uploaded.\n uploaded.uploadId = picked[i].uploadId;\n filesUploaded.push(uploaded);\n } catch (err) {\n filesFailed.push({ file: picked[i], error: err as UploadError });\n }\n }));\n\n const result: PickResponse = { filesUploaded, filesFailed };\n opts.onUploadDone?.(result);\n return result;\n }\n \n picker(opts: import('./picker/types.js').PickerOptions = {}): PickerHandleLike {\n let real: PickerHandleLike | null = null;\n let opened = false;\n let pendingResolve!: (r: PickResponse) => void;\n let pendingReject!: (e: UploadError) => void;\n const donePromise = new Promise<PickResponse>((res, rej) => {\n pendingResolve = res; pendingReject = rej;\n });\n\n return {\n open: async () => {\n if (opened) return donePromise;\n const keyErr = this.missingKeyError();\n if (keyErr) {\n pendingReject(keyErr);\n throw keyErr;\n }\n opened = true;\n const hidePreloader = showPickerPreloader();\n try {\n const mod = await import('./picker/index.js');\n real = mod.openPicker(this, {\n ...opts,\n // Picker fires onOpen right after the modal mounts — that's the\n // moment the preloader hands off to the real UI.\n onOpen: () => { hidePreloader(); opts.onOpen?.(); },\n });\n const result = await real.open();\n hidePreloader();\n pendingResolve(result);\n return result;\n } catch (err) {\n hidePreloader();\n const e = (err as UploadError)?.code\n ? (err as UploadError)\n : makeError('CONFIG', 'Failed to load picker.', { retryable: false, cause: err });\n pendingReject(e);\n throw e;\n }\n },\n close: () => { real?.close(); },\n cancel: () => { real?.cancel(); },\n };\n }\n\n /** Read-only accessor used by the React/picker packages. */\n get config(): Readonly<ResolvedClientConfig> { return this.resolvedCfg; }\n}\n","import { UnionStackClient } from './client.js';\nimport type { ClientConfig } from './types.js';\n\nexport const UnionStack = {\n init(cfg: ClientConfig): UnionStackClient {\n return new UnionStackClient(cfg);\n },\n};\n\nexport { UnionStackClient } from './client.js';\nexport type {\n ClientConfig,\n UploadOptions,\n BatchUploadOptions,\n PickedFile,\n UploadedFile,\n ProgressEvent,\n PickResponse,\n UploadError,\n UploadErrorCode,\n Source,\n} from './types.js';\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["ensureFonts","FONT_ID","preconnect","href","cross","l","link","FONT_HREF","ensureStyles","STYLE_ID","el","BASE_CSS","themeToCssVars","theme","_a","_b","_c","_d","_e","defaults","DARK_DEFAULTS","LIGHT_DEFAULTS","init_styles","__esmMin","el","tag","className","text","node","makeTool","label","iconHtml","btn","escapeText","s","c","clamp","v","lo","hi","loadImage","file","resolve","reject","url","img","looksLikePngMime","mime","ICON","ImageEditor","init_imageEditor","__esmMin","opts","dx","dy","start","maxW","maxH","min","x","y","w","h","nx","ny","nw","nh","err","header","back","toolbar","footer","cancel","ctx","_a","source","ww","wh","scale","dw","dh","inset","box","pos","e","canvasRect","wrapRect","left","top","kind","__spreadValues","sx","sy","sw","sh","next","size","edited","quality","blob","baseName","ext","mergeConfig","server","runtime","_a","_b","_c","_d","_e","_f","_g","_h","_i","_j","_k","_l","_m","_n","_o","_p","_q","_r","_s","_t","merged","__spreadValues","runtimeBranding","runtimeTheme","types","t","iconForMime","mime","ICON","el","tag","className","text","node","formatBytes","n","cryptoId","fetchUrlAsFile","url","res","e","shortHost","blob","filename","filenameFromUrl","type","guessMimeFromExt","last","ext","openPicker","client","opts","picker","Picker","DEFAULT_TITLE","FOOTER_LINK","init_picker","__esmMin","init_styles","init_imageEditor","serverConfig","ensureStyles","root","themeToCssVars","k","v","panel","header","logoWrap","logo","title","body","sources","autoUpload","actions","buttons","footer","raw","tabs","src","btn","source","active","wrap","form","input","add","hint","prevLabel","file","err","msg","isError","dz","icon","constraintBits","dropped","item","files","remaining","chosen","hasQueued","i","row","idx","thumb","objectUrl","main","row1","meta","progress","bar","isImage","editingEnabled","edit","status","nextFile","edited","nameEl","ImageEditor","wasEdited","state","size","total","done","failed","ready","queued","itemByUploadId","filesToUpload","__spreadProps","pickedFiles","p","ev","f","result","fallback","picker_exports","__export","Picker","openPicker","init_picker","__esmMin","loader_exports","__export","Picker","UnionStackClient","init","openPicker","makeError","code","message","opts","_a","defaultRetryable","status","fromApiResponse","body","err","msg","isQuota","SDK_VERSION","SDK_VERSION_HEADER","DEFAULT_CONCURRENCY","DEFAULT_MAX_RETRIES_PER_PART","BACKOFF_MS","Uploader","cfg","file","opts","isFile","filename","mimetype","cryptoRandomId","_a","_b","_c","picked","init","parts","completed","uploaded","__spreadProps","__spreadValues","rawErr","err","normalizeError","sessionId","totalParts","chunkSize","concurrency","maxRetries","urlByPart","p","results","loadedPerPart","cursor","totalBytes","reportProgress","loaded","a","b","totalPercent","ev","worker","_d","_e","makeError","partNumber","start","end","chunk","attempt","lastErr","url","res","etag","sleep","withSessionId","workers","e","method","path","body","signal","fetchImpl","SDK_VERSION_HEADER","cause","parsed","fromApiResponse","ms","r","msg","API_BASE","PRELOADER_STYLE_ID","showPickerPreloader","style","backdrop","spinner","removed","UnionStackClient","cfg","__spreadProps","__spreadValues","Uploader","makeError","file","opts","keyErr","files","_a","_b","_c","_d","err","picked","f","filesUploaded","filesFailed","i","uploaded","result","real","opened","pendingResolve","pendingReject","donePromise","res","rej","hidePreloader","e","UnionStack","cfg","UnionStackClient","init_picker","init","UnionStack","script","apiKey","client","UnionStack","err"]}
|