@emdash-cms/admin 0.34.0 → 0.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{LocaleDirectionProvider-B56zZskH.js → LocaleDirectionProvider-CnKC3o_O.js} +29 -29
- package/dist/{LocaleDirectionProvider-B56zZskH.js.map → LocaleDirectionProvider-CnKC3o_O.js.map} +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +719 -212
- package/dist/index.js.map +1 -1
- package/dist/locales/ar/messages.mjs +1 -1
- package/dist/locales/ca/messages.mjs +1 -1
- package/dist/locales/cs/messages.mjs +1 -1
- package/dist/locales/de/messages.mjs +1 -1
- package/dist/locales/en/messages.mjs +1 -1
- package/dist/locales/en-GB/messages.mjs +1 -1
- package/dist/locales/es-419/messages.mjs +1 -1
- package/dist/locales/es-ES/messages.mjs +1 -1
- package/dist/locales/eu/messages.mjs +1 -1
- package/dist/locales/fa/messages.mjs +1 -1
- package/dist/locales/fr/messages.mjs +1 -1
- package/dist/locales/hi/messages.mjs +1 -1
- package/dist/locales/hu/messages.mjs +1 -1
- package/dist/locales/id/messages.mjs +1 -1
- package/dist/locales/index.js +1 -1
- package/dist/locales/ja/messages.mjs +1 -1
- package/dist/locales/ko/messages.mjs +1 -1
- package/dist/locales/nb/messages.mjs +1 -1
- package/dist/locales/nl/messages.mjs +1 -1
- package/dist/locales/pl/messages.mjs +1 -1
- package/dist/locales/pseudo/messages.mjs +1 -1
- package/dist/locales/pt-BR/messages.mjs +1 -1
- package/dist/locales/sr-Latn/messages.mjs +1 -1
- package/dist/locales/sv/messages.mjs +1 -1
- package/dist/locales/th/messages.mjs +1 -1
- package/dist/locales/tr/messages.mjs +1 -1
- package/dist/locales/uk/messages.mjs +1 -1
- package/dist/locales/zh-CN/messages.mjs +1 -1
- package/dist/locales/zh-TW/messages.mjs +1 -1
- package/dist/messages-1TWaf8a2.js +6 -0
- package/dist/messages-1TWaf8a2.js.map +1 -0
- package/dist/messages-B5sF4YmM.js +6 -0
- package/dist/messages-B5sF4YmM.js.map +1 -0
- package/dist/messages-B9T8Kc4V.js +6 -0
- package/dist/messages-B9T8Kc4V.js.map +1 -0
- package/dist/messages-BOwqz7Dx.js +6 -0
- package/dist/messages-BOwqz7Dx.js.map +1 -0
- package/dist/messages-B_M5MNtr.js +6 -0
- package/dist/messages-B_M5MNtr.js.map +1 -0
- package/dist/messages-Bh-BQAdD.js +6 -0
- package/dist/messages-Bh-BQAdD.js.map +1 -0
- package/dist/messages-ByjvPBKi.js +6 -0
- package/dist/messages-ByjvPBKi.js.map +1 -0
- package/dist/messages-C4xWRT0x.js +6 -0
- package/dist/messages-C4xWRT0x.js.map +1 -0
- package/dist/messages-CAdtKwS0.js +6 -0
- package/dist/messages-CAdtKwS0.js.map +1 -0
- package/dist/messages-CEdjuD3T.js +6 -0
- package/dist/messages-CEdjuD3T.js.map +1 -0
- package/dist/messages-CYCJB96m.js +6 -0
- package/dist/messages-CYCJB96m.js.map +1 -0
- package/dist/messages-C_ry9289.js +6 -0
- package/dist/messages-C_ry9289.js.map +1 -0
- package/dist/messages-CbGBMnfZ.js +6 -0
- package/dist/messages-CbGBMnfZ.js.map +1 -0
- package/dist/messages-Cpt4WQXi.js +6 -0
- package/dist/messages-Cpt4WQXi.js.map +1 -0
- package/dist/messages-D0Q9lbJr.js +6 -0
- package/dist/messages-D0Q9lbJr.js.map +1 -0
- package/dist/messages-D6SjorpS.js +6 -0
- package/dist/messages-D6SjorpS.js.map +1 -0
- package/dist/messages-DACzFKkc.js +6 -0
- package/dist/messages-DACzFKkc.js.map +1 -0
- package/dist/messages-DD7UzBCg.js +6 -0
- package/dist/messages-DD7UzBCg.js.map +1 -0
- package/dist/messages-DQhv3VpQ.js +6 -0
- package/dist/messages-DQhv3VpQ.js.map +1 -0
- package/dist/messages-DkmI0StQ.js +6 -0
- package/dist/messages-DkmI0StQ.js.map +1 -0
- package/dist/messages-DwVVrprv.js +6 -0
- package/dist/messages-DwVVrprv.js.map +1 -0
- package/dist/messages-LPfCWgcA.js +6 -0
- package/dist/messages-LPfCWgcA.js.map +1 -0
- package/dist/messages-NbBfxfDD.js +6 -0
- package/dist/messages-NbBfxfDD.js.map +1 -0
- package/dist/messages-UfXVNVGm.js +6 -0
- package/dist/messages-UfXVNVGm.js.map +1 -0
- package/dist/messages-kV2LI902.js +6 -0
- package/dist/messages-kV2LI902.js.map +1 -0
- package/dist/messages-lZ7KdJCT.js +6 -0
- package/dist/messages-lZ7KdJCT.js.map +1 -0
- package/dist/messages-nYVfh2dw.js +6 -0
- package/dist/messages-nYVfh2dw.js.map +1 -0
- package/dist/messages-ttfIlVd2.js +6 -0
- package/dist/messages-ttfIlVd2.js.map +1 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -2
- package/dist/messages-4l4q4Avf.js +0 -6
- package/dist/messages-4l4q4Avf.js.map +0 -1
- package/dist/messages-8Ng0p6M4.js +0 -6
- package/dist/messages-8Ng0p6M4.js.map +0 -1
- package/dist/messages-BGJ61mbR.js +0 -6
- package/dist/messages-BGJ61mbR.js.map +0 -1
- package/dist/messages-BJjz3hov.js +0 -6
- package/dist/messages-BJjz3hov.js.map +0 -1
- package/dist/messages-BV7vld-9.js +0 -6
- package/dist/messages-BV7vld-9.js.map +0 -1
- package/dist/messages-BVaZauc8.js +0 -6
- package/dist/messages-BVaZauc8.js.map +0 -1
- package/dist/messages-BZ7favjd.js +0 -6
- package/dist/messages-BZ7favjd.js.map +0 -1
- package/dist/messages-BdxqPqXL.js +0 -6
- package/dist/messages-BdxqPqXL.js.map +0 -1
- package/dist/messages-BfSXfW7E.js +0 -6
- package/dist/messages-BfSXfW7E.js.map +0 -1
- package/dist/messages-CPTfUKBv.js +0 -6
- package/dist/messages-CPTfUKBv.js.map +0 -1
- package/dist/messages-CQS1a4au.js +0 -6
- package/dist/messages-CQS1a4au.js.map +0 -1
- package/dist/messages-CXtDjstE.js +0 -6
- package/dist/messages-CXtDjstE.js.map +0 -1
- package/dist/messages-Chlpzhy7.js +0 -6
- package/dist/messages-Chlpzhy7.js.map +0 -1
- package/dist/messages-D4t7iekH.js +0 -6
- package/dist/messages-D4t7iekH.js.map +0 -1
- package/dist/messages-D546HP55.js +0 -6
- package/dist/messages-D546HP55.js.map +0 -1
- package/dist/messages-DJ24Nb04.js +0 -6
- package/dist/messages-DJ24Nb04.js.map +0 -1
- package/dist/messages-DR6ynPV7.js +0 -6
- package/dist/messages-DR6ynPV7.js.map +0 -1
- package/dist/messages-Ddk7AuUS.js +0 -6
- package/dist/messages-Ddk7AuUS.js.map +0 -1
- package/dist/messages-DizwSrvr.js +0 -6
- package/dist/messages-DizwSrvr.js.map +0 -1
- package/dist/messages-Dn1fHt89.js +0 -6
- package/dist/messages-Dn1fHt89.js.map +0 -1
- package/dist/messages-DynzD2co.js +0 -6
- package/dist/messages-DynzD2co.js.map +0 -1
- package/dist/messages-DzCOsjCj.js +0 -6
- package/dist/messages-DzCOsjCj.js.map +0 -1
- package/dist/messages-JIi-gAO0.js +0 -6
- package/dist/messages-JIi-gAO0.js.map +0 -1
- package/dist/messages-Ve8shT7p.js +0 -6
- package/dist/messages-Ve8shT7p.js.map +0 -1
- package/dist/messages-f3phxB2R.js +0 -6
- package/dist/messages-f3phxB2R.js.map +0 -1
- package/dist/messages-rngIIrhI.js +0 -6
- package/dist/messages-rngIIrhI.js.map +0 -1
- package/dist/messages-sKfxXmTC.js +0 -6
- package/dist/messages-sKfxXmTC.js.map +0 -1
- package/dist/messages-tGLAc0ra.js +0 -6
- package/dist/messages-tGLAc0ra.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as fetchPlugins, c as updatePluginSettings, d as fetchAuthMode, f as fetchManifest, i as fetchPluginSettings, l as API_BASE, m as throwResponseError, n as enablePlugin, p as parseApiResponse, r as fetchPlugin, s as setPluginMcpEnabled, t as disablePlugin, u as apiFetch } from "./plugins-CQAV8Ap0.js";
|
|
2
|
-
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, s as getLocaleDir, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-
|
|
2
|
+
import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, s as getLocaleDir, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-CnKC3o_O.js";
|
|
3
3
|
import "./locales/index.js";
|
|
4
4
|
import { slugify } from "./slugify.js";
|
|
5
5
|
import { Autocomplete, Badge, Banner, Button, Checkbox, ClipboardText, Collapsible, Combobox, CommandPalette, Dialog, DropdownMenu, Field, Input, InputArea, Label, LayerCard, LinkButton, LinkProvider, Loader, Popover, Select, Sidebar, Sidebar as KumoSidebar, SkeletonLine, Switch, Tabs, Text, Toast, Toasty, Tooltip, TooltipProvider, buttonVariants, inputVariants, useKumoToastManager, useSidebar, useSidebar as useSidebar$1 } from "@cloudflare/kumo";
|
|
@@ -11,7 +11,7 @@ import * as React$1 from "react";
|
|
|
11
11
|
import React, { createContext, isValidElement, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
12
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { Dialog as Dialog$1 } from "@cloudflare/kumo/primitives";
|
|
14
|
-
import { Archive, ArrowCounterClockwise, ArrowDown, ArrowRightIcon, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowUp, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, Bell, BookOpen, BracketsAngle, BracketsCurly, Browser, Calendar, CalendarBlank, CalendarDots, CardsThree, CaretDown, CaretRightIcon, CaretUp, CaretUpDown, ChartBar, ChartLine, Chats, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Columns, ColumnsPlusLeft, ColumnsPlusRight, Copy, Crop, Cube, Database, DeviceMobile, DotsSixVertical, Download, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, Faders, File, FileArrowDown, FileText, Files, FloppyDisk, Folder, FolderOpen, Folders, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, IdentificationCard, Image as Image$1, ImageBroken, ImageSquare, Images, ImagesSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, LockKey, MagnifyingGlass, Medal, Minus, Monitor, Moon, Newspaper, Palette, PaperPlaneTilt, Paperclip, Paragraph, Path, Pencil, PencilSimple, Plug, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Robot, Rows, RowsPlusBottom, RowsPlusTop, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, Signature, SlidersHorizontal, Sparkle, SquaresFour, Stack, Star, Storefront, Sun, Table, Tag, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextH, TextHFive, TextHFour, TextHOne, TextHSix, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextSubscript, TextSuperscript, TextT, TextUnderline, ToggleLeft, Trash, Trophy, Upload, User, UserCircle, UserCircleIcon, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
|
|
14
|
+
import { Archive, ArrowClockwise, ArrowCounterClockwise, ArrowDown, ArrowRightIcon, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowUp, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, Bell, BookOpen, BracketsAngle, BracketsCurly, Browser, Calendar, CalendarBlank, CalendarDots, CardsThree, CaretDown, CaretRightIcon, CaretUp, CaretUpDown, ChartBar, ChartLine, Chats, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Columns, ColumnsPlusLeft, ColumnsPlusRight, Copy, Crop, Cube, Database, DeviceMobile, DotsSixVertical, Download, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, Faders, File, FileArrowDown, FileAudio, FileImage, FilePdf, FileText, FileVideo, Files, FloppyDisk, Folder, FolderOpen, Folders, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, IdentificationCard, Image as Image$1, ImageBroken, ImageSquare, Images, ImagesSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, LockKey, MagnifyingGlass, Medal, Minus, Monitor, Moon, Newspaper, Palette, PaperPlaneTilt, Paperclip, Paragraph, Path, Pencil, PencilSimple, Plug, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Robot, Rows, RowsPlusBottom, RowsPlusTop, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, Signature, SlidersHorizontal, Sparkle, SquaresFour, Stack, Star, Storefront, Sun, Table, Tag, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextH, TextHFive, TextHFour, TextHOne, TextHSix, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextSubscript, TextSuperscript, TextT, TextUnderline, ToggleLeft, Trash, Trophy, Upload, UploadSimple, User, UserCircle, UserCircleIcon, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
|
|
15
15
|
import { clsx } from "clsx";
|
|
16
16
|
import { twMerge } from "tailwind-merge";
|
|
17
17
|
import { DndContext, DragOverlay, KeyboardSensor, MeasuringStrategy, PointerSensor, closestCenter, rectIntersection, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
|
|
@@ -511,20 +511,25 @@ async function fetchMediaList(options) {
|
|
|
511
511
|
* Used to resolve an id-only reference (e.g. a byline's `avatarMediaId`)
|
|
512
512
|
* back into a full media item for display.
|
|
513
513
|
*/
|
|
514
|
-
async function fetchMediaItem(id) {
|
|
515
|
-
return (await parseApiResponse(await apiFetch(`${API_BASE}/media/${id}
|
|
514
|
+
async function fetchMediaItem(id, options) {
|
|
515
|
+
return (await parseApiResponse(await apiFetch(`${API_BASE}/media/${id}`, { signal: options?.signal }), i18n._({
|
|
516
516
|
id: "8rtatN",
|
|
517
517
|
message: "Failed to fetch media item"
|
|
518
518
|
}))).item;
|
|
519
519
|
}
|
|
520
520
|
const MAX_CLIENT_HASH_BYTES = 8 * 1024 * 1024;
|
|
521
|
-
async function computeContentHash(file) {
|
|
521
|
+
async function computeContentHash(file, signal) {
|
|
522
|
+
signal?.throwIfAborted();
|
|
522
523
|
const subtle = globalThis.crypto?.subtle;
|
|
523
524
|
if (!subtle || file.size === 0 || file.size > MAX_CLIENT_HASH_BYTES) return void 0;
|
|
524
525
|
try {
|
|
525
|
-
const
|
|
526
|
+
const bytes = await file.arrayBuffer();
|
|
527
|
+
signal?.throwIfAborted();
|
|
528
|
+
const hash = await subtle.digest("SHA-1", bytes);
|
|
529
|
+
signal?.throwIfAborted();
|
|
526
530
|
return `sha1:${Array.from(new Uint8Array(hash), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
527
531
|
} catch {
|
|
532
|
+
signal?.throwIfAborted();
|
|
528
533
|
return;
|
|
529
534
|
}
|
|
530
535
|
}
|
|
@@ -534,10 +539,11 @@ async function computeContentHash(file) {
|
|
|
534
539
|
*/
|
|
535
540
|
async function getUploadUrl(file, opts) {
|
|
536
541
|
try {
|
|
537
|
-
const contentHash = await computeContentHash(file);
|
|
542
|
+
const contentHash = await computeContentHash(file, opts?.signal);
|
|
538
543
|
const response = await apiFetch(`${API_BASE}/media/upload-url`, {
|
|
539
544
|
method: "POST",
|
|
540
545
|
headers: { "Content-Type": "application/json" },
|
|
546
|
+
signal: opts?.signal,
|
|
541
547
|
body: JSON.stringify({
|
|
542
548
|
filename: file.name,
|
|
543
549
|
contentType: file.type,
|
|
@@ -552,6 +558,7 @@ async function getUploadUrl(file, opts) {
|
|
|
552
558
|
message: "Failed to get upload URL"
|
|
553
559
|
}));
|
|
554
560
|
} catch (error) {
|
|
561
|
+
opts?.signal?.throwIfAborted();
|
|
555
562
|
if (error instanceof TypeError && error.message.includes("fetch")) return null;
|
|
556
563
|
throw error;
|
|
557
564
|
}
|
|
@@ -559,11 +566,12 @@ async function getUploadUrl(file, opts) {
|
|
|
559
566
|
/**
|
|
560
567
|
* Confirm upload after uploading to signed URL
|
|
561
568
|
*/
|
|
562
|
-
async function confirmUpload(mediaId, metadata) {
|
|
569
|
+
async function confirmUpload(mediaId, metadata, options) {
|
|
563
570
|
return (await parseApiResponse(await apiFetch(`${API_BASE}/media/${mediaId}/confirm`, {
|
|
564
571
|
method: "POST",
|
|
565
572
|
headers: { "Content-Type": "application/json" },
|
|
566
|
-
body: JSON.stringify(metadata || {})
|
|
573
|
+
body: JSON.stringify(metadata || {}),
|
|
574
|
+
signal: options?.signal
|
|
567
575
|
}), i18n._({
|
|
568
576
|
id: "QZDXMs",
|
|
569
577
|
message: "Failed to confirm upload"
|
|
@@ -572,14 +580,15 @@ async function confirmUpload(mediaId, metadata) {
|
|
|
572
580
|
/**
|
|
573
581
|
* Upload directly to signed URL
|
|
574
582
|
*/
|
|
575
|
-
async function uploadToSignedUrl(file, uploadInfo) {
|
|
583
|
+
async function uploadToSignedUrl(file, uploadInfo, options) {
|
|
576
584
|
const response = await fetch(uploadInfo.uploadUrl, {
|
|
577
585
|
method: uploadInfo.method,
|
|
578
586
|
headers: {
|
|
579
587
|
...uploadInfo.headers,
|
|
580
588
|
"Content-Type": file.type
|
|
581
589
|
},
|
|
582
|
-
body: file
|
|
590
|
+
body: file,
|
|
591
|
+
signal: options?.signal
|
|
583
592
|
});
|
|
584
593
|
if (!response.ok) await throwResponseError(response, i18n._({
|
|
585
594
|
id: "4Gz2wC",
|
|
@@ -589,29 +598,47 @@ async function uploadToSignedUrl(file, uploadInfo) {
|
|
|
589
598
|
/**
|
|
590
599
|
* Get image dimensions from a file
|
|
591
600
|
*/
|
|
592
|
-
async function getImageDimensions(file) {
|
|
601
|
+
async function getImageDimensions(file, options) {
|
|
602
|
+
options?.signal?.throwIfAborted();
|
|
593
603
|
if (!file.type.startsWith("image/")) return null;
|
|
594
|
-
return new Promise((resolve) => {
|
|
604
|
+
return new Promise((resolve, reject) => {
|
|
595
605
|
const img = new Image();
|
|
606
|
+
const objectUrl = URL.createObjectURL(file);
|
|
607
|
+
const cleanup = () => {
|
|
608
|
+
img.onload = null;
|
|
609
|
+
img.onerror = null;
|
|
610
|
+
options?.signal?.removeEventListener("abort", handleAbort);
|
|
611
|
+
URL.revokeObjectURL(objectUrl);
|
|
612
|
+
};
|
|
613
|
+
const handleAbort = () => {
|
|
614
|
+
cleanup();
|
|
615
|
+
reject(options?.signal?.reason);
|
|
616
|
+
};
|
|
596
617
|
img.onload = () => {
|
|
597
|
-
|
|
618
|
+
const dimensions = {
|
|
598
619
|
width: img.naturalWidth,
|
|
599
620
|
height: img.naturalHeight
|
|
600
|
-
}
|
|
601
|
-
|
|
621
|
+
};
|
|
622
|
+
cleanup();
|
|
623
|
+
resolve(dimensions);
|
|
602
624
|
};
|
|
603
625
|
img.onerror = () => {
|
|
626
|
+
cleanup();
|
|
604
627
|
resolve(null);
|
|
605
|
-
URL.revokeObjectURL(img.src);
|
|
606
628
|
};
|
|
607
|
-
|
|
629
|
+
options?.signal?.addEventListener("abort", handleAbort, { once: true });
|
|
630
|
+
if (options?.signal?.aborted) {
|
|
631
|
+
handleAbort();
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
img.src = objectUrl;
|
|
608
635
|
});
|
|
609
636
|
}
|
|
610
637
|
/**
|
|
611
638
|
* Upload media file via direct upload (legacy/local storage)
|
|
612
639
|
*/
|
|
613
640
|
async function uploadMediaDirect(file, opts) {
|
|
614
|
-
const dimensions = await getImageDimensions(file);
|
|
641
|
+
const dimensions = await getImageDimensions(file, opts);
|
|
615
642
|
const formData = new FormData();
|
|
616
643
|
formData.append("file", file);
|
|
617
644
|
if (dimensions?.width) formData.append("width", String(dimensions.width));
|
|
@@ -619,7 +646,8 @@ async function uploadMediaDirect(file, opts) {
|
|
|
619
646
|
if (opts?.fieldId) formData.append("fieldId", opts.fieldId);
|
|
620
647
|
return (await parseApiResponse(await apiFetch(`${API_BASE}/media`, {
|
|
621
648
|
method: "POST",
|
|
622
|
-
body: formData
|
|
649
|
+
body: formData,
|
|
650
|
+
signal: opts?.signal
|
|
623
651
|
}), i18n._({
|
|
624
652
|
id: "5IFNMM",
|
|
625
653
|
message: "Failed to upload media"
|
|
@@ -632,16 +660,17 @@ async function uploadMediaDirect(file, opts) {
|
|
|
632
660
|
* (for local storage) if signed URLs are not supported.
|
|
633
661
|
*/
|
|
634
662
|
async function uploadMedia(file, opts) {
|
|
663
|
+
opts?.signal?.throwIfAborted();
|
|
635
664
|
const uploadInfo = await getUploadUrl(file, opts);
|
|
636
665
|
if (!uploadInfo) return uploadMediaDirect(file, opts);
|
|
637
|
-
if ("existing" in uploadInfo) return fetchMediaItem(uploadInfo.mediaId);
|
|
638
|
-
await uploadToSignedUrl(file, uploadInfo);
|
|
639
|
-
const dimensions = await getImageDimensions(file);
|
|
666
|
+
if ("existing" in uploadInfo) return fetchMediaItem(uploadInfo.mediaId, opts);
|
|
667
|
+
await uploadToSignedUrl(file, uploadInfo, opts);
|
|
668
|
+
const dimensions = await getImageDimensions(file, opts);
|
|
640
669
|
return confirmUpload(uploadInfo.mediaId, {
|
|
641
670
|
size: file.size,
|
|
642
671
|
width: dimensions?.width,
|
|
643
672
|
height: dimensions?.height
|
|
644
|
-
});
|
|
673
|
+
}, opts);
|
|
645
674
|
}
|
|
646
675
|
/**
|
|
647
676
|
* Delete media
|
|
@@ -695,13 +724,15 @@ async function fetchProviderMedia(providerId, options) {
|
|
|
695
724
|
/**
|
|
696
725
|
* Upload media to a specific provider
|
|
697
726
|
*/
|
|
698
|
-
async function uploadToProvider(providerId, file, alt) {
|
|
727
|
+
async function uploadToProvider(providerId, file, alt, options) {
|
|
728
|
+
options?.signal?.throwIfAborted();
|
|
699
729
|
const formData = new FormData();
|
|
700
730
|
formData.append("file", file);
|
|
701
731
|
if (alt) formData.append("alt", alt);
|
|
702
732
|
return (await parseApiResponse(await apiFetch(`${API_BASE}/media/providers/${providerId}`, {
|
|
703
733
|
method: "POST",
|
|
704
|
-
body: formData
|
|
734
|
+
body: formData,
|
|
735
|
+
signal: options?.signal
|
|
705
736
|
}), i18n._({
|
|
706
737
|
id: "j66jlb",
|
|
707
738
|
message: "Failed to upload to provider"
|
|
@@ -26714,6 +26745,514 @@ function formatDate$1(isoString) {
|
|
|
26714
26745
|
});
|
|
26715
26746
|
}
|
|
26716
26747
|
|
|
26748
|
+
//#endregion
|
|
26749
|
+
//#region src/components/MediaUploadDialog.tsx
|
|
26750
|
+
const LOCAL_MEDIA_UPLOAD_ACCEPT = "image/png,image/jpeg,image/gif,image/webp,video/*,audio/*,application/pdf";
|
|
26751
|
+
const DEFAULT_CONCURRENCY = 3;
|
|
26752
|
+
const MAX_CONCURRENCY = 6;
|
|
26753
|
+
const MAX_VISIBLE_ROWS = 100;
|
|
26754
|
+
const MAX_PREVIEW_BYTES = 8 * 1024 * 1024;
|
|
26755
|
+
const PREVIEW_MIME_TYPES = new Set([
|
|
26756
|
+
"image/jpeg",
|
|
26757
|
+
"image/png",
|
|
26758
|
+
"image/gif",
|
|
26759
|
+
"image/webp"
|
|
26760
|
+
]);
|
|
26761
|
+
function previewUrlFor(file) {
|
|
26762
|
+
if (file.size > MAX_PREVIEW_BYTES || !PREVIEW_MIME_TYPES.has(file.type)) return void 0;
|
|
26763
|
+
try {
|
|
26764
|
+
return URL.createObjectURL(file);
|
|
26765
|
+
} catch {
|
|
26766
|
+
return;
|
|
26767
|
+
}
|
|
26768
|
+
}
|
|
26769
|
+
function FileKindIcon({ file }) {
|
|
26770
|
+
const className = "h-5 w-5";
|
|
26771
|
+
if (file.type.startsWith("image/")) return /* @__PURE__ */ jsx(FileImage, {
|
|
26772
|
+
className,
|
|
26773
|
+
"aria-hidden": "true"
|
|
26774
|
+
});
|
|
26775
|
+
if (file.type.startsWith("video/")) return /* @__PURE__ */ jsx(FileVideo, {
|
|
26776
|
+
className,
|
|
26777
|
+
"aria-hidden": "true"
|
|
26778
|
+
});
|
|
26779
|
+
if (file.type.startsWith("audio/")) return /* @__PURE__ */ jsx(FileAudio, {
|
|
26780
|
+
className,
|
|
26781
|
+
"aria-hidden": "true"
|
|
26782
|
+
});
|
|
26783
|
+
if (file.type === "application/pdf") return /* @__PURE__ */ jsx(FilePdf, {
|
|
26784
|
+
className,
|
|
26785
|
+
"aria-hidden": "true"
|
|
26786
|
+
});
|
|
26787
|
+
if (file.type.startsWith("text/") || file.type.includes("document")) return /* @__PURE__ */ jsx(FileText, {
|
|
26788
|
+
className,
|
|
26789
|
+
"aria-hidden": "true"
|
|
26790
|
+
});
|
|
26791
|
+
return /* @__PURE__ */ jsx(File, {
|
|
26792
|
+
className,
|
|
26793
|
+
"aria-hidden": "true"
|
|
26794
|
+
});
|
|
26795
|
+
}
|
|
26796
|
+
function UploadStatusLabel({ status }) {
|
|
26797
|
+
const { _: _t } = useLingui();
|
|
26798
|
+
if (status === "uploading") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Loader, { size: "sm" }), _t({
|
|
26799
|
+
id: "/eguJc",
|
|
26800
|
+
message: "Uploading"
|
|
26801
|
+
})] });
|
|
26802
|
+
if (status === "complete") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CheckCircle, {
|
|
26803
|
+
className: "h-4 w-4 text-kumo-success",
|
|
26804
|
+
weight: "fill",
|
|
26805
|
+
"aria-hidden": "true"
|
|
26806
|
+
}), _t({
|
|
26807
|
+
id: "bD8I7O",
|
|
26808
|
+
message: "Complete"
|
|
26809
|
+
})] });
|
|
26810
|
+
if (status === "failed") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(WarningCircle, {
|
|
26811
|
+
className: "h-4 w-4 text-kumo-danger",
|
|
26812
|
+
weight: "fill",
|
|
26813
|
+
"aria-hidden": "true"
|
|
26814
|
+
}), _t({
|
|
26815
|
+
id: "b3Thhd",
|
|
26816
|
+
message: "Upload failed"
|
|
26817
|
+
})] });
|
|
26818
|
+
return /* @__PURE__ */ jsx(Fragment, { children: _t({
|
|
26819
|
+
id: "8wu9lr",
|
|
26820
|
+
message: "Queued"
|
|
26821
|
+
}) });
|
|
26822
|
+
}
|
|
26823
|
+
function UploadFileRow({ row, onCancel, onRetry, onRemove }) {
|
|
26824
|
+
const { _: _t2 } = useLingui();
|
|
26825
|
+
const isBusy = row.status === "queued" || row.status === "uploading";
|
|
26826
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
26827
|
+
className: "grid grid-cols-[2.5rem_minmax(0,1fr)_auto] items-center gap-3 rounded-lg bg-kumo-base px-3 py-2.5 ring ring-kumo-line",
|
|
26828
|
+
children: [
|
|
26829
|
+
/* @__PURE__ */ jsx("div", {
|
|
26830
|
+
className: "flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-md bg-kumo-tint text-kumo-subtle",
|
|
26831
|
+
children: row.previewUrl ? /* @__PURE__ */ jsx("img", {
|
|
26832
|
+
src: row.previewUrl,
|
|
26833
|
+
alt: "",
|
|
26834
|
+
loading: "lazy",
|
|
26835
|
+
className: "h-full w-full object-cover"
|
|
26836
|
+
}) : /* @__PURE__ */ jsx(FileKindIcon, { file: row.file })
|
|
26837
|
+
}),
|
|
26838
|
+
/* @__PURE__ */ jsxs("div", {
|
|
26839
|
+
className: "min-w-0",
|
|
26840
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
26841
|
+
className: "line-clamp-2 text-sm font-medium leading-5",
|
|
26842
|
+
title: row.file.name,
|
|
26843
|
+
children: row.file.name
|
|
26844
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
26845
|
+
className: "mt-0.5 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm text-kumo-subtle",
|
|
26846
|
+
children: [
|
|
26847
|
+
/* @__PURE__ */ jsx("span", {
|
|
26848
|
+
className: "inline-flex items-center gap-1.5",
|
|
26849
|
+
children: /* @__PURE__ */ jsx(UploadStatusLabel, { status: row.status })
|
|
26850
|
+
}),
|
|
26851
|
+
/* @__PURE__ */ jsx("span", {
|
|
26852
|
+
"aria-hidden": "true",
|
|
26853
|
+
children: "·"
|
|
26854
|
+
}),
|
|
26855
|
+
/* @__PURE__ */ jsx("span", {
|
|
26856
|
+
className: "tabular-nums",
|
|
26857
|
+
children: formatFileSize(row.file.size)
|
|
26858
|
+
})
|
|
26859
|
+
]
|
|
26860
|
+
})]
|
|
26861
|
+
}),
|
|
26862
|
+
/* @__PURE__ */ jsxs("div", {
|
|
26863
|
+
className: "flex items-center gap-1",
|
|
26864
|
+
children: [row.status === "failed" && /* @__PURE__ */ jsx(Button, {
|
|
26865
|
+
variant: "ghost",
|
|
26866
|
+
shape: "square",
|
|
26867
|
+
size: "sm",
|
|
26868
|
+
onClick: onRetry,
|
|
26869
|
+
"aria-label": _t2({
|
|
26870
|
+
id: "XUH9K1",
|
|
26871
|
+
message: "Retry {0}",
|
|
26872
|
+
values: { 0: row.file.name }
|
|
26873
|
+
}),
|
|
26874
|
+
icon: /* @__PURE__ */ jsx(ArrowClockwise, { "aria-hidden": "true" })
|
|
26875
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
26876
|
+
variant: "ghost",
|
|
26877
|
+
shape: "square",
|
|
26878
|
+
size: "sm",
|
|
26879
|
+
onClick: isBusy ? onCancel : onRemove,
|
|
26880
|
+
"aria-label": isBusy ? _t2({
|
|
26881
|
+
id: "x9WS62",
|
|
26882
|
+
message: "Cancel {0}",
|
|
26883
|
+
values: { 0: row.file.name }
|
|
26884
|
+
}) : row.status === "complete" ? _t2({
|
|
26885
|
+
id: "UsbCOm",
|
|
26886
|
+
message: "Dismiss completed {0}",
|
|
26887
|
+
values: { 0: row.file.name }
|
|
26888
|
+
}) : _t2({
|
|
26889
|
+
id: "w61W3L",
|
|
26890
|
+
message: "Remove {0}",
|
|
26891
|
+
values: { 0: row.file.name }
|
|
26892
|
+
}),
|
|
26893
|
+
icon: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
26894
|
+
})]
|
|
26895
|
+
})
|
|
26896
|
+
]
|
|
26897
|
+
});
|
|
26898
|
+
}
|
|
26899
|
+
function MediaUploadDialog({ open, providerName, accept, enqueueRequest, onEnqueueRequestConsumed, onOpenChange, onCloseComplete, onQueueIdle, upload, concurrency = DEFAULT_CONCURRENCY }) {
|
|
26900
|
+
const { _: _t3 } = useLingui();
|
|
26901
|
+
const [rows, setRows] = React$1.useState([]);
|
|
26902
|
+
const [overflowCount, setOverflowCount] = React$1.useState(0);
|
|
26903
|
+
const rowsRef = React$1.useRef([]);
|
|
26904
|
+
const activeRef = React$1.useRef(/* @__PURE__ */ new Map());
|
|
26905
|
+
const nextIdRef = React$1.useRef(0);
|
|
26906
|
+
const lastRequestIdRef = React$1.useRef(null);
|
|
26907
|
+
const busyRunRef = React$1.useRef(false);
|
|
26908
|
+
const unmountingRef = React$1.useRef(false);
|
|
26909
|
+
const inputRef = React$1.useRef(null);
|
|
26910
|
+
const requestedConcurrency = Number.isFinite(concurrency) ? Math.floor(concurrency) : DEFAULT_CONCURRENCY;
|
|
26911
|
+
const maxConcurrent = Math.min(MAX_CONCURRENCY, Math.max(1, requestedConcurrency));
|
|
26912
|
+
const updateRows = React$1.useCallback((update) => {
|
|
26913
|
+
const next = update(rowsRef.current);
|
|
26914
|
+
rowsRef.current = next;
|
|
26915
|
+
setRows(next);
|
|
26916
|
+
}, []);
|
|
26917
|
+
const addFiles = React$1.useCallback((files) => {
|
|
26918
|
+
const available = Math.max(0, MAX_VISIBLE_ROWS - rowsRef.current.length);
|
|
26919
|
+
const accepted = files.slice(0, available);
|
|
26920
|
+
setOverflowCount(Math.max(0, files.length - accepted.length));
|
|
26921
|
+
if (accepted.length === 0) return;
|
|
26922
|
+
busyRunRef.current = true;
|
|
26923
|
+
const added = accepted.map((file) => ({
|
|
26924
|
+
id: nextIdRef.current += 1,
|
|
26925
|
+
file,
|
|
26926
|
+
status: "queued",
|
|
26927
|
+
attempt: 1,
|
|
26928
|
+
previewUrl: previewUrlFor(file)
|
|
26929
|
+
}));
|
|
26930
|
+
updateRows((current) => [...current, ...added]);
|
|
26931
|
+
}, [updateRows]);
|
|
26932
|
+
React$1.useEffect(() => {
|
|
26933
|
+
if (!open || !enqueueRequest || enqueueRequest.id === lastRequestIdRef.current) return;
|
|
26934
|
+
lastRequestIdRef.current = enqueueRequest.id;
|
|
26935
|
+
addFiles(enqueueRequest.files);
|
|
26936
|
+
onEnqueueRequestConsumed(enqueueRequest.id);
|
|
26937
|
+
}, [
|
|
26938
|
+
addFiles,
|
|
26939
|
+
enqueueRequest,
|
|
26940
|
+
onEnqueueRequestConsumed,
|
|
26941
|
+
open
|
|
26942
|
+
]);
|
|
26943
|
+
React$1.useEffect(() => {
|
|
26944
|
+
const slots = maxConcurrent - activeRef.current.size;
|
|
26945
|
+
if (slots <= 0) return;
|
|
26946
|
+
const candidates = rowsRef.current.filter((row) => row.status === "queued" && !activeRef.current.has(row.id)).slice(0, slots);
|
|
26947
|
+
if (candidates.length === 0) return;
|
|
26948
|
+
for (const row of candidates) {
|
|
26949
|
+
const controller = new AbortController();
|
|
26950
|
+
activeRef.current.set(row.id, {
|
|
26951
|
+
attempt: row.attempt,
|
|
26952
|
+
controller
|
|
26953
|
+
});
|
|
26954
|
+
updateRows((current) => current.map((item) => item.id === row.id && item.attempt === row.attempt ? {
|
|
26955
|
+
...item,
|
|
26956
|
+
status: "uploading"
|
|
26957
|
+
} : item));
|
|
26958
|
+
Promise.resolve().then(() => upload(row.file, { signal: controller.signal })).then(() => {
|
|
26959
|
+
if (activeRef.current.get(row.id)?.attempt !== row.attempt) return void 0;
|
|
26960
|
+
activeRef.current.delete(row.id);
|
|
26961
|
+
updateRows((current) => current.map((item) => item.id === row.id && item.attempt === row.attempt ? {
|
|
26962
|
+
...item,
|
|
26963
|
+
status: "complete"
|
|
26964
|
+
} : item));
|
|
26965
|
+
}, () => {
|
|
26966
|
+
if (activeRef.current.get(row.id)?.attempt !== row.attempt) return void 0;
|
|
26967
|
+
activeRef.current.delete(row.id);
|
|
26968
|
+
if (controller.signal.aborted) return void 0;
|
|
26969
|
+
updateRows((current) => current.map((item) => item.id === row.id && item.attempt === row.attempt ? {
|
|
26970
|
+
...item,
|
|
26971
|
+
status: "failed"
|
|
26972
|
+
} : item));
|
|
26973
|
+
});
|
|
26974
|
+
}
|
|
26975
|
+
}, [
|
|
26976
|
+
maxConcurrent,
|
|
26977
|
+
rows,
|
|
26978
|
+
updateRows,
|
|
26979
|
+
upload
|
|
26980
|
+
]);
|
|
26981
|
+
const hasUnfinished = rows.some((row) => row.status === "queued" || row.status === "uploading");
|
|
26982
|
+
React$1.useEffect(() => {
|
|
26983
|
+
if (hasUnfinished || !busyRunRef.current || unmountingRef.current) return;
|
|
26984
|
+
busyRunRef.current = false;
|
|
26985
|
+
onQueueIdle?.();
|
|
26986
|
+
}, [hasUnfinished, onQueueIdle]);
|
|
26987
|
+
React$1.useEffect(() => () => {
|
|
26988
|
+
unmountingRef.current = true;
|
|
26989
|
+
activeRef.current.forEach(({ controller }) => controller.abort());
|
|
26990
|
+
activeRef.current.clear();
|
|
26991
|
+
rowsRef.current.forEach((row) => row.previewUrl && URL.revokeObjectURL(row.previewUrl));
|
|
26992
|
+
}, []);
|
|
26993
|
+
const removeRow = React$1.useCallback((id) => {
|
|
26994
|
+
const row = rowsRef.current.find((item) => item.id === id);
|
|
26995
|
+
if (!row) return;
|
|
26996
|
+
activeRef.current.get(id)?.controller.abort();
|
|
26997
|
+
activeRef.current.delete(id);
|
|
26998
|
+
if (row.previewUrl) URL.revokeObjectURL(row.previewUrl);
|
|
26999
|
+
updateRows((current) => current.filter((item) => item.id !== id));
|
|
27000
|
+
}, [updateRows]);
|
|
27001
|
+
const retryRow = React$1.useCallback((id) => {
|
|
27002
|
+
busyRunRef.current = true;
|
|
27003
|
+
updateRows((current) => current.map((row) => row.id === id && row.status === "failed" ? {
|
|
27004
|
+
...row,
|
|
27005
|
+
status: "queued",
|
|
27006
|
+
attempt: row.attempt + 1
|
|
27007
|
+
} : row));
|
|
27008
|
+
}, [updateRows]);
|
|
27009
|
+
const retryFailed = () => {
|
|
27010
|
+
if (!rowsRef.current.some((row) => row.status === "failed")) return;
|
|
27011
|
+
busyRunRef.current = true;
|
|
27012
|
+
updateRows((current) => current.map((row) => row.status === "failed" ? {
|
|
27013
|
+
...row,
|
|
27014
|
+
status: "queued",
|
|
27015
|
+
attempt: row.attempt + 1
|
|
27016
|
+
} : row));
|
|
27017
|
+
};
|
|
27018
|
+
const cancelRemaining = () => {
|
|
27019
|
+
activeRef.current.forEach(({ controller }) => controller.abort());
|
|
27020
|
+
activeRef.current.clear();
|
|
27021
|
+
rowsRef.current.filter((row) => row.status === "queued" || row.status === "uploading").forEach((row) => row.previewUrl && URL.revokeObjectURL(row.previewUrl));
|
|
27022
|
+
updateRows((current) => current.filter((row) => row.status !== "queued" && row.status !== "uploading"));
|
|
27023
|
+
};
|
|
27024
|
+
const clearCompleted = () => {
|
|
27025
|
+
rowsRef.current.filter((row) => row.status === "complete").forEach((row) => row.previewUrl && URL.revokeObjectURL(row.previewUrl));
|
|
27026
|
+
updateRows((current) => current.filter((row) => row.status !== "complete"));
|
|
27027
|
+
};
|
|
27028
|
+
const reset = () => {
|
|
27029
|
+
activeRef.current.forEach(({ controller }) => controller.abort());
|
|
27030
|
+
activeRef.current.clear();
|
|
27031
|
+
rowsRef.current.forEach((row) => row.previewUrl && URL.revokeObjectURL(row.previewUrl));
|
|
27032
|
+
rowsRef.current = [];
|
|
27033
|
+
setRows([]);
|
|
27034
|
+
setOverflowCount(0);
|
|
27035
|
+
busyRunRef.current = false;
|
|
27036
|
+
};
|
|
27037
|
+
const completedCount = rows.filter((row) => row.status === "complete").length;
|
|
27038
|
+
const failedCount = rows.filter((row) => row.status === "failed").length;
|
|
27039
|
+
const liveMessage = hasUnfinished ? _t3({
|
|
27040
|
+
id: "9XrV3P",
|
|
27041
|
+
message: "{completedCount} of {0} uploads complete",
|
|
27042
|
+
values: {
|
|
27043
|
+
0: rows.length,
|
|
27044
|
+
completedCount
|
|
27045
|
+
}
|
|
27046
|
+
}) : failedCount > 0 ? i18n._({
|
|
27047
|
+
id: "65Ew2q",
|
|
27048
|
+
message: "{failedCount, plural, one {# upload failed} other {# uploads failed}}",
|
|
27049
|
+
values: { failedCount }
|
|
27050
|
+
}) : rows.length > 0 ? _t3({
|
|
27051
|
+
id: "h0W4PA",
|
|
27052
|
+
message: "All uploads finished"
|
|
27053
|
+
}) : "";
|
|
27054
|
+
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
27055
|
+
open,
|
|
27056
|
+
onOpenChange: (nextOpen) => {
|
|
27057
|
+
if (!nextOpen && hasUnfinished) return;
|
|
27058
|
+
onOpenChange(nextOpen);
|
|
27059
|
+
},
|
|
27060
|
+
onOpenChangeComplete: (nextOpen) => {
|
|
27061
|
+
if (nextOpen) return;
|
|
27062
|
+
reset();
|
|
27063
|
+
onCloseComplete();
|
|
27064
|
+
},
|
|
27065
|
+
disablePointerDismissal: hasUnfinished,
|
|
27066
|
+
children: /* @__PURE__ */ jsxs(Dialog, {
|
|
27067
|
+
size: "lg",
|
|
27068
|
+
className: "flex max-h-[min(88dvh,46rem)] flex-col overflow-hidden p-0",
|
|
27069
|
+
style: { width: "min(94vw, 42rem)" },
|
|
27070
|
+
children: [
|
|
27071
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27072
|
+
className: "flex shrink-0 items-start justify-between gap-4 border-b border-kumo-line px-6 py-5",
|
|
27073
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
27074
|
+
className: "min-w-0",
|
|
27075
|
+
children: [/* @__PURE__ */ jsx(Dialog.Title, {
|
|
27076
|
+
className: "text-lg font-semibold leading-tight",
|
|
27077
|
+
children: _t3({
|
|
27078
|
+
id: "NKvTB9",
|
|
27079
|
+
message: "Upload to {providerName}",
|
|
27080
|
+
values: { providerName }
|
|
27081
|
+
})
|
|
27082
|
+
}), /* @__PURE__ */ jsx(Dialog.Description, {
|
|
27083
|
+
className: "mt-1 text-sm leading-5 text-kumo-subtle",
|
|
27084
|
+
children: _t3({
|
|
27085
|
+
id: "FAQCNs",
|
|
27086
|
+
message: "Files upload as soon as you add them."
|
|
27087
|
+
})
|
|
27088
|
+
})]
|
|
27089
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
27090
|
+
variant: "ghost",
|
|
27091
|
+
shape: "square",
|
|
27092
|
+
size: "sm",
|
|
27093
|
+
disabled: hasUnfinished,
|
|
27094
|
+
onClick: () => onOpenChange(false),
|
|
27095
|
+
"aria-label": _t3({
|
|
27096
|
+
id: "yz7wBu",
|
|
27097
|
+
message: "Close"
|
|
27098
|
+
}),
|
|
27099
|
+
icon: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
27100
|
+
})]
|
|
27101
|
+
}),
|
|
27102
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27103
|
+
className: "min-h-0 flex-1 space-y-4 overflow-y-auto overscroll-contain px-6 py-5",
|
|
27104
|
+
children: [
|
|
27105
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27106
|
+
className: "flex min-h-64 flex-col items-center justify-center gap-4 rounded-xl border-[3px] border-dashed border-kumo-subtle/60 bg-kumo-base px-6 py-10 text-center sm:min-h-72",
|
|
27107
|
+
onDragOver: (event) => event.preventDefault(),
|
|
27108
|
+
onDrop: (event) => {
|
|
27109
|
+
event.preventDefault();
|
|
27110
|
+
event.stopPropagation();
|
|
27111
|
+
addFiles([...event.dataTransfer.files]);
|
|
27112
|
+
},
|
|
27113
|
+
children: [
|
|
27114
|
+
/* @__PURE__ */ jsx("div", {
|
|
27115
|
+
className: "flex size-14 items-center justify-center rounded-full text-kumo-subtle ring ring-kumo-line",
|
|
27116
|
+
children: /* @__PURE__ */ jsx(UploadSimple, {
|
|
27117
|
+
className: "h-7 w-7",
|
|
27118
|
+
weight: "regular",
|
|
27119
|
+
"aria-hidden": "true"
|
|
27120
|
+
})
|
|
27121
|
+
}),
|
|
27122
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27123
|
+
className: "space-y-1",
|
|
27124
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
27125
|
+
className: "text-lg font-semibold leading-6",
|
|
27126
|
+
children: _t3({
|
|
27127
|
+
id: "+ywUTE",
|
|
27128
|
+
message: "Drag and drop files here"
|
|
27129
|
+
})
|
|
27130
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
27131
|
+
className: "text-sm leading-5 text-kumo-subtle",
|
|
27132
|
+
children: _t3({
|
|
27133
|
+
id: "DbFk6j",
|
|
27134
|
+
message: "Or browse files from your computer"
|
|
27135
|
+
})
|
|
27136
|
+
})]
|
|
27137
|
+
}),
|
|
27138
|
+
/* @__PURE__ */ jsx(Button, {
|
|
27139
|
+
variant: "outline",
|
|
27140
|
+
size: "base",
|
|
27141
|
+
className: "text-sm",
|
|
27142
|
+
onClick: () => inputRef.current?.click(),
|
|
27143
|
+
icon: /* @__PURE__ */ jsx(UploadSimple, { "aria-hidden": "true" }),
|
|
27144
|
+
children: _t3({
|
|
27145
|
+
id: "JYj5R2",
|
|
27146
|
+
message: "Browse files"
|
|
27147
|
+
})
|
|
27148
|
+
}),
|
|
27149
|
+
/* @__PURE__ */ jsx("input", {
|
|
27150
|
+
ref: inputRef,
|
|
27151
|
+
type: "file",
|
|
27152
|
+
multiple: true,
|
|
27153
|
+
accept,
|
|
27154
|
+
className: "sr-only",
|
|
27155
|
+
tabIndex: -1,
|
|
27156
|
+
"aria-label": _t3({
|
|
27157
|
+
id: "0Io5gK",
|
|
27158
|
+
message: "Browse files to upload"
|
|
27159
|
+
}),
|
|
27160
|
+
onChange: (event) => {
|
|
27161
|
+
addFiles([...event.currentTarget.files ?? []]);
|
|
27162
|
+
event.currentTarget.value = "";
|
|
27163
|
+
}
|
|
27164
|
+
})
|
|
27165
|
+
]
|
|
27166
|
+
}),
|
|
27167
|
+
overflowCount > 0 && /* @__PURE__ */ jsx("p", {
|
|
27168
|
+
role: "alert",
|
|
27169
|
+
className: "text-sm text-kumo-danger",
|
|
27170
|
+
children: i18n._({
|
|
27171
|
+
id: "VINwRP",
|
|
27172
|
+
message: "{overflowCount, plural, one {# file was not added because the upload list is full.} other {# files were not added because the upload list is full.}}",
|
|
27173
|
+
values: { overflowCount }
|
|
27174
|
+
})
|
|
27175
|
+
}),
|
|
27176
|
+
rows.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
27177
|
+
className: "space-y-2",
|
|
27178
|
+
children: rows.map((row) => /* @__PURE__ */ jsx(UploadFileRow, {
|
|
27179
|
+
row,
|
|
27180
|
+
onCancel: () => removeRow(row.id),
|
|
27181
|
+
onRetry: () => retryRow(row.id),
|
|
27182
|
+
onRemove: () => removeRow(row.id)
|
|
27183
|
+
}, row.id))
|
|
27184
|
+
})
|
|
27185
|
+
]
|
|
27186
|
+
}),
|
|
27187
|
+
/* @__PURE__ */ jsxs("div", {
|
|
27188
|
+
className: "flex shrink-0 flex-wrap items-center justify-between gap-3 border-t border-kumo-line px-6 py-4",
|
|
27189
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
27190
|
+
className: "text-sm text-kumo-subtle tabular-nums",
|
|
27191
|
+
"aria-hidden": "true",
|
|
27192
|
+
children: rows.length > 0 ? _t3({
|
|
27193
|
+
id: "0zZ04n",
|
|
27194
|
+
message: "{completedCount} of {0} complete",
|
|
27195
|
+
values: {
|
|
27196
|
+
0: rows.length,
|
|
27197
|
+
completedCount
|
|
27198
|
+
}
|
|
27199
|
+
}) : _t3({
|
|
27200
|
+
id: "UGK1Dr",
|
|
27201
|
+
message: "No files added"
|
|
27202
|
+
})
|
|
27203
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
27204
|
+
className: "flex flex-wrap items-center justify-end gap-2",
|
|
27205
|
+
children: [
|
|
27206
|
+
completedCount > 0 && /* @__PURE__ */ jsx(Button, {
|
|
27207
|
+
variant: "ghost",
|
|
27208
|
+
size: "sm",
|
|
27209
|
+
onClick: clearCompleted,
|
|
27210
|
+
children: _t3({
|
|
27211
|
+
id: "3Cz86c",
|
|
27212
|
+
message: "Clear completed"
|
|
27213
|
+
})
|
|
27214
|
+
}),
|
|
27215
|
+
failedCount > 0 && /* @__PURE__ */ jsx(Button, {
|
|
27216
|
+
variant: "outline",
|
|
27217
|
+
size: "sm",
|
|
27218
|
+
onClick: retryFailed,
|
|
27219
|
+
icon: /* @__PURE__ */ jsx(ArrowClockwise, { "aria-hidden": "true" }),
|
|
27220
|
+
children: _t3({
|
|
27221
|
+
id: "H+XODI",
|
|
27222
|
+
message: "Retry failed"
|
|
27223
|
+
})
|
|
27224
|
+
}),
|
|
27225
|
+
hasUnfinished ? /* @__PURE__ */ jsx(Button, {
|
|
27226
|
+
variant: "outline",
|
|
27227
|
+
size: "sm",
|
|
27228
|
+
onClick: cancelRemaining,
|
|
27229
|
+
children: _t3({
|
|
27230
|
+
id: "GLYB+o",
|
|
27231
|
+
message: "Cancel remaining"
|
|
27232
|
+
})
|
|
27233
|
+
}) : /* @__PURE__ */ jsx(Button, {
|
|
27234
|
+
variant: "primary",
|
|
27235
|
+
size: "sm",
|
|
27236
|
+
onClick: () => onOpenChange(false),
|
|
27237
|
+
children: _t3({
|
|
27238
|
+
id: "DPfwMq",
|
|
27239
|
+
message: "Done"
|
|
27240
|
+
})
|
|
27241
|
+
})
|
|
27242
|
+
]
|
|
27243
|
+
})]
|
|
27244
|
+
}),
|
|
27245
|
+
/* @__PURE__ */ jsx("span", {
|
|
27246
|
+
className: "sr-only",
|
|
27247
|
+
role: "status",
|
|
27248
|
+
"aria-live": "polite",
|
|
27249
|
+
children: liveMessage
|
|
27250
|
+
})
|
|
27251
|
+
]
|
|
27252
|
+
})
|
|
27253
|
+
});
|
|
27254
|
+
}
|
|
27255
|
+
|
|
26717
27256
|
//#endregion
|
|
26718
27257
|
//#region src/components/MediaLibrary.tsx
|
|
26719
27258
|
/** Maps a coarse type-filter choice to the media list's `mimeType` filter. */
|
|
@@ -26747,8 +27286,13 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26747
27286
|
activeProvider,
|
|
26748
27287
|
onLocalSearchChange
|
|
26749
27288
|
]);
|
|
26750
|
-
const [
|
|
26751
|
-
const
|
|
27289
|
+
const [uploadDialogOpen, setUploadDialogOpen] = React$1.useState(false);
|
|
27290
|
+
const [enqueueRequest, setEnqueueRequest] = React$1.useState(null);
|
|
27291
|
+
const [uploadTarget, setUploadTarget] = React$1.useState(null);
|
|
27292
|
+
const [isFileDragActive, setIsFileDragActive] = React$1.useState(false);
|
|
27293
|
+
const enqueueIdRef = React$1.useRef(0);
|
|
27294
|
+
const dragDepthRef = React$1.useRef(0);
|
|
27295
|
+
const returnFocusRef = React$1.useRef(null);
|
|
26752
27296
|
const [loadedDimensions, setLoadedDimensions] = React$1.useState({});
|
|
26753
27297
|
const { data: providers } = useQuery({
|
|
26754
27298
|
queryKey: ["media-providers"],
|
|
@@ -26787,6 +27331,8 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26787
27331
|
providers,
|
|
26788
27332
|
_t
|
|
26789
27333
|
]);
|
|
27334
|
+
const canUpload = activeProviderInfo?.capabilities.upload ?? false;
|
|
27335
|
+
const canSearch = activeProviderInfo?.capabilities.search ?? false;
|
|
26790
27336
|
const cancelPendingDetailOpen = React$1.useCallback(() => {
|
|
26791
27337
|
if (detailOpenFrameRef.current === null) return;
|
|
26792
27338
|
window.cancelAnimationFrame(detailOpenFrameRef.current);
|
|
@@ -26809,130 +27355,76 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26809
27355
|
const handleDetailClosed = React$1.useCallback(() => {
|
|
26810
27356
|
setDetailItem(null);
|
|
26811
27357
|
}, []);
|
|
27358
|
+
const enqueueFiles = React$1.useCallback((files, returnFocus) => {
|
|
27359
|
+
if (!canUpload || !activeProviderInfo || files.length === 0) return;
|
|
27360
|
+
if (returnFocus) returnFocusRef.current = returnFocus;
|
|
27361
|
+
setUploadTarget({
|
|
27362
|
+
id: activeProviderInfo.id,
|
|
27363
|
+
name: activeProviderInfo.name
|
|
27364
|
+
});
|
|
27365
|
+
setEnqueueRequest({
|
|
27366
|
+
id: enqueueIdRef.current += 1,
|
|
27367
|
+
files
|
|
27368
|
+
});
|
|
27369
|
+
setUploadDialogOpen(true);
|
|
27370
|
+
}, [activeProviderInfo, canUpload]);
|
|
27371
|
+
const openUploadDialog = (event) => {
|
|
27372
|
+
if (!canUpload || !activeProviderInfo) return;
|
|
27373
|
+
returnFocusRef.current = event.currentTarget;
|
|
27374
|
+
setUploadTarget({
|
|
27375
|
+
id: activeProviderInfo.id,
|
|
27376
|
+
name: activeProviderInfo.name
|
|
27377
|
+
});
|
|
27378
|
+
setEnqueueRequest(null);
|
|
27379
|
+
setUploadDialogOpen(true);
|
|
27380
|
+
};
|
|
26812
27381
|
React$1.useEffect(() => {
|
|
26813
|
-
|
|
26814
|
-
|
|
26815
|
-
|
|
26816
|
-
|
|
26817
|
-
|
|
26818
|
-
|
|
26819
|
-
|
|
26820
|
-
|
|
26821
|
-
|
|
26822
|
-
|
|
26823
|
-
|
|
26824
|
-
|
|
26825
|
-
|
|
26826
|
-
|
|
26827
|
-
|
|
26828
|
-
|
|
26829
|
-
|
|
26830
|
-
|
|
26831
|
-
|
|
26832
|
-
|
|
26833
|
-
let uploaded = 0;
|
|
26834
|
-
let failed = 0;
|
|
26835
|
-
for (const file of fileArray) {
|
|
26836
|
-
try {
|
|
26837
|
-
await onUpload?.(file);
|
|
26838
|
-
uploaded++;
|
|
26839
|
-
} catch (error) {
|
|
26840
|
-
console.error("Upload failed:", error);
|
|
26841
|
-
failed++;
|
|
26842
|
-
}
|
|
26843
|
-
setUploadState({
|
|
26844
|
-
status: "uploading",
|
|
26845
|
-
progress: {
|
|
26846
|
-
current: uploaded + failed,
|
|
26847
|
-
total
|
|
26848
|
-
}
|
|
26849
|
-
});
|
|
26850
|
-
}
|
|
26851
|
-
if (failed === 0) setUploadState({
|
|
26852
|
-
status: "success",
|
|
26853
|
-
message: i18n._({
|
|
26854
|
-
id: "btYZgm",
|
|
26855
|
-
message: "{total, plural, one {File uploaded} other {# files uploaded}}",
|
|
26856
|
-
values: { total }
|
|
26857
|
-
})
|
|
26858
|
-
});
|
|
26859
|
-
else if (uploaded === 0) setUploadState({
|
|
26860
|
-
status: "error",
|
|
26861
|
-
message: i18n._({
|
|
26862
|
-
id: "TUbRbs",
|
|
26863
|
-
message: "{total, plural, one {Upload failed} other {All # uploads failed}}",
|
|
26864
|
-
values: { total }
|
|
26865
|
-
})
|
|
26866
|
-
});
|
|
26867
|
-
else setUploadState({
|
|
26868
|
-
status: "error",
|
|
26869
|
-
message: _t({
|
|
26870
|
-
id: "f5MgHH",
|
|
26871
|
-
message: "{uploaded} uploaded, {failed} failed",
|
|
26872
|
-
values: {
|
|
26873
|
-
uploaded,
|
|
26874
|
-
failed
|
|
26875
|
-
}
|
|
26876
|
-
})
|
|
26877
|
-
});
|
|
26878
|
-
} else if (activeProviderInfo?.capabilities.upload) {
|
|
26879
|
-
setUploadState({
|
|
26880
|
-
status: "uploading",
|
|
26881
|
-
progress: {
|
|
26882
|
-
current: 0,
|
|
26883
|
-
total
|
|
26884
|
-
}
|
|
26885
|
-
});
|
|
26886
|
-
let uploaded = 0;
|
|
26887
|
-
let failed = 0;
|
|
26888
|
-
for (const file of fileArray) {
|
|
26889
|
-
try {
|
|
26890
|
-
await uploadToProvider(activeProvider, file);
|
|
26891
|
-
uploaded++;
|
|
26892
|
-
} catch (error) {
|
|
26893
|
-
console.error("Upload failed:", error);
|
|
26894
|
-
failed++;
|
|
26895
|
-
}
|
|
26896
|
-
setUploadState({
|
|
26897
|
-
status: "uploading",
|
|
26898
|
-
progress: {
|
|
26899
|
-
current: uploaded + failed,
|
|
26900
|
-
total
|
|
26901
|
-
}
|
|
26902
|
-
});
|
|
26903
|
-
}
|
|
26904
|
-
if (failed === 0) setUploadState({
|
|
26905
|
-
status: "success",
|
|
26906
|
-
message: i18n._({
|
|
26907
|
-
id: "btYZgm",
|
|
26908
|
-
message: "{total, plural, one {File uploaded} other {# files uploaded}}",
|
|
26909
|
-
values: { total }
|
|
26910
|
-
})
|
|
26911
|
-
});
|
|
26912
|
-
else if (uploaded === 0) setUploadState({
|
|
26913
|
-
status: "error",
|
|
26914
|
-
message: i18n._({
|
|
26915
|
-
id: "TUbRbs",
|
|
26916
|
-
message: "{total, plural, one {Upload failed} other {All # uploads failed}}",
|
|
26917
|
-
values: { total }
|
|
26918
|
-
})
|
|
26919
|
-
});
|
|
26920
|
-
else setUploadState({
|
|
26921
|
-
status: "error",
|
|
26922
|
-
message: _t({
|
|
26923
|
-
id: "f5MgHH",
|
|
26924
|
-
message: "{uploaded} uploaded, {failed} failed",
|
|
26925
|
-
values: {
|
|
26926
|
-
uploaded,
|
|
26927
|
-
failed
|
|
26928
|
-
}
|
|
26929
|
-
})
|
|
26930
|
-
});
|
|
26931
|
-
refetchProviderMedia();
|
|
27382
|
+
const hasFiles = (event) => event.dataTransfer?.types.includes("Files") ?? false;
|
|
27383
|
+
const resetDrag = () => {
|
|
27384
|
+
dragDepthRef.current = 0;
|
|
27385
|
+
setIsFileDragActive(false);
|
|
27386
|
+
};
|
|
27387
|
+
const handleDragEnter = (event) => {
|
|
27388
|
+
if (!hasFiles(event)) return;
|
|
27389
|
+
event.preventDefault();
|
|
27390
|
+
if (uploadDialogOpen || !canUpload) return;
|
|
27391
|
+
dragDepthRef.current += 1;
|
|
27392
|
+
setIsFileDragActive(true);
|
|
27393
|
+
};
|
|
27394
|
+
const handleDragOver = (event) => {
|
|
27395
|
+
if (hasFiles(event)) event.preventDefault();
|
|
27396
|
+
};
|
|
27397
|
+
const handleDragLeave = (event) => {
|
|
27398
|
+
if (dragDepthRef.current === 0 || uploadDialogOpen || !canUpload) return;
|
|
27399
|
+
if (event.relatedTarget === null) {
|
|
27400
|
+
resetDrag();
|
|
27401
|
+
return;
|
|
26932
27402
|
}
|
|
26933
|
-
|
|
26934
|
-
|
|
26935
|
-
|
|
27403
|
+
dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
|
|
27404
|
+
if (dragDepthRef.current === 0) setIsFileDragActive(false);
|
|
27405
|
+
};
|
|
27406
|
+
const handleDrop = (event) => {
|
|
27407
|
+
if (!hasFiles(event)) return;
|
|
27408
|
+
event.preventDefault();
|
|
27409
|
+
resetDrag();
|
|
27410
|
+
if (uploadDialogOpen || !canUpload) return;
|
|
27411
|
+
enqueueFiles([...event.dataTransfer?.files ?? []], mediaHeadingRef.current);
|
|
27412
|
+
};
|
|
27413
|
+
window.addEventListener("dragenter", handleDragEnter);
|
|
27414
|
+
window.addEventListener("dragover", handleDragOver);
|
|
27415
|
+
window.addEventListener("dragleave", handleDragLeave);
|
|
27416
|
+
window.addEventListener("drop", handleDrop);
|
|
27417
|
+
return () => {
|
|
27418
|
+
window.removeEventListener("dragenter", handleDragEnter);
|
|
27419
|
+
window.removeEventListener("dragover", handleDragOver);
|
|
27420
|
+
window.removeEventListener("dragleave", handleDragLeave);
|
|
27421
|
+
window.removeEventListener("drop", handleDrop);
|
|
27422
|
+
};
|
|
27423
|
+
}, [
|
|
27424
|
+
canUpload,
|
|
27425
|
+
enqueueFiles,
|
|
27426
|
+
uploadDialogOpen
|
|
27427
|
+
]);
|
|
26936
27428
|
const providerTabs = React$1.useMemo(() => {
|
|
26937
27429
|
const tabs = [{
|
|
26938
27430
|
id: "local",
|
|
@@ -26954,8 +27446,6 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26954
27446
|
const currentItems = activeProvider === "local" ? items : [];
|
|
26955
27447
|
const currentProviderItems = activeProvider !== "local" ? providerData?.items || [] : [];
|
|
26956
27448
|
const currentLoading = activeProvider === "local" ? isLoading : providerLoading;
|
|
26957
|
-
const canUpload = activeProviderInfo?.capabilities.upload ?? false;
|
|
26958
|
-
const canSearch = activeProviderInfo?.capabilities.search ?? false;
|
|
26959
27449
|
const resultCount = activeProvider === "local" ? currentItems.length : currentProviderItems.length;
|
|
26960
27450
|
const hasMoreCurrentItems = activeProvider === "local" ? Boolean(hasMore) : Boolean(providerData?.nextCursor);
|
|
26961
27451
|
const resultCountText = resultCount > 0 && !hasMoreCurrentItems ? i18n._({
|
|
@@ -26976,9 +27466,51 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26976
27466
|
if (activeProvider === "local" && next.trim() === "") onLocalSearchChange?.("");
|
|
26977
27467
|
};
|
|
26978
27468
|
const showToolbar = resultCount > 0 || hasActiveQuery;
|
|
27469
|
+
const uploadFile = React$1.useCallback(async (file, options) => {
|
|
27470
|
+
if (!uploadTarget) throw new Error("Upload target unavailable");
|
|
27471
|
+
if (uploadTarget.id === "local") {
|
|
27472
|
+
if (!onUpload) throw new Error("Upload callback unavailable");
|
|
27473
|
+
await onUpload(file, options);
|
|
27474
|
+
return;
|
|
27475
|
+
}
|
|
27476
|
+
await uploadToProvider(uploadTarget.id, file, void 0, options);
|
|
27477
|
+
}, [onUpload, uploadTarget]);
|
|
27478
|
+
const handleUploadDialogClosed = React$1.useCallback(() => {
|
|
27479
|
+
setEnqueueRequest(null);
|
|
27480
|
+
setUploadTarget(null);
|
|
27481
|
+
const returnTarget = returnFocusRef.current;
|
|
27482
|
+
returnFocusRef.current = null;
|
|
27483
|
+
window.requestAnimationFrame(() => {
|
|
27484
|
+
(returnTarget?.isConnected ? returnTarget : mediaHeadingRef.current)?.focus();
|
|
27485
|
+
});
|
|
27486
|
+
}, []);
|
|
27487
|
+
const handleUploadQueueIdle = React$1.useCallback(() => {
|
|
27488
|
+
if (uploadTarget?.id !== "local") refetchProviderMedia();
|
|
27489
|
+
}, [refetchProviderMedia, uploadTarget?.id]);
|
|
26979
27490
|
return /* @__PURE__ */ jsxs("div", {
|
|
26980
27491
|
className: "space-y-4",
|
|
27492
|
+
"data-media-library": true,
|
|
26981
27493
|
children: [
|
|
27494
|
+
isFileDragActive && /* @__PURE__ */ jsx("div", {
|
|
27495
|
+
className: "pointer-events-none fixed inset-0 z-50 bg-kumo-base/70 p-4 backdrop-blur-sm sm:p-8",
|
|
27496
|
+
"aria-hidden": "true",
|
|
27497
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
27498
|
+
className: "flex h-full items-center justify-center rounded-2xl border-[3px] border-dashed border-kumo-brand/80 bg-kumo-tint/70",
|
|
27499
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
27500
|
+
className: "flex flex-col items-center gap-3 text-center",
|
|
27501
|
+
children: [/* @__PURE__ */ jsx(Upload, {
|
|
27502
|
+
className: "h-10 w-10 text-kumo-subtle",
|
|
27503
|
+
"aria-hidden": "true"
|
|
27504
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
27505
|
+
className: "text-lg font-semibold",
|
|
27506
|
+
children: _t({
|
|
27507
|
+
id: "8o3dPc",
|
|
27508
|
+
message: "Drop files to upload"
|
|
27509
|
+
})
|
|
27510
|
+
})]
|
|
27511
|
+
})
|
|
27512
|
+
})
|
|
27513
|
+
}),
|
|
26982
27514
|
/* @__PURE__ */ jsxs("div", {
|
|
26983
27515
|
className: "flex flex-wrap items-center justify-between gap-4",
|
|
26984
27516
|
children: [/* @__PURE__ */ jsx("h1", {
|
|
@@ -26989,62 +27521,20 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
26989
27521
|
id: "ia4TsE",
|
|
26990
27522
|
message: "Media Library"
|
|
26991
27523
|
})
|
|
26992
|
-
}), /* @__PURE__ */
|
|
27524
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
26993
27525
|
className: "flex items-center gap-3",
|
|
26994
|
-
children:
|
|
26995
|
-
|
|
26996
|
-
|
|
26997
|
-
|
|
26998
|
-
|
|
26999
|
-
|
|
27000
|
-
|
|
27001
|
-
|
|
27002
|
-
|
|
27003
|
-
|
|
27004
|
-
|
|
27005
|
-
|
|
27006
|
-
message: "Uploading..."
|
|
27007
|
-
}) })]
|
|
27008
|
-
}),
|
|
27009
|
-
uploadState.status === "success" && /* @__PURE__ */ jsxs("div", {
|
|
27010
|
-
className: "flex items-center gap-2 text-sm text-kumo-success",
|
|
27011
|
-
children: [/* @__PURE__ */ jsx(Check, {
|
|
27012
|
-
className: "h-4 w-4",
|
|
27013
|
-
"aria-hidden": "true"
|
|
27014
|
-
}), /* @__PURE__ */ jsx("span", { children: uploadState.message })]
|
|
27015
|
-
}),
|
|
27016
|
-
uploadState.status === "error" && /* @__PURE__ */ jsxs("div", {
|
|
27017
|
-
className: "flex items-center gap-2 text-sm text-kumo-danger",
|
|
27018
|
-
children: [/* @__PURE__ */ jsx(X, {
|
|
27019
|
-
className: "h-4 w-4",
|
|
27020
|
-
"aria-hidden": "true"
|
|
27021
|
-
}), /* @__PURE__ */ jsx("span", { children: uploadState.message })]
|
|
27022
|
-
}),
|
|
27023
|
-
canUpload && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
27024
|
-
onClick: () => fileInputRef.current?.click(),
|
|
27025
|
-
disabled: uploadState.status === "uploading",
|
|
27026
|
-
icon: uploadState.status === "uploading" ? /* @__PURE__ */ jsx(Loader, { size: "sm" }) : /* @__PURE__ */ jsx(Upload, {}),
|
|
27027
|
-
children: _t({
|
|
27028
|
-
id: "f9mKbV",
|
|
27029
|
-
message: "Upload to {0}",
|
|
27030
|
-
values: { 0: activeProviderInfo?.name || _t({
|
|
27031
|
-
id: "exYcTF",
|
|
27032
|
-
message: "Library"
|
|
27033
|
-
}) }
|
|
27034
|
-
})
|
|
27035
|
-
}), /* @__PURE__ */ jsx("input", {
|
|
27036
|
-
ref: fileInputRef,
|
|
27037
|
-
type: "file",
|
|
27038
|
-
multiple: true,
|
|
27039
|
-
accept: "image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx",
|
|
27040
|
-
className: "sr-only",
|
|
27041
|
-
onChange: handleFileSelect,
|
|
27042
|
-
"aria-label": _t({
|
|
27043
|
-
id: "sCtyHQ",
|
|
27044
|
-
message: "Upload files"
|
|
27045
|
-
})
|
|
27046
|
-
})] })
|
|
27047
|
-
]
|
|
27526
|
+
children: canUpload && /* @__PURE__ */ jsx(Button, {
|
|
27527
|
+
onClick: openUploadDialog,
|
|
27528
|
+
icon: /* @__PURE__ */ jsx(Upload, {}),
|
|
27529
|
+
children: _t({
|
|
27530
|
+
id: "f9mKbV",
|
|
27531
|
+
message: "Upload to {0}",
|
|
27532
|
+
values: { 0: activeProviderInfo?.name || _t({
|
|
27533
|
+
id: "exYcTF",
|
|
27534
|
+
message: "Library"
|
|
27535
|
+
}) }
|
|
27536
|
+
})
|
|
27537
|
+
})
|
|
27048
27538
|
})]
|
|
27049
27539
|
}),
|
|
27050
27540
|
providerTabs.length > 1 && /* @__PURE__ */ jsx(Tabs, {
|
|
@@ -27220,7 +27710,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
27220
27710
|
message: "Upload images, videos, and documents to keep reusable assets in one place."
|
|
27221
27711
|
}),
|
|
27222
27712
|
action: /* @__PURE__ */ jsx(Button, {
|
|
27223
|
-
onClick:
|
|
27713
|
+
onClick: openUploadDialog,
|
|
27224
27714
|
icon: /* @__PURE__ */ jsx(Upload, {}),
|
|
27225
27715
|
children: _t({
|
|
27226
27716
|
id: "HAKBY9",
|
|
@@ -27256,7 +27746,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
27256
27746
|
message: "Upload media to keep reusable assets in one place."
|
|
27257
27747
|
}),
|
|
27258
27748
|
action: /* @__PURE__ */ jsx(Button, {
|
|
27259
|
-
onClick:
|
|
27749
|
+
onClick: openUploadDialog,
|
|
27260
27750
|
icon: /* @__PURE__ */ jsx(Upload, {}),
|
|
27261
27751
|
children: _t({
|
|
27262
27752
|
id: "HAKBY9",
|
|
@@ -27388,6 +27878,20 @@ function MediaLibrary({ items = [], isLoading, onUpload, onItemUpdated, hasMore,
|
|
|
27388
27878
|
})
|
|
27389
27879
|
})
|
|
27390
27880
|
}),
|
|
27881
|
+
/* @__PURE__ */ jsx(MediaUploadDialog, {
|
|
27882
|
+
open: uploadDialogOpen,
|
|
27883
|
+
providerName: uploadTarget?.name ?? activeProviderInfo?.name ?? _t({
|
|
27884
|
+
id: "exYcTF",
|
|
27885
|
+
message: "Library"
|
|
27886
|
+
}),
|
|
27887
|
+
accept: uploadTarget?.id === "local" ? LOCAL_MEDIA_UPLOAD_ACCEPT : void 0,
|
|
27888
|
+
enqueueRequest,
|
|
27889
|
+
onEnqueueRequestConsumed: (id) => setEnqueueRequest((current) => current?.id === id ? null : current),
|
|
27890
|
+
onOpenChange: setUploadDialogOpen,
|
|
27891
|
+
onCloseComplete: handleUploadDialogClosed,
|
|
27892
|
+
onQueueIdle: handleUploadQueueIdle,
|
|
27893
|
+
upload: uploadFile
|
|
27894
|
+
}),
|
|
27391
27895
|
detailItem && /* @__PURE__ */ jsx(MediaDetailPanel, {
|
|
27392
27896
|
open: isDetailOpen,
|
|
27393
27897
|
item: detailItem,
|
|
@@ -47719,7 +48223,7 @@ function MediaPage() {
|
|
|
47719
48223
|
getNextPageParam: (lastPage) => lastPage.nextCursor
|
|
47720
48224
|
});
|
|
47721
48225
|
const uploadMutation = useMutation({
|
|
47722
|
-
mutationFn: (file) => uploadMedia(file),
|
|
48226
|
+
mutationFn: ({ file, options }) => uploadMedia(file, options),
|
|
47723
48227
|
onSuccess: () => {
|
|
47724
48228
|
queryClient.invalidateQueries({ queryKey: ["media"] });
|
|
47725
48229
|
}
|
|
@@ -47733,8 +48237,11 @@ function MediaPage() {
|
|
|
47733
48237
|
isLoading: isLoading || isFetchingNextPage,
|
|
47734
48238
|
hasMore: !!hasNextPage,
|
|
47735
48239
|
onLoadMore: () => void fetchNextPage(),
|
|
47736
|
-
onUpload: async (file) => {
|
|
47737
|
-
await uploadMutation.mutateAsync(
|
|
48240
|
+
onUpload: async (file, options) => {
|
|
48241
|
+
await uploadMutation.mutateAsync({
|
|
48242
|
+
file,
|
|
48243
|
+
options
|
|
48244
|
+
});
|
|
47738
48245
|
},
|
|
47739
48246
|
onLocalSearchChange: setSearch,
|
|
47740
48247
|
onLocalMimeFilterChange: setMimeFilter
|