@rebasepro/studio 0.24.0 → 0.24.1-canary.gcb918fc
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/{ApiExplorer-CCtczj3F.js → ApiExplorer-Bzt7Ye49.js} +5 -3
- package/dist/ApiExplorer-Bzt7Ye49.js.map +1 -0
- package/dist/{ApiKeysView-B66TdzhH.js → ApiKeysView-Bqf6XYR1.js} +2 -1
- package/dist/{ApiKeysView-B66TdzhH.js.map → ApiKeysView-Bqf6XYR1.js.map} +1 -1
- package/dist/{BackupsView-DmSMzAbT.js → BackupsView-BhOHB6iP.js} +6 -20
- package/dist/BackupsView-BhOHB6iP.js.map +1 -0
- package/dist/{BranchesView-DP1a87E-.js → BranchesView-BHfwGTYc.js} +5 -3
- package/dist/BranchesView-BHfwGTYc.js.map +1 -0
- package/dist/{CronJobsView-TDErHTuK.js → CronJobsView-CMNK7eWO.js} +52 -59
- package/dist/CronJobsView-CMNK7eWO.js.map +1 -0
- package/dist/{JSEditor-B59pwg4c.js → JSEditor-DIO2Mw8q.js} +115 -96
- package/dist/JSEditor-DIO2Mw8q.js.map +1 -0
- package/dist/{LogsExplorer-BKM4yfP8.js → LogsExplorer-C-bwDFIl.js} +19 -23
- package/dist/LogsExplorer-C-bwDFIl.js.map +1 -0
- package/dist/{RLSEditor-BlLLV6xs.js → RLSEditor-jFD_d06f.js} +10 -8
- package/dist/RLSEditor-jFD_d06f.js.map +1 -0
- package/dist/{SQLEditor-1An7w0j6.js → SQLEditor-D5Ci4pyW.js} +372 -545
- package/dist/SQLEditor-D5Ci4pyW.js.map +1 -0
- package/dist/{SchemaVisualizer-DCLVfA5t.js → SchemaVisualizer-D-rAqwul.js} +479 -206
- package/dist/SchemaVisualizer-D-rAqwul.js.map +1 -0
- package/dist/{StorageView-CbsHuCBf.js → StorageView-CPkDKbsX.js} +498 -241
- package/dist/StorageView-CPkDKbsX.js.map +1 -0
- package/dist/assets/dagre-layout.worker-BFnhAzuR.js.map +1 -0
- package/dist/components/SQLEditor/SQLEditor.d.ts +11 -0
- package/dist/components/SchemaVisualizer/dagre-layout.d.ts +32 -0
- package/dist/components/SchemaVisualizer/dagre-layout.worker.d.ts +13 -0
- package/dist/components/SchemaVisualizer/layout-runner.d.ts +23 -0
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +21 -10
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +38 -1
- package/dist/components/StorageView/useVisibleFileMetadata.d.ts +46 -0
- package/dist/dagre-layout.worker-j9NbH9Gb.js +27 -0
- package/dist/dagre-layout.worker-j9NbH9Gb.js.map +1 -0
- package/dist/index.es.js +16 -14
- package/dist/index.es.js.map +1 -1
- package/dist/sql_utils-BZD31xE3.js +478 -0
- package/dist/sql_utils-BZD31xE3.js.map +1 -0
- package/dist/utils/sql_utils.d.ts +60 -0
- package/package.json +9 -9
- package/dist/ApiExplorer-CCtczj3F.js.map +0 -1
- package/dist/BackupsView-DmSMzAbT.js.map +0 -1
- package/dist/BranchesView-DP1a87E-.js.map +0 -1
- package/dist/CronJobsView-TDErHTuK.js.map +0 -1
- package/dist/JSEditor-B59pwg4c.js.map +0 -1
- package/dist/LogsExplorer-BKM4yfP8.js.map +0 -1
- package/dist/RLSEditor-BlLLV6xs.js.map +0 -1
- package/dist/SQLEditor-1An7w0j6.js.map +0 -1
- package/dist/SchemaVisualizer-DCLVfA5t.js.map +0 -1
- package/dist/StorageView-CbsHuCBf.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StorageView-CPkDKbsX.js","names":[],"sources":["../src/components/StorageView/useVisibleFileMetadata.ts","../src/components/StorageView/StorageView.tsx"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type { DownloadConfig, StorageSource } from \"@rebasepro/types\";\n\n/**\n * What the storage browser shows of a file beyond its name. The listing carries\n * the size and version — and, except on S3, the type — of every file; the rest\n * is read per file, from the server, when it is needed.\n */\nexport interface FileMetadata {\n size?: number;\n contentType?: string;\n /**\n * A link minted when the metadata was read, for the thumbnail only: a\n * private file's token expires, and a link to share or download is minted\n * when it is used (see `FileLink` in `StorageView.tsx`).\n */\n downloadUrl?: string;\n}\n\nexport function fileMetadataOf(config: DownloadConfig): FileMetadata {\n return {\n size: config.metadata?.size,\n contentType: config.metadata?.contentType,\n downloadUrl: config.url ?? undefined\n };\n}\n\n/**\n * How many metadata requests run at once: what one origin gets over HTTP/1.1.\n * Each is a round trip to the server, an auth hook and a HEAD on the bucket.\n */\nexport const METADATA_CONCURRENCY = 6;\n\nconst NO_METADATA: ReadonlyMap<string, FileMetadata> = new Map();\n\n/**\n * The metadata asked for one listing: each path once, {@link METADATA_CONCURRENCY}\n * at a time, through the source that listed it. Cancelled when the listing is\n * replaced, so an answer for a folder or source the view has left is dropped.\n */\nclass MetadataRun<F extends { fullPath: string }> {\n private readonly requested = new Set<string>();\n private queue: string[] = [];\n private inFlight = 0;\n private cancelled = false;\n private answered: [string, FileMetadata][] = [];\n private flushQueued = false;\n\n constructor(\n private readonly source: StorageSource | null,\n private readonly files: ReadonlyMap<string, F>,\n private readonly publish: (answered: [string, FileMetadata][]) => void,\n /** Whether a file is worth a request at all — see `useVisibleFileMetadata`. */\n private readonly wants: (file: F) => boolean,\n /** Paths already answered for this listing, by an earlier run over it. */\n private readonly known: ReadonlyMap<string, FileMetadata>\n ) {}\n\n want(path: string | undefined): void {\n if (this.cancelled || !this.source || !path || this.requested.has(path) || this.known.has(path)) return;\n const file = this.files.get(path);\n if (!file || !this.wants(file)) return;\n this.requested.add(path);\n this.queue.push(path);\n this.pump();\n }\n\n cancel(): void {\n this.cancelled = true;\n this.queue = [];\n this.answered = [];\n }\n\n private pump(): void {\n const source = this.source;\n if (!source) return;\n while (!this.cancelled && this.inFlight < METADATA_CONCURRENCY && this.queue.length > 0) {\n const path = this.queue.shift();\n if (path === undefined) break;\n this.inFlight++;\n source.getSignedUrl(path)\n .then(config => this.deliver(path, fileMetadataOf(config)))\n // A file whose metadata cannot be read is still listed, with none.\n .catch(() => this.deliver(path, {}))\n .finally(() => {\n this.inFlight--;\n this.pump();\n });\n }\n }\n\n /** Answers that arrive together are published as one update. */\n private deliver(path: string, metadata: FileMetadata): void {\n if (this.cancelled) return;\n this.answered.push([path, metadata]);\n if (this.flushQueued) return;\n this.flushQueued = true;\n queueMicrotask(() => {\n this.flushQueued = false;\n if (this.cancelled || this.answered.length === 0) return;\n const answered = this.answered;\n this.answered = [];\n this.publish(answered);\n });\n }\n}\n\n/**\n * Metadata for the files of a listing, asked for as each comes on screen —\n * and only for those `wants` says need it: the listing already describes most\n * files (see `StorageReference.size`), so what is left is a file a source\n * listed bare, and the link an image's thumbnail is drawn from.\n *\n * The browser used to ask for every file's metadata before it showed any of\n * them: a folder of 1,000 files was 1,001 requests and seconds of a spinner.\n * The listing is shown as it comes back; a file's metadata is asked for when\n * its element — given {@link observe} as its `ref`, and its path as\n * `data-storage-path` — scrolls into view, a few requests at a time.\n *\n * Kept per listing: keyed by path, and dropped when `files` or `source`\n * changes — another folder, another source, or the same folder read again\n * after an upload or a delete, whose files may have changed. Without an\n * `IntersectionObserver` every file of the listing is asked for, still a few\n * at a time and still after the listing is shown.\n */\nexport function useVisibleFileMetadata<F extends { fullPath: string }>(\n source: StorageSource | null,\n files: readonly F[],\n wants: (file: F) => boolean = () => true\n): {\n metadata: ReadonlyMap<string, FileMetadata>;\n observe: (element: HTMLElement | null) => (() => void) | undefined;\n} {\n const [store, setStore] = useState<{ files: readonly F[]; entries: ReadonlyMap<string, FileMetadata> }>(\n () => ({ files, entries: NO_METADATA })\n );\n // What is already answered for this listing, for a run started over it\n // again — `wants` changed, say the grid was switched to — so it does not\n // ask for those twice.\n // Kept current by a layout effect declared before the one that reads it,\n // so it runs first in the same commit.\n const storeRef = useRef(store);\n useLayoutEffect(() => {\n storeRef.current = store;\n });\n const runRef = useRef<MetadataRun<F> | null>(null);\n const elementsRef = useRef(new Set<HTMLElement>());\n const observerRef = useRef<IntersectionObserver | null>(null);\n\n const observer = useCallback((): IntersectionObserver | null => {\n if (observerRef.current) return observerRef.current;\n if (typeof IntersectionObserver !== \"function\") return null;\n observerRef.current = new IntersectionObserver(entries => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) runRef.current?.want(entry.target.dataset.storagePath);\n }\n }, { rootMargin: \"200px\" });\n return observerRef.current;\n }, []);\n\n // Layout, not passive: it runs in the commit that shows the listing,\n // before an observer can report an element of it.\n useLayoutEffect(() => {\n const known = storeRef.current.files === files ? storeRef.current.entries : NO_METADATA;\n const run = new MetadataRun(source, new Map(files.map(file => [file.fullPath, file])), answered => setStore(previous => ({\n files,\n entries: new Map([...(previous.files === files ? previous.entries : NO_METADATA), ...answered])\n })), wants, known);\n runRef.current = run;\n // The elements already on screen — the same ones, when a folder is\n // read again — are looked at afresh for this listing.\n const io = observer();\n for (const element of elementsRef.current) {\n if (io) {\n io.unobserve(element);\n io.observe(element);\n } else {\n run.want(element.dataset.storagePath);\n }\n }\n return () => run.cancel();\n }, [source, files, wants, observer]);\n\n useEffect(() => () => {\n observerRef.current?.disconnect();\n observerRef.current = null;\n }, []);\n\n const observe = useCallback((element: HTMLElement | null) => {\n if (!element) return undefined;\n elementsRef.current.add(element);\n const io = observer();\n if (io) io.observe(element);\n else runRef.current?.want(element.dataset.storagePath);\n return () => {\n elementsRef.current.delete(element);\n io?.unobserve(element);\n };\n }, [observer]);\n\n return { metadata: store.files === files ? store.entries : NO_METADATA, observe };\n}\n","\nimport React, { useState, useEffect, useCallback, useMemo, useRef } from \"react\";\nimport {\n ArrowLeftIcon,\n Button,\n Checkbox,\n CheckIcon,\n Chip,\n CircularProgress,\n cls,\n CopyIcon,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n DownloadIcon,\n FileIcon,\n FileTextIcon,\n FileUpload,\n FolderIcon,\n FolderPlusIcon,\n HomeIcon,\n IconButton,\n iconSize,\n ImageIcon,\n LayoutGridIcon,\n ListIcon,\n LoadingButton,\n Music2Icon,\n PlusIcon,\n RefreshCwIcon,\n ResizablePanels,\n Select,\n SelectItem,\n Table,\n TableBody,\n TableCell,\n TableHeader,\n TableRow,\n TextField,\n Tooltip,\n Trash2Icon,\n Typography,\n UploadCloudIcon,\n VideoIcon,\n XIcon,\n isKeyHandled\n} from \"@rebasepro/ui\";\nimport { useStorageSource, useStorageSources, useSnackbarController, ErrorView, useApiBase, useApiConfig, useTranslation } from \"@rebasepro/app\";\nimport { DEFAULT_STORAGE_SOURCE_KEY, isPublicStorageKey, type DownloadConfig, type StorageReference, type StorageSource } from \"@rebasepro/types\";\nimport { classifyLoadFailure, type LoadFailure } from \"../load-failure\";\nimport { useVisibleFileMetadata } from \"./useVisibleFileMetadata\";\nimport { useSearchParams } from \"react-router\";\nimport { useDropzone } from \"react-dropzone\";\n\n// ──────────────────────────────────────────────\n// Types\n// ──────────────────────────────────────────────\n\ninterface StorageFile {\n name: string;\n fullPath: string;\n isFolder: boolean;\n /**\n * As the listing described it, or once the file's metadata is read — see\n * `useVisibleFileMetadata`. An S3 listing carries no `contentType`.\n */\n size?: number;\n contentType?: string;\n downloadUrl?: string;\n}\n\n/**\n * A link to one file, as the preview shows, downloads and copies it.\n *\n * Minted when it is used rather than taken from the listing: a private file's\n * URL carries a download token (five minutes by default) minted when the folder\n * was listed, and a preview, a download or a copied link built from that one\n * stopped working while the page stayed open.\n */\ninterface FileLink {\n url: string;\n /**\n * How long the link's token was minted for, in seconds: a link to a private\n * file, which opens it for whoever holds the link until then. Absent when\n * the source does not say.\n */\n expiresInSeconds?: number;\n /** A public file's link: no token, open to anyone, and it does not expire. */\n isPublic: boolean;\n /** What the metadata minted with the link says about the file. */\n size?: number;\n contentType?: string;\n}\n\nfunction fileLinkOf(config: DownloadConfig, fullPath: string): FileLink | null {\n if (!config.url) return null;\n const metadata = config.metadata;\n return {\n url: config.url,\n expiresInSeconds: metadata?.token && typeof metadata.tokenExpiresIn === \"number\" ? metadata.tokenExpiresIn : undefined,\n // The SDK skips the metadata round trip only for a key under the public\n // prefix, and the server says so on every other public object.\n isPublic: metadata ? metadata.public === true : isPublicStorageKey(fullPath),\n size: metadata?.size,\n contentType: metadata?.contentType\n };\n}\n\n/** `file`, with what the link minted for it says where the listing did not. */\nfunction withLinkFacts(file: StorageFile, link: FileLink | null): StorageFile {\n return {\n ...file,\n size: file.size ?? link?.size,\n contentType: file.contentType ?? link?.contentType\n };\n}\n\n/** A URL the browser can open from anywhere: relative ones are this page's origin's. */\nfunction absoluteUrl(url: string): string {\n return url.startsWith(\"http\") ? url : `${window.location.origin}${url.startsWith(\"/\") ? \"\" : \"/\"}${url}`;\n}\n\n// ──────────────────────────────────────────────\n// Helpers\n// ──────────────────────────────────────────────\n\n/**\n * The query parameter holding the folder being browsed.\n *\n * Namespaced because Studio does not always own the URL it renders under: the\n * SaaS console embeds this view in a page that keeps its own state in `?tab=`\n * and `?sub=`, and a bare `path` is a name any host app might already be using.\n */\nconst STORAGE_PATH_PARAM = \"storagePath\";\n\nfunction formatFileSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\nconst IMAGE_EXTENSIONS = [\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\", \"svg\"];\nconst VIDEO_EXTENSIONS = [\"mp4\", \"webm\", \"ogg\", \"mov\"];\nconst AUDIO_EXTENSIONS = [\"mp3\", \"wav\", \"ogg\", \"m4a\"];\n\n/**\n * The icon for a file: by its stored type, or — where the listing carried\n * none, as an S3 listing does not — by its extension.\n */\nfunction getFileIcon(contentType: string | undefined, name: string) {\n const ext = getExtension(name).toLowerCase();\n if (contentType ? contentType.startsWith(\"image/\") : IMAGE_EXTENSIONS.includes(ext)) return ImageIcon;\n if (contentType ? contentType.startsWith(\"video/\") : VIDEO_EXTENSIONS.includes(ext)) return VideoIcon;\n if (contentType ? contentType.startsWith(\"audio/\") : AUDIO_EXTENSIONS.includes(ext)) return Music2Icon;\n return FileTextIcon;\n}\n\nfunction getExtension(name: string): string {\n const parts = name.split(\".\");\n return parts.length > 1 ? parts[parts.length - 1].toUpperCase() : \"\";\n}\n\n/** Whether the grid draws `file` as a thumbnail — which needs a link to it. */\nfunction isImageFile(file: { name: string; contentType?: string }): boolean {\n return file.contentType?.startsWith(\"image/\") === true || IMAGE_EXTENSIONS.includes(getExtension(file.name).toLowerCase());\n}\n\nfunction breadcrumbSegments(path: string, rootLabel: string): { label: string; path: string }[] {\n if (!path || path === \"/\") return [{ label: rootLabel,\npath: \"\" }];\n const parts = path.split(\"/\").filter(Boolean);\n const segments = [{ label: rootLabel,\npath: \"\" }];\n let accumulated = \"\";\n for (const part of parts) {\n accumulated = accumulated ? `${accumulated}/${part}` : part;\n segments.push({ label: part,\npath: accumulated });\n }\n return segments;\n}\n\n/**\n * Everything directly under `prefix`, across every page.\n *\n * A listing answers a page at a time — a thousand entries on the built-in\n * controllers — and hands back a token for the next. Reading only the first\n * showed a large folder cut short, and deleted the first page of it.\n */\nasync function listAllPages(\n source: StorageSource,\n prefix: string\n): Promise<{ items: StorageReference[]; prefixes: StorageReference[] }> {\n const items: StorageReference[] = [];\n const prefixes: StorageReference[] = [];\n let pageToken: string | undefined;\n do {\n const page = pageToken\n ? await source.listObjects(prefix, { pageToken })\n : await source.listObjects(prefix);\n items.push(...(page.items ?? []));\n prefixes.push(...(page.prefixes ?? []));\n pageToken = page.nextPageToken;\n } while (pageToken);\n return { items, prefixes };\n}\n\n/** How many deletes run at once: one at a time, a folder of 1,000 files took a minute. */\nconst DELETE_CONCURRENCY = 8;\n\n/**\n * `work` for each of `items`, at most `limit` at once. The first failure stops\n * what has not started; what has started is let finish, then it is thrown.\n */\nasync function forEachBounded<T>(items: readonly T[], limit: number, work: (item: T) => Promise<void>): Promise<void> {\n const run: { next: number; failure?: { error: unknown } } = { next: 0 };\n const lane = async (): Promise<void> => {\n while (!run.failure && run.next < items.length) {\n const item = items[run.next++];\n try {\n await work(item);\n } catch (error: unknown) {\n run.failure ??= { error };\n }\n }\n };\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, lane));\n if (run.failure) throw run.failure.error;\n}\n\n/**\n * Delete a folder: every file in it, every folder below it, and the folder\n * itself — the directory on local disk, or the zero-byte `name/` marker that\n * \"New folder\" writes on S3 and GCS, which no listing shows as an entry.\n *\n * The files of a level go {@link DELETE_CONCURRENCY} at a time; the folders\n * below it one after another, so no more than that are ever in flight, and a\n * folder's own marker goes last, once everything in it has.\n */\nasync function deleteFolderRecursive(source: StorageSource, prefix: string): Promise<void> {\n const { items, prefixes } = await listAllPages(source, prefix);\n await forEachBounded(items, DELETE_CONCURRENCY, item => source.deleteObject(item.fullPath));\n for (const sub of prefixes) {\n await deleteFolderRecursive(source, sub.fullPath);\n }\n try {\n await source.deleteObject(prefix.endsWith(\"/\") ? prefix : `${prefix}/`);\n } catch {\n // A folder with no object of its own (most object-store folders) has\n // nothing to delete here. Whether it is gone is read back by the\n // caller from the folder that held it.\n }\n}\n\n// ──────────────────────────────────────────────\n// Upload Dialog\n// ──────────────────────────────────────────────\n\nfunction UploadDialog({\n open,\n currentPath,\n onClose,\n onUpload\n}: {\n open: boolean;\n currentPath: string;\n onClose: () => void;\n /** `false` when nothing was uploaded and the dialog should stay open. */\n onUpload: (files: File[]) => Promise<boolean>;\n}) {\n const { t } = useTranslation();\n const [uploading, setUploading] = useState(false);\n const [selectedFiles, setSelectedFiles] = useState<File[]>([]);\n const [error, setError] = useState<string | null>(null);\n\n const handleFilesAdded = useCallback((files: File[]) => {\n setSelectedFiles(prev => [...prev, ...files]);\n }, []);\n\n const handleRemoveFile = useCallback((index: number) => {\n setSelectedFiles(prev => prev.filter((_, i) => i !== index));\n }, []);\n\n const handleUpload = useCallback(async () => {\n if (selectedFiles.length === 0) return;\n setUploading(true);\n setError(null);\n try {\n if (await onUpload(selectedFiles)) {\n setSelectedFiles([]);\n onClose();\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : t(\"studio_storage_upload_failed\"));\n } finally {\n setUploading(false);\n }\n }, [selectedFiles, onUpload, onClose, t]);\n\n const handleClose = useCallback(() => {\n if (!uploading) {\n setSelectedFiles([]);\n setError(null);\n onClose();\n }\n }, [uploading, onClose]);\n\n return (\n <Dialog open={open} onOpenChange={(o) => !o && handleClose()} maxWidth=\"md\">\n <DialogTitle>\n {t(\"studio_storage_upload_dialog_title\")}\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark mt-0.5 block\">\n {t(\"studio_storage_upload_to\")} <span className=\"font-mono text-primary\">/{currentPath || t(\"studio_storage_root_folder\")}</span>\n </Typography>\n </DialogTitle>\n <DialogContent className=\"space-y-4\">\n <FileUpload\n onFilesAdded={handleFilesAdded}\n size=\"large\"\n uploadDescription={\n <div className=\"flex flex-col items-center justify-center pointer-events-none\">\n <UploadCloudIcon className=\"text-surface-accent-400 mb-2 w-8 h-8\"/>\n <Typography variant=\"label\">\n {t(\"studio_storage_drop_or_browse\")}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_any_file_type\")}\n </Typography>\n </div>\n }\n />\n\n {error && (\n <Typography variant=\"caption\" className=\"text-red-500 block whitespace-pre-line\">\n {error}\n </Typography>\n )}\n\n {selectedFiles.length > 0 && (\n <div className=\"space-y-2\">\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_selected_files\", { count: selectedFiles.length })}\n </Typography>\n <div className=\"max-h-40 overflow-auto space-y-1\">\n {selectedFiles.map((file, index) => (\n <div\n key={`${file.name}-${index}`}\n className=\"flex items-center justify-between p-2 rounded bg-surface-raised\"\n >\n <div className=\"flex-1 min-w-0 mr-2\">\n <Typography variant=\"body2\" className=\"truncate\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n <IconButton aria-label={t(\"remove\")}\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n handleRemoveFile(index);\n }}\n disabled={uploading}\n >\n <XIcon size={14}/>\n </IconButton>\n </div>\n ))}\n </div>\n </div>\n )}\n </DialogContent>\n\n <DialogActions>\n <Button variant=\"text\" onClick={handleClose} disabled={uploading}>\n {t(\"studio_storage_cancel\")}\n </Button>\n <Button\n variant=\"filled\"\n onClick={handleUpload}\n disabled={selectedFiles.length === 0 || uploading}\n startIcon={uploading ? <CircularProgress size=\"smallest\"/> : <UploadCloudIcon size={14}/>}\n >\n {uploading\n ? t(\"studio_storage_uploading\")\n : selectedFiles.length > 0\n ? t(\"studio_storage_upload_count\", { count: selectedFiles.length })\n : t(\"studio_storage_upload\")}\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n// ──────────────────────────────────────────────\n// FileIcon preview panel\n// ──────────────────────────────────────────────\n\nfunction FilePreviewPanel({\n file,\n onClose,\n onDelete,\n link,\n mintLink\n}: {\n file: StorageFile;\n onClose: () => void;\n onDelete: () => void;\n /** The link minted when the file was opened, shown and previewed. */\n link: FileLink | null;\n /** A fresh link for a download or a copy, minted at the click. */\n mintLink: () => Promise<FileLink | null>;\n}) {\n const { t } = useTranslation();\n const snackbarController = useSnackbarController();\n const isImage = file.contentType?.startsWith(\"image/\");\n const isVideo = file.contentType?.startsWith(\"video/\");\n const isAudio = file.contentType?.startsWith(\"audio/\");\n const FileIconComponent = getFileIcon(file.contentType, file.name);\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [urlCopied, setUrlCopied] = useState(false);\n\n const reportLinkError = (e: unknown) => {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n };\n\n const handleDownload = () => {\n mintLink()\n .then(fresh => { if (fresh) window.open(fresh.url, \"_blank\"); })\n .catch(reportLinkError);\n };\n\n const handleCopy = () => {\n mintLink()\n .then(async fresh => {\n if (!fresh) return;\n await navigator.clipboard.writeText(absoluteUrl(fresh.url));\n setUrlCopied(true);\n setTimeout(() => setUrlCopied(false), 2000);\n })\n .catch(reportLinkError);\n };\n\n return (\n <>\n <div className={cls(\n \"flex flex-col h-full border-l\",\n defaultBorderMixin,\n \"bg-surface-card\"\n )}>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between p-3 border-b shrink-0\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"font-medium truncate flex-1 mr-2\">\n {file.name}\n </Typography>\n <div className=\"flex items-center gap-0.5\">\n {link && (\n <Tooltip title={t(\"studio_storage_download\")}>\n <IconButton aria-label={t(\"studio_storage_download\")}\n size=\"small\"\n onClick={handleDownload}\n >\n <DownloadIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <Tooltip title={t(\"studio_storage_delete\")}>\n <IconButton aria-label={t(\"studio_storage_delete\")}\n size=\"small\"\n onClick={() => setDeleteDialogOpen(true)}\n className=\"text-red-500 hover:bg-red-50 dark:hover:bg-red-900/20\"\n >\n <Trash2Icon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <IconButton aria-label={t(\"close\")} size=\"small\" onClick={onClose}>\n <XIcon size={iconSize.smallest}/>\n </IconButton>\n </div>\n </div>\n\n {/* Preview */}\n <div className=\"flex-1 overflow-auto\">\n <div className={cls(\"flex flex-col items-center justify-center min-h-[200px] p-4 bg-surface-sheet border-b\", defaultBorderMixin)}>\n {(() => {\n const ext = getExtension(file.name)?.toLowerCase() || \"\";\n const isImage = file.contentType?.startsWith(\"image/\") || IMAGE_EXTENSIONS.includes(ext);\n const isVideo = file.contentType?.startsWith(\"video/\") || VIDEO_EXTENSIONS.includes(ext);\n const isAudio = file.contentType?.startsWith(\"audio/\") || AUDIO_EXTENSIONS.includes(ext);\n const previewUrl = link?.url;\n\n if (isImage && previewUrl) {\n return (\n <img\n src={previewUrl}\n alt={file.name}\n className=\"max-w-full max-h-[400px] object-contain rounded-md shadow-sm\"\n />\n );\n } else if (isVideo && previewUrl) {\n return (\n <video\n src={previewUrl}\n className=\"max-w-full max-h-[400px] rounded-md\"\n controls\n />\n );\n } else if (isAudio && previewUrl) {\n return (\n <div className=\"flex flex-col items-center gap-4\">\n <Music2Icon className=\"text-surface-accent-400 w-10 h-10\"/>\n <audio src={previewUrl} controls className=\"w-full max-w-xs\"/>\n </div>\n );\n } else {\n return (\n <div className=\"flex flex-col items-center gap-3 text-surface-accent-400\">\n <FileIconComponent className=\"w-10 h-10\"/>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n {t(\"studio_storage_no_preview\")}\n </Typography>\n </div>\n );\n }\n })()}\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"p-4 space-y-3\">\n <div>\n <Typography variant=\"label\" color=\"secondary\" className=\"mb-1 block\">\n {t(\"studio_storage_file_info\")}\n </Typography>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_name\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] break-all\">\n {file.name}\n </Typography>\n </div>\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_type\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {file.contentType || t(\"studio_storage_type_unknown\")}\n </Typography>\n </div>\n {file.size !== undefined && (\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_size\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {formatFileSize(file.size)}\n </Typography>\n </div>\n )}\n <div>\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_extension\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono\">\n {getExtension(file.name) || \"—\"}\n </Typography>\n </div>\n <div className=\"col-span-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px]\">\n {t(\"studio_storage_path\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-[13px] font-mono break-all\">\n {file.fullPath}\n </Typography>\n </div>\n </div>\n\n {link && (\n <div className=\"pt-2\">\n <Typography variant=\"caption\" className=\"text-surface-accent-500 text-[11px] block mb-1\">\n {t(\"studio_storage_url\")}\n </Typography>\n <div\n className={cls(\n \"flex items-center gap-2 p-2 rounded cursor-pointer transition-colors\",\n \"bg-surface-raised hover:bg-surface-raised-hover\"\n )}\n onClick={handleCopy}\n >\n <Typography variant=\"caption\" className=\"font-mono text-[11px] truncate flex-1 min-w-0 text-primary\">\n {absoluteUrl(link.url)}\n </Typography>\n <Tooltip title={urlCopied ? t(\"studio_storage_url_copied\") : t(\"studio_storage_copy_url\")}>\n <div className=\"shrink-0\">\n {urlCopied\n ? <CheckIcon size={14} className=\"text-green-500\"/>\n : <CopyIcon size={14} className=\"text-surface-accent-400\"/>\n }\n </div>\n </Tooltip>\n </div>\n {link.isPublic ? (\n <Typography variant=\"caption\" color=\"secondary\" className=\"block mt-1 text-[11px]\">\n {t(\"studio_storage_url_public\")}\n </Typography>\n ) : link.expiresInSeconds !== undefined ? (\n <Typography variant=\"caption\" color=\"secondary\" className=\"block mt-1 text-[11px]\">\n {t(\"studio_storage_url_temporary\", { minutes: Math.max(1, Math.round(link.expiresInSeconds / 60)) })}\n </Typography>\n ) : null}\n </div>\n )}\n </div>\n </div>\n\n {/* Delete Confirmation */}\n <Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>\n <DialogTitle hidden>{t(\"studio_storage_delete_file_title\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"mb-2\">\n {t(\"studio_storage_delete_file_question\")}\n </Typography>\n <Typography className=\"text-surface-accent-600 dark:text-surface-accent-400\">\n {t(\"studio_storage_delete_file_body\", { name: file.name })}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => setDeleteDialogOpen(false)}>\n {t(\"studio_storage_cancel\")}\n </Button>\n <Button\n variant=\"filled\"\n color=\"error\"\n onClick={() => {\n setDeleteDialogOpen(false);\n onDelete();\n }}\n >\n {t(\"studio_storage_delete\")}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n}\n\n// ──────────────────────────────────────────────\n// Main StorageView Export\n// ──────────────────────────────────────────────\n\nexport const StorageView = () => {\n const { t } = useTranslation();\n const defaultStorageSource = useStorageSource();\n const storageSources = useStorageSources();\n const snackbarController = useSnackbarController();\n\n // Available backends to browse. Always includes the default; named\n // sources come from `<Rebase storageSources={...}>`.\n const sourceKeys = useMemo(() => {\n const keys = Object.keys(storageSources.sources);\n if (!keys.includes(DEFAULT_STORAGE_SOURCE_KEY)) keys.unshift(DEFAULT_STORAGE_SOURCE_KEY);\n return keys;\n }, [storageSources.sources]);\n\n const [selectedSourceKey, setSelectedSourceKey] = useState<string>(DEFAULT_STORAGE_SOURCE_KEY);\n\n const storageSource = storageSources.sources[selectedSourceKey] ?? defaultStorageSource;\n\n // Navigation\n const [searchParams, setSearchParams] = useSearchParams();\n // Accepts the historical unqualified `path` so links already shared keep\n // working; only the namespaced one is ever written.\n const currentPath = searchParams.get(STORAGE_PATH_PARAM) ?? searchParams.get(\"path\") ?? \"\";\n const [loading, setLoading] = useState(true);\n /** Why the listing failed, classified — see `load-failure.ts`. */\n const [failure, setFailure] = useState<LoadFailure | null>(null);\n\n // Contents\n const [folders, setFolders] = useState<StorageFile[]>([]);\n const [files, setFiles] = useState<StorageFile[]>([]);\n /** The source `files` were listed from: their metadata is read through it, whatever is selected since. */\n const [listedFrom, setListedFrom] = useState<StorageSource | null>(null);\n // View mode: decides which files need more than the listing says.\n const [viewMode, setViewMode] = useState<\"grid\" | \"list\">(\"grid\");\n /**\n * Which files are worth a metadata request: one the listing did not\n * describe (a source whose listing carries no sizes), and, in the grid, an\n * image — its thumbnail is drawn from the link the request mints. A folder\n * listed with sizes, shown as a list, makes none.\n */\n const wantsMetadata = useCallback((file: StorageFile): boolean =>\n file.size === undefined || (viewMode === \"grid\" && isImageFile(file)), [viewMode]);\n const { metadata, observe: observeFile } = useVisibleFileMetadata(listedFrom, files, wantsMetadata);\n /** `file` with what its metadata has said so far, over what the listing said. */\n const withMetadata = useCallback((file: StorageFile): StorageFile => {\n const read = metadata.get(file.fullPath);\n if (!read) return file;\n return {\n ...file,\n size: read.size ?? file.size,\n contentType: read.contentType ?? file.contentType,\n downloadUrl: read.downloadUrl ?? file.downloadUrl\n };\n }, [metadata]);\n\n // Selection and preview\n const [selectedFile, setSelectedFile] = useState<StorageFile | null>(null);\n const [selectedLink, setSelectedLink] = useState<FileLink | null>(null);\n /** The latest link asked for the preview: an earlier one answering later is dropped. */\n const linkRequestRef = useRef(0);\n\n // Upload\n const [uploadDialogOpen, setUploadDialogOpen] = useState(false);\n\n // Multi-selection\n const [selectedPaths, setSelectedPaths] = useState<Set<string>>(new Set());\n const lastClickedRef = useRef<string | null>(null);\n\n // Bulk / folder delete\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);\n const [deleteDialogTarget, setDeleteDialogTarget] = useState<\"selection\" | StorageFile | null>(null);\n const [deleting, setDeleting] = useState(false);\n\n // New folder\n const [newFolderDialogOpen, setNewFolderDialogOpen] = useState(false);\n const [newFolderName, setNewFolderName] = useState(\"\");\n const [creatingFolder, setCreatingFolder] = useState(false);\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n const storageSourceRef = React.useRef(storageSource);\n useEffect(() => {\n storageSourceRef.current = storageSource;\n }, [storageSource]);\n\n /**\n * The latest listing asked for. Only its answer is shown: a listing of a\n * large folder takes several pages, so an earlier one — of the source or\n * folder the view has since left — can answer after a later one, and\n * showed that source's files under the one selected, where their actions\n * ran.\n */\n const listingRequestRef = useRef(0);\n\n // ── Fetch directory contents ──\n const fetchContents = useCallback(async (path: string) => {\n const request = ++listingRequestRef.current;\n const isCurrent = () => request === listingRequestRef.current;\n // One source for the whole listing; its metadata is read through it too.\n const source = storageSourceRef.current;\n setLoading(true);\n setFailure(null);\n try {\n const result = await listAllPages(source, path);\n\n const folderItems: StorageFile[] = result.prefixes.map(ref => ({\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: true\n }));\n\n // Shown as listed, with what the listing says of each file. What\n // it does not say is read when the file comes on screen, and only\n // if it is needed: see `useVisibleFileMetadata`.\n const fileItems: StorageFile[] = result.items.map(ref => ({\n name: ref.name,\n fullPath: ref.fullPath,\n isFolder: false,\n size: ref.size,\n contentType: ref.contentType\n }));\n\n if (!isCurrent()) return;\n setFolders(folderItems);\n setFiles(fileItems);\n setListedFrom(source);\n } catch (e) {\n if (!isCurrent()) return;\n console.error(\"Storage list error:\", e);\n // A refusal from the project's own `storageAuthorize` hook is not a\n // fault — see `load-failure.ts`. Rendering both the same way told\n // a customer with a working project that their storage was broken.\n setFailure(classifyLoadFailure(e));\n } finally {\n if (isCurrent()) setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n // `selectedSourceKey` is a dep so switching backend re-lists; the\n // ref-sync effect above runs first, so `storageSourceRef` is current.\n fetchContents(currentPath);\n }, [currentPath, fetchContents, selectedSourceKey]);\n\n /** A link to `file`, minted now through the source being browsed — see {@link FileLink}. */\n const mintFileLink = useCallback(async (file: StorageFile): Promise<FileLink | null> =>\n fileLinkOf(await storageSourceRef.current.getSignedUrl(file.fullPath), file.fullPath), []);\n\n /** Open `file` in the preview, with a link minted for it now. */\n const openPreview = useCallback((file: StorageFile) => {\n const request = ++linkRequestRef.current;\n setSelectedFile(file);\n setSelectedLink(null);\n mintFileLink(file)\n .then(link => { if (request === linkRequestRef.current) setSelectedLink(link); })\n .catch(() => { if (request === linkRequestRef.current) setSelectedLink(null); });\n }, [mintFileLink]);\n\n /** A fresh link to the previewed file, for a download or a copy. It replaces the one shown. */\n const refreshPreviewLink = useCallback(async (): Promise<FileLink | null> => {\n if (!selectedFile) return null;\n const request = ++linkRequestRef.current;\n const link = await mintFileLink(selectedFile);\n if (request === linkRequestRef.current) setSelectedLink(link);\n return link;\n }, [selectedFile, mintFileLink]);\n\n // What was previewed and selected belongs to the listing it was picked in.\n const clearSelection = useCallback(() => {\n setSelectedFile(null);\n setSelectedLink(null);\n setSelectedPaths(new Set());\n lastClickedRef.current = null;\n }, []);\n\n // Navigate to path\n //\n // Updates only this view's own parameter and leaves the rest of the query\n // string alone. It used to assign the whole thing (`setSearchParams({})`),\n // which is fine when Studio owns the URL but destructive when it is\n // embedded: the SaaS console keeps the open tab in `?tab=`, so opening a\n // folder erased it and bounced the user out of Storage entirely. The key is\n // namespaced for the same reason — a bare `path` is a name a host app can\n // easily be using for something else.\n const handleNavigate = useCallback((path: string) => {\n setSearchParams(prev => {\n const next = new URLSearchParams(prev);\n if (path) next.set(STORAGE_PATH_PARAM, path);\n else next.delete(STORAGE_PATH_PARAM);\n return next;\n }, { replace: true });\n clearSelection();\n }, [setSearchParams, clearSelection]);\n\n // Switching source keeps the folder path, not the files picked in it: the\n // preview's and the selection's actions run through the selected source,\n // so a file opened in one would be deleted, by its path, from the other.\n const handleSourceChange = useCallback((key: string) => {\n setSelectedSourceKey(key);\n clearSelection();\n }, [clearSelection]);\n\n // Navigate up one level\n const handleNavigateUp = useCallback(() => {\n const parts = currentPath.split(\"/\").filter(Boolean);\n parts.pop();\n handleNavigate(parts.join(\"/\"));\n }, [currentPath, handleNavigate]);\n\n // All items (folders + files) in display order, for shift-range select\n const allItems = useMemo(() => [...folders, ...files], [folders, files]);\n\n // ── Multi-select click handler ──\n const handleItemClick = useCallback((item: StorageFile, e: React.MouseEvent) => {\n const path = item.fullPath;\n if (e.metaKey || e.ctrlKey) {\n // Toggle individual item\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(path)) next.delete(path);\n else next.add(path);\n return next;\n });\n lastClickedRef.current = path;\n } else if (e.shiftKey && lastClickedRef.current) {\n // Range select\n const allPaths = allItems.map(i => i.fullPath);\n const anchorIdx = allPaths.indexOf(lastClickedRef.current);\n const currentIdx = allPaths.indexOf(path);\n if (anchorIdx >= 0 && currentIdx >= 0) {\n const [start, end] = anchorIdx < currentIdx ? [anchorIdx, currentIdx] : [currentIdx, anchorIdx];\n setSelectedPaths(prev => {\n const next = new Set(prev);\n for (let i = start; i <= end; i++) next.add(allPaths[i]);\n return next;\n });\n }\n } else {\n // Exclusive select\n setSelectedPaths(new Set([path]));\n lastClickedRef.current = path;\n // Also open preview if it's a file\n if (!item.isFolder) {\n openPreview(item);\n } else {\n setSelectedFile(null);\n setSelectedLink(null);\n }\n }\n }, [allItems, openPreview]);\n\n // Double-click: open folder or preview file\n const handleItemDoubleClick = useCallback((item: StorageFile) => {\n if (item.isFolder) {\n handleNavigate(item.fullPath);\n } else {\n openPreview(item);\n }\n }, [handleNavigate, openPreview]);\n\n /**\n * Files an upload would write over, waiting for the person to say whether\n * it should. `answer` settles the upload that asked.\n */\n const [replacePrompt, setReplacePrompt] = useState<{ names: string[]; answer: (replace: boolean) => void } | null>(null);\n\n /**\n * Whether `uploadFiles` may go ahead: at once when none of them has the\n * name of a file in the folder, and otherwise once the person says to\n * replace those. An upload is a put, and a put over an existing key\n * replaces the file without a trace.\n */\n const mayReplace = useCallback((uploadFiles: File[]): Promise<boolean> => {\n const listed = new Set(files.map(file => file.name));\n const names = [...new Set(uploadFiles.map(file => file.name))].filter(name => listed.has(name));\n if (names.length === 0) return Promise.resolve(true);\n return new Promise(resolve => setReplacePrompt({\n names,\n answer: (replace) => {\n setReplacePrompt(null);\n resolve(replace);\n }\n }));\n }, [files]);\n\n // Upload files. `false` when the person chose not to replace a file.\n const handleUpload = useCallback(async (uploadFiles: File[]): Promise<boolean> => {\n if (!(await mayReplace(uploadFiles))) return false;\n try {\n for (const file of uploadFiles) {\n const key = currentPath ? `${currentPath}/${file.name}` : file.name;\n await storageSourceRef.current.putObject({\n file,\n key\n });\n }\n } finally {\n // Listed whether or not every file made it: the ones uploaded\n // before a failure are in the folder.\n await fetchContents(currentPath);\n }\n snackbarController.open({\n type: \"success\",\n message: t(\"studio_storage_files_uploaded\", { count: uploadFiles.length })\n });\n return true;\n }, [currentPath, snackbarController, fetchContents, mayReplace, t]);\n\n // Create new folder\n const handleCreateFolder = useCallback(async () => {\n if (!newFolderName.trim() || !apiConfig?.apiUrl) return;\n\n // Validate folder name\n const name = newFolderName.trim();\n if (name.includes(\"/\") || name.includes(\"\\\\\")) {\n snackbarController.open({ type: \"error\",\nmessage: t(\"studio_storage_folder_name_has_slash\") });\n return;\n }\n\n // Check if folder already exists\n const existingFolder = folders.find(f => f.name === name);\n if (existingFolder) {\n snackbarController.open({ type: \"error\",\nmessage: t(\"studio_storage_folder_exists\", { name }) });\n return;\n }\n\n setCreatingFolder(true);\n try {\n const folderPath = currentPath ? `default/${currentPath}/${name}` : `default/${name}`;\n const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;\n const response = await fetch(`${apiBase}/storage/folder`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(token ? { \"Authorization\": `Bearer ${token}` } : {})\n },\n // The selected source, as every other action here routes by\n // it: without `storageId` the folder went to the default\n // backend whichever one was being browsed.\n body: JSON.stringify({\n path: folderPath,\n ...(selectedSourceKey !== DEFAULT_STORAGE_SOURCE_KEY ? { storageId: selectedSourceKey } : {})\n })\n });\n\n if (!response.ok) {\n const err = await response.json().catch(() => ({ error: t(\"studio_storage_create_folder_failed\") }));\n throw new Error(err.error || t(\"studio_storage_create_folder_failed\"));\n }\n\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_folder_created\", { name }) });\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n await fetchContents(currentPath);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n setCreatingFolder(false);\n }\n }, [newFolderName, currentPath, apiConfig, apiBase, snackbarController, fetchContents, folders, selectedSourceKey, t]);\n\n // Drag-and-drop on main view\n const handleDropFiles = useCallback(async (droppedFiles: File[]) => {\n if (droppedFiles.length === 0) return;\n try {\n await handleUpload(droppedFiles);\n } catch (e) {\n snackbarController.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n }\n }, [handleUpload, snackbarController]);\n\n const {\n getRootProps: getDropRootProps,\n getInputProps: getDropInputProps,\n isDragActive\n } = useDropzone({\n onDrop: handleDropFiles,\n noClick: true,\n noKeyboard: true,\n noDragEventsBubbling: true\n });\n\n /**\n * Delete files and folders from the source being browsed, then read back\n * the folder they were listed in and return the ones still there.\n *\n * Nothing is reported deleted on the strength of the requests alone: a\n * delete can answer success and leave the object behind — a directory that\n * is not empty yet, a folder marker, a hook — and the folder listing is\n * what the user sees next. One source for the whole operation, taken when\n * it starts.\n */\n const deleteItems = useCallback(async (items: StorageFile[], folder: string): Promise<StorageFile[]> => {\n const source = storageSourceRef.current;\n await forEachBounded(items.filter(item => !item.isFolder), DELETE_CONCURRENCY, item => source.deleteObject(item.fullPath));\n for (const item of items) {\n if (item.isFolder) await deleteFolderRecursive(source, item.fullPath);\n }\n const listed = await listAllPages(source, folder);\n const stillListed = new Set([...listed.items, ...listed.prefixes].map(ref => ref.fullPath));\n return items.filter(item => stillListed.has(item.fullPath));\n }, []);\n\n /** What a delete left behind, said as the error it is rather than a success. */\n const reportStillThere = useCallback((remaining: StorageFile[]) => {\n snackbarController.open({\n type: \"error\",\n message: t(\"studio_storage_delete_incomplete\", {\n count: remaining.length,\n names: remaining.map(item => item.name).join(\", \")\n })\n });\n }, [snackbarController, t]);\n\n // Delete a single file\n const handleDeleteFile = useCallback(async (file: StorageFile) => {\n try {\n const remaining = await deleteItems([file], currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_file_deleted\", { name: file.name }) });\n }\n setSelectedFile(null);\n setSelectedLink(null);\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(file.fullPath);\n return next;\n });\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n // A delete that failed part-way has still deleted what it got to.\n fetchContents(currentPath);\n }\n }, [currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Bulk delete (selected items)\n const handleBulkDelete = useCallback(async () => {\n setDeleting(true);\n try {\n const items = allItems.filter(i => selectedPaths.has(i.fullPath));\n const remaining = await deleteItems(items, currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: t(\"studio_storage_items_deleted\", { count: items.length }) });\n }\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n await fetchContents(currentPath);\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [allItems, selectedPaths, currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Confirm delete for a single file or folder\n const handleConfirmDeleteItem = useCallback(async () => {\n if (!deleteDialogTarget || deleteDialogTarget === \"selection\") return;\n setDeleting(true);\n try {\n const remaining = await deleteItems([deleteDialogTarget], currentPath);\n if (remaining.length > 0) {\n reportStillThere(remaining);\n } else {\n snackbarController.open({ type: \"success\",\nmessage: deleteDialogTarget.isFolder\n ? t(\"studio_storage_folder_deleted\", { name: deleteDialogTarget.name })\n : t(\"studio_storage_file_deleted\", { name: deleteDialogTarget.name }) });\n }\n if (!deleteDialogTarget.isFolder && selectedFile?.fullPath === deleteDialogTarget.fullPath) {\n setSelectedFile(null);\n setSelectedLink(null);\n }\n setSelectedPaths(prev => {\n const next = new Set(prev);\n next.delete(deleteDialogTarget.fullPath);\n return next;\n });\n } catch (e) {\n snackbarController.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally {\n await fetchContents(currentPath);\n setDeleting(false);\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }, [deleteDialogTarget, selectedFile, currentPath, snackbarController, fetchContents, deleteItems, reportStillThere, t]);\n\n // Select all / deselect\n const handleSelectAll = useCallback(() => {\n if (selectedPaths.size === allItems.length) {\n setSelectedPaths(new Set());\n } else {\n setSelectedPaths(new Set(allItems.map(i => i.fullPath)));\n }\n }, [allItems, selectedPaths]);\n\n // ── Keyboard shortcuts ──\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n // Don't handle shortcuts when a dialog is open\n if (deleteDialogOpen || uploadDialogOpen || newFolderDialogOpen || replacePrompt) return;\n // Nor a key a layer above already acted on — Escape that closed a\n // menu must not also clear the selection.\n if (isKeyHandled(e)) return;\n // Cmd/Ctrl+A: select all\n if ((e.metaKey || e.ctrlKey) && e.key === \"a\") {\n e.preventDefault();\n handleSelectAll();\n }\n // Escape: deselect\n if (e.key === \"Escape\") {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }\n // Delete / Backspace: delete selected\n if ((e.key === \"Delete\" || e.key === \"Backspace\") && selectedPaths.size > 0 && !e.metaKey && !e.ctrlKey) {\n // Don't trigger if user is typing in an input\n if ((e.target as HTMLElement)?.tagName === \"INPUT\" || (e.target as HTMLElement)?.tagName === \"TEXTAREA\") return;\n e.preventDefault();\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n }, [handleSelectAll, selectedPaths, deleteDialogOpen, uploadDialogOpen, newFolderDialogOpen, replacePrompt]);\n\n // Handle refresh\n const handleRefresh = useCallback(() => {\n fetchContents(currentPath);\n }, [currentPath, fetchContents]);\n\n const segments = breadcrumbSegments(currentPath, t(\"studio_storage_root\"));\n\n\n // ── Render file grid/list ──\n const renderContents = () => {\n if (loading) {\n return (\n <div className=\"flex-grow flex items-center justify-center\">\n <div className=\"text-center\">\n <CircularProgress size=\"medium\"/>\n <Typography variant=\"body2\" className=\"mt-4 text-text-secondary dark:text-text-secondary-dark font-mono tracking-tight animate-pulse\">\n {t(\"studio_storage_loading\")}\n </Typography>\n </div>\n </div>\n );\n }\n\n if (failure?.kind === \"denied\") {\n return (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <div className=\"max-w-md text-center\">\n <Typography variant=\"subtitle2\" className=\"block\">\n {t(\"studio_storage_denied_title\")}\n </Typography>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark block mt-2\">\n {t(\"studio_storage_denied_hint\")}\n </Typography>\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark block mt-3 font-mono break-all\">\n {failure.detail}\n </Typography>\n </div>\n </div>\n );\n }\n\n if (failure) {\n return (\n <div className=\"flex-grow flex items-center justify-center p-6 overflow-auto\">\n <ErrorView\n title={t(\"studio_storage_read_failed\")}\n error={failure.detail}\n onRetry={failure.retryable ? handleRefresh : undefined}\n />\n </div>\n );\n }\n\n\n if (allItems.length === 0) {\n return (\n <div className=\"flex-grow flex items-center justify-center text-text-disabled dark:text-text-disabled-dark\">\n <div className=\"text-center\">\n <svg className=\"w-12 h-12 mx-auto mb-4 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={1} d=\"M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z\"/>\n </svg>\n <Typography variant=\"body2\">\n {t(\"studio_storage_empty\")}\n </Typography>\n <div className=\"flex items-center gap-2 mt-3\">\n <Button variant=\"text\" onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}>\n <FolderPlusIcon size={iconSize.smallest}/>\n {t(\"studio_storage_new_folder\")}\n </Button>\n <Button onClick={() => setUploadDialogOpen(true)}>\n <PlusIcon size={iconSize.smallest}/>\n {t(\"studio_storage_upload_files\")}\n </Button>\n </div>\n </div>\n </div>\n );\n }\n\n if (viewMode === \"list\") {\n return (\n <div className=\"flex-grow overflow-auto\">\n <Table className=\"w-full\">\n <TableHeader className=\"text-xs font-normal text-text-secondary dark:text-text-secondary-dark\">\n <TableCell className=\"pl-3 pr-0 py-2 w-8\">\n <Checkbox\n size=\"small\"\n checked={allItems.length > 0 && selectedPaths.size === allItems.length}\n indeterminate={selectedPaths.size > 0 && selectedPaths.size < allItems.length}\n onCheckedChange={handleSelectAll}\n />\n </TableCell>\n <TableCell className=\"px-2 py-2 font-medium\">{t(\"studio_storage_name\")}</TableCell>\n <TableCell className=\"px-4 py-2 font-medium w-24\">{t(\"studio_storage_type\")}</TableCell>\n <TableCell className=\"px-4 py-2 font-medium w-24 text-right\">{t(\"studio_storage_size\")}</TableCell>\n <TableCell className=\"px-2 py-2 w-10\"/>\n </TableHeader>\n {/* A list on the sheet: rows have no fill of their own. */}\n <TableBody variant=\"list\">\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <TableRow\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"group\",\n isChecked && \"bg-primary/5 dark:bg-primary/10\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <TableCell className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(folder.fullPath)) next.delete(folder.fullPath);\n else next.add(folder.fullPath);\n return next;\n });\n }}\n />\n </TableCell>\n <TableCell className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n </TableCell>\n <TableCell className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_folder\")}\n </Typography>\n </TableCell>\n <TableCell className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark\">\n —\n </Typography>\n </TableCell>\n <TableCell className=\"px-2 py-2.5\"/>\n </TableRow>\n );\n })}\n {files.map(listed => {\n const file = withMetadata(listed);\n const FileIconComp = getFileIcon(file.contentType, file.name);\n const isChecked = selectedPaths.has(file.fullPath);\n return (\n <TableRow\n key={file.fullPath}\n ref={observeFile}\n data-storage-path={file.fullPath}\n data-storage-item\n className={cls(\n \"group\",\n isChecked && \"bg-primary/5 dark:bg-primary/10\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n <TableCell className=\"pl-3 pr-0 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n size=\"small\"\n checked={isChecked}\n onCheckedChange={() => {\n setSelectedPaths(prev => {\n const next = new Set(prev);\n if (next.has(file.fullPath)) next.delete(file.fullPath);\n else next.add(file.fullPath);\n return next;\n });\n }}\n />\n </TableCell>\n <TableCell className=\"px-2 py-2.5\">\n <div className=\"flex items-center gap-2\">\n <FileIconComp size={iconSize.smallest} className=\"text-surface-accent-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] truncate\">\n {file.name}\n </Typography>\n </div>\n </TableCell>\n <TableCell className=\"px-4 py-2.5\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {getExtension(file.name) || file.contentType?.split(\"/\")[1]?.toUpperCase() || \"—\"}\n </Typography>\n </TableCell>\n <TableCell className=\"px-4 py-2.5 text-right\">\n <Typography variant=\"caption\" className=\"text-text-secondary dark:text-text-secondary-dark font-mono text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </TableCell>\n <TableCell className=\"px-2 py-2.5\" onClick={(e) => e.stopPropagation()}>\n <IconButton aria-label={t(\"studio_storage_delete\")}\n size=\"smallest\"\n className=\"opacity-0 group-hover:opacity-100 transition-opacity\"\n onClick={() => {\n setDeleteDialogTarget(file);\n setDeleteDialogOpen(true);\n }}\n >\n <Trash2Icon size={14}/>\n </IconButton>\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n );\n }\n\n // Grid view\n return (\n <div className=\"flex-grow overflow-auto p-4\">\n {/* Folder cards */}\n {folders.length > 0 && (\n <div className=\"mb-4\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n {t(\"studio_storage_folders\")}\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {folders.map(folder => {\n const isChecked = selectedPaths.has(folder.fullPath);\n return (\n <div\n key={folder.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg p-3 cursor-pointer border\",\n \"transition-colors duration-150\",\n defaultBorderMixin,\n \"hover:bg-surface-hover hover:shadow-sm\",\n \"flex items-center gap-2\",\n isChecked && \"ring-2 ring-primary bg-primary/5 dark:bg-primary/10\"\n )}\n onClick={(e) => handleItemClick(folder, e)}\n onDoubleClick={() => handleItemDoubleClick(folder)}\n >\n <FolderIcon size={iconSize.smallest} className=\"text-amber-500 dark:text-amber-400 shrink-0\"/>\n <Typography variant=\"body2\" className=\"text-[13px] font-medium truncate\">\n {folder.name}\n </Typography>\n </div>\n );\n })}\n </div>\n </div>\n )}\n\n {/* FileIcon cards */}\n {files.length > 0 && (\n <div>\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark mb-2 block\">\n {t(\"studio_storage_files_heading\", { count: files.length })}\n </Typography>\n <div className=\"grid gap-3 grid-cols-[repeat(auto-fill,minmax(140px,1fr))]\">\n {files.map(listed => {\n const file = withMetadata(listed);\n const FileIconComp = getFileIcon(file.contentType, file.name);\n const isImage = isImageFile(file);\n const isChecked = selectedPaths.has(file.fullPath);\n\n return (\n <div\n key={file.fullPath}\n ref={observeFile}\n data-storage-path={file.fullPath}\n data-storage-item\n className={cls(\n \"rounded-lg overflow-hidden cursor-pointer border\",\n \"transition-shadow duration-150\",\n defaultBorderMixin,\n \"hover:shadow-md\",\n isChecked && \"ring-2 ring-primary\"\n )}\n onClick={(e) => handleItemClick(file, e)}\n onDoubleClick={() => handleItemDoubleClick(file)}\n >\n {/* Thumbnail or icon */}\n <div className=\"aspect-square relative overflow-hidden bg-surface-raised flex items-center justify-center\">\n {isImage && file.downloadUrl ? (\n <img\n src={file.downloadUrl}\n alt={file.name}\n className=\"w-full h-full object-cover\"\n loading=\"lazy\"\n />\n ) : (\n <FileIconComp className=\"text-surface-accent-400 dark:text-surface-accent-500 w-8 h-8\"/>\n )}\n\n {/* Extension badge */}\n {getExtension(file.name) && (\n <div className=\"absolute bottom-1.5 right-1.5 px-1.5 py-0.5 rounded text-[9px] font-semibold uppercase bg-black/50 text-white backdrop-blur-sm\">\n {getExtension(file.name)}\n </div>\n )}\n </div>\n\n {/* Name & size */}\n <div className=\"p-2.5\">\n <Typography variant=\"body2\" className=\"text-[12px] font-medium truncate text-surface-900 dark:text-white\">\n {file.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate block mt-0.5 text-[11px]\">\n {file.size !== undefined ? formatFileSize(file.size) : \"—\"}\n </Typography>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div className=\"flex h-full w-full bg-surface-card overflow-hidden text-text-primary dark:text-text-primary-dark\">\n <div className=\"flex h-full w-full\">\n {/* Main content */}\n <div className=\"flex-grow flex flex-col min-w-0 h-full\">\n {/* Toolbar */}\n <div className={cls(\"flex items-center justify-between pr-2 border-b bg-surface-card shrink-0 h-10\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-1.5 flex-grow overflow-hidden px-3 py-2\">\n {/* Breadcrumbs — always visible */}\n {currentPath && (\n <Tooltip title={t(\"studio_storage_go_up\")}>\n <IconButton aria-label={t(\"studio_storage_go_up\")} size=\"small\" onClick={handleNavigateUp}>\n <ArrowLeftIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n )}\n <div className=\"flex items-center gap-0.5 overflow-x-auto no-scrollbar\">\n {segments.map((seg, i) => (\n <React.Fragment key={seg.path}>\n {i > 0 && (\n <Typography variant=\"caption\" className=\"text-text-disabled dark:text-text-disabled-dark mx-0.5\">/</Typography>\n )}\n <Button\n variant=\"text\"\n size=\"small\"\n className={cls(\n \"px-1.5 py-0.5 min-h-0 min-w-0 h-6 text-xs whitespace-nowrap normal-case font-normal\",\n i === segments.length - 1\n ? \"text-text-primary dark:text-text-primary-dark font-medium\"\n : \"text-text-secondary dark:text-text-secondary-dark\"\n )}\n onClick={() => handleNavigate(seg.path)}\n >\n {seg.label}\n </Button>\n </React.Fragment>\n ))}\n </div>\n\n <div className=\"flex-1\"/>\n\n {/* Selection actions or file count */}\n {selectedPaths.size > 0 ? (\n <div className=\"flex items-center gap-1.5 shrink-0\">\n <Typography variant=\"body2\" className=\"text-[13px] font-medium whitespace-nowrap\">\n {t(\"studio_storage_selected_count\", { count: selectedPaths.size })}\n </Typography>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setDeleteDialogTarget(\"selection\");\n setDeleteDialogOpen(true);\n }}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_delete\")}\n </Button>\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={() => {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }}\n >\n <XIcon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_deselect\")}\n </Button>\n </div>\n ) : !loading ? (\n <Chip size=\"small\" className=\"shrink-0 text-[10px]\">\n {t(\"studio_storage_file_count\", { count: files.length })}\n {folders.length > 0 ? `, ${t(\"studio_storage_folder_count\", { count: folders.length })}` : \"\"}\n </Chip>\n ) : null}\n </div>\n\n <div className=\"flex shrink-0 items-center justify-end gap-1.5 pr-1\">\n\n {/* Backend picker — only shown when more than one storage source is available */}\n {sourceKeys.length > 1 && (\n <Select\n size=\"small\"\n position=\"item-aligned\"\n value={selectedSourceKey}\n onValueChange={(value) => {\n if (value && value !== selectedSourceKey) handleSourceChange(value);\n }}\n renderValue={(key) => {\n const label = storageSources.registry[key]?.label;\n return label ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? t(\"studio_storage_default_source\") : key);\n }}>\n {sourceKeys.map((key) => (\n <SelectItem key={key} value={key}>\n {storageSources.registry[key]?.label\n ?? (key === DEFAULT_STORAGE_SOURCE_KEY ? t(\"studio_storage_default_source\") : key)}\n </SelectItem>\n ))}\n </Select>\n )}\n\n <Tooltip title={t(\"studio_storage_grid_view\")}>\n <IconButton aria-label={t(\"studio_storage_grid_view\")}\n size=\"small\"\n onClick={() => setViewMode(\"grid\")}\n className={cls(viewMode === \"grid\" && \"bg-surface-raised\")}\n >\n <LayoutGridIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Tooltip title={t(\"studio_storage_list_view\")}>\n <IconButton aria-label={t(\"studio_storage_list_view\")}\n size=\"small\"\n onClick={() => setViewMode(\"list\")}\n className={cls(viewMode === \"list\" && \"bg-surface-raised\")}\n >\n <ListIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <div className={cls(\"h-4 w-px mx-0.5\", defaultBorderMixin, \"bg-surface-raised\")}/>\n\n <Tooltip title={t(\"studio_storage_refresh\")}>\n <IconButton aria-label={t(\"studio_storage_refresh\")} size=\"small\" onClick={handleRefresh} disabled={loading}>\n <RefreshCwIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n\n <Tooltip title={t(\"studio_storage_new_folder\")}>\n <IconButton aria-label={t(\"studio_storage_new_folder\")}\n size=\"small\"\n onClick={() => {\n setNewFolderName(\"\");\n setNewFolderDialogOpen(true);\n }}\n >\n <FolderPlusIcon size={iconSize.smallest}/>\n </IconButton>\n </Tooltip>\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => setUploadDialogOpen(true)}\n >\n <UploadCloudIcon size={iconSize.smallest} className=\"mr-1\"/>\n {t(\"studio_storage_upload\")}\n </Button>\n </div>\n </div>\n\n {/* File grid / list — drop zone */}\n <div {...getDropRootProps()}\n className=\"flex-grow flex flex-col overflow-hidden min-h-0 relative\"\n onClick={(e) => {\n const target = e.target as HTMLElement;\n if (!target.closest(\"[data-storage-item]\") && selectedPaths.size > 0) {\n setSelectedPaths(new Set());\n setSelectedFile(null);\n setSelectedLink(null);\n }\n }}\n >\n <input {...getDropInputProps()} />\n {renderContents()}\n {/* Drag overlay */}\n {isDragActive && (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-primary/5 dark:bg-primary/10 backdrop-blur-[2px]\">\n <div className=\"flex flex-col items-center gap-2 p-6 rounded-xl border-2 border-dashed border-primary bg-surface-scrim\">\n <UploadCloudIcon className=\"w-10 h-10 text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"text-primary font-semibold\">\n {t(\"studio_storage_drop_to_upload\")}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n {t(\"studio_storage_upload_to\")} /{currentPath || t(\"studio_storage_root_folder\")}\n </Typography>\n </div>\n </div>\n )}\n </div>\n\n {/* Status bar */}\n <div className={cls(\"px-4 py-1.5 border-t bg-surface-sheet flex items-center justify-between shrink-0\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-4 text-[11px]\">\n <span className=\"text-text-disabled dark:text-text-disabled-dark font-semibold uppercase tracking-tighter\">\n {t(\"studio_storage_path\")}\n </span>\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">\n /{currentPath || \"\"}\n </span>\n </div>\n {selectedPaths.size > 0 ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_items_selected\", { count: selectedPaths.size })}\n </div>\n ) : selectedFile ? (\n <div className=\"text-[11px] text-text-secondary dark:text-text-secondary-dark\">\n {t(\"studio_storage_selected_label\")} <span className=\"font-mono\">{selectedFile.name}</span>\n </div>\n ) : null}\n </div>\n </div>\n\n {/* Preview panel */}\n {selectedFile && (\n <div className=\"w-80 lg:w-96 shrink-0\">\n <FilePreviewPanel\n file={withLinkFacts(withMetadata(selectedFile), selectedLink)}\n link={selectedLink}\n mintLink={refreshPreviewLink}\n onClose={() => {\n setSelectedFile(null);\n setSelectedLink(null);\n }}\n onDelete={() => handleDeleteFile(selectedFile)}\n />\n </div>\n )}\n </div>\n\n {/* Upload Dialog */}\n <UploadDialog\n open={uploadDialogOpen}\n currentPath={currentPath}\n onClose={() => setUploadDialogOpen(false)}\n onUpload={handleUpload}\n />\n\n {/* Replace confirmation, for an upload over existing files */}\n <Dialog\n open={replacePrompt !== null}\n onOpenChange={(open) => {\n if (!open) replacePrompt?.answer(false);\n }}\n >\n <DialogTitle hidden>{t(\"studio_storage_replace_title\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n {t(\"studio_storage_replace_question\", { count: replacePrompt?.names.length ?? 0 })}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n {t(\"studio_storage_replace_body\", {\n count: replacePrompt?.names.length ?? 0,\n names: replacePrompt?.names.join(\", \") ?? \"\"\n })}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={() => replacePrompt?.answer(false)}>\n {t(\"studio_storage_cancel\")}\n </Button>\n <Button variant=\"filled\" onClick={() => replacePrompt?.answer(true)}>\n {t(\"studio_storage_replace\")}\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Delete confirmation dialog */}\n <Dialog\n open={deleteDialogOpen}\n onOpenChange={(open) => {\n if (!open && !deleting) {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }\n }}\n >\n <DialogTitle hidden>{t(\"studio_storage_delete_confirmation_title\")}</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n {deleteDialogTarget === \"selection\"\n ? t(\"studio_storage_delete_items_question\", { count: selectedPaths.size })\n : deleteDialogTarget?.isFolder\n ? t(\"studio_storage_delete_folder_question\", { name: deleteDialogTarget.name })\n : deleteDialogTarget\n ? t(\"studio_storage_delete_file_question\")\n : t(\"studio_storage_delete_question\")}\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n {deleteDialogTarget === \"selection\"\n ? t(\"studio_storage_delete_selection_body\")\n : deleteDialogTarget && !deleteDialogTarget.isFolder\n ? t(\"studio_storage_delete_file_body\", { name: deleteDialogTarget.name })\n : t(\"studio_storage_delete_folder_body\")}\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setDeleteDialogOpen(false);\n setDeleteDialogTarget(null);\n }}\n disabled={deleting}\n >\n {t(\"studio_storage_cancel\")}\n </Button>\n <LoadingButton\n color=\"error\"\n loading={deleting}\n onClick={deleteDialogTarget === \"selection\" ? handleBulkDelete : handleConfirmDeleteItem}\n >\n <Trash2Icon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_delete\")}\n </LoadingButton>\n </DialogActions>\n </Dialog>\n\n {/* New Folder Dialog */}\n <Dialog\n open={newFolderDialogOpen}\n onOpenChange={(open) => {\n if (!open && !creatingFolder) {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }\n }}\n >\n <DialogTitle hidden>{t(\"studio_storage_new_folder_title\")}</DialogTitle>\n <DialogContent>\n <TextField\n autoFocus\n size=\"small\"\n label={t(\"studio_storage_folder_name\")}\n value={newFolderName}\n onChange={(e) => setNewFolderName(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && newFolderName.trim()) {\n e.preventDefault();\n handleCreateFolder();\n }\n }}\n disabled={creatingFolder}\n placeholder={t(\"studio_storage_folder_name_placeholder\")}\n />\n {currentPath && (\n <Typography variant=\"caption\" color=\"secondary\" className=\"mt-2\">\n {t(\"studio_storage_created_in\")} <span className=\"font-mono\">/{currentPath}/</span>\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => {\n setNewFolderDialogOpen(false);\n setNewFolderName(\"\");\n }}\n disabled={creatingFolder}\n >\n {t(\"studio_storage_cancel\")}\n </Button>\n <LoadingButton\n color=\"primary\"\n loading={creatingFolder}\n disabled={!newFolderName.trim()}\n onClick={handleCreateFolder}\n >\n <FolderPlusIcon size={14} className=\"mr-1\"/>\n {t(\"studio_storage_create\")}\n </LoadingButton>\n </DialogActions>\n </Dialog>\n </div>\n );\n};\n"],"mappings":"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"}
|