@ai-matrx/media 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -0
- package/README.md +40 -3
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/react.cjs +43 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +9 -2
- package/dist/react.d.ts +9 -2
- package/dist/react.js +43 -0
- package/dist/react.js.map +1 -1
- package/dist/share.cjs +606 -0
- package/dist/share.cjs.map +1 -0
- package/dist/share.d.cts +52 -0
- package/dist/share.d.ts +52 -0
- package/dist/share.js +598 -0
- package/dist/share.js.map +1 -0
- package/dist/use-media-upload-Dg2HSvTq.d.ts +53 -0
- package/dist/use-media-upload-v5Uq9Ruq.d.cts +53 -0
- package/package.json +17 -2
- package/dist/use-media-upload-VjUhqbyY.d.cts +0 -270
- package/dist/use-media-upload-VjUhqbyY.d.ts +0 -270
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/share/media-share-popover.tsx","../src/core/context.ts","../src/react/cn.ts","../src/react/icons.tsx"],"sourcesContent":["/**\n * src/share/media-share-popover.tsx — THE SHARE MANDATE's popover BODY\n * (wave M-SHARE, ruling C19).\n *\n * The complete share surface for one media ref, ported from the canonical\n * matrx-frontend share flows (`features/files/blocks/image/ImageSharePopover.tsx`\n * + the video twin — ONE share mechanism, this is its package body) with the\n * coupling seams inverted:\n *\n * • `useSharing`/`pythonShareUrl`/`shareableMediaUrl` → the ONE\n * `MediaClient.shareableUrl` door (law 4 — fails closed: `null` renders\n * the action unavailable; the package never mints or classifies a URL).\n * • `ShareLinkDialog` (the iam share-link manager — app content per C8)\n * → the `manageLinks` host callback; row hidden when the host binds none.\n * • `AccessSummaryPanel` (iam reachability — app content per C8) → the\n * `AccessSummary` host slot.\n * • toasts → the optional `notify` adapter; every failure ALSO renders as\n * inline state so no outcome is silent without it.\n * • the association system → `@ai-matrx/associations/react` consumed\n * through its PUBLIC surface only (provider context + hooks). The\n * \"Linked to\" / \"Attach to…\" affordances render when an\n * `AssociationsProvider` is mounted above and degrade to ABSENT (never\n * broken chrome) when it is not — associations stays an OPTIONAL peer\n * and only this `/share` entry carries it (C8).\n *\n * Matrx refs (a `file_id` identity) get the full surface; external refs get\n * the simplified copy-external-link surface, exactly like the originals.\n * `shareableUrl` is called on CLICK for matrx refs (the host may mint a\n * no-expiry share link — a side effect the origin also deferred to the\n * click), but prefetched for external refs (pure classification, mirroring\n * the origin's synchronous `shareableMediaUrl` display).\n */\n\n\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type {\n AssociationTargetType,\n EntityTypeToken,\n} from \"@ai-matrx/associations\";\nimport {\n useAssociations,\n useAssociationsStore,\n useEntityTitles,\n useUniversalEntitySearch,\n} from \"@ai-matrx/associations/react\";\nimport type {\n MediaActionContext,\n MediaKind,\n MediaRefLike,\n MediaSharePopoverProps,\n MediaSlotComponent,\n} from \"../types\";\nimport { useMediaClient } from \"../core/context\";\nimport { cn } from \"../react/cn\";\nimport {\n CheckIcon,\n GlobeIcon,\n Link2Icon,\n PlusIcon,\n SearchIcon,\n Settings2Icon,\n SpinnerIcon,\n XIcon,\n} from \"../react/icons\";\n\n// ---------------------------------------------------------------------------\n// Options — the host-shaped seams. Bound once via `createMediaSharePopover`\n// (the factory returns a component matching the `SharePopover` slot of\n// `MediaActionsPort`), or passed directly to `MediaSharePopover`.\n// ---------------------------------------------------------------------------\n\n/** Toast adapter (host-shaped). Failures also render inline regardless. */\nexport interface MediaShareNotifier {\n success(message: string, opts?: { description?: string }): void;\n error(message: string, opts?: { description?: string }): void;\n}\n\nexport interface MediaShareAccessSummaryProps {\n /** The entity token the summary is for (the popover's `entityToken`). */\n entityType: string;\n /** The matrx file id. */\n entityId: string;\n context: MediaActionContext;\n}\n\nexport interface MediaSharePopoverOptions {\n /**\n * Opens the host's full share-link manager (\"Manage all links\" — custom\n * expiry / permissions / revoke). The iam share-link dialog is app content\n * (C8); absent → the row is hidden. Matrx refs only.\n */\n manageLinks?: ((ctx: MediaActionContext) => void) | undefined;\n /**\n * The host's \"who can see this\" panel (iam reachability — app content per\n * C8). Rendered under the quick actions for matrx refs when provided.\n */\n AccessSummary?: MediaSlotComponent<MediaShareAccessSummaryProps> | undefined;\n /** Toast adapter; failures also render inline without one. */\n notify?: MediaShareNotifier | undefined;\n /** The ref's entity token in the association registry. Default `\"file\"`. */\n entityToken?: string | undefined;\n /** Containers offered by \"Attach to…\". Must be association TARGET types. */\n attachTokens?: readonly AssociationTargetType[] | undefined;\n /** Set false to hide the association affordances even with a provider. */\n associations?: boolean | undefined;\n className?: string | undefined;\n}\n\nconst DEFAULT_ATTACH_TOKENS: readonly AssociationTargetType[] = [\n \"organization\",\n \"project\",\n \"task\",\n \"conversation\",\n \"scope\",\n];\n\nconst KIND_LABEL: Record<MediaKind, string> = {\n image: \"image\",\n video: \"video\",\n audio: \"audio\",\n file: \"file\",\n};\n\nfunction fileIdOf(ref: MediaRefLike | null | undefined): string | null {\n if (!ref || typeof ref === \"string\") return null;\n return ref.file_id ?? null;\n}\n\nasync function copyToClipboard(text: string): Promise<boolean> {\n try {\n if (typeof navigator === \"undefined\" || !navigator.clipboard) return false;\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// The popover body.\n// ---------------------------------------------------------------------------\n\nexport type MediaSharePopoverComponentProps = MediaSharePopoverProps &\n MediaSharePopoverOptions;\n\nexport function MediaSharePopover({\n context,\n onClose,\n manageLinks,\n AccessSummary,\n notify,\n entityToken = \"file\",\n attachTokens = DEFAULT_ATTACH_TOKENS,\n associations = true,\n className,\n}: MediaSharePopoverComponentProps) {\n const fileId = fileIdOf(context.ref);\n const kind = context.resolution?.kind ?? \"file\";\n const associationsStore = useOptionalAssociationsStore();\n const AccessSummarySlot = (AccessSummary ??\n null) as ComponentType<MediaShareAccessSummaryProps> | null;\n\n return (\n <div\n className={cn(\n \"w-80 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md\",\n className,\n )}\n >\n <div className=\"border-b px-3 py-2\">\n <div className=\"text-xs font-semibold\">Share {KIND_LABEL[kind]}</div>\n <div className=\"text-[11px] text-muted-foreground\">\n Public links use Matrx sharing · playback URLs stay private\n </div>\n </div>\n <div className=\"flex flex-col gap-1 p-2\">\n {fileId ? (\n <MatrxQuickActions\n context={context}\n notify={notify}\n onActionComplete={onClose}\n />\n ) : (\n <ExternalQuickActions\n context={context}\n kind={kind}\n notify={notify}\n onActionComplete={onClose}\n />\n )}\n {fileId && manageLinks ? (\n <>\n <div className=\"my-1 h-px bg-border\" />\n <QuickActionRow\n icon={<Settings2Icon className=\"h-4 w-4 text-muted-foreground\" width={16} height={16} />}\n label=\"Manage all links\"\n sublabel=\"Custom expiry · permissions · revoke\"\n onClick={() => {\n manageLinks(context);\n onClose();\n }}\n muted\n />\n </>\n ) : null}\n {fileId && AccessSummarySlot ? (\n <ShareSection title=\"Who can see this\">\n <AccessSummarySlot\n entityType={entityToken}\n entityId={fileId}\n context={context}\n />\n </ShareSection>\n ) : null}\n {fileId && associations && associationsStore ? (\n <AssociationsSection\n entityToken={entityToken}\n entityId={fileId}\n attachTokens={attachTokens}\n notify={notify}\n />\n ) : null}\n </div>\n </div>\n );\n}\n\n/**\n * Bind the host-shaped options once; the result is exactly the\n * `SharePopover` slot shape of `MediaActionsPort` — inject it and the\n * toolbar/lightbox shells open the complete share experience.\n */\nexport function createMediaSharePopover(\n options: MediaSharePopoverOptions = {},\n): MediaSlotComponent<MediaSharePopoverProps> {\n return function ConfiguredMediaSharePopover(props: MediaSharePopoverProps) {\n return <MediaSharePopover {...props} {...options} />;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Optional associations store — the clean-degradation probe.\n//\n// `useAssociationsStore` is documented to THROW when no AssociationsProvider\n// is mounted. Its hook usage is a single `useContext` call made BEFORE the\n// throw, so catching preserves hook order across renders in both states —\n// present and absent trees call the same hooks. The behavioral contract\n// (affordances present with a provider, cleanly absent without, never a\n// crash) is pinned by this package's tests.\n// ---------------------------------------------------------------------------\n\nfunction useOptionalAssociationsStore(): ReturnType<\n typeof useAssociationsStore\n> | null {\n try {\n return useAssociationsStore();\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Matrx quick actions — the \"truly permanent\" public-link path, verbatim\n// from the origin body. The origin's two paths (permanent CDN URL for\n// visibility=\"public\"; else reuse-or-mint a no-expiry read-only share link)\n// live behind the host's ONE `shareableUrl` implementation — the package\n// asks once, on click, and fails closed on `null`.\n// ---------------------------------------------------------------------------\n\nfunction MatrxQuickActions({\n context,\n notify,\n onActionComplete,\n}: {\n context: MediaActionContext;\n notify: MediaShareNotifier | undefined;\n onActionComplete: () => void;\n}) {\n const client = useMediaClient();\n const [publicBusy, setPublicBusy] = useState(false);\n const [copied, setCopied] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const handleCopyPublic = useCallback(async () => {\n if (publicBusy) return;\n setPublicBusy(true);\n setError(null);\n try {\n const url = await client.shareableUrl(context.ref);\n if (!url) {\n // Law 4: fails closed — never a signed URL, never a silent no-op.\n setError(\"This file cannot be shared publicly\");\n notify?.error(\"Could not create public link\", {\n description: \"This file cannot be shared publicly.\",\n });\n return;\n }\n const wrote = await copyToClipboard(url);\n if (!wrote) {\n setError(\"Could not copy link\");\n notify?.error(\"Could not copy link\");\n return;\n }\n notify?.success(\"Public link copied\", {\n description: \"This link will never expire.\",\n });\n setCopied(true);\n onActionComplete();\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n setError(message);\n notify?.error(\"Could not create public link\", { description: message });\n } finally {\n setPublicBusy(false);\n }\n }, [publicBusy, client, context.ref, notify, onActionComplete]);\n\n return (\n <div className=\"flex flex-col gap-1\">\n <QuickActionRow\n icon={<GlobeIcon className=\"h-4 w-4 text-emerald-500\" width={16} height={16} />}\n label=\"Copy public link\"\n sublabel=\"Never expires · anyone with the link\"\n onClick={() => void handleCopyPublic()}\n busy={publicBusy}\n copied={copied}\n />\n {error ? (\n <p role=\"alert\" className=\"px-3 text-[11px] text-destructive\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// External quick actions — no share-link surface, just the safe URL we have.\n// `shareableUrl` here is pure classification (no matrx identity to mint links\n// for), so it is prefetched to drive the enabled/disabled row like the\n// origin's synchronous `shareableMediaUrl(externalUrl)` did.\n// ---------------------------------------------------------------------------\n\nfunction ExternalQuickActions({\n context,\n kind,\n notify,\n onActionComplete,\n}: {\n context: MediaActionContext;\n kind: MediaKind;\n notify: MediaShareNotifier | undefined;\n onActionComplete: () => void;\n}) {\n const client = useMediaClient();\n const [externalUrl, setExternalUrl] = useState<string | null>(null);\n const [checked, setChecked] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n setChecked(false);\n void client\n .shareableUrl(context.ref)\n .then((url) => {\n if (cancelled) return;\n setExternalUrl(url);\n setChecked(true);\n })\n .catch(() => {\n if (cancelled) return;\n setExternalUrl(null);\n setChecked(true);\n });\n return () => {\n cancelled = true;\n };\n }, [client, context.ref]);\n\n const handleCopy = useCallback(async () => {\n if (!externalUrl) {\n setError(\"This private playback URL cannot be shared\");\n notify?.error(\"This private playback URL cannot be shared\");\n return;\n }\n const wrote = await copyToClipboard(externalUrl);\n if (!wrote) {\n setError(\"Could not copy link\");\n notify?.error(\"Could not copy link\");\n return;\n }\n notify?.success(\"Link copied\");\n onActionComplete();\n }, [externalUrl, notify, onActionComplete]);\n\n return (\n <div className=\"flex flex-col gap-1\">\n <QuickActionRow\n icon={<Link2Icon className=\"h-4 w-4\" width={16} height={16} />}\n label=\"Copy external link\"\n sublabel={externalUrl ?? \"No safe external URL available\"}\n onClick={() => void handleCopy()}\n busy={!checked}\n disabled={checked && !externalUrl}\n />\n {error ? (\n <p role=\"alert\" className=\"px-3 text-[11px] text-destructive\">\n {error}\n </p>\n ) : null}\n <p className=\"px-3 pt-2 text-[11px] text-muted-foreground\">\n This {KIND_LABEL[kind]} is hosted externally — we can't create a\n permanent Matrx share link for it.\n </p>\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Associations — \"Linked to\" + \"Attach to…\", THE canonical edge system\n// consumed through @ai-matrx/associations' public hooks. Rendered only when\n// an AssociationsProvider is mounted (the parent gates on the store probe).\n// ---------------------------------------------------------------------------\n\nfunction AssociationsSection({\n entityToken,\n entityId,\n attachTokens,\n notify,\n}: {\n entityToken: string;\n entityId: string;\n attachTokens: readonly AssociationTargetType[];\n notify: MediaShareNotifier | undefined;\n}) {\n const { edges, status, error, add, remove } = useAssociations({\n type: entityToken,\n id: entityId,\n });\n const [attachOpen, setAttachOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n const [busyKey, setBusyKey] = useState<string | null>(null);\n const [writeError, setWriteError] = useState<string | null>(null);\n\n // Containers this ref is attached to (the ref is the edge SOURCE —\n // little→big is registry-owned; never flipped here).\n const outgoing = useMemo(\n () => edges.filter((edge) => edge.direction === \"outgoing\"),\n [edges],\n );\n const titles = useEntityTitles(\n outgoing.map((edge) => ({\n token: edge.otherType,\n id: edge.otherId,\n label: edge.label,\n })),\n );\n const attachedKeys = useMemo(\n () => new Set(outgoing.map((edge) => `${edge.otherType}:${edge.otherId}`)),\n [outgoing],\n );\n\n const search = useUniversalEntitySearch({\n query,\n tokens: [...attachTokens] as EntityTypeToken[],\n enabled: attachOpen,\n });\n\n const runWrite = useCallback(\n async (key: string, verb: string, op: () => Promise<{ ok: boolean; error?: string }>) => {\n if (busyKey) return;\n setBusyKey(key);\n setWriteError(null);\n try {\n const res = await op();\n // A silent no-op attach is the bug class this scream kills.\n if (!res.ok) {\n const message = `Couldn't ${verb}${res.error ? `: ${res.error}` : \"\"}`;\n setWriteError(message);\n notify?.error(message);\n }\n } finally {\n setBusyKey(null);\n }\n },\n [busyKey, notify],\n );\n\n return (\n <ShareSection\n title=\"Linked to\"\n action={\n <button\n type=\"button\"\n onClick={() => setAttachOpen((open) => !open)}\n className=\"inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[11px] text-muted-foreground hover:bg-accent hover:text-foreground\"\n >\n <PlusIcon className=\"h-3 w-3\" width={12} height={12} />\n Attach to…\n </button>\n }\n >\n {status === \"loading\" && outgoing.length === 0 ? (\n <div className=\"flex items-center gap-2 px-3 py-1.5 text-[11px] text-muted-foreground\">\n <SpinnerIcon className=\"h-3.5 w-3.5 animate-spin\" width={14} height={14} />\n Loading links…\n </div>\n ) : null}\n {error ? (\n <p role=\"alert\" className=\"px-3 text-[11px] text-destructive\">\n {error}\n </p>\n ) : null}\n {status !== \"loading\" && !error && outgoing.length === 0 ? (\n <p className=\"px-3 py-1 text-[11px] text-muted-foreground\">\n Not linked to anything yet.\n </p>\n ) : null}\n {outgoing.map((edge) => {\n const ref = { token: edge.otherType, id: edge.otherId, label: edge.label };\n const unresolved = titles.isUnresolved(ref);\n const key = `edge:${edge.id}`;\n return (\n <div\n key={edge.id}\n className=\"flex items-center gap-2 rounded-md px-3 py-1.5 hover:bg-accent/50\"\n >\n <Link2Icon\n className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\"\n width={14}\n height={14}\n />\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span\n className={cn(\n \"truncate text-xs\",\n unresolved && \"text-destructive\",\n )}\n >\n {unresolved ? \"Unavailable\" : titles.titleFor(ref)}\n </span>\n <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {edge.otherType}\n {edge.role ? ` · ${edge.role}` : \"\"}\n </span>\n </span>\n <button\n type=\"button\"\n aria-label={`Detach ${unresolved ? edge.otherType : titles.titleFor(ref)}`}\n disabled={busyKey === key}\n onClick={() =>\n void runWrite(key, \"detach\", () =>\n remove({\n targetType: edge.otherType,\n targetId: edge.otherId,\n ...(edge.role != null ? { role: edge.role } : {}),\n }),\n )\n }\n className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-destructive\"\n >\n {busyKey === key ? (\n <SpinnerIcon className=\"h-3 w-3 animate-spin\" width={12} height={12} />\n ) : (\n <XIcon className=\"h-3 w-3\" width={12} height={12} />\n )}\n </button>\n </div>\n );\n })}\n {attachOpen ? (\n <div className=\"flex flex-col gap-1 border-t pt-2\">\n <div className=\"relative px-3\">\n <SearchIcon\n className=\"pointer-events-none absolute left-5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\"\n width={14}\n height={14}\n />\n <input\n type=\"text\"\n value={query}\n onChange={(event) => setQuery(event.target.value)}\n placeholder=\"Search to attach…\"\n aria-label=\"Search to attach\"\n className=\"w-full rounded-md border bg-background py-1 pl-7 pr-2 text-xs\"\n />\n </div>\n {search.loading ? (\n <div className=\"flex items-center gap-2 px-3 py-1.5 text-[11px] text-muted-foreground\">\n <SpinnerIcon className=\"h-3.5 w-3.5 animate-spin\" width={14} height={14} />\n Searching…\n </div>\n ) : null}\n {search.results.map((candidate) => {\n const candidateKey = `${candidate.token}:${candidate.id}`;\n const attached = attachedKeys.has(candidateKey);\n return (\n <button\n key={candidateKey}\n type=\"button\"\n disabled={busyKey === candidateKey}\n onClick={() =>\n void runWrite(\n candidateKey,\n attached\n ? `detach \"${candidate.title}\"`\n : `attach \"${candidate.title}\"`,\n () =>\n attached\n ? remove({\n targetType: candidate.token,\n targetId: candidate.id,\n })\n : add({\n targetType: candidate.token as AssociationTargetType,\n targetId: candidate.id,\n label: candidate.title,\n }),\n )\n }\n className=\"flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-left hover:bg-accent\"\n >\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate text-xs\">{candidate.title}</span>\n <span className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {candidate.token}\n </span>\n </span>\n {busyKey === candidateKey ? (\n <SpinnerIcon className=\"h-3.5 w-3.5 shrink-0 animate-spin\" width={14} height={14} />\n ) : attached ? (\n <CheckIcon className=\"h-3.5 w-3.5 shrink-0 text-emerald-500\" width={14} height={14} />\n ) : (\n <PlusIcon className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" width={14} height={14} />\n )}\n </button>\n );\n })}\n {!search.loading && query.trim() && search.results.length === 0 ? (\n <p className=\"px-3 py-1 text-[11px] text-muted-foreground\">\n No matches.\n </p>\n ) : null}\n </div>\n ) : null}\n {writeError ? (\n <p role=\"alert\" className=\"px-3 text-[11px] text-destructive\">\n {writeError}\n </p>\n ) : null}\n </ShareSection>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Chrome shared by the sections.\n// ---------------------------------------------------------------------------\n\nfunction ShareSection({\n title,\n action,\n children,\n}: {\n title: string;\n action?: ReactNode | undefined;\n children: ReactNode;\n}) {\n return (\n <div className=\"mt-1 flex flex-col gap-1 border-t pt-2\">\n <div className=\"flex items-center justify-between px-3\">\n <span className=\"text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n {title}\n </span>\n {action}\n </div>\n {children}\n </div>\n );\n}\n\n// One tappable, two-line action button — ported verbatim from the origin\n// popovers' QuickActionRow.\nfunction QuickActionRow({\n icon,\n label,\n sublabel,\n onClick,\n busy,\n disabled,\n copied,\n muted,\n}: {\n icon: ReactNode;\n label: string;\n sublabel?: string | undefined;\n onClick: () => void;\n busy?: boolean | undefined;\n disabled?: boolean | undefined;\n copied?: boolean | undefined;\n muted?: boolean | undefined;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={busy || disabled}\n className={cn(\n \"flex w-full items-center gap-3 rounded-md px-3 py-2 text-left transition-colors\",\n \"hover:bg-accent focus-visible:bg-accent focus-visible:outline-none\",\n (busy || disabled) && \"opacity-50 cursor-not-allowed\",\n muted && \"text-muted-foreground\",\n )}\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted\">\n {busy ? (\n <SpinnerIcon className=\"h-4 w-4 animate-spin\" width={16} height={16} />\n ) : (\n icon\n )}\n </span>\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"text-sm font-medium leading-tight\">{label}</span>\n {sublabel ? (\n <span className=\"truncate text-[11px] text-muted-foreground\">\n {sublabel}\n </span>\n ) : null}\n </span>\n {copied ? (\n <CheckIcon className=\"h-4 w-4 shrink-0 text-emerald-500\" width={16} height={16} />\n ) : null}\n </button>\n );\n}\n","/**\n * src/core/context.ts — the MediaClient / host-ports React contexts.\n *\n * The context OBJECTS live on `globalThis` under `Symbol.for` slots — NOT at\n * module level. With `splitting: false` this module is duplicated into the\n * `/core` and `/react` bundles, and CJS/ESM each instantiate their own module\n * graph; a module-level `createContext` would silently produce two different\n * contexts (a Provider from one graph invisible to a consumer from the\n * other) — the kit tap-target/link-registry lesson. Never \"clean this up\".\n */\n\nimport {\n createContext,\n createElement,\n useContext,\n type Context,\n type ReactNode,\n} from \"react\";\nimport type { MediaClient, MediaHostPorts } from \"../types\";\n\nconst CLIENT_SLOT = Symbol.for(\"ai-matrx.media.client-context\");\nconst PORTS_SLOT = Symbol.for(\"ai-matrx.media.host-ports-context\");\n\ntype SlotHost = typeof globalThis & {\n [CLIENT_SLOT]?: Context<MediaClient | null>;\n [PORTS_SLOT]?: Context<MediaHostPorts>;\n};\n\nfunction clientContext(): Context<MediaClient | null> {\n const host = globalThis as SlotHost;\n host[CLIENT_SLOT] ??= createContext<MediaClient | null>(null);\n return host[CLIENT_SLOT];\n}\n\nconst INERT_PORTS: MediaHostPorts = {};\n\nfunction portsContext(): Context<MediaHostPorts> {\n const host = globalThis as SlotHost;\n host[PORTS_SLOT] ??= createContext<MediaHostPorts>(INERT_PORTS);\n return host[PORTS_SLOT];\n}\n\nexport interface MediaProviderProps {\n client: MediaClient;\n /**\n * Optional host ports (playback session, actions, ImageComponent). Must be\n * referentially stable — the playback port members are hooks.\n */\n ports?: MediaHostPorts | undefined;\n children?: ReactNode;\n}\n\n/** Wire the injected MediaClient (and optional host ports) into the tree. */\nexport function MediaProvider({ client, ports, children }: MediaProviderProps) {\n const ClientCtx = clientContext();\n const PortsCtx = portsContext();\n return createElement(\n ClientCtx.Provider,\n { value: client },\n createElement(PortsCtx.Provider, { value: ports ?? INERT_PORTS }, children),\n );\n}\n\n/** The injected MediaClient. Throws when no MediaProvider is mounted. */\nexport function useMediaClient(): MediaClient {\n const client = useContext(clientContext());\n if (!client) {\n throw new Error(\n \"@ai-matrx/media: no MediaClient. Mount <MediaProvider client={...}> \" +\n \"above every media surface (canonically wired to @ai-matrx/data/files).\",\n );\n }\n return client;\n}\n\n/** The injected host ports; inert defaults when none were provided. */\nexport function useMediaHostPorts(): MediaHostPorts {\n return useContext(portsContext());\n}\n","/**\n * src/react/cn.ts — conditional class join with last-wins Tailwind conflict\n * merging. The originals used the app's `cn` (clsx + tailwind-merge); public\n * `className` overrides depend on last-wins class-conflict merging, so a\n * naive join would be a behavior divergence (the kit `./confirm` precedent —\n * this is why `tailwind-merge` is a sanctioned runtime dep of `/react`).\n */\n\nimport { twMerge } from \"tailwind-merge\";\n\nexport type ClassValue = string | false | null | undefined | 0;\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(inputs.filter(Boolean).join(\" \"));\n}\n","/**\n * src/react/icons.tsx — the package's OWN inlined SVG icons.\n *\n * Per campaign ruling C19 (2026-08-29): no icon-library dependency, ever.\n * Every glyph below is copied from lucide-react v1.22.0 (ISC license) exactly,\n * following the @ai-matrx/tap-target precedent. Add a new icon by copying the\n * lucide __iconNode data — never by adding a lucide peer.\n *\n * Internal module: icons are exported for the package's own components and for\n * hosts that want visual parity in slots, but they are not the product.\n */\n\nimport type { SVGProps } from \"react\";\n\nexport type MediaIconProps = SVGProps<SVGSVGElement>;\n\nfunction svgProps(props: MediaIconProps): MediaIconProps {\n return {\n xmlns: \"http://www.w3.org/2000/svg\",\n viewBox: \"0 0 24 24\",\n width: 24,\n height: 24,\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\",\n \"aria-hidden\": true,\n ...props,\n };\n}\n\n// lucide: archive\nexport function ArchiveIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"20\" height=\"5\" x=\"2\" y=\"3\" rx=\"1\" />\n <path d=\"M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8\" />\n <path d=\"M10 12h4\" />\n </svg>\n );\n}\n\n// lucide: braces\nexport function BracesIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1\" />\n <path d=\"M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1\" />\n </svg>\n );\n}\n\n// lucide: check\nexport function CheckIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n );\n}\n\n// lucide: code\nexport function CodeIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m16 18 6-6-6-6\" />\n <path d=\"m8 6-6 6 6 6\" />\n </svg>\n );\n}\n\n// lucide: copy\nexport function CopyIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n );\n}\n\n// lucide: database\nexport function DatabaseIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <ellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\" />\n <path d=\"M3 5V19A9 3 0 0 0 21 19V5\" />\n <path d=\"M3 12A9 3 0 0 0 21 12\" />\n </svg>\n );\n}\n\n// lucide: download\nexport function DownloadIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M12 15V3\" />\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <path d=\"m7 10 5 5 5-5\" />\n </svg>\n );\n}\n\n// lucide: externalLink\nexport function ExternalLinkIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M15 3h6v6\" />\n <path d=\"M10 14 21 3\" />\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n </svg>\n );\n}\n\n// lucide: file\nexport function FileIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n </svg>\n );\n}\n\n// lucide: fileAudio\nexport function FileAudioIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M4 6.835V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-.343\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M2 19a2 2 0 0 1 4 0v1a2 2 0 0 1-4 0v-4a6 6 0 0 1 12 0v4a2 2 0 0 1-4 0v-1a2 2 0 0 1 4 0\" />\n </svg>\n );\n}\n\n// lucide: fileCode\nexport function FileCodeIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M10 12.5 8 15l2 2.5\" />\n <path d=\"m14 12.5 2 2.5-2 2.5\" />\n </svg>\n );\n}\n\n// lucide: fileJson\nexport function FileJsonIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1\" />\n <path d=\"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1\" />\n </svg>\n );\n}\n\n// lucide: fileSpreadsheet\nexport function FileSpreadsheetIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M8 13h2\" />\n <path d=\"M14 13h2\" />\n <path d=\"M8 17h2\" />\n <path d=\"M14 17h2\" />\n </svg>\n );\n}\n\n// lucide: fileText\nexport function FileTextIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </svg>\n );\n}\n\n// lucide: fileType\nexport function FileTypeIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M11 18h2\" />\n <path d=\"M12 12v6\" />\n <path d=\"M9 13v-.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v.5\" />\n </svg>\n );\n}\n\n// lucide: fileVideo\nexport function FileVideoIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\" />\n <path d=\"M14 2v5a1 1 0 0 0 1 1h5\" />\n <path d=\"M15.033 13.44a.647.647 0 0 1 0 1.12l-4.065 2.352a.645.645 0 0 1-.968-.56v-4.704a.645.645 0 0 1 .967-.56z\" />\n </svg>\n );\n}\n\n// lucide: folder\nexport function FolderIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\" />\n </svg>\n );\n}\n\n// lucide: folderOpen\nexport function FolderOpenIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2\" />\n </svg>\n );\n}\n\n// lucide: image\nexport function ImageIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" ry=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21\" />\n </svg>\n );\n}\n\n// lucide: imageOff\nexport function ImageOffIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" />\n <path d=\"M10.41 10.41a2 2 0 1 1-2.83-2.83\" />\n <line x1=\"13.5\" x2=\"6\" y1=\"13.5\" y2=\"21\" />\n <line x1=\"18\" x2=\"21\" y1=\"12\" y2=\"15\" />\n <path d=\"M3.59 3.59A1.99 1.99 0 0 0 3 5v14a2 2 0 0 0 2 2h14c.55 0 1.052-.22 1.41-.59\" />\n <path d=\"M21 15V5a2 2 0 0 0-2-2H9\" />\n </svg>\n );\n}\n\n// lucide: music\nexport function MusicIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M9 18V5l12-2v13\" />\n <circle cx=\"6\" cy=\"18\" r=\"3\" />\n <circle cx=\"18\" cy=\"16\" r=\"3\" />\n </svg>\n );\n}\n\n// lucide: packageBox\nexport function PackageBoxIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z\" />\n <path d=\"M12 22V12\" />\n <polyline points=\"3.29 7 12 12 20.71 7\" />\n <path d=\"m7.5 4.27 9 5.15\" />\n </svg>\n );\n}\n\n// lucide: penTool\nexport function PenToolIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M15.707 21.293a1 1 0 0 1-1.414 0l-1.586-1.586a1 1 0 0 1 0-1.414l5.586-5.586a1 1 0 0 1 1.414 0l1.586 1.586a1 1 0 0 1 0 1.414z\" />\n <path d=\"m18 13-1.375-6.874a1 1 0 0 0-.746-.776L3.235 2.028a1 1 0 0 0-1.207 1.207L5.35 15.879a1 1 0 0 0 .776.746L13 18\" />\n <path d=\"m2.3 2.3 7.286 7.286\" />\n <circle cx=\"11\" cy=\"11\" r=\"2\" />\n </svg>\n );\n}\n\n// lucide: share2\nexport function Share2Icon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"18\" cy=\"5\" r=\"3\" />\n <circle cx=\"6\" cy=\"12\" r=\"3\" />\n <circle cx=\"18\" cy=\"19\" r=\"3\" />\n <line x1=\"8.59\" x2=\"15.42\" y1=\"13.51\" y2=\"17.49\" />\n <line x1=\"15.41\" x2=\"8.59\" y1=\"6.51\" y2=\"10.49\" />\n </svg>\n );\n}\n\n// lucide: subtitles\nexport function SubtitlesIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <rect width=\"18\" height=\"14\" x=\"3\" y=\"5\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 15h4M15 15h2M7 11h2M13 11h4\" />\n </svg>\n );\n}\n\n// lucide: upload\nexport function UploadIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M12 3v12\" />\n <path d=\"m17 8-5-5-5 5\" />\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n </svg>\n );\n}\n\n// lucide: video\nexport function VideoIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5\" />\n <rect x=\"2\" y=\"6\" width=\"14\" height=\"12\" rx=\"2\" />\n </svg>\n );\n}\n\n// lucide: videoOff\nexport function VideoOffIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M10.66 6H14a2 2 0 0 1 2 2v2.5l5.248-3.062A.5.5 0 0 1 22 7.87v8.196\" />\n <path d=\"M16 16a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2\" />\n <path d=\"m2 2 20 20\" />\n </svg>\n );\n}\n\n// lucide: volumeX\nexport function VolumeXIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z\" />\n <line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\" />\n <line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\" />\n </svg>\n );\n}\n\n// lucide: x\nexport function XIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M18 6 6 18\" />\n <path d=\"m6 6 12 12\" />\n </svg>\n );\n}\n\n// lucide: alertCircle\nexport function AlertCircleIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <line x1=\"12\" x2=\"12\" y1=\"8\" y2=\"12\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"16\" y2=\"16\" />\n </svg>\n );\n}\n\n// lucide: checkCircle2\nexport function CheckCircle2Icon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21.801 10A10 10 0 1 1 17 3.335\" />\n <path d=\"m9 11 3 3L22 4\" />\n </svg>\n );\n}\n\n// lucide: globe\nexport function GlobeIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20\" />\n <path d=\"M2 12h20\" />\n </svg>\n );\n}\n\n// lucide: link-2\nexport function Link2Icon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M9 17H7A5 5 0 0 1 7 7h2\" />\n <path d=\"M15 7h2a5 5 0 1 1 0 10h-2\" />\n <line x1=\"8\" x2=\"16\" y1=\"12\" y2=\"12\" />\n </svg>\n );\n}\n\n// lucide: settings-2\nexport function Settings2Icon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M20 7h-9\" />\n <path d=\"M14 17H5\" />\n <circle cx=\"17\" cy=\"17\" r=\"3\" />\n <circle cx=\"7\" cy=\"7\" r=\"3\" />\n </svg>\n );\n}\n\n// lucide: loader-circle (a.k.a. Loader2)\nexport function SpinnerIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </svg>\n );\n}\n\n// lucide: plus\nexport function PlusIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <path d=\"M5 12h14\" />\n <path d=\"M12 5v14\" />\n </svg>\n );\n}\n\n// lucide: search\nexport function SearchIcon(props: MediaIconProps) {\n return (\n <svg {...svgProps(props)}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n );\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MediaUploadOptions, MediaUploadResult, MediaMultiUploadResult } from './index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* src/core/use-media-upload.ts — the React-side upload primitive.
|
|
5
|
+
*
|
|
6
|
+
* Ported from matrx-frontend `handler/hooks/useFileUpload.ts` with two seam
|
|
7
|
+
* inversions: the transport is `MediaClient.upload` (never touched directly),
|
|
8
|
+
* and the multi-file pre-flight (SHA-256 dedup dialog) is the client's
|
|
9
|
+
* optional `uploadMany` — when absent, the batch runs sequentially through
|
|
10
|
+
* `upload` with per-file results.
|
|
11
|
+
*
|
|
12
|
+
* Additionally exposes a per-file `entries` tray state (name, size, bytes,
|
|
13
|
+
* status) so the dropzone's UploadProgressList works without any app store —
|
|
14
|
+
* the Redux `selectVisibleUploads` coupling of the original, inverted.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface MediaUploadProgress {
|
|
18
|
+
loaded: number;
|
|
19
|
+
total: number;
|
|
20
|
+
ratio: number;
|
|
21
|
+
}
|
|
22
|
+
type MediaUploadEntryStatus = "uploading" | "success" | "error";
|
|
23
|
+
interface MediaUploadEntry {
|
|
24
|
+
id: string;
|
|
25
|
+
fileName: string;
|
|
26
|
+
fileSize: number;
|
|
27
|
+
bytesUploaded: number;
|
|
28
|
+
status: MediaUploadEntryStatus;
|
|
29
|
+
error: string | null;
|
|
30
|
+
fileId: string | null;
|
|
31
|
+
completedAt: number | null;
|
|
32
|
+
}
|
|
33
|
+
interface UseMediaUploadResult {
|
|
34
|
+
/** Upload one file/blob. Returns the durable identity. */
|
|
35
|
+
upload: (file: File | Blob, opts?: MediaUploadOptions) => Promise<MediaUploadResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Upload many files. Uses the client's `uploadMany` (host pre-flight /
|
|
38
|
+
* dedup dialog) when implemented; otherwise a sequential per-file loop with
|
|
39
|
+
* real per-file error messages.
|
|
40
|
+
*/
|
|
41
|
+
uploadMany: (files: File[], opts?: MediaUploadOptions) => Promise<MediaMultiUploadResult>;
|
|
42
|
+
uploading: boolean;
|
|
43
|
+
progress: MediaUploadProgress | null;
|
|
44
|
+
result: MediaUploadResult | null;
|
|
45
|
+
error: Error | null;
|
|
46
|
+
/** Live per-file tray entries for progress UI. */
|
|
47
|
+
entries: MediaUploadEntry[];
|
|
48
|
+
clearEntry: (id: string) => void;
|
|
49
|
+
reset: () => void;
|
|
50
|
+
}
|
|
51
|
+
declare function useMediaUpload(): UseMediaUploadResult;
|
|
52
|
+
|
|
53
|
+
export { type MediaUploadEntry as M, type UseMediaUploadResult as U, type MediaUploadEntryStatus as a, type MediaUploadProgress as b, useMediaUpload as u };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { MediaUploadOptions, MediaUploadResult, MediaMultiUploadResult } from './index.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* src/core/use-media-upload.ts — the React-side upload primitive.
|
|
5
|
+
*
|
|
6
|
+
* Ported from matrx-frontend `handler/hooks/useFileUpload.ts` with two seam
|
|
7
|
+
* inversions: the transport is `MediaClient.upload` (never touched directly),
|
|
8
|
+
* and the multi-file pre-flight (SHA-256 dedup dialog) is the client's
|
|
9
|
+
* optional `uploadMany` — when absent, the batch runs sequentially through
|
|
10
|
+
* `upload` with per-file results.
|
|
11
|
+
*
|
|
12
|
+
* Additionally exposes a per-file `entries` tray state (name, size, bytes,
|
|
13
|
+
* status) so the dropzone's UploadProgressList works without any app store —
|
|
14
|
+
* the Redux `selectVisibleUploads` coupling of the original, inverted.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface MediaUploadProgress {
|
|
18
|
+
loaded: number;
|
|
19
|
+
total: number;
|
|
20
|
+
ratio: number;
|
|
21
|
+
}
|
|
22
|
+
type MediaUploadEntryStatus = "uploading" | "success" | "error";
|
|
23
|
+
interface MediaUploadEntry {
|
|
24
|
+
id: string;
|
|
25
|
+
fileName: string;
|
|
26
|
+
fileSize: number;
|
|
27
|
+
bytesUploaded: number;
|
|
28
|
+
status: MediaUploadEntryStatus;
|
|
29
|
+
error: string | null;
|
|
30
|
+
fileId: string | null;
|
|
31
|
+
completedAt: number | null;
|
|
32
|
+
}
|
|
33
|
+
interface UseMediaUploadResult {
|
|
34
|
+
/** Upload one file/blob. Returns the durable identity. */
|
|
35
|
+
upload: (file: File | Blob, opts?: MediaUploadOptions) => Promise<MediaUploadResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Upload many files. Uses the client's `uploadMany` (host pre-flight /
|
|
38
|
+
* dedup dialog) when implemented; otherwise a sequential per-file loop with
|
|
39
|
+
* real per-file error messages.
|
|
40
|
+
*/
|
|
41
|
+
uploadMany: (files: File[], opts?: MediaUploadOptions) => Promise<MediaMultiUploadResult>;
|
|
42
|
+
uploading: boolean;
|
|
43
|
+
progress: MediaUploadProgress | null;
|
|
44
|
+
result: MediaUploadResult | null;
|
|
45
|
+
error: Error | null;
|
|
46
|
+
/** Live per-file tray entries for progress UI. */
|
|
47
|
+
entries: MediaUploadEntry[];
|
|
48
|
+
clearEntry: (id: string) => void;
|
|
49
|
+
reset: () => void;
|
|
50
|
+
}
|
|
51
|
+
declare function useMediaUpload(): UseMediaUploadResult;
|
|
52
|
+
|
|
53
|
+
export { type MediaUploadEntry as M, type UseMediaUploadResult as U, type MediaUploadEntryStatus as a, type MediaUploadProgress as b, useMediaUpload as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-matrx/media",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "The AI Matrx media components kit: durable-ref-only renderers for images, video, audio and file thumbnails that just work with the Matrx file system — headless core hooks plus DOM bindings, wired to an injected MediaClient. A signed URL is a handoff, never an identity.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -66,16 +66,30 @@
|
|
|
66
66
|
"default": "./dist/react.cjs"
|
|
67
67
|
}
|
|
68
68
|
},
|
|
69
|
+
"./share": {
|
|
70
|
+
"import": {
|
|
71
|
+
"types": "./dist/share.d.ts",
|
|
72
|
+
"default": "./dist/share.js"
|
|
73
|
+
},
|
|
74
|
+
"require": {
|
|
75
|
+
"types": "./dist/share.d.cts",
|
|
76
|
+
"default": "./dist/share.cjs"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
69
79
|
"./package.json": "./package.json"
|
|
70
80
|
},
|
|
71
81
|
"dependencies": {
|
|
72
82
|
"tailwind-merge": "^3.6.0"
|
|
73
83
|
},
|
|
74
84
|
"peerDependencies": {
|
|
85
|
+
"@ai-matrx/associations": ">=0.4.0",
|
|
75
86
|
"react": ">=18.0.0",
|
|
76
87
|
"react-dom": ">=18.0.0"
|
|
77
88
|
},
|
|
78
89
|
"peerDependenciesMeta": {
|
|
90
|
+
"@ai-matrx/associations": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
79
93
|
"react-dom": {
|
|
80
94
|
"optional": true
|
|
81
95
|
}
|
|
@@ -92,7 +106,8 @@
|
|
|
92
106
|
"react-dom": "^19.1.0",
|
|
93
107
|
"tsup": "^8.5.1",
|
|
94
108
|
"typescript": "^5.9.3",
|
|
95
|
-
"vitest": "^4.1.6"
|
|
109
|
+
"vitest": "^4.1.6",
|
|
110
|
+
"@ai-matrx/associations": "0.5.0"
|
|
96
111
|
},
|
|
97
112
|
"publishConfig": {
|
|
98
113
|
"access": "public",
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @ai-matrx/media — the root types: the MediaClient port and the host ports.
|
|
3
|
-
*
|
|
4
|
-
* The package renders Matrx durable media; it NEVER talks to a network.
|
|
5
|
-
* Everything data-shaped arrives through the injected {@link MediaClient}
|
|
6
|
-
* (canonically implemented by `@ai-matrx/data/files`; any structural
|
|
7
|
-
* implementation works — the strangler step wires it over an app's existing
|
|
8
|
-
* file handler). The five file-handling laws are enforced here at the type
|
|
9
|
-
* level:
|
|
10
|
-
*
|
|
11
|
-
* 1. Never fetch a media URL yourself — the package contains zero network
|
|
12
|
-
* primitives; bytes exist only via {@link MediaClient.getBlob}.
|
|
13
|
-
* 2. Off-boundary code resolves media itself — every component takes a
|
|
14
|
-
* {@link MediaRefLike} and calls `resolve()` unconditionally. There is
|
|
15
|
-
* NO `src` prop on any component in this package.
|
|
16
|
-
* 3. A signed URL is a handoff, never an identity — the only string that
|
|
17
|
-
* reaches a media element is the branded {@link DurableSrc}, which only
|
|
18
|
-
* a MediaClient implementation can mint (via {@link mintDurableSrc}).
|
|
19
|
-
* 4. Public-facing media is persisted public, never signed —
|
|
20
|
-
* {@link MediaClient.shareableUrl} fails closed (`null` → the action
|
|
21
|
-
* renders disabled).
|
|
22
|
-
* 5. One URL contract; the engine builds URLs — zero URL construction in
|
|
23
|
-
* this package; `resolve()` is the only door.
|
|
24
|
-
*/
|
|
25
|
-
/** The canonical durable media reference. `file_id` is the identity. */
|
|
26
|
-
interface MediaRef {
|
|
27
|
-
file_id?: string | null;
|
|
28
|
-
url?: string | null;
|
|
29
|
-
mime_type?: string | null;
|
|
30
|
-
metadata?: Record<string, unknown> | null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Identity in. A raw string is a fileId or an EXTERNAL public URL — never a
|
|
34
|
-
* signed URL (the client's `resolve()` classifies and screams on one).
|
|
35
|
-
*/
|
|
36
|
-
type MediaRefLike = MediaRef | string;
|
|
37
|
-
declare const durable: unique symbol;
|
|
38
|
-
/**
|
|
39
|
-
* A URL that has passed through `MediaClient.resolve()` — the only string
|
|
40
|
-
* type any media element in this package will bind. A caller cannot pass an
|
|
41
|
-
* expiring URL through the package's types without an explicit unsafe cast.
|
|
42
|
-
*/
|
|
43
|
-
type DurableSrc = string & {
|
|
44
|
-
readonly [durable]: true;
|
|
45
|
-
};
|
|
46
|
-
type MediaKind = "image" | "video" | "audio" | "file";
|
|
47
|
-
interface MediaResolution {
|
|
48
|
-
src: DurableSrc;
|
|
49
|
-
/** RN auth lane (`<Image source={{uri, headers}}>`); absent on web (cookie session). */
|
|
50
|
-
headers?: Record<string, string> | undefined;
|
|
51
|
-
mimeType?: string | undefined;
|
|
52
|
-
kind: MediaKind;
|
|
53
|
-
/**
|
|
54
|
-
* How pixels must travel. `"element"` (default): bind `src` directly to the
|
|
55
|
-
* media element. `"blob"`: the bytes require bearer auth the element cannot
|
|
56
|
-
* attach — consumers go through {@link MediaClient.getBlob} and render the
|
|
57
|
-
* returned object URL (the private-image lane).
|
|
58
|
-
*/
|
|
59
|
-
transport?: "element" | "blob" | undefined;
|
|
60
|
-
/**
|
|
61
|
-
* A server-rendered thumbnail for this file, when the client knows one
|
|
62
|
-
* (Matrx: `FileRecord.thumbnail_url` / asset variants). Consumed by the
|
|
63
|
-
* thumbnail tier machine.
|
|
64
|
-
*/
|
|
65
|
-
thumbnailSrc?: DurableSrc | undefined;
|
|
66
|
-
/**
|
|
67
|
-
* True when this is a permanent public CDN URL — lets a host-supplied
|
|
68
|
-
* `ImageComponent` (e.g. a next/image adapter) take the render, and marks
|
|
69
|
-
* the URL CORS-safe for element-level `crossOrigin`.
|
|
70
|
-
*/
|
|
71
|
-
isCdn?: boolean | undefined;
|
|
72
|
-
/**
|
|
73
|
-
* Whether a load error can be recovered by the client's ONE retry policy
|
|
74
|
-
* (session-refresh → same-URL retry). False for foreign/external URLs —
|
|
75
|
-
* a session refresh can't resurrect someone else's link rot. Default true.
|
|
76
|
-
*/
|
|
77
|
-
recoverable?: boolean | undefined;
|
|
78
|
-
}
|
|
79
|
-
/** Typed unavailable states — render-as-state, never thrown as incidents. */
|
|
80
|
-
type MediaUnavailableReason = "access_denied" | "not_found" | "deleted" | "unknown";
|
|
81
|
-
interface MediaBlobHandle {
|
|
82
|
-
/** `blob:` object URL safe for any browser API. */
|
|
83
|
-
url: string;
|
|
84
|
-
blob: Blob;
|
|
85
|
-
/** Release the handle. Implementations backed by a shared cache may no-op. */
|
|
86
|
-
release(): void;
|
|
87
|
-
}
|
|
88
|
-
interface MediaUploadOptions {
|
|
89
|
-
onProgress?: ((loaded: number, total: number) => void) | undefined;
|
|
90
|
-
fileName?: string | undefined;
|
|
91
|
-
mimeType?: string | undefined;
|
|
92
|
-
/** Host visibility vocabulary passes through structurally. */
|
|
93
|
-
visibility?: string | undefined;
|
|
94
|
-
parentFolderId?: string | null | undefined;
|
|
95
|
-
metadata?: Record<string, unknown> | undefined;
|
|
96
|
-
}
|
|
97
|
-
/** Upload → durable identity. `fileId` is required at compile time and runtime. */
|
|
98
|
-
interface MediaUploadResult {
|
|
99
|
-
fileId: string;
|
|
100
|
-
ref: MediaRef;
|
|
101
|
-
}
|
|
102
|
-
interface MediaMultiUploadResult {
|
|
103
|
-
/** File ids for every successful upload. */
|
|
104
|
-
uploaded: string[];
|
|
105
|
-
/** Per-file failure with the real backend error message. */
|
|
106
|
-
failed: Array<{
|
|
107
|
-
name: string;
|
|
108
|
-
error: string;
|
|
109
|
-
}>;
|
|
110
|
-
/** True when the host's pre-flight (e.g. a duplicate dialog) cancelled the batch. */
|
|
111
|
-
cancelled: boolean;
|
|
112
|
-
}
|
|
113
|
-
interface MediaClient {
|
|
114
|
-
/**
|
|
115
|
-
* Synchronous durable-URL build + classification. Returns `null` when the
|
|
116
|
-
* ref carries no usable identity (components render their fallback).
|
|
117
|
-
* THROWS (and screams via its own diagnostics sink) on a signed/expiring
|
|
118
|
-
* URL — the D108 guard lives behind this door.
|
|
119
|
-
*/
|
|
120
|
-
resolve(ref: MediaRefLike): MediaResolution | null;
|
|
121
|
-
/** Bytes for canvas/crossOrigin/private-pixel consumers. */
|
|
122
|
-
getBlob(ref: MediaRefLike): Promise<MediaBlobHandle>;
|
|
123
|
-
/**
|
|
124
|
-
* Session bootstrap + the ONE retry policy: refresh-then-same-URL-retry,
|
|
125
|
-
* second failure terminal. Called by the package's onError path, never
|
|
126
|
-
* re-implemented.
|
|
127
|
-
*/
|
|
128
|
-
recoverLoadError(src: DurableSrc, attempt: number): Promise<"retry" | "terminal">;
|
|
129
|
-
/** Upload → durable identity. */
|
|
130
|
-
upload(file: File | Blob, opts?: MediaUploadOptions): Promise<MediaUploadResult>;
|
|
131
|
-
/**
|
|
132
|
-
* Optional batch upload with host pre-flight (dedup dialogs etc.). When
|
|
133
|
-
* absent, `useMediaUpload().uploadMany` falls back to sequential `upload`.
|
|
134
|
-
*/
|
|
135
|
-
uploadMany?(files: File[], opts?: MediaUploadOptions): Promise<MediaMultiUploadResult>;
|
|
136
|
-
/** Share affordances — fails closed (null), never emits a signed URL. */
|
|
137
|
-
shareableUrl(ref: MediaRefLike): Promise<string | null>;
|
|
138
|
-
/** Typed unavailable states — render-as-state, never thrown as incidents. */
|
|
139
|
-
classifyError(err: unknown): MediaUnavailableReason;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Structural component slot. `(props) => unknown` keeps the root entry free
|
|
143
|
-
* of React type imports; any React function component satisfies it.
|
|
144
|
-
*/
|
|
145
|
-
type MediaSlotComponent<P> = (props: P) => unknown;
|
|
146
|
-
interface MediaImageComponentProps {
|
|
147
|
-
src: string;
|
|
148
|
-
alt: string;
|
|
149
|
-
width: number;
|
|
150
|
-
height: number;
|
|
151
|
-
className?: string | undefined;
|
|
152
|
-
onError?: ((event: unknown) => void) | undefined;
|
|
153
|
-
onClick?: ((event: unknown) => void) | undefined;
|
|
154
|
-
onKeyDown?: ((event: unknown) => void) | undefined;
|
|
155
|
-
role?: string | undefined;
|
|
156
|
-
tabIndex?: number | undefined;
|
|
157
|
-
}
|
|
158
|
-
interface PlaybackSessionRegistration {
|
|
159
|
-
isPlaying: boolean;
|
|
160
|
-
source: string;
|
|
161
|
-
label: string;
|
|
162
|
-
trackKey?: string | undefined;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* The host's audio system (exclusive-playback lock, output-sink routing).
|
|
166
|
-
* Both members are HOOKS — the package calls them unconditionally on every
|
|
167
|
-
* render of a media element, so implementations must obey the Rules of Hooks
|
|
168
|
-
* and the port object must be referentially stable.
|
|
169
|
-
*/
|
|
170
|
-
interface PlaybackSessionPort {
|
|
171
|
-
/**
|
|
172
|
-
* Returns a ref callback for the underlying media element (routes audio
|
|
173
|
-
* output / setSinkId on hosts that support it) and forwards to the caller's
|
|
174
|
-
* own element ref.
|
|
175
|
-
*/
|
|
176
|
-
useMediaElementSink(forward: ((node: unknown) => void) | {
|
|
177
|
-
current: unknown;
|
|
178
|
-
} | null | undefined): (node: unknown) => void;
|
|
179
|
-
/** Joins the host's unified playback session for the element. */
|
|
180
|
-
usePlaybackSession(registration: PlaybackSessionRegistration, elementRef: {
|
|
181
|
-
current: unknown;
|
|
182
|
-
}): void;
|
|
183
|
-
}
|
|
184
|
-
type MediaActionKind = "share" | "download" | "copy" | "open";
|
|
185
|
-
interface MediaActionContext {
|
|
186
|
-
ref: MediaRefLike;
|
|
187
|
-
resolution: MediaResolution | null;
|
|
188
|
-
fileName?: string | undefined;
|
|
189
|
-
alt?: string | undefined;
|
|
190
|
-
}
|
|
191
|
-
interface MediaSharePopoverProps {
|
|
192
|
-
context: MediaActionContext;
|
|
193
|
-
onClose: () => void;
|
|
194
|
-
}
|
|
195
|
-
type MediaActionHandler = (ctx: MediaActionContext) => Promise<void>;
|
|
196
|
-
interface MediaActionsPort {
|
|
197
|
-
/**
|
|
198
|
-
* Quick-share handler (copy a share link, open the native share sheet…).
|
|
199
|
-
* Implementations obtain URLs ONLY via `MediaClient.shareableUrl` /
|
|
200
|
-
* durable identities — never a signed URL (law 4).
|
|
201
|
-
*/
|
|
202
|
-
share?: MediaActionHandler | undefined;
|
|
203
|
-
download?: MediaActionHandler | undefined;
|
|
204
|
-
copy?: MediaActionHandler | undefined;
|
|
205
|
-
open?: MediaActionHandler | undefined;
|
|
206
|
-
/**
|
|
207
|
-
* Rich share UI body — the slot the associations package fills. When
|
|
208
|
-
* present, the shells open it instead of (or on top of) the `share`
|
|
209
|
-
* handler. Receives {@link MediaSharePopoverProps}.
|
|
210
|
-
*/
|
|
211
|
-
SharePopover?: MediaSlotComponent<MediaSharePopoverProps> | undefined;
|
|
212
|
-
}
|
|
213
|
-
interface MediaHostPorts {
|
|
214
|
-
playbackSession?: PlaybackSessionPort | undefined;
|
|
215
|
-
actions?: MediaActionsPort | undefined;
|
|
216
|
-
/** Web host may pass a next/image adapter; default is a plain `<img>`. */
|
|
217
|
-
ImageComponent?: MediaSlotComponent<MediaImageComponentProps> | undefined;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* src/core/use-media-upload.ts — the React-side upload primitive.
|
|
222
|
-
*
|
|
223
|
-
* Ported from matrx-frontend `handler/hooks/useFileUpload.ts` with two seam
|
|
224
|
-
* inversions: the transport is `MediaClient.upload` (never touched directly),
|
|
225
|
-
* and the multi-file pre-flight (SHA-256 dedup dialog) is the client's
|
|
226
|
-
* optional `uploadMany` — when absent, the batch runs sequentially through
|
|
227
|
-
* `upload` with per-file results.
|
|
228
|
-
*
|
|
229
|
-
* Additionally exposes a per-file `entries` tray state (name, size, bytes,
|
|
230
|
-
* status) so the dropzone's UploadProgressList works without any app store —
|
|
231
|
-
* the Redux `selectVisibleUploads` coupling of the original, inverted.
|
|
232
|
-
*/
|
|
233
|
-
|
|
234
|
-
interface MediaUploadProgress {
|
|
235
|
-
loaded: number;
|
|
236
|
-
total: number;
|
|
237
|
-
ratio: number;
|
|
238
|
-
}
|
|
239
|
-
type MediaUploadEntryStatus = "uploading" | "success" | "error";
|
|
240
|
-
interface MediaUploadEntry {
|
|
241
|
-
id: string;
|
|
242
|
-
fileName: string;
|
|
243
|
-
fileSize: number;
|
|
244
|
-
bytesUploaded: number;
|
|
245
|
-
status: MediaUploadEntryStatus;
|
|
246
|
-
error: string | null;
|
|
247
|
-
fileId: string | null;
|
|
248
|
-
completedAt: number | null;
|
|
249
|
-
}
|
|
250
|
-
interface UseMediaUploadResult {
|
|
251
|
-
/** Upload one file/blob. Returns the durable identity. */
|
|
252
|
-
upload: (file: File | Blob, opts?: MediaUploadOptions) => Promise<MediaUploadResult>;
|
|
253
|
-
/**
|
|
254
|
-
* Upload many files. Uses the client's `uploadMany` (host pre-flight /
|
|
255
|
-
* dedup dialog) when implemented; otherwise a sequential per-file loop with
|
|
256
|
-
* real per-file error messages.
|
|
257
|
-
*/
|
|
258
|
-
uploadMany: (files: File[], opts?: MediaUploadOptions) => Promise<MediaMultiUploadResult>;
|
|
259
|
-
uploading: boolean;
|
|
260
|
-
progress: MediaUploadProgress | null;
|
|
261
|
-
result: MediaUploadResult | null;
|
|
262
|
-
error: Error | null;
|
|
263
|
-
/** Live per-file tray entries for progress UI. */
|
|
264
|
-
entries: MediaUploadEntry[];
|
|
265
|
-
clearEntry: (id: string) => void;
|
|
266
|
-
reset: () => void;
|
|
267
|
-
}
|
|
268
|
-
declare function useMediaUpload(): UseMediaUploadResult;
|
|
269
|
-
|
|
270
|
-
export { type DurableSrc as D, type MediaClient as M, type UseMediaUploadResult as U, type MediaHostPorts as a, type MediaRefLike as b, type MediaResolution as c, type MediaUnavailableReason as d, type MediaActionContext as e, type MediaActionKind as f, type MediaSlotComponent as g, type MediaSharePopoverProps as h, type MediaUploadEntry as i, type MediaUploadEntryStatus as j, type MediaUploadProgress as k, type MediaUploadOptions as l, useMediaUpload as u };
|