@elytracms/studio 0.0.23 → 0.0.25
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/components/assets/asset-library.js +5 -2
- package/dist/components/assets/asset-library.js.map +1 -1
- package/dist/components/assets/assets-store.js +7 -3
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/cms/asset-picker.js +14 -2
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/field-control.js +17 -9
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/lib/assets/delete-asset.js +7 -3
- package/dist/lib/assets/delete-asset.js.map +1 -1
- package/dist/lib/references/references-store.js +6 -0
- package/dist/lib/references/references-store.js.map +1 -1
- package/package.json +2 -2
|
@@ -170,8 +170,11 @@ function DeleteAssetButton({ asset }) {
|
|
|
170
170
|
const confirm = async () => {
|
|
171
171
|
setBusy(true);
|
|
172
172
|
setError(null);
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
// The dialog has already enumerated usages (or shown there are none) and the user
|
|
174
|
+
// is confirming deletion either way — so confirm unconditionally, letting
|
|
175
|
+
// deleteAsset skip its redundant inbound re-check (EC-301 perf). Weak-by-default:
|
|
176
|
+
// a still-referenced asset deletes anyway, dangling into a visible issue.
|
|
177
|
+
const result = await store.remove(asset.id, { confirmInUse: true });
|
|
175
178
|
setBusy(false);
|
|
176
179
|
if (result.outcome === 'deleted' || result.outcome === 'not-found') {
|
|
177
180
|
setOpen(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-library.js","sourceRoot":"","sources":["../../../src/components/assets/asset-library.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AlertTriangle,\n ImageIcon,\n Link2,\n Pencil,\n RotateCcw,\n Search,\n Trash2,\n Upload,\n X,\n} from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport type { ChangeEvent, DragEvent } from 'react'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { UploadState } from '@elytracms/studio/lib/assets'\nimport { buildAssetReferenceImpact } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { ReferenceImpactRows } from '../cms/reference-confirm'\nimport { ReferencesPanel } from '../references/references-panel'\nimport {\n Badge,\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Field,\n Grid,\n Heading,\n Input,\n Panel,\n PanelBody,\n PanelHeader,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { filterAssets } from './asset-search'\nimport { useAssets } from './assets-store'\nimport type { UploadEntry } from './assets-store'\n\n/**\n * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads\n * with visible progress/failure/retry, a searchable grid of stored assets\n * (filename/alt/title), metadata editing (alt, title — persisted through the\n * operations asset facade), and reference-respecting deletion (usage count +\n * block-or-confirm). All storage access goes through the persistence adapter\n * via the assets store. Each card answers \"where is this asset used?\" via the\n * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link\n * param) scrolls that card into view and opens its references dialog.\n */\nexport function AssetLibrary({\n projectId,\n focusAssetId,\n}: {\n projectId: string\n focusAssetId?: string\n}) {\n const store = useAssets()\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const [dragging, setDragging] = useState(false)\n const [query, setQuery] = useState('')\n\n const onPick = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.files) store.upload([...event.target.files])\n event.target.value = ''\n }\n\n const onDrop = (event: DragEvent) => {\n event.preventDefault()\n setDragging(false)\n if (event.dataTransfer.files.length > 0) store.upload([...event.dataTransfer.files])\n }\n\n if (store.status === 'error') {\n return (\n <Stack gap=\"2\" className=\"p-6\" data-testid=\"assets-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text weight=\"semibold\">Assets failed to load</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? 'The persistence backend is unreachable.'}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> Retry\n </Button>\n </Row>\n </Stack>\n )\n }\n\n return (\n <Stack\n gap=\"4\"\n className={cn(\n 'flex-1 p-4 sm:p-6',\n dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4',\n )}\n onDragOver={(event) => {\n event.preventDefault()\n setDragging(true)\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n data-testid=\"asset-library\"\n >\n <Row gap=\"2\">\n <Heading level={2} size=\"h2\">\n Assets\n </Heading>\n <Badge variant=\"outline\">{store.assets.length}</Badge>\n <Spacer />\n <AssetSearchInput value={query} onChange={setQuery} />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={onPick}\n data-testid=\"asset-file-input\"\n />\n <Button size=\"sm\" onClick={() => fileInputRef.current?.click()}>\n <Upload /> Upload\n </Button>\n </Row>\n\n <DropHint visible={dragging || store.assets.length === 0} />\n\n {store.uploads.length > 0 ? (\n <Panel data-testid=\"upload-queue\">\n <PanelHeader>\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\">\n Uploads\n </Text>\n </PanelHeader>\n <PanelBody className=\"divide-y divide-border p-0\">\n {store.uploads.map((entry) => (\n <UploadRow\n key={entry.id}\n entry={entry}\n onRetry={() => store.retryUpload(entry.id)}\n onDismiss={() => store.dismissUpload(entry.id)}\n />\n ))}\n </PanelBody>\n </Panel>\n ) : null}\n\n {store.status === 'loading' ? (\n <Text size=\"sm\" tone=\"muted\">\n Loading assets…\n </Text>\n ) : (\n <AssetGrid\n assets={filterAssets(store.assets, query)}\n filtered={query.trim().length > 0}\n projectId={projectId}\n focusAssetId={focusAssetId}\n />\n )}\n </Stack>\n )\n}\n\n/** Named search primitive over filename / alt / title (EC-152). */\nfunction AssetSearchInput({\n value,\n onChange,\n}: {\n value: string\n onChange: (value: string) => void\n}) {\n return (\n <span className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"Search assets…\"\n aria-label=\"Search assets\"\n className=\"w-56 pl-8\"\n data-testid=\"asset-search\"\n />\n </span>\n )\n}\n\n/** Persistent drop affordance; doubles as the empty state. */\nfunction DropHint({ visible }: { visible: boolean }) {\n return (\n <Stack\n gap=\"1\"\n align=\"center\"\n className={cn(\n 'rounded-lg border border-dashed border-border p-6 text-center',\n !visible && 'hidden sm:flex',\n )}\n >\n <ImageIcon className=\"size-5 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n Drop files anywhere to upload, or use the Upload button.\n </Text>\n </Stack>\n )\n}\n\nconst PHASE_LABEL: Record<UploadState['phase'], string> = {\n idle: 'queued',\n preparing: 'preparing',\n uploading: 'uploading',\n confirming: 'saving metadata',\n done: 'done',\n failed: 'failed',\n}\n\n/** One in-flight upload: progress while transferring, explicit failure + retry. */\nfunction UploadRow({\n entry,\n onRetry,\n onDismiss,\n}: {\n entry: UploadEntry\n onRetry: () => void\n onDismiss: () => void\n}) {\n const { state } = entry\n const failed = state.phase === 'failed'\n return (\n <Stack gap=\"1\" className=\"px-3 py-2\" data-testid=\"upload-row\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {entry.filename}\n </Text>\n <Badge variant={failed ? 'error' : state.phase === 'done' ? 'success' : 'info'}>\n {PHASE_LABEL[state.phase]}\n {state.attempt > 1 ? ` · attempt ${state.attempt}` : ''}\n </Badge>\n {failed ? (\n <>\n {state.failure?.retryable !== false ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} data-testid=\"upload-retry\">\n <RotateCcw /> Retry\n </Button>\n ) : null}\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onDismiss} aria-label=\"Dismiss upload\">\n <X />\n </Button>\n </>\n ) : null}\n </Row>\n {failed ? (\n <Text size=\"xs\" tone=\"muted\" data-testid=\"upload-error\">\n {state.failure?.stage}: {state.failure?.message}\n </Text>\n ) : (\n <ProgressBar value={state.progress} />\n )}\n </Stack>\n )\n}\n\n/** Named progress primitive for byte transfer. */\nfunction ProgressBar({ value }: { value: number }) {\n return (\n <span className=\"block h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <span\n className=\"block h-full rounded-full bg-primary transition-[width]\"\n style={{ width: `${Math.round(value * 100)}%` }}\n />\n </span>\n )\n}\n\nfunction AssetGrid({\n assets,\n filtered,\n projectId,\n focusAssetId,\n}: {\n assets: readonly AssetRecord[]\n filtered: boolean\n projectId: string\n focusAssetId?: string\n}) {\n if (assets.length === 0) {\n // The unfiltered empty state is the DropHint above; the *filtered* empty\n // state must say so explicitly (deterministic, reproducible).\n return filtered ? (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"asset-search-empty\">\n No assets match your search.\n </Text>\n ) : null\n }\n return (\n <Grid minItemWidth=\"220px\" gap=\"3\" data-testid=\"asset-grid\">\n {assets.map((asset) => (\n <AssetCard\n key={asset.id}\n asset={asset}\n projectId={projectId}\n focused={asset.id === focusAssetId}\n />\n ))}\n </Grid>\n )\n}\n\nfunction formatBytes(byteSize: number): string {\n if (byteSize < 1024) return `${byteSize} B`\n if (byteSize < 1024 * 1024) return `${(byteSize / 1024).toFixed(1)} KB`\n return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction AssetCard({\n asset,\n projectId,\n focused,\n}: {\n asset: AssetRecord\n projectId: string\n /** Deep-link target (`?asset=`): scroll into view + open the references dialog. */\n focused?: boolean\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n if (focused) cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }, [focused])\n return (\n <div ref={cardRef}>\n <Panel\n className={cn('overflow-hidden', focused && 'ring-2 ring-primary/70')}\n data-testid=\"asset-card\"\n >\n <AssetThumbnail asset={asset} />\n <Stack gap=\"1\" className=\"p-3\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\" title={asset.filename}>\n {asset.title ?? asset.filename}\n </Text>\n {asset.title ? (\n <Text size=\"xs\" tone=\"muted\" className=\"truncate\">\n {asset.filename}\n </Text>\n ) : null}\n <Text\n size=\"xs\"\n tone={asset.alt ? 'muted' : 'subtle'}\n className=\"truncate\"\n data-testid=\"asset-alt\"\n >\n {asset.alt ? `Alt: ${asset.alt}` : 'No alt text'}\n </Text>\n <Row gap=\"1\" wrap>\n <Code tone=\"muted\">{asset.contentType}</Code>\n {asset.width && asset.height ? (\n <Code tone=\"muted\">\n {asset.width}×{asset.height}\n </Code>\n ) : null}\n <Code tone=\"muted\">{formatBytes(asset.byteSize)}</Code>\n </Row>\n <Row gap=\"1\">\n <Code tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {asset.id}\n </Code>\n <AssetReferencesButton asset={asset} projectId={projectId} autoOpen={focused} />\n <EditAssetButton asset={asset} />\n <DeleteAssetButton asset={asset} />\n </Row>\n </Stack>\n </Panel>\n </div>\n )\n}\n\n/**\n * \"Where is this asset used?\" (EC-172): the shared references panel, inbound\n * over the EC-155 index, hosted in a dialog per asset card.\n */\nfunction AssetReferencesButton({\n asset,\n projectId,\n autoOpen,\n}: {\n asset: AssetRecord\n projectId: string\n autoOpen?: boolean\n}) {\n const [open, setOpen] = useState(false)\n useEffect(() => {\n if (autoOpen) setOpen(true)\n }, [autoOpen])\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`References of ${asset.filename}`}\n onClick={() => setOpen(true)}\n data-testid=\"asset-references\"\n >\n <Link2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>Used by — {asset.title ?? asset.filename}</DialogTitle>\n <DialogDescription>\n Everything referencing this asset, with deep links to the exact field or node.\n </DialogDescription>\n </DialogHeader>\n {open ? (\n <ReferencesPanel projectId={projectId} target={{ type: 'asset', id: asset.id }} />\n ) : null}\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/**\n * Metadata editing (EC-152): alt + title, persisted through the operations\n * asset facade (`assets.upsert`). Failures render in the dialog — never a\n * silent loss.\n */\nfunction EditAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [alt, setAlt] = useState('')\n const [title, setTitle] = useState('')\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = () => {\n setAlt(asset.alt ?? '')\n setTitle(asset.title ?? '')\n setError(null)\n setOpen(true)\n }\n\n const save = async () => {\n setBusy(true)\n setError(null)\n const result = await store.updateMetadata(asset.id, { alt, title })\n setBusy(false)\n if (result.ok) setOpen(false)\n else setError(result.message)\n }\n\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Edit ${asset.filename}`}\n onClick={openDialog}\n data-testid=\"asset-edit\"\n >\n <Pencil />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Edit {asset.filename}</DialogTitle>\n <DialogDescription>\n Alt text flows through the delivery shape into rendered markup; the title is the\n library display name.\n </DialogDescription>\n </DialogHeader>\n <Field label=\"Alt text\" htmlFor={`asset-alt-${asset.id}`}>\n <Input\n id={`asset-alt-${asset.id}`}\n value={alt}\n onChange={(e) => setAlt(e.target.value)}\n placeholder=\"Describe the image…\"\n data-testid=\"asset-edit-alt\"\n />\n </Field>\n <Field label=\"Title\" htmlFor={`asset-title-${asset.id}`}>\n <Input\n id={`asset-title-${asset.id}`}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={asset.filename}\n data-testid=\"asset-edit-title\"\n />\n </Field>\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-edit-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void save()}\n disabled={busy}\n data-testid=\"asset-edit-save\"\n >\n Save\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/** Image preview from the stored serve URL; icon fallback for other types. */\nfunction AssetThumbnail({ asset }: { asset: AssetRecord }) {\n const isImage = asset.contentType.startsWith('image/')\n // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.\n const resolvable = asset.url?.startsWith('http') ?? false\n if (isImage && asset.url && resolvable) {\n return (\n <img\n src={asset.url}\n alt={asset.alt ?? asset.filename}\n className=\"aspect-video w-full bg-muted object-cover\"\n />\n )\n }\n return (\n <span className=\"grid aspect-video w-full place-items-center bg-muted text-muted-foreground\">\n <ImageIcon className=\"size-6\" />\n </span>\n )\n}\n\n/**\n * Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact\n * inbound references — index-accurate (EC-155), with deep links to the field or\n * node — using the shared EC-173 impact rows. Deleting a referenced asset is\n * weak-by-default (allowed, with an explicit \"delete anyway\"): every reference\n * dangles into a visible validation issue rather than silently breaking.\n */\nfunction DeleteAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [usages, setUsages] = useState<ReferenceEntryView[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = async () => {\n setOpen(true)\n setError(null)\n setUsages(null)\n try {\n setUsages(await store.usagesOf(asset.id))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n }\n }\n\n const confirm = async () => {\n setBusy(true)\n setError(null)\n const inUse = (usages?.length ?? 0) > 0\n const result = await store.remove(asset.id, { confirmInUse: inUse })\n setBusy(false)\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n setOpen(false)\n } else if (result.outcome === 'blocked') {\n // A reference appeared between the pre-flight read and confirm (rare race).\n setError(`Asset is now referenced in ${result.usageCount} place(s) — reopen to review.`)\n } else {\n setError(result.message)\n }\n }\n\n const impact = usages ? buildAssetReferenceImpact(usages) : null\n const usageCount = impact?.broken.length ?? 0\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Delete ${asset.filename}`}\n onClick={() => void openDialog()}\n data-testid=\"asset-delete\"\n >\n <Trash2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-h-[80vh] max-w-md overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Delete {asset.filename}?</DialogTitle>\n <DialogDescription>\n Removes the stored bytes and the metadata record. This cannot be undone.\n </DialogDescription>\n </DialogHeader>\n {usages === null && error === null ? (\n <Text size=\"sm\" tone=\"muted\">\n Checking references…\n </Text>\n ) : usageCount > 0 ? (\n <Stack gap=\"2\" data-testid=\"asset-usage-warning\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-warning\" />\n <Text size=\"sm\" weight=\"semibold\">\n In use in {usageCount} place{usageCount === 1 ? '' : 's'}\n </Text>\n </Row>\n <ReferenceImpactRows\n projectId={store.projectId}\n broken={impact!.broken}\n onNavigate={() => setOpen(false)}\n />\n <Text size=\"xs\" tone=\"muted\">\n Deleting will leave these references broken — they will surface as validation\n issues.\n </Text>\n </Stack>\n ) : (\n <Text size=\"sm\" tone=\"muted\">\n No references found. Safe to delete.\n </Text>\n )}\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-delete-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => void confirm()}\n disabled={busy || usages === null}\n data-testid=\"asset-delete-confirm\"\n >\n {usageCount > 0 ? 'Delete anyway' : 'Delete'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asset-library.js","sourceRoot":"","sources":["../../../src/components/assets/asset-library.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AlertTriangle,\n ImageIcon,\n Link2,\n Pencil,\n RotateCcw,\n Search,\n Trash2,\n Upload,\n X,\n} from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport type { ChangeEvent, DragEvent } from 'react'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport type { UploadState } from '@elytracms/studio/lib/assets'\nimport { buildAssetReferenceImpact } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { ReferenceImpactRows } from '../cms/reference-confirm'\nimport { ReferencesPanel } from '../references/references-panel'\nimport {\n Badge,\n Button,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Field,\n Grid,\n Heading,\n Input,\n Panel,\n PanelBody,\n PanelHeader,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { filterAssets } from './asset-search'\nimport { useAssets } from './assets-store'\nimport type { UploadEntry } from './assets-store'\n\n/**\n * Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads\n * with visible progress/failure/retry, a searchable grid of stored assets\n * (filename/alt/title), metadata editing (alt, title — persisted through the\n * operations asset facade), and reference-respecting deletion (usage count +\n * block-or-confirm). All storage access goes through the persistence adapter\n * via the assets store. Each card answers \"where is this asset used?\" via the\n * shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link\n * param) scrolls that card into view and opens its references dialog.\n */\nexport function AssetLibrary({\n projectId,\n focusAssetId,\n}: {\n projectId: string\n focusAssetId?: string\n}) {\n const store = useAssets()\n const fileInputRef = useRef<HTMLInputElement | null>(null)\n const [dragging, setDragging] = useState(false)\n const [query, setQuery] = useState('')\n\n const onPick = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.files) store.upload([...event.target.files])\n event.target.value = ''\n }\n\n const onDrop = (event: DragEvent) => {\n event.preventDefault()\n setDragging(false)\n if (event.dataTransfer.files.length > 0) store.upload([...event.dataTransfer.files])\n }\n\n if (store.status === 'error') {\n return (\n <Stack gap=\"2\" className=\"p-6\" data-testid=\"assets-error\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text weight=\"semibold\">Assets failed to load</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? 'The persistence backend is unreachable.'}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> Retry\n </Button>\n </Row>\n </Stack>\n )\n }\n\n return (\n <Stack\n gap=\"4\"\n className={cn(\n 'flex-1 p-4 sm:p-6',\n dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4',\n )}\n onDragOver={(event) => {\n event.preventDefault()\n setDragging(true)\n }}\n onDragLeave={() => setDragging(false)}\n onDrop={onDrop}\n data-testid=\"asset-library\"\n >\n <Row gap=\"2\">\n <Heading level={2} size=\"h2\">\n Assets\n </Heading>\n <Badge variant=\"outline\">{store.assets.length}</Badge>\n <Spacer />\n <AssetSearchInput value={query} onChange={setQuery} />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={onPick}\n data-testid=\"asset-file-input\"\n />\n <Button size=\"sm\" onClick={() => fileInputRef.current?.click()}>\n <Upload /> Upload\n </Button>\n </Row>\n\n <DropHint visible={dragging || store.assets.length === 0} />\n\n {store.uploads.length > 0 ? (\n <Panel data-testid=\"upload-queue\">\n <PanelHeader>\n <Text size=\"xs\" weight=\"semibold\" tone=\"muted\">\n Uploads\n </Text>\n </PanelHeader>\n <PanelBody className=\"divide-y divide-border p-0\">\n {store.uploads.map((entry) => (\n <UploadRow\n key={entry.id}\n entry={entry}\n onRetry={() => store.retryUpload(entry.id)}\n onDismiss={() => store.dismissUpload(entry.id)}\n />\n ))}\n </PanelBody>\n </Panel>\n ) : null}\n\n {store.status === 'loading' ? (\n <Text size=\"sm\" tone=\"muted\">\n Loading assets…\n </Text>\n ) : (\n <AssetGrid\n assets={filterAssets(store.assets, query)}\n filtered={query.trim().length > 0}\n projectId={projectId}\n focusAssetId={focusAssetId}\n />\n )}\n </Stack>\n )\n}\n\n/** Named search primitive over filename / alt / title (EC-152). */\nfunction AssetSearchInput({\n value,\n onChange,\n}: {\n value: string\n onChange: (value: string) => void\n}) {\n return (\n <span className=\"relative\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder=\"Search assets…\"\n aria-label=\"Search assets\"\n className=\"w-56 pl-8\"\n data-testid=\"asset-search\"\n />\n </span>\n )\n}\n\n/** Persistent drop affordance; doubles as the empty state. */\nfunction DropHint({ visible }: { visible: boolean }) {\n return (\n <Stack\n gap=\"1\"\n align=\"center\"\n className={cn(\n 'rounded-lg border border-dashed border-border p-6 text-center',\n !visible && 'hidden sm:flex',\n )}\n >\n <ImageIcon className=\"size-5 text-muted-foreground\" />\n <Text size=\"sm\" tone=\"muted\">\n Drop files anywhere to upload, or use the Upload button.\n </Text>\n </Stack>\n )\n}\n\nconst PHASE_LABEL: Record<UploadState['phase'], string> = {\n idle: 'queued',\n preparing: 'preparing',\n uploading: 'uploading',\n confirming: 'saving metadata',\n done: 'done',\n failed: 'failed',\n}\n\n/** One in-flight upload: progress while transferring, explicit failure + retry. */\nfunction UploadRow({\n entry,\n onRetry,\n onDismiss,\n}: {\n entry: UploadEntry\n onRetry: () => void\n onDismiss: () => void\n}) {\n const { state } = entry\n const failed = state.phase === 'failed'\n return (\n <Stack gap=\"1\" className=\"px-3 py-2\" data-testid=\"upload-row\">\n <Row gap=\"2\">\n <Text size=\"sm\" weight=\"medium\" className=\"min-w-0 flex-1 truncate\">\n {entry.filename}\n </Text>\n <Badge variant={failed ? 'error' : state.phase === 'done' ? 'success' : 'info'}>\n {PHASE_LABEL[state.phase]}\n {state.attempt > 1 ? ` · attempt ${state.attempt}` : ''}\n </Badge>\n {failed ? (\n <>\n {state.failure?.retryable !== false ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onRetry} data-testid=\"upload-retry\">\n <RotateCcw /> Retry\n </Button>\n ) : null}\n <Button variant=\"ghost\" size=\"icon-sm\" onClick={onDismiss} aria-label=\"Dismiss upload\">\n <X />\n </Button>\n </>\n ) : null}\n </Row>\n {failed ? (\n <Text size=\"xs\" tone=\"muted\" data-testid=\"upload-error\">\n {state.failure?.stage}: {state.failure?.message}\n </Text>\n ) : (\n <ProgressBar value={state.progress} />\n )}\n </Stack>\n )\n}\n\n/** Named progress primitive for byte transfer. */\nfunction ProgressBar({ value }: { value: number }) {\n return (\n <span className=\"block h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <span\n className=\"block h-full rounded-full bg-primary transition-[width]\"\n style={{ width: `${Math.round(value * 100)}%` }}\n />\n </span>\n )\n}\n\nfunction AssetGrid({\n assets,\n filtered,\n projectId,\n focusAssetId,\n}: {\n assets: readonly AssetRecord[]\n filtered: boolean\n projectId: string\n focusAssetId?: string\n}) {\n if (assets.length === 0) {\n // The unfiltered empty state is the DropHint above; the *filtered* empty\n // state must say so explicitly (deterministic, reproducible).\n return filtered ? (\n <Text size=\"sm\" tone=\"muted\" data-testid=\"asset-search-empty\">\n No assets match your search.\n </Text>\n ) : null\n }\n return (\n <Grid minItemWidth=\"220px\" gap=\"3\" data-testid=\"asset-grid\">\n {assets.map((asset) => (\n <AssetCard\n key={asset.id}\n asset={asset}\n projectId={projectId}\n focused={asset.id === focusAssetId}\n />\n ))}\n </Grid>\n )\n}\n\nfunction formatBytes(byteSize: number): string {\n if (byteSize < 1024) return `${byteSize} B`\n if (byteSize < 1024 * 1024) return `${(byteSize / 1024).toFixed(1)} KB`\n return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`\n}\n\nfunction AssetCard({\n asset,\n projectId,\n focused,\n}: {\n asset: AssetRecord\n projectId: string\n /** Deep-link target (`?asset=`): scroll into view + open the references dialog. */\n focused?: boolean\n}) {\n const cardRef = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n if (focused) cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' })\n }, [focused])\n return (\n <div ref={cardRef}>\n <Panel\n className={cn('overflow-hidden', focused && 'ring-2 ring-primary/70')}\n data-testid=\"asset-card\"\n >\n <AssetThumbnail asset={asset} />\n <Stack gap=\"1\" className=\"p-3\">\n <Text size=\"sm\" weight=\"medium\" className=\"truncate\" title={asset.filename}>\n {asset.title ?? asset.filename}\n </Text>\n {asset.title ? (\n <Text size=\"xs\" tone=\"muted\" className=\"truncate\">\n {asset.filename}\n </Text>\n ) : null}\n <Text\n size=\"xs\"\n tone={asset.alt ? 'muted' : 'subtle'}\n className=\"truncate\"\n data-testid=\"asset-alt\"\n >\n {asset.alt ? `Alt: ${asset.alt}` : 'No alt text'}\n </Text>\n <Row gap=\"1\" wrap>\n <Code tone=\"muted\">{asset.contentType}</Code>\n {asset.width && asset.height ? (\n <Code tone=\"muted\">\n {asset.width}×{asset.height}\n </Code>\n ) : null}\n <Code tone=\"muted\">{formatBytes(asset.byteSize)}</Code>\n </Row>\n <Row gap=\"1\">\n <Code tone=\"muted\" className=\"min-w-0 flex-1 truncate\">\n {asset.id}\n </Code>\n <AssetReferencesButton asset={asset} projectId={projectId} autoOpen={focused} />\n <EditAssetButton asset={asset} />\n <DeleteAssetButton asset={asset} />\n </Row>\n </Stack>\n </Panel>\n </div>\n )\n}\n\n/**\n * \"Where is this asset used?\" (EC-172): the shared references panel, inbound\n * over the EC-155 index, hosted in a dialog per asset card.\n */\nfunction AssetReferencesButton({\n asset,\n projectId,\n autoOpen,\n}: {\n asset: AssetRecord\n projectId: string\n autoOpen?: boolean\n}) {\n const [open, setOpen] = useState(false)\n useEffect(() => {\n if (autoOpen) setOpen(true)\n }, [autoOpen])\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`References of ${asset.filename}`}\n onClick={() => setOpen(true)}\n data-testid=\"asset-references\"\n >\n <Link2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>Used by — {asset.title ?? asset.filename}</DialogTitle>\n <DialogDescription>\n Everything referencing this asset, with deep links to the exact field or node.\n </DialogDescription>\n </DialogHeader>\n {open ? (\n <ReferencesPanel projectId={projectId} target={{ type: 'asset', id: asset.id }} />\n ) : null}\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/**\n * Metadata editing (EC-152): alt + title, persisted through the operations\n * asset facade (`assets.upsert`). Failures render in the dialog — never a\n * silent loss.\n */\nfunction EditAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [alt, setAlt] = useState('')\n const [title, setTitle] = useState('')\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = () => {\n setAlt(asset.alt ?? '')\n setTitle(asset.title ?? '')\n setError(null)\n setOpen(true)\n }\n\n const save = async () => {\n setBusy(true)\n setError(null)\n const result = await store.updateMetadata(asset.id, { alt, title })\n setBusy(false)\n if (result.ok) setOpen(false)\n else setError(result.message)\n }\n\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Edit ${asset.filename}`}\n onClick={openDialog}\n data-testid=\"asset-edit\"\n >\n <Pencil />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Edit {asset.filename}</DialogTitle>\n <DialogDescription>\n Alt text flows through the delivery shape into rendered markup; the title is the\n library display name.\n </DialogDescription>\n </DialogHeader>\n <Field label=\"Alt text\" htmlFor={`asset-alt-${asset.id}`}>\n <Input\n id={`asset-alt-${asset.id}`}\n value={alt}\n onChange={(e) => setAlt(e.target.value)}\n placeholder=\"Describe the image…\"\n data-testid=\"asset-edit-alt\"\n />\n </Field>\n <Field label=\"Title\" htmlFor={`asset-title-${asset.id}`}>\n <Input\n id={`asset-title-${asset.id}`}\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder={asset.filename}\n data-testid=\"asset-edit-title\"\n />\n </Field>\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-edit-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void save()}\n disabled={busy}\n data-testid=\"asset-edit-save\"\n >\n Save\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n\n/** Image preview from the stored serve URL; icon fallback for other types. */\nfunction AssetThumbnail({ asset }: { asset: AssetRecord }) {\n const isImage = asset.contentType.startsWith('image/')\n // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.\n const resolvable = asset.url?.startsWith('http') ?? false\n if (isImage && asset.url && resolvable) {\n return (\n <img\n src={asset.url}\n alt={asset.alt ?? asset.filename}\n className=\"aspect-video w-full bg-muted object-cover\"\n />\n )\n }\n return (\n <span className=\"grid aspect-video w-full place-items-center bg-muted text-muted-foreground\">\n <ImageIcon className=\"size-6\" />\n </span>\n )\n}\n\n/**\n * Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact\n * inbound references — index-accurate (EC-155), with deep links to the field or\n * node — using the shared EC-173 impact rows. Deleting a referenced asset is\n * weak-by-default (allowed, with an explicit \"delete anyway\"): every reference\n * dangles into a visible validation issue rather than silently breaking.\n */\nfunction DeleteAssetButton({ asset }: { asset: AssetRecord }) {\n const store = useAssets()\n const [open, setOpen] = useState(false)\n const [usages, setUsages] = useState<ReferenceEntryView[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n\n const openDialog = async () => {\n setOpen(true)\n setError(null)\n setUsages(null)\n try {\n setUsages(await store.usagesOf(asset.id))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n }\n }\n\n const confirm = async () => {\n setBusy(true)\n setError(null)\n // The dialog has already enumerated usages (or shown there are none) and the user\n // is confirming deletion either way — so confirm unconditionally, letting\n // deleteAsset skip its redundant inbound re-check (EC-301 perf). Weak-by-default:\n // a still-referenced asset deletes anyway, dangling into a visible issue.\n const result = await store.remove(asset.id, { confirmInUse: true })\n setBusy(false)\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n setOpen(false)\n } else if (result.outcome === 'blocked') {\n // A reference appeared between the pre-flight read and confirm (rare race).\n setError(`Asset is now referenced in ${result.usageCount} place(s) — reopen to review.`)\n } else {\n setError(result.message)\n }\n }\n\n const impact = usages ? buildAssetReferenceImpact(usages) : null\n const usageCount = impact?.broken.length ?? 0\n return (\n <>\n <Button\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label={`Delete ${asset.filename}`}\n onClick={() => void openDialog()}\n data-testid=\"asset-delete\"\n >\n <Trash2 />\n </Button>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogContent className=\"max-h-[80vh] max-w-md overflow-y-auto\">\n <DialogHeader>\n <DialogTitle>Delete {asset.filename}?</DialogTitle>\n <DialogDescription>\n Removes the stored bytes and the metadata record. This cannot be undone.\n </DialogDescription>\n </DialogHeader>\n {usages === null && error === null ? (\n <Text size=\"sm\" tone=\"muted\">\n Checking references…\n </Text>\n ) : usageCount > 0 ? (\n <Stack gap=\"2\" data-testid=\"asset-usage-warning\">\n <Row gap=\"2\">\n <AlertTriangle className=\"size-4 text-warning\" />\n <Text size=\"sm\" weight=\"semibold\">\n In use in {usageCount} place{usageCount === 1 ? '' : 's'}\n </Text>\n </Row>\n <ReferenceImpactRows\n projectId={store.projectId}\n broken={impact!.broken}\n onNavigate={() => setOpen(false)}\n />\n <Text size=\"xs\" tone=\"muted\">\n Deleting will leave these references broken — they will surface as validation\n issues.\n </Text>\n </Stack>\n ) : (\n <Text size=\"sm\" tone=\"muted\">\n No references found. Safe to delete.\n </Text>\n )}\n {error ? (\n <Text size=\"sm\" className=\"text-destructive\" data-testid=\"asset-delete-error\">\n {error}\n </Text>\n ) : null}\n <DialogFooter>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setOpen(false)} disabled={busy}>\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={() => void confirm()}\n disabled={busy || usages === null}\n data-testid=\"asset-delete-confirm\"\n >\n {usageCount > 0 ? 'Delete anyway' : 'Delete'}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"]}
|
|
@@ -94,10 +94,14 @@ export function AssetsProvider({ projectId, children, }) {
|
|
|
94
94
|
// facade); blob byte deletion stays on the adapter; the usage gate reads
|
|
95
95
|
// the EC-155 reference index (`adapter.references`, EC-202).
|
|
96
96
|
const result = await deleteAsset({ assets: assetRepo, blobs: adapter.blobs, references: adapter.references }, projectId, assetId, options);
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
// Drop the one removed record from local state rather than re-listing the whole
|
|
98
|
+
// library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full
|
|
99
|
+
// `assets.list` round-trip on top of the deletion itself.
|
|
100
|
+
if (result.outcome === 'deleted' || result.outcome === 'not-found') {
|
|
101
|
+
setAssets((prev) => prev.filter((a) => a.id !== assetId));
|
|
102
|
+
}
|
|
99
103
|
return result;
|
|
100
|
-
}, [studio, projectId
|
|
104
|
+
}, [studio, projectId]);
|
|
101
105
|
const updateMetadata = useCallback(async (assetId, patch) => {
|
|
102
106
|
// Metadata writes go through the `assets.upsert` command via the
|
|
103
107
|
// operations facade (EC-141) — never straight at a repository.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGpG,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AAEzF,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAA;AAEjF,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AAEjG,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAuCtD,MAAM,aAAa,GAAG,aAAa,CAAqB,IAAI,CAAC,CAAA;AAE7D,MAAM,UAAU,cAAc,CAAC,EAC7B,SAAS,EACT,QAAQ,GAIT;IACC,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,GAAG,EAA0B,CAAC,CAAA;IAChE,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;IAC3B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAwB,SAAS,CAAC,CAAA;IACtE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAqB,SAAS,CAAC,CAAA;IAC/E,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAyB,EAAE,CAAC,CAAA;IAChE,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAyB,EAAE,CAAC,CAAA;IAElE,+EAA+E;IAC/E,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAE9B,EAAE;QACF,MAAM,MAAM,GAAG,MAAM,mBAAmB,EAAE,CAAA;QAC1C,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,yBAAyB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAA;IAC/E,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,EAAE,CAAA;YACrC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;YAC1D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,SAAS,CAAC,OAAO,CAAC,CAAA;gBAClB,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;gBACrC,OAAM;YACR,CAAC;YACD,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvB,SAAS,CAAC,OAAO,CAAC,CAAA;YAClB,eAAe,CAAC,SAAS,CAAC,CAAA;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,CAAC,OAAO,CAAC,CAAA;YAClB,eAAe,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACzE,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAA;IAEvB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,OAAO,EAAE,CAAA;IAChB,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAA;IAEb,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,QAAgB,EAAE,KAAkB,EAAE,EAAE;QAC1E,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAClF,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,QAAQ,GAAG,WAAW,CAC1B,KAAK,EAAE,QAAgB,EAAE,IAAoB,EAAE,QAAsB,EAAE,EAAE;QACvE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,EAAE,CAAA;QAC7C,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B;YACE,iEAAiE;YACjE,2DAA2D;YAC3D,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,SAAS;YACjB,GAAG,CAAC,OAAO,cAAc,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpF,EACD;YACE,SAAS;YACT,IAAI;YACJ,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC;SACpD,EACD,QAAQ,CACT,CAAA;QACD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,uEAAuE;YACvE,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;YACvC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAA;YAC3D,MAAM,OAAO,EAAE,CAAA;QACjB,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,CAAC,CAC7C,CAAA;IAED,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,KAA+B,EAAE,EAAE;QAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,SAAS,CAAC,OAAO,IAAI,CAAC,CAAA;YACtB,MAAM,QAAQ,GAAG,UAAU,SAAS,CAAC,OAAO,EAAE,CAAA;YAC9C,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;YAC1C,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnB,GAAG,IAAI;gBACP,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;aACzF,CAAC,CAAA;YACF,KAAK,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,CAAC,CACX,CAAA;IAED,MAAM,WAAW,GAAG,WAAW,CAC7B,CAAC,QAAgB,EAAE,EAAE;QACnB,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAA;QACpD,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAM;QAC7D,KAAK,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IAC5C,CAAC,EACD,CAAC,QAAQ,EAAE,OAAO,CAAC,CACpB,CAAA;IAED,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,QAAgB,EAAE,EAAE;QACrD,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACvC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAA;IAC7D,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,QAAQ,GAAG,WAAW;IAC1B,4EAA4E;IAC5E,qEAAqE;IACrE,KAAK,EAAE,OAAe,EAAE,EAAE,CAAC,8BAA8B,CAAC,SAAS,EAAE,OAAO,CAAC,EAC7E,CAAC,SAAS,CAAC,CACZ,CAAA;IAED,MAAM,MAAM,GAAG,WAAW,CACxB,KAAK,EAAE,OAAe,EAAE,OAAoC,EAAE,EAAE;QAC9D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,EAAE,CAAA;QAC7C,qEAAqE;QACrE,yEAAyE;QACzE,6DAA6D;QAC7D,MAAM,MAAM,GAAG,MAAM,WAAW,CAC9B,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,EAC3E,SAAS,EACT,OAAO,EACP,OAAO,CACR,CAAA;QACD,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,WAAW;YAAE,MAAM,OAAO,EAAE,CAAA;QACnF,OAAO,MAAM,CAAA;IACf,CAAC,EACD,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAC7B,CAAA;IAED,MAAM,cAAc,GAAG,WAAW,CAChC,KAAK,EAAE,OAAe,EAAE,KAAyB,EAAE,EAAE;QACnD,iEAAiE;QACjE,+DAA+D;QAC/D,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,EAAE,CAAA;QACpC,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;QAC1F,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,2EAA2E;YAC3E,4EAA4E;YAC5E,4EAA4E;YAC5E,iEAAiE;YACjE,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAA;YAC5B,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QACxE,CAAC;QACD,OAAO,MAAM,CAAA;IACf,CAAC,EACD,CAAC,MAAM,EAAE,SAAS,CAAC,CACpB,CAAA;IAED,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,SAAS;QACT,MAAM;QACN,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM;QACN,OAAO;QACP,MAAM;QACN,WAAW;QACX,aAAa;QACb,QAAQ;QACR,MAAM;QACN,cAAc;QACd,OAAO;KACR,CAAC,EACF;QACE,SAAS;QACT,MAAM;QACN,YAAY;QACZ,MAAM;QACN,OAAO;QACP,MAAM;QACN,WAAW;QACX,aAAa;QACb,QAAQ;QACR,MAAM;QACN,cAAc;QACd,OAAO;KACR,CACF,CAAA;IAED,OAAO,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA0B,CAAA;AAClF,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,KAAK,GAAG,UAAU,CAAC,aAAa,CAAC,CAAA;IACvC,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAA;IACjF,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,UAAU,CAAC,aAAa,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runUpload, xhrUploadTransport } from '@elytracms/studio/lib/assets'\nimport type { DeleteAssetResult, UploadState, UploadableFile } from '@elytracms/studio/lib/assets'\nimport { loadInboundAssetReferenceViews } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '@elytracms/studio/lib/operations'\nimport type { StudioOperations } from '@elytracms/studio/lib/operations'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n upload(files: Iterable<UploadableFile>): void\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n refresh(): Promise<void>\n}\n\nconst AssetsContext = createContext<AssetsStore | null>(null)\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n const uploadSeq = useRef(0)\n const [status, setStatus] = useState<AssetsStore['status']>('loading')\n const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)\n const [assets, setAssets] = useState<readonly AssetRecord[]>([])\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n const refresh = useCallback(async () => {\n try {\n const { operations } = await studio()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) {\n setStatus('error')\n setErrorMessage(result.error.message)\n return\n }\n setAssets(result.value)\n setStatus('ready')\n setErrorMessage(undefined)\n } catch (error) {\n setStatus('error')\n setErrorMessage(error instanceof Error ? error.message : String(error))\n }\n }, [studio, projectId])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>) => {\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n },\n [runEntry],\n )\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n if (result.outcome === 'deleted' || result.outcome === 'not-found') await refresh()\n return result\n },\n [studio, projectId, refresh],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into local state rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n setAssets((prev) => prev.map((a) => (a.id === assetId ? updated : a)))\n }\n return result\n },\n [studio, projectId],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport function useAssets(): AssetsStore {\n const store = useContext(AssetsContext)\n if (!store) throw new Error('useAssets must be used within an <AssetsProvider>.')\n return store\n}\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assets-store.js","sourceRoot":"","sources":["../../../src/components/assets/assets-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { AssetRecord, AssetRepository } from '@elytracms/core/persistence'\nimport { deleteAsset, runUpload, xhrUploadTransport } from '@elytracms/studio/lib/assets'\nimport type { DeleteAssetResult, UploadState, UploadableFile } from '@elytracms/studio/lib/assets'\nimport { loadInboundAssetReferenceViews } from '@elytracms/studio/lib/references'\nimport type { ReferenceEntryView } from '@elytracms/studio/lib/references'\nimport { getStudioOperations, operationsAssetRepository } from '@elytracms/studio/lib/operations'\nimport type { StudioOperations } from '@elytracms/studio/lib/operations'\nimport { updateAssetMetadata } from './asset-metadata'\nimport type { AssetMetadataPatch, UpdateAssetMetadataResult } from './asset-metadata'\n\n/**\n * Asset library store (EC-151, EC-141). Asset *metadata* mutations go through\n * the shared operations client (`assets.*` commands — validate → authorize →\n * mutate → event); blob *bytes* ride on `adapter.blobs`, which is transport,\n * not domain. The UI works identically against the in-memory seed and a live\n * Convex deployment.\n */\n\nexport interface UploadEntry {\n id: string\n filename: string\n state: UploadState\n}\n\nexport interface AssetsStore {\n /** The active project (e.g. for reference deep links, EC-202). */\n projectId: string\n status: 'loading' | 'ready' | 'error'\n errorMessage?: string\n assets: readonly AssetRecord[]\n uploads: readonly UploadEntry[]\n upload(files: Iterable<UploadableFile>): void\n retryUpload(uploadId: string): void\n dismissUpload(uploadId: string): void\n /**\n * Index-accurate inbound references of an asset (EC-202): the \"used by\"\n * pre-flight of the block-or-confirm delete dialog, read from the EC-155\n * reference index (not the retired string scan).\n */\n usagesOf(assetId: string): Promise<ReferenceEntryView[]>\n remove(assetId: string, options?: { confirmInUse?: boolean }): Promise<DeleteAssetResult>\n /** Edit alt/title (EC-152); persists through the operations asset facade. */\n updateMetadata(assetId: string, patch: AssetMetadataPatch): Promise<UpdateAssetMetadataResult>\n refresh(): Promise<void>\n}\n\nconst AssetsContext = createContext<AssetsStore | null>(null)\n\nexport function AssetsProvider({\n projectId,\n children,\n}: {\n projectId: string\n children: ReactNode\n}) {\n const uploadFilesRef = useRef(new Map<string, UploadableFile>())\n const uploadSeq = useRef(0)\n const [status, setStatus] = useState<AssetsStore['status']>('loading')\n const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)\n const [assets, setAssets] = useState<readonly AssetRecord[]>([])\n const [uploads, setUploads] = useState<readonly UploadEntry[]>([])\n\n /** The shared studio operations bundle + the metadata-write facade over it. */\n const studio = useCallback(async (): Promise<\n StudioOperations & { assetRepo: AssetRepository }\n > => {\n const bundle = await getStudioOperations()\n return { ...bundle, assetRepo: operationsAssetRepository(bundle.operations) }\n }, [])\n\n const refresh = useCallback(async () => {\n try {\n const { operations } = await studio()\n const result = await operations.assets.list({ projectId })\n if (!result.ok) {\n setStatus('error')\n setErrorMessage(result.error.message)\n return\n }\n setAssets(result.value)\n setStatus('ready')\n setErrorMessage(undefined)\n } catch (error) {\n setStatus('error')\n setErrorMessage(error instanceof Error ? error.message : String(error))\n }\n }, [studio, projectId])\n\n useEffect(() => {\n void refresh()\n }, [refresh])\n\n const setUploadState = useCallback((uploadId: string, state: UploadState) => {\n setUploads((prev) => prev.map((u) => (u.id === uploadId ? { ...u, state } : u)))\n }, [])\n\n const runEntry = useCallback(\n async (uploadId: string, file: UploadableFile, previous?: UploadState) => {\n const { adapter, assetRepo } = await studio()\n const result = await runUpload(\n {\n // Bytes ride on blob storage (transport); the metadata record is\n // confirmed through the `assets.upsert` operation command.\n blobs: adapter.blobs,\n assets: assetRepo,\n ...(typeof XMLHttpRequest !== 'undefined' ? { transport: xhrUploadTransport } : {}),\n },\n {\n projectId,\n file,\n onState: (state) => setUploadState(uploadId, state),\n },\n previous,\n )\n if (result.ok) {\n // Completed uploads leave the queue; the asset appears in the library.\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n await refresh()\n }\n },\n [studio, projectId, refresh, setUploadState],\n )\n\n const upload = useCallback(\n (files: Iterable<UploadableFile>) => {\n for (const file of files) {\n uploadSeq.current += 1\n const uploadId = `upload-${uploadSeq.current}`\n uploadFilesRef.current.set(uploadId, file)\n setUploads((prev) => [\n ...prev,\n { id: uploadId, filename: file.name, state: { phase: 'idle', progress: 0, attempt: 0 } },\n ])\n void runEntry(uploadId, file)\n }\n },\n [runEntry],\n )\n\n const retryUpload = useCallback(\n (uploadId: string) => {\n const file = uploadFilesRef.current.get(uploadId)\n const entry = uploads.find((u) => u.id === uploadId)\n if (!file || !entry || entry.state.phase !== 'failed') return\n void runEntry(uploadId, file, entry.state)\n },\n [runEntry, uploads],\n )\n\n const dismissUpload = useCallback((uploadId: string) => {\n uploadFilesRef.current.delete(uploadId)\n setUploads((prev) => prev.filter((u) => u.id !== uploadId))\n }, [])\n\n const usagesOf = useCallback(\n // Index-accurate (EC-202): inbound references from the EC-155 index via the\n // operations queries — the same machinery the References panel uses.\n async (assetId: string) => loadInboundAssetReferenceViews(projectId, assetId),\n [projectId],\n )\n\n const remove = useCallback(\n async (assetId: string, options?: { confirmInUse?: boolean }) => {\n const { adapter, assetRepo } = await studio()\n // Metadata removal goes through the `assets.remove` command (via the\n // facade); blob byte deletion stays on the adapter; the usage gate reads\n // the EC-155 reference index (`adapter.references`, EC-202).\n const result = await deleteAsset(\n { assets: assetRepo, blobs: adapter.blobs, references: adapter.references },\n projectId,\n assetId,\n options,\n )\n // Drop the one removed record from local state rather than re-listing the whole\n // library (EC-301 perf, mirrors updateMetadata) — a delete must not trigger a full\n // `assets.list` round-trip on top of the deletion itself.\n if (result.outcome === 'deleted' || result.outcome === 'not-found') {\n setAssets((prev) => prev.filter((a) => a.id !== assetId))\n }\n return result\n },\n [studio, projectId],\n )\n\n const updateMetadata = useCallback(\n async (assetId: string, patch: AssetMetadataPatch) => {\n // Metadata writes go through the `assets.upsert` command via the\n // operations facade (EC-141) — never straight at a repository.\n const { assetRepo } = await studio()\n const result = await updateAssetMetadata({ assets: assetRepo }, projectId, assetId, patch)\n if (result.ok) {\n // Patch the one updated record into local state rather than re-listing the\n // whole library (EC-301). A focal-point or alt edit must not trigger a full\n // `assets.list` round-trip — that re-list (plus the marker only moving once\n // it returned) was the focal-point picker's lag and query storm.\n const updated = result.asset\n setAssets((prev) => prev.map((a) => (a.id === assetId ? updated : a)))\n }\n return result\n },\n [studio, projectId],\n )\n\n const store = useMemo<AssetsStore>(\n () => ({\n projectId,\n status,\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n assets,\n uploads,\n upload,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n refresh,\n }),\n [\n projectId,\n status,\n errorMessage,\n assets,\n uploads,\n upload,\n retryUpload,\n dismissUpload,\n usagesOf,\n remove,\n updateMetadata,\n refresh,\n ],\n )\n\n return <AssetsContext.Provider value={store}>{children}</AssetsContext.Provider>\n}\n\nexport function useAssets(): AssetsStore {\n const store = useContext(AssetsContext)\n if (!store) throw new Error('useAssets must be used within an <AssetsProvider>.')\n return store\n}\n\n/**\n * The assets store when an `<AssetsProvider>` is mounted above, else `null`.\n * Lets surfaces outside the Assets section (the CMS asset picker, EC-152)\n * reuse an ambient provider or mount their own.\n */\nexport function useOptionalAssets(): AssetsStore | null {\n return useContext(AssetsContext)\n}\n"]}
|
|
@@ -66,13 +66,24 @@ function AssetPickerDialog({ value, onOpenChange, onChange, accept, }) {
|
|
|
66
66
|
pendingRef.current = new Set(libraryIds);
|
|
67
67
|
store.upload(files);
|
|
68
68
|
}
|
|
69
|
+
// Radix portals the dialog in the DOM, but React synthetic events still bubble up
|
|
70
|
+
// the COMPONENT tree — and this picker is rendered as a child of the asset field,
|
|
71
|
+
// which has its own drag-to-upload handlers. Without stopPropagation a drop inside
|
|
72
|
+
// the dialog would fire BOTH the picker's and the field's `onDrop`, uploading the
|
|
73
|
+
// file twice. Stop every DnD event here so the picker owns its drop entirely.
|
|
69
74
|
function onDragEnter(e) {
|
|
70
75
|
e.preventDefault();
|
|
76
|
+
e.stopPropagation();
|
|
71
77
|
dragDepth.current += 1;
|
|
72
78
|
if (!dragging)
|
|
73
79
|
setDragging(true);
|
|
74
80
|
}
|
|
75
|
-
function
|
|
81
|
+
function onDragOver(e) {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
}
|
|
85
|
+
function onDragLeave(e) {
|
|
86
|
+
e.stopPropagation();
|
|
76
87
|
dragDepth.current -= 1;
|
|
77
88
|
if (dragDepth.current <= 0) {
|
|
78
89
|
dragDepth.current = 0;
|
|
@@ -81,11 +92,12 @@ function AssetPickerDialog({ value, onOpenChange, onChange, accept, }) {
|
|
|
81
92
|
}
|
|
82
93
|
function onDrop(e) {
|
|
83
94
|
e.preventDefault();
|
|
95
|
+
e.stopPropagation();
|
|
84
96
|
dragDepth.current = 0;
|
|
85
97
|
setDragging(false);
|
|
86
98
|
handleUpload(Array.from(e.dataTransfer.files));
|
|
87
99
|
}
|
|
88
|
-
return (_jsx(Dialog, { open: true, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: cn(maximized ? 'max-w-[95vw]' : 'max-w-2xl'), onDragEnter: onDragEnter, onDragOver:
|
|
100
|
+
return (_jsx(Dialog, { open: true, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: cn(maximized ? 'max-w-[95vw]' : 'max-w-2xl'), onDragEnter: onDragEnter, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, children: [_jsx(DialogHeader, { children: _jsxs(Row, { gap: "2", align: "center", children: [_jsxs(Stack, { gap: "1", className: "min-w-0 flex-1", children: [_jsx(DialogTitle, { children: "Pick an asset" }), _jsx(DialogDescription, { children: "Select from the project's asset library." })] }), _jsx(Button, { variant: "ghost", size: "icon", "aria-label": maximized ? 'Restore dialog size' : 'Maximize dialog', onClick: () => setMaximized((v) => !v), className: "mr-7", children: maximized ? _jsx(Minimize2, { className: "size-4" }) : _jsx(Maximize2, { className: "size-4" }) })] }) }), _jsxs(Row, { gap: "2", align: "center", children: [_jsxs("span", { className: "relative flex-1", children: [_jsx(Search, { className: "pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search assets\u2026", className: "pl-8", "data-testid": "asset-picker-search" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, {}), " Upload"] }), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, accept: accept ?? 'image/*', className: "hidden", onChange: (e) => {
|
|
89
101
|
handleUpload(Array.from(e.target.files ?? []));
|
|
90
102
|
e.target.value = '';
|
|
91
103
|
} })] }), _jsx("div", { className: cn('grid gap-2 overflow-auto', maximized
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset-picker.js","sourceRoot":"","sources":["../../../src/components/cms/asset-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, Check, ImageIcon, Maximize2, Minimize2, Search, Upload } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { filterAssets } from '../assets/asset-search'\nimport { AssetsProvider, useAssets, useOptionalAssets } from '../assets/assets-store'\nimport { newlyAddedAssetIds } from '../assets/focal-point'\nimport { useCms } from './cms-store'\n\n/**\n * Pick an asset for a CMS `asset` field (brief §8.4, §8.6, EC-075 → EC-152).\n * Backed by the *real* asset library (the assets store over the operations\n * client) — the same records the Assets section lists — not by fixture\n * documents. Selecting stores the asset id on the document field. The dialog\n * is form-width with a maximize toggle and supports drag/click upload that\n * auto-selects the new asset (EC-301).\n */\nexport function AssetPicker({\n value,\n open,\n onOpenChange,\n onChange,\n accept,\n}: {\n value: string | null\n open: boolean\n onOpenChange: (open: boolean) => void\n onChange: (assetId: string | null) => void\n /** The file input's `accept` attribute for in-picker upload (field-derived). */\n accept?: string\n}) {\n const cms = useCms()\n const ambient = useOptionalAssets()\n // Mount nothing while closed: the self-provided assets store would\n // otherwise load the library eagerly for every asset field on the form.\n if (!open) return null\n if (ambient) {\n return (\n <AssetPickerDialog value={value} onOpenChange={onOpenChange} onChange={onChange} accept={accept} />\n )\n }\n if (cms.projectId === undefined) {\n // No project scope (fixture-only CMS session): the library is\n // unreachable — say so instead of silently showing nothing.\n return (\n <Dialog open onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Pick an asset</DialogTitle>\n <DialogDescription>Select from the project's asset library.</DialogDescription>\n </DialogHeader>\n <Row gap=\"2\" data-testid=\"asset-picker-no-project\">\n <AlertTriangle className=\"size-4 text-warning\" />\n <Text size=\"sm\" tone=\"muted\">\n The asset library needs a project — this CMS session has none.\n </Text>\n </Row>\n </DialogContent>\n </Dialog>\n )\n }\n return (\n <AssetsProvider projectId={cms.projectId}>\n <AssetPickerDialog value={value} onOpenChange={onOpenChange} onChange={onChange} accept={accept} />\n </AssetsProvider>\n )\n}\n\n/** The open dialog over a mounted assets store. */\nfunction AssetPickerDialog({\n value,\n onOpenChange,\n onChange,\n accept,\n}: {\n value: string | null\n onOpenChange: (open: boolean) => void\n onChange: (assetId: string | null) => void\n accept?: string\n}) {\n const store = useAssets()\n // Fresh per open: the picker unmounts entirely while closed.\n const [query, setQuery] = useState('')\n const [maximized, setMaximized] = useState(false)\n const [dragging, setDragging] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n // Library ids when an upload started; the effect below diffs against it to\n // learn the new asset and select it (the `upload()` API returns nothing).\n const pendingRef = useRef<ReadonlySet<string> | null>(null)\n // Drag depth (EC-301): keeps the drop overlay steady as the pointer crosses\n // child boundaries — the same per-element dragenter/dragleave flicker the field\n // fixes. Only hide when the count returns to zero.\n const dragDepth = useRef(0)\n const assets = filterAssets(store.assets, query)\n const libraryIds = useMemo(() => store.assets.map((a) => a.id), [store.assets])\n\n // Auto-select an asset uploaded from within the picker, then close.\n useEffect(() => {\n if (!pendingRef.current) return\n const added = newlyAddedAssetIds(pendingRef.current, libraryIds)\n if (added.length === 0) return\n pendingRef.current = null\n onChange(added[0]!)\n onOpenChange(false)\n }, [libraryIds, onChange, onOpenChange])\n\n function handleUpload(files: File[]) {\n if (files.length === 0) return\n pendingRef.current = new Set(libraryIds)\n store.upload(files)\n }\n function onDragEnter(e: React.DragEvent) {\n e.preventDefault()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragLeave() {\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n e.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n handleUpload(Array.from(e.dataTransfer.files))\n }\n\n return (\n <Dialog open onOpenChange={onOpenChange}>\n <DialogContent\n className={cn(maximized ? 'max-w-[95vw]' : 'max-w-2xl')}\n onDragEnter={onDragEnter}\n onDragOver={(e) => e.preventDefault()}\n onDragLeave={onDragLeave}\n onDrop={onDrop}\n >\n <DialogHeader>\n <Row gap=\"2\" align=\"center\">\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <DialogTitle>Pick an asset</DialogTitle>\n <DialogDescription>Select from the project's asset library.</DialogDescription>\n </Stack>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={maximized ? 'Restore dialog size' : 'Maximize dialog'}\n onClick={() => setMaximized((v) => !v)}\n className=\"mr-7\"\n >\n {maximized ? <Minimize2 className=\"size-4\" /> : <Maximize2 className=\"size-4\" />}\n </Button>\n </Row>\n </DialogHeader>\n\n <Row gap=\"2\" align=\"center\">\n <span className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n autoFocus\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search assets…\"\n className=\"pl-8\"\n data-testid=\"asset-picker-search\"\n />\n </span>\n <Button variant=\"outline\" size=\"sm\" onClick={() => fileInputRef.current?.click()}>\n <Upload /> Upload\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept={accept ?? 'image/*'}\n className=\"hidden\"\n onChange={(e) => {\n handleUpload(Array.from(e.target.files ?? []))\n e.target.value = ''\n }}\n />\n </Row>\n\n <div\n className={cn(\n 'grid gap-2 overflow-auto',\n maximized\n ? 'max-h-[78vh] grid-cols-4 lg:grid-cols-6'\n : 'max-h-[60vh] grid-cols-3 sm:grid-cols-4',\n )}\n data-testid=\"asset-picker-grid\"\n >\n {store.status === 'loading' ? (\n <Text size=\"sm\" tone=\"muted\" className=\"col-span-full p-3\">\n Loading assets…\n </Text>\n ) : store.status === 'error' ? (\n <Row gap=\"2\" className=\"col-span-full p-3\" data-testid=\"asset-picker-error\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? 'Assets failed to load.'}\n </Text>\n </Row>\n ) : assets.length === 0 ? (\n <Text size=\"sm\" tone=\"muted\" className=\"col-span-full p-3\">\n {query.trim()\n ? 'No assets match your search.'\n : 'No assets yet — drop files here or use Upload.'}\n </Text>\n ) : (\n assets.map((asset) => (\n <AssetPickerCard\n key={asset.id}\n asset={asset}\n selected={asset.id === value}\n onPick={() => {\n onChange(asset.id)\n onOpenChange(false)\n }}\n />\n ))\n )}\n </div>\n\n <Row gap=\"2\">\n <Spacer />\n {value ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n onChange(null)\n onOpenChange(false)\n }}\n >\n Clear\n </Button>\n ) : null}\n </Row>\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-xl border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-5 text-primary\" />\n <Text className=\"font-medium\">Drop to upload</Text>\n </Row>\n </div>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** One selectable asset: thumbnail + title/filename + alt text. The internal\n * asset id is intentionally not shown — it's noise to editors (EC-301). */\nfunction AssetPickerCard({\n asset,\n selected,\n onPick,\n}: {\n asset: AssetRecord\n selected: boolean\n onPick: () => void\n}) {\n return (\n <button\n type=\"button\"\n onClick={onPick}\n className={cn(\n 'flex flex-col gap-1.5 rounded-lg border p-2 text-left transition-colors',\n selected ? 'border-primary bg-primary/5' : 'border-border hover:bg-muted',\n )}\n data-testid=\"asset-picker-card\"\n >\n <PickerThumbnail asset={asset} />\n <Row gap=\"1\">\n <Text size=\"xs\" className=\"min-w-0 flex-1 truncate font-medium\">\n {asset.title ?? asset.filename}\n </Text>\n {selected ? <Check className=\"size-3.5 text-primary\" /> : null}\n </Row>\n <Text size=\"xs\" tone={asset.alt ? 'muted' : 'subtle'} className=\"truncate\">\n {asset.alt ?? 'No alt text'}\n </Text>\n </button>\n )\n}\n\n/** Image preview from the stored serve URL; icon fallback for other types. */\nfunction PickerThumbnail({ asset }: { asset: AssetRecord }) {\n const isImage = asset.contentType.startsWith('image/')\n // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.\n const resolvable = asset.url?.startsWith('http') ?? false\n if (isImage && asset.url && resolvable) {\n return (\n <img\n src={asset.url}\n alt={asset.alt ?? asset.filename}\n className=\"aspect-video w-full rounded-md bg-muted object-cover\"\n />\n )\n }\n return (\n <span className=\"grid aspect-video w-full place-items-center rounded-md bg-muted text-muted-foreground\">\n <ImageIcon className=\"size-6\" />\n </span>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"asset-picker.js","sourceRoot":"","sources":["../../../src/components/cms/asset-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, Check, ImageIcon, Maximize2, Minimize2, Search, Upload } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport type { AssetRecord } from '@elytracms/core/persistence'\nimport {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Input,\n Row,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { filterAssets } from '../assets/asset-search'\nimport { AssetsProvider, useAssets, useOptionalAssets } from '../assets/assets-store'\nimport { newlyAddedAssetIds } from '../assets/focal-point'\nimport { useCms } from './cms-store'\n\n/**\n * Pick an asset for a CMS `asset` field (brief §8.4, §8.6, EC-075 → EC-152).\n * Backed by the *real* asset library (the assets store over the operations\n * client) — the same records the Assets section lists — not by fixture\n * documents. Selecting stores the asset id on the document field. The dialog\n * is form-width with a maximize toggle and supports drag/click upload that\n * auto-selects the new asset (EC-301).\n */\nexport function AssetPicker({\n value,\n open,\n onOpenChange,\n onChange,\n accept,\n}: {\n value: string | null\n open: boolean\n onOpenChange: (open: boolean) => void\n onChange: (assetId: string | null) => void\n /** The file input's `accept` attribute for in-picker upload (field-derived). */\n accept?: string\n}) {\n const cms = useCms()\n const ambient = useOptionalAssets()\n // Mount nothing while closed: the self-provided assets store would\n // otherwise load the library eagerly for every asset field on the form.\n if (!open) return null\n if (ambient) {\n return (\n <AssetPickerDialog value={value} onOpenChange={onOpenChange} onChange={onChange} accept={accept} />\n )\n }\n if (cms.projectId === undefined) {\n // No project scope (fixture-only CMS session): the library is\n // unreachable — say so instead of silently showing nothing.\n return (\n <Dialog open onOpenChange={onOpenChange}>\n <DialogContent className=\"max-w-md\">\n <DialogHeader>\n <DialogTitle>Pick an asset</DialogTitle>\n <DialogDescription>Select from the project's asset library.</DialogDescription>\n </DialogHeader>\n <Row gap=\"2\" data-testid=\"asset-picker-no-project\">\n <AlertTriangle className=\"size-4 text-warning\" />\n <Text size=\"sm\" tone=\"muted\">\n The asset library needs a project — this CMS session has none.\n </Text>\n </Row>\n </DialogContent>\n </Dialog>\n )\n }\n return (\n <AssetsProvider projectId={cms.projectId}>\n <AssetPickerDialog value={value} onOpenChange={onOpenChange} onChange={onChange} accept={accept} />\n </AssetsProvider>\n )\n}\n\n/** The open dialog over a mounted assets store. */\nfunction AssetPickerDialog({\n value,\n onOpenChange,\n onChange,\n accept,\n}: {\n value: string | null\n onOpenChange: (open: boolean) => void\n onChange: (assetId: string | null) => void\n accept?: string\n}) {\n const store = useAssets()\n // Fresh per open: the picker unmounts entirely while closed.\n const [query, setQuery] = useState('')\n const [maximized, setMaximized] = useState(false)\n const [dragging, setDragging] = useState(false)\n const fileInputRef = useRef<HTMLInputElement>(null)\n // Library ids when an upload started; the effect below diffs against it to\n // learn the new asset and select it (the `upload()` API returns nothing).\n const pendingRef = useRef<ReadonlySet<string> | null>(null)\n // Drag depth (EC-301): keeps the drop overlay steady as the pointer crosses\n // child boundaries — the same per-element dragenter/dragleave flicker the field\n // fixes. Only hide when the count returns to zero.\n const dragDepth = useRef(0)\n const assets = filterAssets(store.assets, query)\n const libraryIds = useMemo(() => store.assets.map((a) => a.id), [store.assets])\n\n // Auto-select an asset uploaded from within the picker, then close.\n useEffect(() => {\n if (!pendingRef.current) return\n const added = newlyAddedAssetIds(pendingRef.current, libraryIds)\n if (added.length === 0) return\n pendingRef.current = null\n onChange(added[0]!)\n onOpenChange(false)\n }, [libraryIds, onChange, onOpenChange])\n\n function handleUpload(files: File[]) {\n if (files.length === 0) return\n pendingRef.current = new Set(libraryIds)\n store.upload(files)\n }\n // Radix portals the dialog in the DOM, but React synthetic events still bubble up\n // the COMPONENT tree — and this picker is rendered as a child of the asset field,\n // which has its own drag-to-upload handlers. Without stopPropagation a drop inside\n // the dialog would fire BOTH the picker's and the field's `onDrop`, uploading the\n // file twice. Stop every DnD event here so the picker owns its drop entirely.\n function onDragEnter(e: React.DragEvent) {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragOver(e: React.DragEvent) {\n e.preventDefault()\n e.stopPropagation()\n }\n function onDragLeave(e: React.DragEvent) {\n e.stopPropagation()\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current = 0\n setDragging(false)\n handleUpload(Array.from(e.dataTransfer.files))\n }\n\n return (\n <Dialog open onOpenChange={onOpenChange}>\n <DialogContent\n className={cn(maximized ? 'max-w-[95vw]' : 'max-w-2xl')}\n onDragEnter={onDragEnter}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n onDrop={onDrop}\n >\n <DialogHeader>\n <Row gap=\"2\" align=\"center\">\n <Stack gap=\"1\" className=\"min-w-0 flex-1\">\n <DialogTitle>Pick an asset</DialogTitle>\n <DialogDescription>Select from the project's asset library.</DialogDescription>\n </Stack>\n <Button\n variant=\"ghost\"\n size=\"icon\"\n aria-label={maximized ? 'Restore dialog size' : 'Maximize dialog'}\n onClick={() => setMaximized((v) => !v)}\n className=\"mr-7\"\n >\n {maximized ? <Minimize2 className=\"size-4\" /> : <Maximize2 className=\"size-4\" />}\n </Button>\n </Row>\n </DialogHeader>\n\n <Row gap=\"2\" align=\"center\">\n <span className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground\" />\n <Input\n autoFocus\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search assets…\"\n className=\"pl-8\"\n data-testid=\"asset-picker-search\"\n />\n </span>\n <Button variant=\"outline\" size=\"sm\" onClick={() => fileInputRef.current?.click()}>\n <Upload /> Upload\n </Button>\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept={accept ?? 'image/*'}\n className=\"hidden\"\n onChange={(e) => {\n handleUpload(Array.from(e.target.files ?? []))\n e.target.value = ''\n }}\n />\n </Row>\n\n <div\n className={cn(\n 'grid gap-2 overflow-auto',\n maximized\n ? 'max-h-[78vh] grid-cols-4 lg:grid-cols-6'\n : 'max-h-[60vh] grid-cols-3 sm:grid-cols-4',\n )}\n data-testid=\"asset-picker-grid\"\n >\n {store.status === 'loading' ? (\n <Text size=\"sm\" tone=\"muted\" className=\"col-span-full p-3\">\n Loading assets…\n </Text>\n ) : store.status === 'error' ? (\n <Row gap=\"2\" className=\"col-span-full p-3\" data-testid=\"asset-picker-error\">\n <AlertTriangle className=\"size-4 text-destructive\" />\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? 'Assets failed to load.'}\n </Text>\n </Row>\n ) : assets.length === 0 ? (\n <Text size=\"sm\" tone=\"muted\" className=\"col-span-full p-3\">\n {query.trim()\n ? 'No assets match your search.'\n : 'No assets yet — drop files here or use Upload.'}\n </Text>\n ) : (\n assets.map((asset) => (\n <AssetPickerCard\n key={asset.id}\n asset={asset}\n selected={asset.id === value}\n onPick={() => {\n onChange(asset.id)\n onOpenChange(false)\n }}\n />\n ))\n )}\n </div>\n\n <Row gap=\"2\">\n <Spacer />\n {value ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n onChange(null)\n onOpenChange(false)\n }}\n >\n Clear\n </Button>\n ) : null}\n </Row>\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-xl border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-5 text-primary\" />\n <Text className=\"font-medium\">Drop to upload</Text>\n </Row>\n </div>\n ) : null}\n </DialogContent>\n </Dialog>\n )\n}\n\n/** One selectable asset: thumbnail + title/filename + alt text. The internal\n * asset id is intentionally not shown — it's noise to editors (EC-301). */\nfunction AssetPickerCard({\n asset,\n selected,\n onPick,\n}: {\n asset: AssetRecord\n selected: boolean\n onPick: () => void\n}) {\n return (\n <button\n type=\"button\"\n onClick={onPick}\n className={cn(\n 'flex flex-col gap-1.5 rounded-lg border p-2 text-left transition-colors',\n selected ? 'border-primary bg-primary/5' : 'border-border hover:bg-muted',\n )}\n data-testid=\"asset-picker-card\"\n >\n <PickerThumbnail asset={asset} />\n <Row gap=\"1\">\n <Text size=\"xs\" className=\"min-w-0 flex-1 truncate font-medium\">\n {asset.title ?? asset.filename}\n </Text>\n {selected ? <Check className=\"size-3.5 text-primary\" /> : null}\n </Row>\n <Text size=\"xs\" tone={asset.alt ? 'muted' : 'subtle'} className=\"truncate\">\n {asset.alt ?? 'No alt text'}\n </Text>\n </button>\n )\n}\n\n/** Image preview from the stored serve URL; icon fallback for other types. */\nfunction PickerThumbnail({ asset }: { asset: AssetRecord }) {\n const isImage = asset.contentType.startsWith('image/')\n // In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.\n const resolvable = asset.url?.startsWith('http') ?? false\n if (isImage && asset.url && resolvable) {\n return (\n <img\n src={asset.url}\n alt={asset.alt ?? asset.filename}\n className=\"aspect-video w-full rounded-md bg-muted object-cover\"\n />\n )\n }\n return (\n <span className=\"grid aspect-video w-full place-items-center rounded-md bg-muted text-muted-foreground\">\n <ImageIcon className=\"size-6\" />\n </span>\n )\n}\n"]}
|
|
@@ -417,7 +417,11 @@ function AssetField({ field, value, onChange, readOnly, }) {
|
|
|
417
417
|
setDragging(false);
|
|
418
418
|
uploadFiles(Array.from(e.dataTransfer.files));
|
|
419
419
|
}
|
|
420
|
-
|
|
420
|
+
// While the picker is open the field is behind the modal, so any drop is happening
|
|
421
|
+
// INSIDE the dialog — detach the field's drag handlers entirely so a drop there can
|
|
422
|
+
// only be handled by the picker, never double-counted by the field too (EC-301). This
|
|
423
|
+
// is the belt to the picker's stopPropagation braces: the field cannot react at all.
|
|
424
|
+
const dragProps = canUpload && !open ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {};
|
|
421
425
|
const selectButton = !readOnly ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setOpen(true), children: [_jsx(ImagePlus, {}), " Select"] })) : null;
|
|
422
426
|
return (_jsxs(Stack, { gap: "2", className: cn('relative rounded-lg transition-shadow', dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background'), ...dragProps, children: [many && hasSub ? (
|
|
423
427
|
// Gallery with per-use sub-fields (EC-301): a card per placement — thumbnail +
|
|
@@ -433,7 +437,10 @@ function AssetField({ field, value, onChange, readOnly, }) {
|
|
|
433
437
|
}, onRemove: readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i)) }, `${aid}-${i}`));
|
|
434
438
|
}) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : many ? (
|
|
435
439
|
// Gallery: a grid of focal-aware previews + the single Select entry.
|
|
436
|
-
_jsxs(Stack, { gap: "2", children: [ids.length > 0 ? (_jsx("div", { className: cn('grid gap-2', compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4'), children: ids.map((aid, i) => (_jsx(AssetChip, { assetId: aid, collectionId: assetCollectionId, onRemove: readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid)) }, `${aid}-${i}`))) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : single
|
|
440
|
+
_jsxs(Stack, { gap: "2", children: [ids.length > 0 ? (_jsx("div", { className: cn('grid gap-2', compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4'), children: ids.map((aid, i) => (_jsx(AssetChip, { assetId: aid, collectionId: assetCollectionId, onRemove: readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid)) }, `${aid}-${i}`))) })) : (_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, onSelect: () => setOpen(true) })), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })) : single && hasSub ? (
|
|
441
|
+
// With per-use sub-fields the asset + its fields read as ONE grouped unit
|
|
442
|
+
// (like an object), so it's clear they belong together (EC-301 feedback).
|
|
443
|
+
_jsx(Card, { className: "border-l-2 border-l-border p-3", children: _jsxs(Stack, { gap: "3", children: [_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: cms.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, field.cardinality)) }), _jsx("div", { className: "border-t border-border/60 pt-3", children: _jsx(ObjectItemForm, { fields: subFields, value: assetSubfieldsOf(value), readOnly: readOnly, idPrefix: "asset-sub", onChange: (next) => onChange(assetNode(single, next)) }) })] }) })) : single ? (_jsx(AssetSinglePreview, { assetId: single, compact: compact, readOnly: readOnly, hasLibrary: assets !== null, fallbackTitle: cms.titleOf({ collection: assetCollectionId, id: single }), selectButton: selectButton, onEdit: () => setEditing(true), onRemove: () => onChange(applyAssetSelection(value, null, field.cardinality)) })) : (_jsxs(Stack, { gap: "2", children: [_jsx(EmptyAssetState, { compact: compact, canUpload: canUpload, onSelect: () => setOpen(true) }), _jsx(Row, { gap: "2", wrap: true, align: "center", children: selectButton })] })), !many && single && assets ? (_jsx(AssetOptionsDialog, { assetId: single, readOnly: readOnly, open: editing, onOpenChange: setEditing })) : null, _jsx(AssetPicker, { value: single, open: open, onOpenChange: setOpen, accept: assetAcceptAttr(field.accept), onChange: applyPicked }), rejected.length > 0 ? (_jsxs(Row, { gap: "2", align: "center", children: [_jsx(AlertTriangle, { className: "size-4 shrink-0 text-warning" }), _jsxs(Text, { size: "xs", tone: "muted", children: [rejected.length === 1 ? `“${rejected[0]}”` : `${rejected.length} files`, " skipped \u2014 only", ' ', describeAccept(field.accept), " allowed here."] })] })) : null, dragging ? (_jsx("div", { className: "pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85", children: _jsxs(Row, { gap: "2", align: "center", children: [_jsx(Upload, { className: "size-4 text-primary" }), _jsx(Text, { size: "sm", className: "font-medium", children: "Drop to upload" })] }) })) : null] }));
|
|
437
444
|
}
|
|
438
445
|
/** Empty-state placeholder shown when an asset field has no selection (EC-301). */
|
|
439
446
|
function EmptyAssetState({ compact, canUpload, onSelect, }) {
|
|
@@ -472,17 +479,18 @@ function AssetChip({ assetId, collectionId, onRemove, }) {
|
|
|
472
479
|
return (_jsxs("div", { className: "group relative overflow-hidden rounded-lg border border-border bg-muted/40", children: [_jsx(AssetThumb, { record: record, alt: title, className: "aspect-square w-full rounded-none border-0" }), _jsxs(Row, { gap: "1", align: "center", className: "px-2 py-1", children: [_jsx(Text, { size: "xs", tone: "subtle", className: "min-w-0 flex-1 truncate", title: title, children: title }), onRemove ? (_jsx("button", { type: "button", "aria-label": "Remove", onClick: onRemove, className: "text-muted-foreground hover:text-destructive", children: _jsx(X, { className: "size-3.5" }) })) : null] })] }));
|
|
473
480
|
}
|
|
474
481
|
/**
|
|
475
|
-
* The
|
|
476
|
-
*
|
|
477
|
-
*
|
|
482
|
+
* The selected single asset's preview row (EC-301): large focal-aware preview + info
|
|
483
|
+
* column (filename/dimensions, Select/Edit/Remove, alt). Extracted so it renders either
|
|
484
|
+
* bare (a plain asset field) or inside the grouping card when the field carries per-use
|
|
485
|
+
* sub-fields — the asset + its fields then read as one unit.
|
|
478
486
|
*/
|
|
479
|
-
function
|
|
480
|
-
return (_jsxs(
|
|
487
|
+
function AssetSinglePreview({ assetId, compact, readOnly, hasLibrary, fallbackTitle, selectButton, onEdit, onRemove, }) {
|
|
488
|
+
return (_jsxs("div", { className: compact ? 'flex flex-col gap-3' : 'flex gap-4', children: [_jsx("div", { className: compact ? 'w-full' : 'w-1/2 shrink-0', children: _jsx(AssetPreview, { assetId: assetId }) }), _jsxs(Stack, { gap: "2", className: cn('min-w-0', !compact && 'flex-1'), children: [_jsx(AssetInfo, { assetId: assetId, fallbackTitle: fallbackTitle }), !readOnly ? (_jsxs(Row, { gap: "2", wrap: true, align: "center", children: [selectButton, hasLibrary ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: onEdit, children: [_jsx(SlidersHorizontal, {}), " Edit"] })) : null, _jsxs(Button, { variant: "ghost", size: "sm", onClick: onRemove, children: [_jsx(Trash2, {}), " Remove"] })] })) : null, _jsx(AssetAltLine, { assetId: assetId })] })] }));
|
|
481
489
|
}
|
|
482
490
|
/**
|
|
483
491
|
* One gallery placement when a `many` asset field declares per-use sub-fields (EC-301):
|
|
484
|
-
* a thumbnail + filename + remove header over the placement's own sub-field form
|
|
485
|
-
*
|
|
492
|
+
* a thumbnail + filename + remove header over the placement's own sub-field form,
|
|
493
|
+
* modelled on a repeater item card so a gallery's placements read as grouped units.
|
|
486
494
|
*/
|
|
487
495
|
function AssetGalleryItem({ assetId, collectionId, fields, value, onChangeSubs, onRemove, readOnly, idPrefix, }) {
|
|
488
496
|
const cms = useCms();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-control.js","sourceRoot":"","sources":["../../../src/components/cms/field-control.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, ChevronDown, ChevronUp, GripVertical, ImagePlus, Link2, Pencil, Plus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport type {\n AssetFieldDef,\n DocumentRef,\n FieldDef,\n ObjectFieldDef,\n RelationFieldDef,\n SelectFieldDef,\n} from '@elytracms/core/cms-core'\nimport {\n BUILTIN_FIELD_CONTROLS,\n assetAcceptAttr,\n assetMatchesAccept,\n assetNode,\n assetSubfieldsOf,\n describeAccept,\n descendantIds,\n isFieldHidden,\n parentRelationField,\n readAssetId,\n} from '@elytracms/core/cms-core'\nimport {\n Badge,\n Button,\n Card,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Field,\n Input,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { applyAssetSelection, assetIdsOf, singleAssetIdOf } from '../assets/asset-selection'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb'\nimport { newlyAddedAssetIds } from '../assets/focal-point'\nimport { fieldLabel, useCms } from './cms-store'\nimport { useFieldDensity } from './field-density'\nimport { useOptionalDocumentEditor } from './document-editor-context'\nimport { toRefArray } from './cell'\nimport { RelationPicker } from './relation-picker'\nimport { AssetPicker } from './asset-picker'\nimport {\n FieldRendererBoundary,\n FieldRendererIssue,\n useFieldRenderers,\n} from './field-renderer'\n\ninterface FieldControlProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\n\n/**\n * The document-editor field control. Resolves a project's custom renderer by the\n * field's `form.control` (EC-238, customization seam EC-236) and falls back to the\n * built-in control for the field's datatype: a registered renderer renders inside\n * an error boundary (a throw → the built-in + a visible issue); a rejected\n * registration (non-component) shows the built-in with the issue above it; a field\n * naming a control with NO renderer and NO built-in name (a typo) shows the\n * built-in with a loud issue — never a silent fall-through; otherwise the built-in\n * renders alone.\n */\nexport function FieldControl(props: FieldControlProps) {\n const renderers = useFieldRenderers()\n const builtIn = <BuiltInFieldControl {...props} />\n const control = props.field.form?.control\n const Custom = control ? renderers.get(control) : undefined\n if (Custom) {\n return (\n <FieldRendererBoundary\n fallback={builtIn}\n issue={<FieldRendererIssue message=\"Custom renderer crashed — showing the built-in control.\" />}\n >\n <Custom {...props} />\n </FieldRendererBoundary>\n )\n }\n if (control) {\n const rejected = renderers.issues.find((issue) => issue.id === control)\n if (rejected) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={rejected.message} />\n {builtIn}\n </Stack>\n )\n }\n // The field asks for a control with no registered renderer and no built-in\n // name — surface it (a typo'd `control` would otherwise read as a plain input).\n if (!(BUILTIN_FIELD_CONTROLS as readonly string[]).includes(control)) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={`No field renderer registered for control “${control}”.`} />\n {builtIn}\n </Stack>\n )\n }\n }\n return builtIn\n}\n\nfunction BuiltInFieldControl({ field, value, onChange, readOnly, id }: FieldControlProps) {\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={value === true}\n disabled={readOnly}\n onChange={(e) => onChange(e.target.checked)}\n className=\"size-4 accent-primary disabled:opacity-50\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {value === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n id={id}\n type=\"number\"\n disabled={readOnly}\n value={typeof value === 'number' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? undefined : Number(e.target.value))}\n />\n )\n case 'date':\n return (\n <Input\n id={id}\n type=\"date\"\n disabled={readOnly}\n value={typeof value === 'string' ? value.slice(0, 10) : ''}\n onChange={(e) => onChange(e.target.value ? `${e.target.value}T00:00:00.000Z` : undefined)}\n />\n )\n case 'select':\n if (field.multiple) {\n const selected = Array.isArray(value) ? (value as string[]) : []\n return (\n <Row gap=\"2\" wrap>\n {field.options.map((opt) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-sm\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={readOnly}\n onChange={() =>\n onChange(\n on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value],\n )\n }\n className=\"size-4 accent-primary\"\n />\n {opt.label ?? opt.value}\n </label>\n )\n })}\n </Row>\n )\n }\n // EC-295: the `options` control renders a small fixed enumeration as inline\n // segmented buttons instead of the dropdown (single-select only).\n if (field.form?.control === 'options') {\n return (\n <SegmentedOptions field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n )\n }\n return (\n <Select\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label ?? opt.value}\n </option>\n ))}\n </Select>\n )\n case 'richText':\n return (\n <textarea\n id={id}\n rows={4}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none disabled:opacity-50\"\n />\n )\n case 'relation':\n return <RelationField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'asset':\n return <AssetField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'object':\n return <ObjectField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n default:\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n}\n\n/**\n * EC-295: the `options` control — a `select` shown as inline segmented buttons\n * (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer\n * registration); single-select only. Each option shows `label ?? value` and a\n * `title` tooltip from the option's `description`; the field `default` + `readOnly`\n * flow through the shared form. Picks one — to allow an empty/unset value use the\n * default dropdown control.\n */\nexport function SegmentedOptions({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: SelectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const current = typeof value === 'string' ? value : ''\n return (\n <Row gap=\"1\" wrap role=\"radiogroup\" id={id} aria-label={fieldLabel(field)}>\n {field.options.map((opt) => {\n const active = current === opt.value\n return (\n <Button\n key={opt.value}\n type=\"button\"\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n role=\"radio\"\n aria-checked={active}\n title={opt.description}\n disabled={readOnly}\n onClick={() => onChange(opt.value)}\n >\n {opt.label ?? opt.value}\n </Button>\n )\n })}\n </Row>\n )\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n}\n\n/**\n * Move `from` → `to`, returning a NEW array (all elements preserved). The repeater\n * applies the same `(from, to)` to its value array AND its parallel stable-key array,\n * so the two stay aligned through a reorder — a row's child/DOM state (an open\n * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).\n */\nexport function reorder<T>(list: readonly T[], from: number, to: number): T[] {\n const next = list.slice()\n const [moved] = next.splice(from, 1)\n if (moved !== undefined) next.splice(to, 0, moved)\n return next\n}\n\nconst SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType']\n\n/**\n * A scannable one-line summary for a collapsed repeater item (EC-294): the first\n * meaningful scalar subfield value — preferring title-ish field names — else `#n`.\n * Pure (no store): summarises from the item's own scalar values, so it works for\n * any object shape without resolving relations/assets.\n */\nexport function objectItemSummary(\n fields: readonly { name: string }[],\n item: Record<string, unknown>,\n index: number,\n): string {\n const scalar = (name: string): string | null => {\n const v = item[name]\n if (typeof v === 'string' && v.trim()) return v.trim()\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return null\n }\n const names = fields.map((f) => f.name)\n for (const name of SUMMARY_PREFERRED_FIELDS) {\n if (names.includes(name)) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n }\n for (const name of names) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n return `#${index + 1}`\n}\n\nfunction truncateSummary(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s\n}\n\n/**\n * The nested subfield form for one object item (EC-253). Reuses {@link FieldControl}\n * recursively, so any subfield type — including a nested object/repeater — works for\n * free. Writing a subfield merges it back into the item and bubbles the new item up.\n */\nfunction ObjectItemForm({\n fields,\n value,\n onChange,\n readOnly,\n idPrefix,\n}: {\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChange: (value: Record<string, unknown>) => void\n readOnly: boolean\n idPrefix: string\n}) {\n return (\n <Stack gap=\"3\" className=\"mt-2\">\n {fields.map((sub) => {\n // EC-273: a conditionally-hidden subfield (its predicate keys off the item's\n // own sibling values) is omitted — e.g. a discriminated link's `href` shows\n // only when `linkType === 'href'`. Re-evaluated as the item is edited.\n if (isFieldHidden(sub, { values: value })) return null\n const subId = `${idPrefix}-${sub.name}`\n return (\n <Field\n key={sub.name}\n htmlFor={subId}\n hint={sub.form?.description}\n label={\n <Row gap=\"2\">\n <span>{fieldLabel(sub)}</span>\n {sub.validation?.required ? <span className=\"text-destructive\">*</span> : null}\n </Row>\n }\n >\n <FieldControl\n id={subId}\n field={sub}\n value={value[sub.name]}\n readOnly={readOnly}\n onChange={(next) => {\n const updated = { ...value }\n if (next === undefined) delete updated[sub.name]\n else updated[sub.name] = next\n onChange(updated)\n }}\n />\n </Field>\n )\n })}\n </Stack>\n )\n}\n\n/**\n * Object / repeater field control (EC-253). `cardinality: 'one'` renders a single\n * nested form in a card; `'many'` renders an add/remove/reorder list of item cards.\n * Editing happens entirely inside the shared field form — no canvas escape (unlike\n * `blocks`), so props and document fields share this control.\n */\nfunction ObjectField({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: ObjectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n if (field.cardinality !== 'many') {\n return (\n <Card className=\"border-l-2 border-l-border p-3\">\n <ObjectItemForm\n fields={field.fields}\n value={asRecord(value)}\n readOnly={readOnly}\n idPrefix={id}\n onChange={onChange}\n />\n </Card>\n )\n }\n\n const items = Array.isArray(value) ? (value as Record<string, unknown>[]) : []\n // Stable React keys for the value-only items (they carry no intrinsic id). Index\n // keys would mis-associate a row's child/DOM state (an open relation/asset picker,\n // focus) when rows are reordered or removed; we keep a parallel id list in lockstep\n // with structural ops, reconciled by length when the array is replaced wholesale\n // (e.g. switching documents). Edits keep the keys — only structure changes them.\n const seq = useRef(0)\n const keysRef = useRef<number[]>([])\n if (keysRef.current.length !== items.length) {\n keysRef.current = items.map((_, i) => keysRef.current[i] ?? seq.current++)\n }\n const keys = keysRef.current\n const commit = (nextItems: Record<string, unknown>[], nextKeys: number[]) => {\n keysRef.current = nextKeys\n onChange(nextItems)\n }\n // The same (from,to) drives both the value array AND the parallel key array, so\n // they stay aligned through a reorder (EC-253 C2 / EC-294).\n const move = (from: number, to: number) => commit(reorder(items, from, to), reorder(keys, from, to))\n const removeAt = (i: number) =>\n commit(items.filter((_, j) => j !== i), keys.filter((_, j) => j !== i))\n const addItem = () => {\n const seed: Record<string, unknown> = {}\n for (const sub of field.fields) if (sub.default !== undefined) seed[sub.name] = sub.default\n commit([...items, seed], [...keys, seq.current++])\n }\n const setItem = (i: number, updated: Record<string, unknown>) => {\n const nextItems = items.slice()\n nextItems[i] = updated\n commit(nextItems, keys)\n }\n\n // EC-294: per-item collapse (ephemeral disclosure state keyed by the stable key —\n // not persisted) + native drag-to-reorder. ▲/▼ stay as the keyboard/a11y fallback.\n const [collapsedKeys, setCollapsedKeys] = useState<ReadonlySet<number>>(() => new Set())\n const toggleCollapsed = (key: number) =>\n setCollapsedKeys((prev) => {\n const next = new Set(prev)\n if (next.has(key)) next.delete(key)\n else next.add(key)\n return next\n })\n const allCollapsed = items.length > 0 && keys.every((k) => collapsedKeys.has(k))\n const [dragIndex, setDragIndex] = useState<number | null>(null)\n const [overIndex, setOverIndex] = useState<number | null>(null)\n const endDrag = () => {\n setDragIndex(null)\n setOverIndex(null)\n }\n const drop = (to: number) => {\n if (dragIndex !== null && dragIndex !== to) move(dragIndex, to)\n endDrag()\n }\n\n return (\n <Stack gap=\"2\">\n {items.length > 1 && !readOnly ? (\n <Row justify=\"end\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setCollapsedKeys(allCollapsed ? new Set() : new Set(keys))}\n >\n {allCollapsed ? 'Expand all' : 'Collapse all'}\n </Button>\n </Row>\n ) : null}\n {items.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No items\n </Text>\n ) : (\n items.map((item, i) => {\n // keysRef is reconciled to items.length above, so keys[i] is always present;\n // `?? i` only satisfies noUncheckedIndexedAccess (never taken at runtime).\n const key = keys[i] ?? i\n const record = asRecord(item)\n const isCollapsed = collapsedKeys.has(key)\n return (\n <Card\n key={key}\n onDragOver={(e) => {\n if (dragIndex === null) return\n e.preventDefault()\n if (overIndex !== i) setOverIndex(i)\n }}\n onDrop={() => drop(i)}\n className={cn(\n 'border-l-2 border-l-border p-0',\n dragIndex === i ? 'opacity-50' : '',\n overIndex === i && dragIndex !== null && dragIndex !== i\n ? 'border-l-primary ring-1 ring-primary/40'\n : '',\n )}\n >\n <Row className=\"select-none border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n {!readOnly ? (\n <span\n draggable\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(i))\n setDragIndex(i)\n }}\n onDragEnd={endDrag}\n aria-label=\"Drag to reorder\"\n className=\"cursor-grab text-muted-foreground/50 hover:text-muted-foreground active:cursor-grabbing\"\n >\n <GripVertical className=\"size-3.5\" />\n </span>\n ) : null}\n <button\n type=\"button\"\n onClick={() => toggleCollapsed(key)}\n aria-expanded={!isCollapsed}\n className=\"flex min-w-0 flex-1 items-center gap-1.5 text-left\"\n >\n <ChevronDown\n className={cn(\n 'size-3.5 shrink-0 text-muted-foreground transition-transform',\n isCollapsed ? '-rotate-90' : '',\n )}\n aria-hidden\n />\n <Text size=\"sm\" className=\"min-w-0 truncate font-medium\">\n {objectItemSummary(field.fields, record, i)}\n </Text>\n </button>\n {!readOnly ? (\n <Row gap=\"1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Move up\"\n disabled={i === 0}\n onClick={() => move(i, i - 1)}\n >\n <ChevronUp className=\"size-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Move down\"\n disabled={i === items.length - 1}\n onClick={() => move(i, i + 1)}\n >\n <ChevronDown className=\"size-3\" />\n </Button>\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Remove item\" onClick={() => removeAt(i)}>\n <Trash2 className=\"size-3\" />\n </Button>\n </Row>\n ) : null}\n </Row>\n {!isCollapsed ? (\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={field.fields}\n value={record}\n readOnly={readOnly}\n idPrefix={`${id}-${i}`}\n onChange={(updated) => setItem(i, updated)}\n />\n </div>\n ) : null}\n </Card>\n )\n })\n )}\n {!readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={addItem}>\n <Plus className=\"size-3\" /> Add item\n </Button>\n ) : null}\n </Stack>\n )\n}\n\nfunction RelationField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: RelationFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const cms = useCms()\n const editor = useOptionalDocumentEditor()\n const [open, setOpen] = useState(false)\n const refs = toRefArray(value)\n const projectId = cms.projectId\n\n // EC-218: for the editing document's `parent` self-relation, exclude the\n // document itself and its descendants from the picker — choosing one would make\n // the page its own ancestor (a cycle). Only applies to the parent field of a\n // hierarchical collection; ordinary relations (and other self-relations) are\n // unrestricted. validateDocument is the backstop where this context is absent.\n const excludeIds = useMemo<Set<string> | undefined>(() => {\n if (!editor || field.target !== editor.collection.id) return undefined\n if (parentRelationField(editor.collection)?.name !== field.name) return undefined\n const ids = descendantIds(editor.docRef.id, cms.documentsOf(editor.collection.id))\n ids.add(editor.docRef.id)\n return ids\n }, [editor, field.target, field.name, cms])\n\n function apply(next: DocumentRef[]) {\n onChange(field.cardinality === 'many' ? next : (next[0] ?? undefined))\n }\n\n return (\n <Row gap=\"2\" wrap>\n {refs.map((ref, i) => {\n // EC-296: the relation title navigates to the referenced document (autosave\n // keeps the current edit safe). Without a projectId (no persistence, e.g.\n // fixtures) it stays plain text.\n const title = cms.titleOf(ref)\n return (\n <Badge key={`${ref.id}-${i}`} variant=\"info\">\n {projectId ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(ref.collection, ref.id) }}\n title={`Open ${title}`}\n className=\"inline-flex items-center gap-1 hover:underline\"\n >\n <Link2 className=\"size-3\" /> {title}\n </Link>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <Link2 className=\"size-3\" /> {title}\n </span>\n )}\n {!readOnly ? (\n <button\n type=\"button\"\n aria-label=\"Remove\"\n onClick={() => apply(refs.filter((r) => r.id !== ref.id))}\n className=\"ml-1 hover:text-destructive\"\n >\n <X className=\"size-3\" />\n </button>\n ) : null}\n </Badge>\n )\n })}\n {!readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <Pencil /> {refs.length > 0 ? 'Change' : 'Pick'}\n </Button>\n ) : null}\n <RelationPicker\n targetCollection={field.target}\n cardinality={field.cardinality}\n value={refs}\n open={open}\n onOpenChange={setOpen}\n onChange={apply}\n excludeIds={excludeIds}\n />\n </Row>\n )\n}\n\n/**\n * The asset field (EC-152 + EC-248). Beyond picking from the library it now shows\n * a focal-point-aware preview, uploads directly from the field (reusing the EC-152\n * upload path — no parallel uploader), and edits the selected image's alt + focal\n * point inline (persisted on the asset document, where delivery reads them, EC-230).\n * With no `<AssetsProvider>` (fixtures/playground) it degrades to the labelled-badge\n * picker — no preview/upload — so it stays usable without a real library.\n */\nfunction AssetField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: AssetFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const cms = useCms()\n const assets = useOptionalAssets()\n const density = useFieldDensity()\n const compact = density === 'compact'\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [dragging, setDragging] = useState(false)\n // Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown\n // as a transient inline issue, cleared on the next valid upload. Never a silent drop.\n const [rejected, setRejected] = useState<string[]>([])\n // Asset ids known when an upload-from-field started; the effect below diffs the\n // library against it to learn the new id (the `upload()` API returns nothing).\n const pendingRef = useRef<ReadonlySet<string> | null>(null)\n const assetCollectionId = cms.collections.find((c) => c.kind === 'asset')?.id ?? 'asset'\n const many = field.cardinality === 'many'\n const ids = many ? assetIdsOf(value) : []\n const single = !many ? singleAssetIdOf(value) : null\n // EC-301 per-use sub-fields: with `field.fields` the value stores asset ENVELOPES\n // carrying the sub-field siblings (`{ type:'asset', id, caption }`); without them it\n // stays bare ids (self-typed on save). Picks/edits preserve a placement's sub-fields.\n const subFields = field.fields ?? []\n const hasSub = subFields.length > 0\n // The raw `many` entries (envelopes or bare ids), index-aligned with `ids`, so each\n // gallery item can read/write its own sub-fields.\n const entries = many && Array.isArray(value) ? (value as unknown[]) : []\n\n const libraryIds = useMemo(\n () => (assets?.status === 'ready' ? assets.assets.map((a) => a.id) : []),\n [assets],\n )\n\n // Auto-select an asset uploaded FROM this field once it lands in the library.\n useEffect(() => {\n if (!pendingRef.current) return\n const added = newlyAddedAssetIds(pendingRef.current, libraryIds)\n if (added.length === 0) return\n pendingRef.current = null\n if (many) {\n const current = Array.isArray(value) ? (value as unknown[]) : []\n onChange([...current, ...added.map((id) => (hasSub ? assetNode(id) : id))])\n } else {\n onChange(hasSub ? assetNode(added[0]!, assetSubfieldsOf(value)) : applyAssetSelection(value, added[0]!, 'one'))\n }\n // Reacts to the library set growing (an upload landing), not to unrelated\n // value edits — `value`/`onChange` are read from the closure on purpose.\n }, [libraryIds, many, hasSub, onChange, value])\n\n function uploadFiles(files: File[]) {\n if (!assets || files.length === 0) return\n // EC-301: a drag-drop bypasses the file dialog's `accept` filter — re-check each\n // file against the field's `accept` and surface a visible issue for the rejects\n // instead of silently uploading the wrong kind.\n const ok: File[] = []\n const bad: string[] = []\n for (const file of files) {\n if (assetMatchesAccept({ contentType: file.type, filename: file.name }, field.accept)) ok.push(file)\n else bad.push(file.name)\n }\n setRejected(bad)\n if (ok.length === 0) return\n // Remember the library set so the effect above can learn the new id and\n // auto-select it once the upload lands (the upload API returns nothing).\n pendingRef.current = new Set(libraryIds)\n assets.upload(ok)\n }\n\n // Apply a picked asset id, preserving a placement's per-use sub-fields (EC-301).\n // Without sub-fields it stays the plain id-list behaviour (self-typed on save).\n function applyPicked(picked: string | null) {\n if (!hasSub) {\n onChange(applyAssetSelection(value, picked, field.cardinality))\n return\n }\n if (many) {\n if (picked === null || entries.some((e) => readAssetId(e) === picked)) return\n onChange([...entries, assetNode(picked)])\n } else {\n onChange(picked ? assetNode(picked, assetSubfieldsOf(value)) : undefined)\n }\n }\n\n const canUpload = !readOnly && assets !== null\n\n // Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay\n // steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`\n // fire per descendant, so toggling on a bare `dragleave` blinks constantly; we\n // only hide the overlay when the counter returns to zero (the pointer has truly\n // left the field). The overlay is pointer-events-none, so it never fires its own\n // enter/leave to confuse the count.\n const dragDepth = useRef(0)\n function onDragEnter(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragOver(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault() // required so a drop is allowed\n }\n function onDragLeave() {\n if (!canUpload) return\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n uploadFiles(Array.from(e.dataTransfer.files))\n }\n const dragProps = canUpload ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {}\n\n const selectButton = !readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <ImagePlus /> Select\n </Button>\n ) : null\n\n return (\n <Stack\n gap=\"2\"\n className={cn(\n 'relative rounded-lg transition-shadow',\n dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background',\n )}\n {...dragProps}\n >\n {many && hasSub ? (\n // Gallery with per-use sub-fields (EC-301): a card per placement — thumbnail +\n // its own sub-field form — reusing the object/repeater item rendering.\n <Stack gap=\"2\">\n {entries.length > 0 ? (\n <Stack gap=\"2\">\n {entries.map((entry, i) => {\n const aid = readAssetId(entry)\n if (!aid) return null\n return (\n <AssetGalleryItem\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n fields={subFields}\n value={assetSubfieldsOf(entry)}\n readOnly={readOnly}\n idPrefix={`asset-${i}`}\n onChangeSubs={(next) => {\n const nextEntries = entries.slice()\n nextEntries[i] = assetNode(aid, next)\n onChange(nextEntries)\n }}\n onRemove={readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i))}\n />\n )\n })}\n </Stack>\n ) : (\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : many ? (\n // Gallery: a grid of focal-aware previews + the single Select entry.\n <Stack gap=\"2\">\n {ids.length > 0 ? (\n <div\n className={cn(\n 'grid gap-2',\n compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4',\n )}\n >\n {ids.map((aid, i) => (\n <AssetChip\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n onRemove={readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid))}\n />\n ))}\n </div>\n ) : (\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : single ? (\n <Stack gap=\"3\">\n <div className={compact ? 'flex flex-col gap-3' : 'flex gap-4'}>\n <div className={compact ? 'w-full' : 'w-1/2 shrink-0'}>\n <AssetPreview assetId={single} />\n </div>\n <Stack gap=\"2\" className={cn('min-w-0', !compact && 'flex-1')}>\n <AssetInfo assetId={single} fallbackTitle={cms.titleOf({ collection: assetCollectionId, id: single })} />\n {!readOnly ? (\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n {assets ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setEditing(true)}>\n <SlidersHorizontal /> Edit\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => onChange(applyAssetSelection(value, null, field.cardinality))}\n >\n <Trash2 /> Remove\n </Button>\n </Row>\n ) : null}\n <AssetAltLine assetId={single} />\n </Stack>\n </div>\n {hasSub ? (\n <AssetSubfields\n fields={subFields}\n value={assetSubfieldsOf(value)}\n readOnly={readOnly}\n idPrefix=\"asset-sub\"\n onChange={(next) => onChange(assetNode(single, next))}\n />\n ) : null}\n </Stack>\n ) : (\n <Stack gap=\"2\">\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n )}\n\n {/* Image options (focal point + metadata) for a single selected asset, in a\n dialog rather than an inline disclosure (EC-301). Shared across every use. */}\n {!many && single && assets ? (\n <AssetOptionsDialog\n assetId={single}\n readOnly={readOnly}\n open={editing}\n onOpenChange={setEditing}\n />\n ) : null}\n\n <AssetPicker\n value={single}\n open={open}\n onOpenChange={setOpen}\n accept={assetAcceptAttr(field.accept)}\n onChange={applyPicked}\n />\n\n {rejected.length > 0 ? (\n <Row gap=\"2\" align=\"center\">\n <AlertTriangle className=\"size-4 shrink-0 text-warning\" />\n <Text size=\"xs\" tone=\"muted\">\n {rejected.length === 1 ? `“${rejected[0]}”` : `${rejected.length} files`} skipped — only{' '}\n {describeAccept(field.accept)} allowed here.\n </Text>\n </Row>\n ) : null}\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-4 text-primary\" />\n <Text size=\"sm\" className=\"font-medium\">\n Drop to upload\n </Text>\n </Row>\n </div>\n ) : null}\n </Stack>\n )\n}\n\n/** Empty-state placeholder shown when an asset field has no selection (EC-301). */\nfunction EmptyAssetState({\n compact,\n canUpload,\n onSelect,\n}: {\n compact: boolean\n canUpload: boolean\n onSelect: () => void\n}) {\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n className={cn(\n 'grid w-full place-items-center gap-1 rounded-lg border border-dashed border-border bg-muted/30 text-center text-muted-foreground transition-colors hover:bg-muted/60',\n compact ? 'aspect-video' : 'aspect-[5/2]',\n )}\n >\n <ImagePlus className=\"size-6\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {canUpload ? 'Select or drop an image' : 'Select an asset'}\n </Text>\n </button>\n )\n}\n\n/** A large focal-aware preview of the selected asset (EC-301). */\nfunction AssetPreview({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} className=\"aspect-[4/3] w-full\" />\n}\n\n/** Filename + dimensions/type for the selected asset — the id is never shown (EC-301). */\nfunction AssetInfo({ assetId, fallbackTitle }: { assetId: string; fallbackTitle?: string }) {\n const record = useAssetRecord(assetId)\n const name = record?.title ?? record?.filename ?? fallbackTitle ?? assetId\n const dims = record?.width && record?.height ? `${record.width}×${record.height}` : null\n const kind = record?.contentType?.split('/')[1]?.toUpperCase()\n const meta = [dims, kind].filter(Boolean).join(' · ')\n return (\n <Stack gap=\"1\">\n <Text size=\"sm\" className=\"truncate font-medium\" title={name}>\n {name}\n </Text>\n {meta ? (\n <Text size=\"xs\" tone=\"subtle\">\n {meta}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The selected image's alt text shown in the field (EC-301) — what an editor most\n * wants at a glance; edited in the options dialog. Reads `meta.alt` with the legacy\n * `alt` column as fallback, matching delivery.\n */\nfunction AssetAltLine({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n const metaAlt = typeof record?.meta?.alt === 'string' ? record.meta.alt : undefined\n const alt = metaAlt ?? record?.alt\n return (\n <Row gap=\"1\" align=\"baseline\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n Alt:\n </Text>\n <Text\n size=\"xs\"\n tone={alt ? 'muted' : 'subtle'}\n className={cn('min-w-0 truncate', !alt && 'italic')}\n title={alt ?? undefined}\n >\n {alt || 'not set'}\n </Text>\n </Row>\n )\n}\n\n/** A single asset reference: focal-aware thumbnail + title, with an optional remove. */\nfunction AssetChip({\n assetId,\n collectionId,\n onRemove,\n}: {\n assetId: string\n collectionId: string\n onRemove?: () => void\n}) {\n const cms = useCms()\n const record = useAssetRecord(assetId)\n const title = cms.titleOf({ collection: collectionId, id: assetId })\n return (\n <div className=\"group relative overflow-hidden rounded-lg border border-border bg-muted/40\">\n <AssetThumb record={record} alt={title} className=\"aspect-square w-full rounded-none border-0\" />\n <Row gap=\"1\" align=\"center\" className=\"px-2 py-1\">\n <Text size=\"xs\" tone=\"subtle\" className=\"min-w-0 flex-1 truncate\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <button\n type=\"button\"\n aria-label=\"Remove\"\n onClick={onRemove}\n className=\"text-muted-foreground hover:text-destructive\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </Row>\n </div>\n )\n}\n\n/**\n * The per-use sub-field group shown below a single asset (EC-301): a placement's own\n * metadata (a `caption` here), distinct from the asset-level metadata in the options\n * dialog. Reuses the object/repeater item form, so any sub-field type works for free.\n */\nfunction AssetSubfields({\n fields,\n value,\n onChange,\n readOnly,\n idPrefix,\n}: {\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChange: (value: Record<string, unknown>) => void\n readOnly: boolean\n idPrefix: string\n}) {\n return (\n <Card className=\"border-l-2 border-l-border px-3 pb-3 pt-2\">\n <Text size=\"xs\" tone=\"subtle\" className=\"font-medium uppercase tracking-wide\">\n This use\n </Text>\n <ObjectItemForm\n fields={fields}\n value={value}\n onChange={onChange}\n readOnly={readOnly}\n idPrefix={idPrefix}\n />\n </Card>\n )\n}\n\n/**\n * One gallery placement when a `many` asset field declares per-use sub-fields (EC-301):\n * a thumbnail + filename + remove header over the placement's own sub-field form. The\n * `many` analogue of {@link AssetSubfields}, modelled on a repeater item card.\n */\nfunction AssetGalleryItem({\n assetId,\n collectionId,\n fields,\n value,\n onChangeSubs,\n onRemove,\n readOnly,\n idPrefix,\n}: {\n assetId: string\n collectionId: string\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChangeSubs: (value: Record<string, unknown>) => void\n onRemove?: () => void\n readOnly: boolean\n idPrefix: string\n}) {\n const cms = useCms()\n const record = useAssetRecord(assetId)\n const title = cms.titleOf({ collection: collectionId, id: assetId })\n return (\n <Card className=\"border-l-2 border-l-border p-0\">\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n <AssetThumb record={record} alt={title} className=\"size-10 shrink-0\" />\n <Text size=\"sm\" className=\"min-w-0 flex-1 truncate font-medium\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Remove\" onClick={onRemove}>\n <Trash2 className=\"size-3.5\" />\n </Button>\n ) : null}\n </Row>\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={fields}\n value={value}\n onChange={onChangeSubs}\n readOnly={readOnly}\n idPrefix={idPrefix}\n />\n </div>\n </Card>\n )\n}\n\n/**\n * System columns the upload pipeline owns — never editable metadata, so they're\n * excluded from the asset collection's schema-driven options form (EC-301). The\n * filename + dimensions already show in the field's info column.\n */\nconst SYSTEM_ASSET_FIELDS = new Set([\n 'filename',\n 'url',\n 'contentType',\n 'byteSize',\n 'width',\n 'height',\n 'storageKey',\n 'title',\n])\n\n/** Drop `undefined` values so the persisted meta bag stays JSON/Convex-clean. */\nfunction pruneMeta(meta: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(meta).filter(([, v]) => v !== undefined))\n}\n\n/**\n * The image options dialog (EC-301): the focal point (engine-special visual\n * control) + the asset's **schema-driven** metadata, rendered from the\n * `kind:'asset'` collection's `fields` through the same generic field engine\n * documents use — so a project declares asset metadata (alt, copyright, credit, …)\n * in code, not hardcoded here. Edits persist on the asset, shared across every use.\n * Metadata is buffered locally and committed once on close (one write); the focal\n * point commits immediately (it drives a live visual). No-ops without a library.\n */\nfunction AssetOptionsDialog({\n assetId,\n readOnly,\n open,\n onOpenChange,\n}: {\n assetId: string\n readOnly: boolean\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const cms = useCms()\n const assets = useOptionalAssets()\n const record = useAssetRecord(assetId)\n const metaFields = useMemo(() => {\n const collection = cms.collections.find((c) => c.kind === 'asset')\n return (collection?.fields ?? []).filter(\n (f) => !SYSTEM_ASSET_FIELDS.has(f.name) && !f.form?.readOnly,\n )\n }, [cms])\n\n // Local edit buffers, seeded once per open from the record (so an external edit\n // still wins between opens). Metadata is non-localized in v1 (the asset\n // resolution path is locale-agnostic) — localized asset metadata is a follow-up.\n const [meta, setMeta] = useState<Record<string, unknown>>({})\n const [focal, setFocal] = useState<{ x: number; y: number } | undefined>(undefined)\n const dirty = useRef(false)\n const seeded = useRef(false)\n useEffect(() => {\n if (!open) {\n seeded.current = false\n return\n }\n if (seeded.current || !record) return\n // Seed from the meta bag, but lift the legacy `alt` column in when meta lacks\n // it (EC-301 transition): a not-yet-migrated asset still shows its alt, and\n // saving writes it into meta — a lazy migration-on-edit, matching delivery's\n // `meta.alt ?? record.alt` read fallback.\n const seedMeta = { ...(record.alt !== undefined ? { alt: record.alt } : {}), ...(record.meta ?? {}) }\n setMeta(seedMeta)\n setFocal(record.focalPoint)\n dirty.current = false\n seeded.current = true\n }, [open, record])\n\n function handleOpenChange(next: boolean) {\n // Commit the buffered metadata once, on close (one write, not per keystroke).\n if (!next && !readOnly && assets && dirty.current) {\n void assets.updateMetadata(assetId, { meta: pruneMeta(meta) })\n dirty.current = false\n }\n onOpenChange(next)\n }\n\n function setField(name: string, value: unknown) {\n dirty.current = true\n setMeta((m) => ({ ...m, [name]: value }))\n }\n\n // The focal point drives a live crop preview → commit immediately (optimistic\n // marker), not deferred to close.\n function commitFocal(point: { x: number; y: number } | null) {\n if (readOnly || !assets) return\n setFocal(point ?? undefined)\n void assets.updateMetadata(assetId, { focalPoint: point })\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>Image options</DialogTitle>\n <DialogDescription>\n Focal point and metadata — shared across every use of this image.\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"3\">\n <Stack gap=\"2\">\n <Row align=\"center\">\n <Text size=\"sm\" className=\"font-medium\">\n Focal point\n </Text>\n <Spacer />\n {focal && !readOnly ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => commitFocal(null)}>\n <RotateCcw /> Reset to centre\n </Button>\n ) : null}\n </Row>\n <FocalPointPicker\n record={record}\n point={focal}\n onChange={commitFocal}\n onPreview={setFocal}\n disabled={readOnly}\n />\n <Text size=\"xs\" tone=\"subtle\">\n Click or drag on the image to set the focal point used when it’s cropped.\n </Text>\n <FocalCropPreviews record={record} point={focal} />\n </Stack>\n {metaFields.map((f) => (\n <Field key={f.name} label={fieldLabel(f)} hint={f.form?.description}>\n <FieldControl\n id={`asset-meta-${f.name}`}\n field={f}\n value={meta[f.name]}\n readOnly={readOnly}\n onChange={(v) => setField(f.name, v)}\n />\n </Field>\n ))}\n {metaFields.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No editable metadata fields are declared on the asset collection. Add fields (e.g.\n <Code tone=\"muted\"> alt</Code>, <Code tone=\"muted\">copyright</Code>) to it in code.\n </Text>\n ) : null}\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"field-control.js","sourceRoot":"","sources":["../../../src/components/cms/field-control.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AlertTriangle, ChevronDown, ChevronUp, GripVertical, ImagePlus, Link2, Pencil, Plus, RotateCcw, SlidersHorizontal, Trash2, Upload, X } from 'lucide-react'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { Link, STUDIO_PROJECT_ROUTE, studioPath } from '@elytracms/studio/router'\nimport type {\n AssetFieldDef,\n DocumentRef,\n FieldDef,\n ObjectFieldDef,\n RelationFieldDef,\n SelectFieldDef,\n} from '@elytracms/core/cms-core'\nimport {\n BUILTIN_FIELD_CONTROLS,\n assetAcceptAttr,\n assetMatchesAccept,\n assetNode,\n assetSubfieldsOf,\n describeAccept,\n descendantIds,\n isFieldHidden,\n parentRelationField,\n readAssetId,\n} from '@elytracms/core/cms-core'\nimport {\n Badge,\n Button,\n Card,\n Code,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n Field,\n Input,\n Row,\n Select,\n Spacer,\n Stack,\n Text,\n} from '@elytracms/studio/ui'\nimport { cn } from '@elytracms/studio/lib/utils'\nimport { applyAssetSelection, assetIdsOf, singleAssetIdOf } from '../assets/asset-selection'\nimport { useOptionalAssets } from '../assets/assets-store'\nimport { AssetThumb, FocalCropPreviews, FocalPointPicker, useAssetRecord } from '../assets/asset-thumb'\nimport { newlyAddedAssetIds } from '../assets/focal-point'\nimport { fieldLabel, useCms } from './cms-store'\nimport { useFieldDensity } from './field-density'\nimport { useOptionalDocumentEditor } from './document-editor-context'\nimport { toRefArray } from './cell'\nimport { RelationPicker } from './relation-picker'\nimport { AssetPicker } from './asset-picker'\nimport {\n FieldRendererBoundary,\n FieldRendererIssue,\n useFieldRenderers,\n} from './field-renderer'\n\ninterface FieldControlProps {\n field: FieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}\n\n/**\n * The document-editor field control. Resolves a project's custom renderer by the\n * field's `form.control` (EC-238, customization seam EC-236) and falls back to the\n * built-in control for the field's datatype: a registered renderer renders inside\n * an error boundary (a throw → the built-in + a visible issue); a rejected\n * registration (non-component) shows the built-in with the issue above it; a field\n * naming a control with NO renderer and NO built-in name (a typo) shows the\n * built-in with a loud issue — never a silent fall-through; otherwise the built-in\n * renders alone.\n */\nexport function FieldControl(props: FieldControlProps) {\n const renderers = useFieldRenderers()\n const builtIn = <BuiltInFieldControl {...props} />\n const control = props.field.form?.control\n const Custom = control ? renderers.get(control) : undefined\n if (Custom) {\n return (\n <FieldRendererBoundary\n fallback={builtIn}\n issue={<FieldRendererIssue message=\"Custom renderer crashed — showing the built-in control.\" />}\n >\n <Custom {...props} />\n </FieldRendererBoundary>\n )\n }\n if (control) {\n const rejected = renderers.issues.find((issue) => issue.id === control)\n if (rejected) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={rejected.message} />\n {builtIn}\n </Stack>\n )\n }\n // The field asks for a control with no registered renderer and no built-in\n // name — surface it (a typo'd `control` would otherwise read as a plain input).\n if (!(BUILTIN_FIELD_CONTROLS as readonly string[]).includes(control)) {\n return (\n <Stack gap=\"1\">\n <FieldRendererIssue message={`No field renderer registered for control “${control}”.`} />\n {builtIn}\n </Stack>\n )\n }\n }\n return builtIn\n}\n\nfunction BuiltInFieldControl({ field, value, onChange, readOnly, id }: FieldControlProps) {\n switch (field.type) {\n case 'boolean':\n return (\n <label className=\"flex h-8 items-center gap-2 text-sm\">\n <input\n id={id}\n type=\"checkbox\"\n checked={value === true}\n disabled={readOnly}\n onChange={(e) => onChange(e.target.checked)}\n className=\"size-4 accent-primary disabled:opacity-50\"\n />\n <Text size=\"sm\" tone=\"muted\">\n {value === true ? 'true' : 'false'}\n </Text>\n </label>\n )\n case 'number':\n return (\n <Input\n id={id}\n type=\"number\"\n disabled={readOnly}\n value={typeof value === 'number' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? undefined : Number(e.target.value))}\n />\n )\n case 'date':\n return (\n <Input\n id={id}\n type=\"date\"\n disabled={readOnly}\n value={typeof value === 'string' ? value.slice(0, 10) : ''}\n onChange={(e) => onChange(e.target.value ? `${e.target.value}T00:00:00.000Z` : undefined)}\n />\n )\n case 'select':\n if (field.multiple) {\n const selected = Array.isArray(value) ? (value as string[]) : []\n return (\n <Row gap=\"2\" wrap>\n {field.options.map((opt) => {\n const on = selected.includes(opt.value)\n return (\n <label key={opt.value} className=\"flex items-center gap-1.5 text-sm\">\n <input\n type=\"checkbox\"\n checked={on}\n disabled={readOnly}\n onChange={() =>\n onChange(\n on ? selected.filter((v) => v !== opt.value) : [...selected, opt.value],\n )\n }\n className=\"size-4 accent-primary\"\n />\n {opt.label ?? opt.value}\n </label>\n )\n })}\n </Row>\n )\n }\n // EC-295: the `options` control renders a small fixed enumeration as inline\n // segmented buttons instead of the dropdown (single-select only).\n if (field.form?.control === 'options') {\n return (\n <SegmentedOptions field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n )\n }\n return (\n <Select\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n >\n <option value=\"\">—</option>\n {field.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label ?? opt.value}\n </option>\n ))}\n </Select>\n )\n case 'richText':\n return (\n <textarea\n id={id}\n rows={4}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n className=\"flex w-full rounded-md border border-input bg-background px-2.5 py-1.5 font-mono text-xs text-foreground shadow-sm focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none disabled:opacity-50\"\n />\n )\n case 'relation':\n return <RelationField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'asset':\n return <AssetField field={field} value={value} onChange={onChange} readOnly={readOnly} />\n case 'object':\n return <ObjectField field={field} value={value} onChange={onChange} readOnly={readOnly} id={id} />\n default:\n return (\n <Input\n id={id}\n disabled={readOnly}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value || undefined)}\n />\n )\n }\n}\n\n/**\n * EC-295: the `options` control — a `select` shown as inline segmented buttons\n * (`[[Page]] [URL] [File]`) instead of a dropdown. Engine built-in (no consumer\n * registration); single-select only. Each option shows `label ?? value` and a\n * `title` tooltip from the option's `description`; the field `default` + `readOnly`\n * flow through the shared form. Picks one — to allow an empty/unset value use the\n * default dropdown control.\n */\nexport function SegmentedOptions({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: SelectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n const current = typeof value === 'string' ? value : ''\n return (\n <Row gap=\"1\" wrap role=\"radiogroup\" id={id} aria-label={fieldLabel(field)}>\n {field.options.map((opt) => {\n const active = current === opt.value\n return (\n <Button\n key={opt.value}\n type=\"button\"\n variant={active ? 'default' : 'outline'}\n size=\"sm\"\n role=\"radio\"\n aria-checked={active}\n title={opt.description}\n disabled={readOnly}\n onClick={() => onChange(opt.value)}\n >\n {opt.label ?? opt.value}\n </Button>\n )\n })}\n </Row>\n )\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {}\n}\n\n/**\n * Move `from` → `to`, returning a NEW array (all elements preserved). The repeater\n * applies the same `(from, to)` to its value array AND its parallel stable-key array,\n * so the two stay aligned through a reorder — a row's child/DOM state (an open\n * relation/asset picker, focus) never mis-associates (EC-253 C2, EC-294).\n */\nexport function reorder<T>(list: readonly T[], from: number, to: number): T[] {\n const next = list.slice()\n const [moved] = next.splice(from, 1)\n if (moved !== undefined) next.splice(to, 0, moved)\n return next\n}\n\nconst SUMMARY_PREFERRED_FIELDS = ['title', 'label', 'name', 'heading', 'text', 'linkType']\n\n/**\n * A scannable one-line summary for a collapsed repeater item (EC-294): the first\n * meaningful scalar subfield value — preferring title-ish field names — else `#n`.\n * Pure (no store): summarises from the item's own scalar values, so it works for\n * any object shape without resolving relations/assets.\n */\nexport function objectItemSummary(\n fields: readonly { name: string }[],\n item: Record<string, unknown>,\n index: number,\n): string {\n const scalar = (name: string): string | null => {\n const v = item[name]\n if (typeof v === 'string' && v.trim()) return v.trim()\n if (typeof v === 'number' || typeof v === 'boolean') return String(v)\n return null\n }\n const names = fields.map((f) => f.name)\n for (const name of SUMMARY_PREFERRED_FIELDS) {\n if (names.includes(name)) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n }\n for (const name of names) {\n const s = scalar(name)\n if (s) return truncateSummary(s)\n }\n return `#${index + 1}`\n}\n\nfunction truncateSummary(s: string, max = 60): string {\n return s.length > max ? `${s.slice(0, max - 1)}…` : s\n}\n\n/**\n * The nested subfield form for one object item (EC-253). Reuses {@link FieldControl}\n * recursively, so any subfield type — including a nested object/repeater — works for\n * free. Writing a subfield merges it back into the item and bubbles the new item up.\n */\nfunction ObjectItemForm({\n fields,\n value,\n onChange,\n readOnly,\n idPrefix,\n}: {\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChange: (value: Record<string, unknown>) => void\n readOnly: boolean\n idPrefix: string\n}) {\n return (\n <Stack gap=\"3\" className=\"mt-2\">\n {fields.map((sub) => {\n // EC-273: a conditionally-hidden subfield (its predicate keys off the item's\n // own sibling values) is omitted — e.g. a discriminated link's `href` shows\n // only when `linkType === 'href'`. Re-evaluated as the item is edited.\n if (isFieldHidden(sub, { values: value })) return null\n const subId = `${idPrefix}-${sub.name}`\n return (\n <Field\n key={sub.name}\n htmlFor={subId}\n hint={sub.form?.description}\n label={\n <Row gap=\"2\">\n <span>{fieldLabel(sub)}</span>\n {sub.validation?.required ? <span className=\"text-destructive\">*</span> : null}\n </Row>\n }\n >\n <FieldControl\n id={subId}\n field={sub}\n value={value[sub.name]}\n readOnly={readOnly}\n onChange={(next) => {\n const updated = { ...value }\n if (next === undefined) delete updated[sub.name]\n else updated[sub.name] = next\n onChange(updated)\n }}\n />\n </Field>\n )\n })}\n </Stack>\n )\n}\n\n/**\n * Object / repeater field control (EC-253). `cardinality: 'one'` renders a single\n * nested form in a card; `'many'` renders an add/remove/reorder list of item cards.\n * Editing happens entirely inside the shared field form — no canvas escape (unlike\n * `blocks`), so props and document fields share this control.\n */\nfunction ObjectField({\n field,\n value,\n onChange,\n readOnly,\n id,\n}: {\n field: ObjectFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n id: string\n}) {\n if (field.cardinality !== 'many') {\n return (\n <Card className=\"border-l-2 border-l-border p-3\">\n <ObjectItemForm\n fields={field.fields}\n value={asRecord(value)}\n readOnly={readOnly}\n idPrefix={id}\n onChange={onChange}\n />\n </Card>\n )\n }\n\n const items = Array.isArray(value) ? (value as Record<string, unknown>[]) : []\n // Stable React keys for the value-only items (they carry no intrinsic id). Index\n // keys would mis-associate a row's child/DOM state (an open relation/asset picker,\n // focus) when rows are reordered or removed; we keep a parallel id list in lockstep\n // with structural ops, reconciled by length when the array is replaced wholesale\n // (e.g. switching documents). Edits keep the keys — only structure changes them.\n const seq = useRef(0)\n const keysRef = useRef<number[]>([])\n if (keysRef.current.length !== items.length) {\n keysRef.current = items.map((_, i) => keysRef.current[i] ?? seq.current++)\n }\n const keys = keysRef.current\n const commit = (nextItems: Record<string, unknown>[], nextKeys: number[]) => {\n keysRef.current = nextKeys\n onChange(nextItems)\n }\n // The same (from,to) drives both the value array AND the parallel key array, so\n // they stay aligned through a reorder (EC-253 C2 / EC-294).\n const move = (from: number, to: number) => commit(reorder(items, from, to), reorder(keys, from, to))\n const removeAt = (i: number) =>\n commit(items.filter((_, j) => j !== i), keys.filter((_, j) => j !== i))\n const addItem = () => {\n const seed: Record<string, unknown> = {}\n for (const sub of field.fields) if (sub.default !== undefined) seed[sub.name] = sub.default\n commit([...items, seed], [...keys, seq.current++])\n }\n const setItem = (i: number, updated: Record<string, unknown>) => {\n const nextItems = items.slice()\n nextItems[i] = updated\n commit(nextItems, keys)\n }\n\n // EC-294: per-item collapse (ephemeral disclosure state keyed by the stable key —\n // not persisted) + native drag-to-reorder. ▲/▼ stay as the keyboard/a11y fallback.\n const [collapsedKeys, setCollapsedKeys] = useState<ReadonlySet<number>>(() => new Set())\n const toggleCollapsed = (key: number) =>\n setCollapsedKeys((prev) => {\n const next = new Set(prev)\n if (next.has(key)) next.delete(key)\n else next.add(key)\n return next\n })\n const allCollapsed = items.length > 0 && keys.every((k) => collapsedKeys.has(k))\n const [dragIndex, setDragIndex] = useState<number | null>(null)\n const [overIndex, setOverIndex] = useState<number | null>(null)\n const endDrag = () => {\n setDragIndex(null)\n setOverIndex(null)\n }\n const drop = (to: number) => {\n if (dragIndex !== null && dragIndex !== to) move(dragIndex, to)\n endDrag()\n }\n\n return (\n <Stack gap=\"2\">\n {items.length > 1 && !readOnly ? (\n <Row justify=\"end\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => setCollapsedKeys(allCollapsed ? new Set() : new Set(keys))}\n >\n {allCollapsed ? 'Expand all' : 'Collapse all'}\n </Button>\n </Row>\n ) : null}\n {items.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No items\n </Text>\n ) : (\n items.map((item, i) => {\n // keysRef is reconciled to items.length above, so keys[i] is always present;\n // `?? i` only satisfies noUncheckedIndexedAccess (never taken at runtime).\n const key = keys[i] ?? i\n const record = asRecord(item)\n const isCollapsed = collapsedKeys.has(key)\n return (\n <Card\n key={key}\n onDragOver={(e) => {\n if (dragIndex === null) return\n e.preventDefault()\n if (overIndex !== i) setOverIndex(i)\n }}\n onDrop={() => drop(i)}\n className={cn(\n 'border-l-2 border-l-border p-0',\n dragIndex === i ? 'opacity-50' : '',\n overIndex === i && dragIndex !== null && dragIndex !== i\n ? 'border-l-primary ring-1 ring-primary/40'\n : '',\n )}\n >\n <Row className=\"select-none border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n {!readOnly ? (\n <span\n draggable\n onDragStart={(e) => {\n e.dataTransfer.effectAllowed = 'move'\n e.dataTransfer.setData('text/plain', String(i))\n setDragIndex(i)\n }}\n onDragEnd={endDrag}\n aria-label=\"Drag to reorder\"\n className=\"cursor-grab text-muted-foreground/50 hover:text-muted-foreground active:cursor-grabbing\"\n >\n <GripVertical className=\"size-3.5\" />\n </span>\n ) : null}\n <button\n type=\"button\"\n onClick={() => toggleCollapsed(key)}\n aria-expanded={!isCollapsed}\n className=\"flex min-w-0 flex-1 items-center gap-1.5 text-left\"\n >\n <ChevronDown\n className={cn(\n 'size-3.5 shrink-0 text-muted-foreground transition-transform',\n isCollapsed ? '-rotate-90' : '',\n )}\n aria-hidden\n />\n <Text size=\"sm\" className=\"min-w-0 truncate font-medium\">\n {objectItemSummary(field.fields, record, i)}\n </Text>\n </button>\n {!readOnly ? (\n <Row gap=\"1\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Move up\"\n disabled={i === 0}\n onClick={() => move(i, i - 1)}\n >\n <ChevronUp className=\"size-3\" />\n </Button>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Move down\"\n disabled={i === items.length - 1}\n onClick={() => move(i, i + 1)}\n >\n <ChevronDown className=\"size-3\" />\n </Button>\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Remove item\" onClick={() => removeAt(i)}>\n <Trash2 className=\"size-3\" />\n </Button>\n </Row>\n ) : null}\n </Row>\n {!isCollapsed ? (\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={field.fields}\n value={record}\n readOnly={readOnly}\n idPrefix={`${id}-${i}`}\n onChange={(updated) => setItem(i, updated)}\n />\n </div>\n ) : null}\n </Card>\n )\n })\n )}\n {!readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={addItem}>\n <Plus className=\"size-3\" /> Add item\n </Button>\n ) : null}\n </Stack>\n )\n}\n\nfunction RelationField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: RelationFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const cms = useCms()\n const editor = useOptionalDocumentEditor()\n const [open, setOpen] = useState(false)\n const refs = toRefArray(value)\n const projectId = cms.projectId\n\n // EC-218: for the editing document's `parent` self-relation, exclude the\n // document itself and its descendants from the picker — choosing one would make\n // the page its own ancestor (a cycle). Only applies to the parent field of a\n // hierarchical collection; ordinary relations (and other self-relations) are\n // unrestricted. validateDocument is the backstop where this context is absent.\n const excludeIds = useMemo<Set<string> | undefined>(() => {\n if (!editor || field.target !== editor.collection.id) return undefined\n if (parentRelationField(editor.collection)?.name !== field.name) return undefined\n const ids = descendantIds(editor.docRef.id, cms.documentsOf(editor.collection.id))\n ids.add(editor.docRef.id)\n return ids\n }, [editor, field.target, field.name, cms])\n\n function apply(next: DocumentRef[]) {\n onChange(field.cardinality === 'many' ? next : (next[0] ?? undefined))\n }\n\n return (\n <Row gap=\"2\" wrap>\n {refs.map((ref, i) => {\n // EC-296: the relation title navigates to the referenced document (autosave\n // keeps the current edit safe). Without a projectId (no persistence, e.g.\n // fixtures) it stays plain text.\n const title = cms.titleOf(ref)\n return (\n <Badge key={`${ref.id}-${i}`} variant=\"info\">\n {projectId ? (\n <Link\n to={STUDIO_PROJECT_ROUTE}\n params={{ projectId, _splat: studioPath.cmsDocument(ref.collection, ref.id) }}\n title={`Open ${title}`}\n className=\"inline-flex items-center gap-1 hover:underline\"\n >\n <Link2 className=\"size-3\" /> {title}\n </Link>\n ) : (\n <span className=\"inline-flex items-center gap-1\">\n <Link2 className=\"size-3\" /> {title}\n </span>\n )}\n {!readOnly ? (\n <button\n type=\"button\"\n aria-label=\"Remove\"\n onClick={() => apply(refs.filter((r) => r.id !== ref.id))}\n className=\"ml-1 hover:text-destructive\"\n >\n <X className=\"size-3\" />\n </button>\n ) : null}\n </Badge>\n )\n })}\n {!readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <Pencil /> {refs.length > 0 ? 'Change' : 'Pick'}\n </Button>\n ) : null}\n <RelationPicker\n targetCollection={field.target}\n cardinality={field.cardinality}\n value={refs}\n open={open}\n onOpenChange={setOpen}\n onChange={apply}\n excludeIds={excludeIds}\n />\n </Row>\n )\n}\n\n/**\n * The asset field (EC-152 + EC-248). Beyond picking from the library it now shows\n * a focal-point-aware preview, uploads directly from the field (reusing the EC-152\n * upload path — no parallel uploader), and edits the selected image's alt + focal\n * point inline (persisted on the asset document, where delivery reads them, EC-230).\n * With no `<AssetsProvider>` (fixtures/playground) it degrades to the labelled-badge\n * picker — no preview/upload — so it stays usable without a real library.\n */\nfunction AssetField({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: AssetFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly: boolean\n}) {\n const cms = useCms()\n const assets = useOptionalAssets()\n const density = useFieldDensity()\n const compact = density === 'compact'\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [dragging, setDragging] = useState(false)\n // Names of files a drop/pick rejected against the field's `accept` (EC-301) — shown\n // as a transient inline issue, cleared on the next valid upload. Never a silent drop.\n const [rejected, setRejected] = useState<string[]>([])\n // Asset ids known when an upload-from-field started; the effect below diffs the\n // library against it to learn the new id (the `upload()` API returns nothing).\n const pendingRef = useRef<ReadonlySet<string> | null>(null)\n const assetCollectionId = cms.collections.find((c) => c.kind === 'asset')?.id ?? 'asset'\n const many = field.cardinality === 'many'\n const ids = many ? assetIdsOf(value) : []\n const single = !many ? singleAssetIdOf(value) : null\n // EC-301 per-use sub-fields: with `field.fields` the value stores asset ENVELOPES\n // carrying the sub-field siblings (`{ type:'asset', id, caption }`); without them it\n // stays bare ids (self-typed on save). Picks/edits preserve a placement's sub-fields.\n const subFields = field.fields ?? []\n const hasSub = subFields.length > 0\n // The raw `many` entries (envelopes or bare ids), index-aligned with `ids`, so each\n // gallery item can read/write its own sub-fields.\n const entries = many && Array.isArray(value) ? (value as unknown[]) : []\n\n const libraryIds = useMemo(\n () => (assets?.status === 'ready' ? assets.assets.map((a) => a.id) : []),\n [assets],\n )\n\n // Auto-select an asset uploaded FROM this field once it lands in the library.\n useEffect(() => {\n if (!pendingRef.current) return\n const added = newlyAddedAssetIds(pendingRef.current, libraryIds)\n if (added.length === 0) return\n pendingRef.current = null\n if (many) {\n const current = Array.isArray(value) ? (value as unknown[]) : []\n onChange([...current, ...added.map((id) => (hasSub ? assetNode(id) : id))])\n } else {\n onChange(hasSub ? assetNode(added[0]!, assetSubfieldsOf(value)) : applyAssetSelection(value, added[0]!, 'one'))\n }\n // Reacts to the library set growing (an upload landing), not to unrelated\n // value edits — `value`/`onChange` are read from the closure on purpose.\n }, [libraryIds, many, hasSub, onChange, value])\n\n function uploadFiles(files: File[]) {\n if (!assets || files.length === 0) return\n // EC-301: a drag-drop bypasses the file dialog's `accept` filter — re-check each\n // file against the field's `accept` and surface a visible issue for the rejects\n // instead of silently uploading the wrong kind.\n const ok: File[] = []\n const bad: string[] = []\n for (const file of files) {\n if (assetMatchesAccept({ contentType: file.type, filename: file.name }, field.accept)) ok.push(file)\n else bad.push(file.name)\n }\n setRejected(bad)\n if (ok.length === 0) return\n // Remember the library set so the effect above can learn the new id and\n // auto-select it once the upload lands (the upload API returns nothing).\n pendingRef.current = new Set(libraryIds)\n assets.upload(ok)\n }\n\n // Apply a picked asset id, preserving a placement's per-use sub-fields (EC-301).\n // Without sub-fields it stays the plain id-list behaviour (self-typed on save).\n function applyPicked(picked: string | null) {\n if (!hasSub) {\n onChange(applyAssetSelection(value, picked, field.cardinality))\n return\n }\n if (many) {\n if (picked === null || entries.some((e) => readAssetId(e) === picked)) return\n onChange([...entries, assetNode(picked)])\n } else {\n onChange(picked ? assetNode(picked, assetSubfieldsOf(value)) : undefined)\n }\n }\n\n const canUpload = !readOnly && assets !== null\n\n // Drag-to-upload onto the field (EC-301): a depth counter keeps the overlay\n // steady as the pointer crosses child element boundaries. `dragenter`/`dragleave`\n // fire per descendant, so toggling on a bare `dragleave` blinks constantly; we\n // only hide the overlay when the counter returns to zero (the pointer has truly\n // left the field). The overlay is pointer-events-none, so it never fires its own\n // enter/leave to confuse the count.\n const dragDepth = useRef(0)\n function onDragEnter(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current += 1\n if (!dragging) setDragging(true)\n }\n function onDragOver(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault() // required so a drop is allowed\n }\n function onDragLeave() {\n if (!canUpload) return\n dragDepth.current -= 1\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragging(false)\n }\n }\n function onDrop(e: React.DragEvent) {\n if (!canUpload) return\n e.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n uploadFiles(Array.from(e.dataTransfer.files))\n }\n // While the picker is open the field is behind the modal, so any drop is happening\n // INSIDE the dialog — detach the field's drag handlers entirely so a drop there can\n // only be handled by the picker, never double-counted by the field too (EC-301). This\n // is the belt to the picker's stopPropagation braces: the field cannot react at all.\n const dragProps = canUpload && !open ? { onDragEnter, onDragOver, onDragLeave, onDrop } : {}\n\n const selectButton = !readOnly ? (\n <Button variant=\"outline\" size=\"sm\" onClick={() => setOpen(true)}>\n <ImagePlus /> Select\n </Button>\n ) : null\n\n return (\n <Stack\n gap=\"2\"\n className={cn(\n 'relative rounded-lg transition-shadow',\n dragging && 'ring-2 ring-primary ring-offset-2 ring-offset-background',\n )}\n {...dragProps}\n >\n {many && hasSub ? (\n // Gallery with per-use sub-fields (EC-301): a card per placement — thumbnail +\n // its own sub-field form — reusing the object/repeater item rendering.\n <Stack gap=\"2\">\n {entries.length > 0 ? (\n <Stack gap=\"2\">\n {entries.map((entry, i) => {\n const aid = readAssetId(entry)\n if (!aid) return null\n return (\n <AssetGalleryItem\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n fields={subFields}\n value={assetSubfieldsOf(entry)}\n readOnly={readOnly}\n idPrefix={`asset-${i}`}\n onChangeSubs={(next) => {\n const nextEntries = entries.slice()\n nextEntries[i] = assetNode(aid, next)\n onChange(nextEntries)\n }}\n onRemove={readOnly ? undefined : () => onChange(entries.filter((_, j) => j !== i))}\n />\n )\n })}\n </Stack>\n ) : (\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : many ? (\n // Gallery: a grid of focal-aware previews + the single Select entry.\n <Stack gap=\"2\">\n {ids.length > 0 ? (\n <div\n className={cn(\n 'grid gap-2',\n compact ? 'grid-cols-2' : 'grid-cols-3 sm:grid-cols-4',\n )}\n >\n {ids.map((aid, i) => (\n <AssetChip\n key={`${aid}-${i}`}\n assetId={aid}\n collectionId={assetCollectionId}\n onRemove={readOnly ? undefined : () => onChange(ids.filter((x) => x !== aid))}\n />\n ))}\n </div>\n ) : (\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n )}\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n ) : single && hasSub ? (\n // With per-use sub-fields the asset + its fields read as ONE grouped unit\n // (like an object), so it's clear they belong together (EC-301 feedback).\n <Card className=\"border-l-2 border-l-border p-3\">\n <Stack gap=\"3\">\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={cms.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, field.cardinality))}\n />\n <div className=\"border-t border-border/60 pt-3\">\n <ObjectItemForm\n fields={subFields}\n value={assetSubfieldsOf(value)}\n readOnly={readOnly}\n idPrefix=\"asset-sub\"\n onChange={(next) => onChange(assetNode(single, next))}\n />\n </div>\n </Stack>\n </Card>\n ) : single ? (\n <AssetSinglePreview\n assetId={single}\n compact={compact}\n readOnly={readOnly}\n hasLibrary={assets !== null}\n fallbackTitle={cms.titleOf({ collection: assetCollectionId, id: single })}\n selectButton={selectButton}\n onEdit={() => setEditing(true)}\n onRemove={() => onChange(applyAssetSelection(value, null, field.cardinality))}\n />\n ) : (\n <Stack gap=\"2\">\n <EmptyAssetState compact={compact} canUpload={canUpload} onSelect={() => setOpen(true)} />\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n </Row>\n </Stack>\n )}\n\n {/* Image options (focal point + metadata) for a single selected asset, in a\n dialog rather than an inline disclosure (EC-301). Shared across every use. */}\n {!many && single && assets ? (\n <AssetOptionsDialog\n assetId={single}\n readOnly={readOnly}\n open={editing}\n onOpenChange={setEditing}\n />\n ) : null}\n\n <AssetPicker\n value={single}\n open={open}\n onOpenChange={setOpen}\n accept={assetAcceptAttr(field.accept)}\n onChange={applyPicked}\n />\n\n {rejected.length > 0 ? (\n <Row gap=\"2\" align=\"center\">\n <AlertTriangle className=\"size-4 shrink-0 text-warning\" />\n <Text size=\"xs\" tone=\"muted\">\n {rejected.length === 1 ? `“${rejected[0]}”` : `${rejected.length} files`} skipped — only{' '}\n {describeAccept(field.accept)} allowed here.\n </Text>\n </Row>\n ) : null}\n\n {dragging ? (\n <div className=\"pointer-events-none absolute inset-0 z-10 grid place-items-center rounded-lg border-2 border-dashed border-primary bg-background/85\">\n <Row gap=\"2\" align=\"center\">\n <Upload className=\"size-4 text-primary\" />\n <Text size=\"sm\" className=\"font-medium\">\n Drop to upload\n </Text>\n </Row>\n </div>\n ) : null}\n </Stack>\n )\n}\n\n/** Empty-state placeholder shown when an asset field has no selection (EC-301). */\nfunction EmptyAssetState({\n compact,\n canUpload,\n onSelect,\n}: {\n compact: boolean\n canUpload: boolean\n onSelect: () => void\n}) {\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n className={cn(\n 'grid w-full place-items-center gap-1 rounded-lg border border-dashed border-border bg-muted/30 text-center text-muted-foreground transition-colors hover:bg-muted/60',\n compact ? 'aspect-video' : 'aspect-[5/2]',\n )}\n >\n <ImagePlus className=\"size-6\" aria-hidden />\n <Text size=\"xs\" tone=\"muted\">\n {canUpload ? 'Select or drop an image' : 'Select an asset'}\n </Text>\n </button>\n )\n}\n\n/** A large focal-aware preview of the selected asset (EC-301). */\nfunction AssetPreview({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n return <AssetThumb record={record} className=\"aspect-[4/3] w-full\" />\n}\n\n/** Filename + dimensions/type for the selected asset — the id is never shown (EC-301). */\nfunction AssetInfo({ assetId, fallbackTitle }: { assetId: string; fallbackTitle?: string }) {\n const record = useAssetRecord(assetId)\n const name = record?.title ?? record?.filename ?? fallbackTitle ?? assetId\n const dims = record?.width && record?.height ? `${record.width}×${record.height}` : null\n const kind = record?.contentType?.split('/')[1]?.toUpperCase()\n const meta = [dims, kind].filter(Boolean).join(' · ')\n return (\n <Stack gap=\"1\">\n <Text size=\"sm\" className=\"truncate font-medium\" title={name}>\n {name}\n </Text>\n {meta ? (\n <Text size=\"xs\" tone=\"subtle\">\n {meta}\n </Text>\n ) : null}\n </Stack>\n )\n}\n\n/**\n * The selected image's alt text shown in the field (EC-301) — what an editor most\n * wants at a glance; edited in the options dialog. Reads `meta.alt` with the legacy\n * `alt` column as fallback, matching delivery.\n */\nfunction AssetAltLine({ assetId }: { assetId: string }) {\n const record = useAssetRecord(assetId)\n const metaAlt = typeof record?.meta?.alt === 'string' ? record.meta.alt : undefined\n const alt = metaAlt ?? record?.alt\n return (\n <Row gap=\"1\" align=\"baseline\" className=\"min-w-0\">\n <Text size=\"xs\" tone=\"subtle\" className=\"shrink-0\">\n Alt:\n </Text>\n <Text\n size=\"xs\"\n tone={alt ? 'muted' : 'subtle'}\n className={cn('min-w-0 truncate', !alt && 'italic')}\n title={alt ?? undefined}\n >\n {alt || 'not set'}\n </Text>\n </Row>\n )\n}\n\n/** A single asset reference: focal-aware thumbnail + title, with an optional remove. */\nfunction AssetChip({\n assetId,\n collectionId,\n onRemove,\n}: {\n assetId: string\n collectionId: string\n onRemove?: () => void\n}) {\n const cms = useCms()\n const record = useAssetRecord(assetId)\n const title = cms.titleOf({ collection: collectionId, id: assetId })\n return (\n <div className=\"group relative overflow-hidden rounded-lg border border-border bg-muted/40\">\n <AssetThumb record={record} alt={title} className=\"aspect-square w-full rounded-none border-0\" />\n <Row gap=\"1\" align=\"center\" className=\"px-2 py-1\">\n <Text size=\"xs\" tone=\"subtle\" className=\"min-w-0 flex-1 truncate\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <button\n type=\"button\"\n aria-label=\"Remove\"\n onClick={onRemove}\n className=\"text-muted-foreground hover:text-destructive\"\n >\n <X className=\"size-3.5\" />\n </button>\n ) : null}\n </Row>\n </div>\n )\n}\n\n/**\n * The selected single asset's preview row (EC-301): large focal-aware preview + info\n * column (filename/dimensions, Select/Edit/Remove, alt). Extracted so it renders either\n * bare (a plain asset field) or inside the grouping card when the field carries per-use\n * sub-fields — the asset + its fields then read as one unit.\n */\nfunction AssetSinglePreview({\n assetId,\n compact,\n readOnly,\n hasLibrary,\n fallbackTitle,\n selectButton,\n onEdit,\n onRemove,\n}: {\n assetId: string\n compact: boolean\n readOnly: boolean\n hasLibrary: boolean\n fallbackTitle?: string\n selectButton: React.ReactNode\n onEdit: () => void\n onRemove: () => void\n}) {\n return (\n <div className={compact ? 'flex flex-col gap-3' : 'flex gap-4'}>\n <div className={compact ? 'w-full' : 'w-1/2 shrink-0'}>\n <AssetPreview assetId={assetId} />\n </div>\n <Stack gap=\"2\" className={cn('min-w-0', !compact && 'flex-1')}>\n <AssetInfo assetId={assetId} fallbackTitle={fallbackTitle} />\n {!readOnly ? (\n <Row gap=\"2\" wrap align=\"center\">\n {selectButton}\n {hasLibrary ? (\n <Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n <SlidersHorizontal /> Edit\n </Button>\n ) : null}\n <Button variant=\"ghost\" size=\"sm\" onClick={onRemove}>\n <Trash2 /> Remove\n </Button>\n </Row>\n ) : null}\n <AssetAltLine assetId={assetId} />\n </Stack>\n </div>\n )\n}\n\n/**\n * One gallery placement when a `many` asset field declares per-use sub-fields (EC-301):\n * a thumbnail + filename + remove header over the placement's own sub-field form,\n * modelled on a repeater item card so a gallery's placements read as grouped units.\n */\nfunction AssetGalleryItem({\n assetId,\n collectionId,\n fields,\n value,\n onChangeSubs,\n onRemove,\n readOnly,\n idPrefix,\n}: {\n assetId: string\n collectionId: string\n fields: readonly FieldDef[]\n value: Record<string, unknown>\n onChangeSubs: (value: Record<string, unknown>) => void\n onRemove?: () => void\n readOnly: boolean\n idPrefix: string\n}) {\n const cms = useCms()\n const record = useAssetRecord(assetId)\n const title = cms.titleOf({ collection: collectionId, id: assetId })\n return (\n <Card className=\"border-l-2 border-l-border p-0\">\n <Row gap=\"2\" align=\"center\" className=\"border-b border-border/60 bg-muted/30 px-2 py-1.5\">\n <AssetThumb record={record} alt={title} className=\"size-10 shrink-0\" />\n <Text size=\"sm\" className=\"min-w-0 flex-1 truncate font-medium\" title={title}>\n {title}\n </Text>\n {onRemove ? (\n <Button variant=\"ghost\" size=\"sm\" aria-label=\"Remove\" onClick={onRemove}>\n <Trash2 className=\"size-3.5\" />\n </Button>\n ) : null}\n </Row>\n <div className=\"px-3 pb-3\">\n <ObjectItemForm\n fields={fields}\n value={value}\n onChange={onChangeSubs}\n readOnly={readOnly}\n idPrefix={idPrefix}\n />\n </div>\n </Card>\n )\n}\n\n/**\n * System columns the upload pipeline owns — never editable metadata, so they're\n * excluded from the asset collection's schema-driven options form (EC-301). The\n * filename + dimensions already show in the field's info column.\n */\nconst SYSTEM_ASSET_FIELDS = new Set([\n 'filename',\n 'url',\n 'contentType',\n 'byteSize',\n 'width',\n 'height',\n 'storageKey',\n 'title',\n])\n\n/** Drop `undefined` values so the persisted meta bag stays JSON/Convex-clean. */\nfunction pruneMeta(meta: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(meta).filter(([, v]) => v !== undefined))\n}\n\n/**\n * The image options dialog (EC-301): the focal point (engine-special visual\n * control) + the asset's **schema-driven** metadata, rendered from the\n * `kind:'asset'` collection's `fields` through the same generic field engine\n * documents use — so a project declares asset metadata (alt, copyright, credit, …)\n * in code, not hardcoded here. Edits persist on the asset, shared across every use.\n * Metadata is buffered locally and committed once on close (one write); the focal\n * point commits immediately (it drives a live visual). No-ops without a library.\n */\nfunction AssetOptionsDialog({\n assetId,\n readOnly,\n open,\n onOpenChange,\n}: {\n assetId: string\n readOnly: boolean\n open: boolean\n onOpenChange: (open: boolean) => void\n}) {\n const cms = useCms()\n const assets = useOptionalAssets()\n const record = useAssetRecord(assetId)\n const metaFields = useMemo(() => {\n const collection = cms.collections.find((c) => c.kind === 'asset')\n return (collection?.fields ?? []).filter(\n (f) => !SYSTEM_ASSET_FIELDS.has(f.name) && !f.form?.readOnly,\n )\n }, [cms])\n\n // Local edit buffers, seeded once per open from the record (so an external edit\n // still wins between opens). Metadata is non-localized in v1 (the asset\n // resolution path is locale-agnostic) — localized asset metadata is a follow-up.\n const [meta, setMeta] = useState<Record<string, unknown>>({})\n const [focal, setFocal] = useState<{ x: number; y: number } | undefined>(undefined)\n const dirty = useRef(false)\n const seeded = useRef(false)\n useEffect(() => {\n if (!open) {\n seeded.current = false\n return\n }\n if (seeded.current || !record) return\n // Seed from the meta bag, but lift the legacy `alt` column in when meta lacks\n // it (EC-301 transition): a not-yet-migrated asset still shows its alt, and\n // saving writes it into meta — a lazy migration-on-edit, matching delivery's\n // `meta.alt ?? record.alt` read fallback.\n const seedMeta = { ...(record.alt !== undefined ? { alt: record.alt } : {}), ...(record.meta ?? {}) }\n setMeta(seedMeta)\n setFocal(record.focalPoint)\n dirty.current = false\n seeded.current = true\n }, [open, record])\n\n function handleOpenChange(next: boolean) {\n // Commit the buffered metadata once, on close (one write, not per keystroke).\n if (!next && !readOnly && assets && dirty.current) {\n void assets.updateMetadata(assetId, { meta: pruneMeta(meta) })\n dirty.current = false\n }\n onOpenChange(next)\n }\n\n function setField(name: string, value: unknown) {\n dirty.current = true\n setMeta((m) => ({ ...m, [name]: value }))\n }\n\n // The focal point drives a live crop preview → commit immediately (optimistic\n // marker), not deferred to close.\n function commitFocal(point: { x: number; y: number } | null) {\n if (readOnly || !assets) return\n setFocal(point ?? undefined)\n void assets.updateMetadata(assetId, { focalPoint: point })\n }\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent className=\"max-w-lg\">\n <DialogHeader>\n <DialogTitle>Image options</DialogTitle>\n <DialogDescription>\n Focal point and metadata — shared across every use of this image.\n </DialogDescription>\n </DialogHeader>\n <Stack gap=\"3\">\n <Stack gap=\"2\">\n <Row align=\"center\">\n <Text size=\"sm\" className=\"font-medium\">\n Focal point\n </Text>\n <Spacer />\n {focal && !readOnly ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => commitFocal(null)}>\n <RotateCcw /> Reset to centre\n </Button>\n ) : null}\n </Row>\n <FocalPointPicker\n record={record}\n point={focal}\n onChange={commitFocal}\n onPreview={setFocal}\n disabled={readOnly}\n />\n <Text size=\"xs\" tone=\"subtle\">\n Click or drag on the image to set the focal point used when it’s cropped.\n </Text>\n <FocalCropPreviews record={record} point={focal} />\n </Stack>\n {metaFields.map((f) => (\n <Field key={f.name} label={fieldLabel(f)} hint={f.form?.description}>\n <FieldControl\n id={`asset-meta-${f.name}`}\n field={f}\n value={meta[f.name]}\n readOnly={readOnly}\n onChange={(v) => setField(f.name, v)}\n />\n </Field>\n ))}\n {metaFields.length === 0 ? (\n <Text size=\"sm\" tone=\"subtle\">\n No editable metadata fields are declared on the asset collection. Add fields (e.g.\n <Code tone=\"muted\"> alt</Code>, <Code tone=\"muted\">copyright</Code>) to it in code.\n </Text>\n ) : null}\n </Stack>\n </DialogContent>\n </Dialog>\n )\n}\n"]}
|
|
@@ -4,9 +4,13 @@ export async function deleteAsset(deps, projectId, assetId, options = {}) {
|
|
|
4
4
|
const asset = await deps.assets.findAsset(projectId, assetId);
|
|
5
5
|
if (!asset)
|
|
6
6
|
return { outcome: 'not-found' };
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
// Only consult the reference index when the caller has NOT already confirmed.
|
|
8
|
+
// The delete dialog enumerates usages up-front and confirms with `confirmInUse`,
|
|
9
|
+
// so a confirmed delete skips this redundant inbound query entirely (EC-301 perf).
|
|
10
|
+
if (options.confirmInUse !== true) {
|
|
11
|
+
const usageCount = await assetUsageCount(deps, projectId, assetId);
|
|
12
|
+
if (usageCount > 0)
|
|
13
|
+
return { outcome: 'blocked', usageCount };
|
|
10
14
|
}
|
|
11
15
|
// Bytes first, metadata last: a failure midway leaves a visible metadata
|
|
12
16
|
// record (re-deletable) rather than invisible orphaned bytes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-asset.js","sourceRoot":"","sources":["../../../src/lib/assets/delete-asset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAoCjE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAqB,EACrB,SAAiB,EACjB,OAAe,EACf,UAA8B,EAAE;IAEhC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC7D,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;QAE3C,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"delete-asset.js","sourceRoot":"","sources":["../../../src/lib/assets/delete-asset.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAoCjE,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAqB,EACrB,SAAiB,EACjB,OAAe,EACf,UAA8B,EAAE;IAEhC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC7D,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;QAE3C,8EAA8E;QAC9E,iFAAiF;QACjF,mFAAmF;QACnF,IAAI,OAAO,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YAClC,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,CAAA;YAClE,IAAI,UAAU,GAAG,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAA;QAC/D,CAAC;QAED,yEAAyE;QACzE,8DAA8D;QAC9D,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACzC,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACjD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,mBAAmB;YAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;QACzE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;IAC9F,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,eAAe,CAC5B,IAAqB,EACrB,SAAiB,EACjB,OAAe;IAEf,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;IACxF,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAChF,CAAA;IACD,OAAO,QAAQ,CAAC,IAAI,CAAA;AACtB,CAAC","sourcesContent":["import { RecordNotFoundError } from '@elytracms/core/persistence'\nimport type {\n AssetRepository,\n BlobStorageAdapter,\n ReferenceIndexRepository,\n} from '@elytracms/core/persistence'\n\n/**\n * Reference-respecting asset deletion (EC-151, reworked EC-202). Deleting an\n * asset that is referenced is blocked until explicitly confirmed; the usage\n * count comes from the **EC-155 reference index** (`references.inbound` for the\n * `{ type: 'asset' }` target) — index-accurate, replacing the interim string\n * scan (`find-asset-usages`), which could miss rich-text asset hrefs and\n * false-positive on unrelated strings. Deleting an unused asset removes bytes\n * (blob) AND the metadata record. Results are structured data — never throws.\n */\n\nexport interface DeleteAssetDeps {\n assets: AssetRepository\n blobs: BlobStorageAdapter\n /** The EC-155 reference index — the accurate inbound-usage source (EC-202). */\n references: Pick<ReferenceIndexRepository, 'inbound'>\n}\n\nexport type DeleteAssetResult =\n | { outcome: 'deleted' }\n /** The asset is referenced and `confirmInUse` was not set: nothing was removed. */\n | { outcome: 'blocked'; usageCount: number }\n | { outcome: 'not-found' }\n | { outcome: 'error'; message: string }\n\nexport interface DeleteAssetOptions {\n /** Caller has seen the usage count and explicitly confirmed deletion. */\n confirmInUse?: boolean\n}\n\nexport async function deleteAsset(\n deps: DeleteAssetDeps,\n projectId: string,\n assetId: string,\n options: DeleteAssetOptions = {},\n): Promise<DeleteAssetResult> {\n try {\n const asset = await deps.assets.findAsset(projectId, assetId)\n if (!asset) return { outcome: 'not-found' }\n\n // Only consult the reference index when the caller has NOT already confirmed.\n // The delete dialog enumerates usages up-front and confirms with `confirmInUse`,\n // so a confirmed delete skips this redundant inbound query entirely (EC-301 perf).\n if (options.confirmInUse !== true) {\n const usageCount = await assetUsageCount(deps, projectId, assetId)\n if (usageCount > 0) return { outcome: 'blocked', usageCount }\n }\n\n // Bytes first, metadata last: a failure midway leaves a visible metadata\n // record (re-deletable) rather than invisible orphaned bytes.\n await deps.blobs.delete(asset.storageKey)\n await deps.assets.removeAsset(projectId, assetId)\n return { outcome: 'deleted' }\n } catch (error) {\n if (error instanceof RecordNotFoundError) return { outcome: 'not-found' }\n return { outcome: 'error', message: error instanceof Error ? error.message : String(error) }\n }\n}\n\n/**\n * Distinct inbound usages of an asset from the reference index (EC-202). One\n * `source + path` (e.g. `post:hello · values/cover`) can carry both a draft and\n * a published index entry — that is a single usage, so variants collapse before\n * counting (the gate is \"is this referenced\", not \"how many index rows\").\n */\nasync function assetUsageCount(\n deps: DeleteAssetDeps,\n projectId: string,\n assetId: string,\n): Promise<number> {\n const inbound = await deps.references.inbound(projectId, { type: 'asset', id: assetId })\n const distinct = new Set(\n inbound.map((entry) => `${entry.source.type}:${entry.source.id}:${entry.path}`),\n )\n return distinct.size\n}\n"]}
|
|
@@ -158,6 +158,12 @@ export async function loadInboundAssetReferenceViews(projectId, assetId) {
|
|
|
158
158
|
});
|
|
159
159
|
if (!result.ok)
|
|
160
160
|
throw new Error(result.error.message);
|
|
161
|
+
// No inbound references → no sources to label, so skip the label-context load
|
|
162
|
+
// entirely (schema + ALL documents + ALL assets). This is the common case for a
|
|
163
|
+
// deletable asset, making the delete dialog's pre-flight a single indexed query
|
|
164
|
+
// instead of three full-table lists (EC-301 perf).
|
|
165
|
+
if (result.value.length === 0)
|
|
166
|
+
return [];
|
|
161
167
|
const context = await loadLabelContext(operations, projectId);
|
|
162
168
|
return buildReferenceEntryViews(result.value, context);
|
|
163
169
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n componentTitle: (componentId) => registry.getManifest(componentId)?.title,\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n const recordsPerTarget = await Promise.all(\n targets.map((ref) =>\n operations.references\n .inbound({ projectId, target: { type: 'document', id: documentKey(ref) } })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n }),\n ),\n )\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set([\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n])\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\n/**\n * Trailing-debounce window (ms) for event-driven panel reloads (EC-269): a burst\n * of content commands — e.g. one `documents.upsert` per keystroke from autosave —\n * coalesces into a SINGLE reload once the writes settle, instead of one full\n * (multi-query) reload per event. The initial mount load stays immediate.\n */\nexport const REFERENCE_RELOAD_DEBOUNCE_MS = 300\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity: loads on mount, exposes explicit\n * loading/error states, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write).\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const [state, setState] = useState<Omit<ReferencePanelState, 'refresh'>>({\n status: 'loading',\n data: null,\n errorMessage: null,\n })\n const epochRef = useRef(0)\n\n const load = useCallback(async () => {\n const epoch = ++epochRef.current\n try {\n const data = await loadReferencePanelData(stableQuery)\n if (epochRef.current === epoch) {\n setState({ status: 'ready', data, errorMessage: null })\n }\n } catch (cause) {\n if (epochRef.current === epoch) {\n setState({\n status: 'error',\n data: null,\n errorMessage: cause instanceof Error ? cause.message : String(cause),\n })\n }\n }\n }, [stableQuery])\n\n useEffect(() => {\n // Initial load is immediate; event-driven reloads are trailing-debounced so a\n // burst of content commands (per-keystroke autosave) coalesces into one reload\n // (EC-269).\n setState({ status: 'loading', data: null, errorMessage: null })\n void load()\n let disposed = false\n let unsubscribe: (() => void) | undefined\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n const scheduleReload = () => {\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n reloadTimer = undefined\n void load()\n }, REFERENCE_RELOAD_DEBOUNCE_MS)\n }\n void getStudioOperations().then((studio) => {\n if (disposed) return\n unsubscribe = studio.operations.events.subscribe((event) => {\n if (isReferenceRefreshEvent(event)) scheduleReload()\n })\n })\n return () => {\n disposed = true\n epochRef.current += 1\n if (reloadTimer) clearTimeout(reloadTimer)\n unsubscribe?.()\n }\n }, [load])\n\n const refresh = useCallback(() => {\n setState((previous) => ({ ...previous, status: 'loading' }))\n void load()\n }, [load])\n\n return { ...state, refresh }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"references-store.js","sourceRoot":"","sources":["../../../src/lib/references/references-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { documentKey } from '@elytracms/core/cms-core'\nimport type { CmsDocument, DocumentRef } from '@elytracms/core/cms-core'\nimport type {\n ReferenceEntryRecord,\n ReferenceSource,\n ReferenceTarget,\n} from '@elytracms/core/persistence'\nimport { getStudioOperations } from '../operations'\nimport type { OperationEvent, OperationsClient, StudioOperations } from '../operations'\nimport { getStudioRegistry } from '../registry'\nimport { buildReferenceEntryViews } from './reference-model'\nimport type { ReferenceEntryView, ReferenceLabelContext } from './reference-model'\n\n/**\n * Data layer for the EC-172 references panels. All reads go through\n * `@elytracms/core/operations` queries (`references.outbound` / `references.inbound`\n * plus the labeling queries) — UI code never touches persistence repositories\n * or index tables. The hook keeps panels live: any succeeded command in a\n * content-bearing domain re-queries after the EC-155 maintained-on-write\n * attachment has flushed.\n */\n\nexport interface ReferencePanelQuery {\n projectId: string\n /** Enables the outbound (\"references\") section. */\n source?: ReferenceSource | undefined\n /** Enables the inbound (\"used by\") section. */\n target?: ReferenceTarget | undefined\n}\n\nexport interface ReferencePanelData {\n outbound: ReferenceEntryView[]\n inbound: ReferenceEntryView[]\n /**\n * Per-direction read error (EC-205): a direction whose query returns a\n * structured non-ok degrades to an empty list plus this message, surfaced as a\n * visible per-section error — instead of collapsing the whole panel into its\n * hard-error state. `null` when the direction loaded (or was not requested).\n */\n outboundError: string | null\n inboundError: string | null\n}\n\n// --- Index bootstrap -----------------------------------------------------------\n\n/**\n * The EC-155 index is maintained on write, but seeded/imported projects start\n * with an empty index. {@link ensureReferenceIndex} seeds it once per project per\n * operations client; failures (e.g. a viewer's command denial) degrade to\n * whatever the maintained-on-write index already holds.\n */\nconst backfillsByClient = new WeakMap<StudioOperations, Map<string, Promise<void>>>()\n\n/**\n * Seed the index once if it has never been backfilled, then never again — the\n * EC-269 mutation-free read path. The index is maintained on write, so the\n * (clear + rebuild) `backfill` is only needed to seed a never-built index. A\n * persisted marker (`references.buildMarker`, a read) records completion; when\n * present this returns after a single query with ZERO mutations — so opening the\n * References panel no longer rewrites the whole index every session (the normal\n * Convex case: the EC-233 import already backfilled). Only a genuinely empty/\n * never-built project still pays the one-time backfill (and a viewer who cannot\n * run it degrades to the maintained-on-write index, as before).\n */\nasync function seedReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n try {\n const marker = await studio.operations.references.buildMarker({ projectId })\n if (marker.ok && marker.value !== null) return\n } catch {\n // A marker-read failure falls through to the backfill (best-effort seed).\n }\n await studio.operations.references.backfill({ projectId }).then(\n () => undefined,\n () => undefined,\n )\n}\n\nexport function ensureReferenceIndex(studio: StudioOperations, projectId: string): Promise<void> {\n let byProject = backfillsByClient.get(studio)\n if (!byProject) {\n byProject = new Map()\n backfillsByClient.set(studio, byProject)\n }\n let pending = byProject.get(projectId)\n if (!pending) {\n pending = seedReferenceIndex(studio, projectId)\n byProject.set(projectId, pending)\n }\n return pending\n}\n\n// --- Label context loading -------------------------------------------------------\n\nasync function loadLabelContext(\n operations: OperationsClient,\n projectId: string,\n): Promise<ReferenceLabelContext> {\n const [schemaResult, documentsResult, assetsResult] = await Promise.all([\n operations.schema.get({ projectId }),\n operations.documents.list({ projectId }),\n operations.assets.list({ projectId }),\n ])\n\n const collections = schemaResult.ok ? schemaResult.value.collections : []\n const documents = new Map<string, CmsDocument>()\n if (documentsResult.ok) {\n for (const record of documentsResult.value) {\n documents.set(\n documentKey({ collection: record.document.collection, id: record.document.id }),\n record.document,\n )\n }\n }\n // EC-287: the project graph is retired; reference source labels resolve from\n // CMS documents only. `page`/`layout` graph sources no longer exist.\n const assets = new Map<string, { filename: string; title?: string | undefined }>()\n if (assetsResult.ok) {\n for (const asset of assetsResult.value) {\n assets.set(asset.id, { filename: asset.filename, title: asset.title })\n }\n }\n const registry = getStudioRegistry().registry\n return {\n collections,\n documents,\n assets,\n componentTitle: (componentId) => registry.getManifest(componentId)?.title,\n }\n}\n\n// --- Panel data loading -----------------------------------------------------------\n\n/** Load both directions + labels for one panel (operations queries only). */\nexport async function loadReferencePanelData(\n query: ReferencePanelQuery,\n): Promise<ReferencePanelData> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, query.projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n // EC-205: load each direction INDEPENDENTLY. A structured non-ok from one\n // direction is a degradable operation result (it carries an OperationError —\n // the documented contract), not an exception: degrade that direction to empty\n // and surface its error per-section (references-panel) while the other still\n // renders, instead of throwing the whole panel into its hard-error state. A\n // genuinely broken backend still throws from getStudioOperations/flush above.\n const [outbound, inbound] = await Promise.all([\n query.source\n ? operations.references.outbound({ projectId: query.projectId, source: query.source })\n : null,\n query.target\n ? operations.references.inbound({ projectId: query.projectId, target: query.target })\n : null,\n ])\n\n type DirectionResult = Awaited<ReturnType<typeof operations.references.outbound>>\n const settle = (\n result: DirectionResult | null,\n ): { records: ReferenceEntryRecord[]; error: string | null } =>\n result === null\n ? { records: [], error: null }\n : result.ok\n ? { records: result.value, error: null }\n : { records: [], error: result.error.message }\n const out = settle(outbound)\n const inb = settle(inbound)\n\n const context = await loadLabelContext(operations, query.projectId)\n return {\n outbound: buildReferenceEntryViews(out.records, context),\n inbound: buildReferenceEntryViews(inb.records, context),\n outboundError: out.error,\n inboundError: inb.error,\n }\n}\n\n// --- Impact loading (EC-173 safe unpublish/delete) -------------------------------\n\n/**\n * Load the labelled inbound entry views for a set of target documents in ONE\n * pass (one index flush, one label-context load) — the pre-flight data of the\n * EC-173 confirmation dialogs, including the bulk flows. Returns entries in\n * `targets` order; pair with `buildReferenceImpact` for break/block semantics.\n */\nexport async function loadInboundReferenceViews(\n projectId: string,\n targets: readonly DocumentRef[],\n): Promise<{ ref: DocumentRef; inbound: ReferenceEntryView[] }[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n\n const recordsPerTarget = await Promise.all(\n targets.map((ref) =>\n operations.references\n .inbound({ projectId, target: { type: 'document', id: documentKey(ref) } })\n .then((result) => {\n if (!result.ok) throw new Error(result.error.message)\n return result.value\n }),\n ),\n )\n const context = await loadLabelContext(operations, projectId)\n return targets.map((ref, index) => ({\n ref,\n inbound: buildReferenceEntryViews(recordsPerTarget[index] ?? [], context),\n }))\n}\n\n/**\n * Inbound reference views for ONE asset (EC-202): the \"what uses this asset\"\n * pre-flight of the asset-delete dialog, index-accurate (the EC-155 reference\n * index, not the retired `find-asset-usages` string scan). Mirrors\n * {@link loadInboundReferenceViews} for a single `{ type: 'asset' }` target —\n * the source labels resolve the referencing documents/graph-nodes identically.\n */\nexport async function loadInboundAssetReferenceViews(\n projectId: string,\n assetId: string,\n): Promise<ReferenceEntryView[]> {\n const studio = await getStudioOperations()\n await ensureReferenceIndex(studio, projectId)\n // Observe a settled index: maintained-on-write updates run async after events.\n await studio.references.flush()\n const operations = studio.operations\n const result = await operations.references.inbound({\n projectId,\n target: { type: 'asset', id: assetId },\n })\n if (!result.ok) throw new Error(result.error.message)\n // No inbound references → no sources to label, so skip the label-context load\n // entirely (schema + ALL documents + ALL assets). This is the common case for a\n // deletable asset, making the delete dialog's pre-flight a single indexed query\n // instead of three full-table lists (EC-301 perf).\n if (result.value.length === 0) return []\n const context = await loadLabelContext(operations, projectId)\n return buildReferenceEntryViews(result.value, context)\n}\n\n// --- React hook ----------------------------------------------------------------\n\n/**\n * Domains whose successful commands can change the reference index or labels and\n * should refresh an open panel. `references` is deliberately ABSENT (EC-269):\n * reacting to reference-index command events (the `backfill`) would make the\n * panel re-trigger on its OWN index maintenance — a self-cascade that, compounded\n * with the per-keystroke `documents` events, produced hundreds of redundant\n * reloads per document. The index is maintained on write; the panel reacts to the\n * CONTENT domains that feed it, never to its own index writes.\n */\nconst REFRESH_DOMAINS: ReadonlySet<string> = new Set([\n 'documents',\n 'graph',\n 'publishing',\n 'assets',\n 'schema',\n])\n\n/**\n * Whether one operation event should refresh an open reference panel. Exported\n * for tests — pins the EC-269 contract that reference-index commands (and any\n * non-succeeded / non-command event) never trigger a reload.\n */\nexport function isReferenceRefreshEvent(event: OperationEvent): boolean {\n return (\n event.kind === 'command' && event.status === 'succeeded' && REFRESH_DOMAINS.has(event.domain)\n )\n}\n\n/**\n * Trailing-debounce window (ms) for event-driven panel reloads (EC-269): a burst\n * of content commands — e.g. one `documents.upsert` per keystroke from autosave —\n * coalesces into a SINGLE reload once the writes settle, instead of one full\n * (multi-query) reload per event. The initial mount load stays immediate.\n */\nexport const REFERENCE_RELOAD_DEBOUNCE_MS = 300\n\nexport type ReferencePanelStatus = 'loading' | 'ready' | 'error'\n\nexport interface ReferencePanelState {\n status: ReferencePanelStatus\n data: ReferencePanelData | null\n errorMessage: string | null\n refresh: () => void\n}\n\n/**\n * Live panel state for one entity: loads on mount, exposes explicit\n * loading/error states, and re-queries whenever a content-bearing command\n * succeeds anywhere in the studio (the index is maintained on write).\n */\nexport function useReferencePanel(query: ReferencePanelQuery): ReferencePanelState {\n const { projectId } = query\n const sourceType = query.source?.type\n const sourceId = query.source?.id\n const targetType = query.target?.type\n const targetId = query.target?.id\n\n const stableQuery = useMemo<ReferencePanelQuery>(\n () => ({\n projectId,\n ...(sourceType && sourceId ? { source: { type: sourceType, id: sourceId } } : {}),\n ...(targetType && targetId ? { target: { type: targetType, id: targetId } } : {}),\n }),\n [projectId, sourceType, sourceId, targetType, targetId],\n )\n\n const [state, setState] = useState<Omit<ReferencePanelState, 'refresh'>>({\n status: 'loading',\n data: null,\n errorMessage: null,\n })\n const epochRef = useRef(0)\n\n const load = useCallback(async () => {\n const epoch = ++epochRef.current\n try {\n const data = await loadReferencePanelData(stableQuery)\n if (epochRef.current === epoch) {\n setState({ status: 'ready', data, errorMessage: null })\n }\n } catch (cause) {\n if (epochRef.current === epoch) {\n setState({\n status: 'error',\n data: null,\n errorMessage: cause instanceof Error ? cause.message : String(cause),\n })\n }\n }\n }, [stableQuery])\n\n useEffect(() => {\n // Initial load is immediate; event-driven reloads are trailing-debounced so a\n // burst of content commands (per-keystroke autosave) coalesces into one reload\n // (EC-269).\n setState({ status: 'loading', data: null, errorMessage: null })\n void load()\n let disposed = false\n let unsubscribe: (() => void) | undefined\n let reloadTimer: ReturnType<typeof setTimeout> | undefined\n const scheduleReload = () => {\n if (reloadTimer) clearTimeout(reloadTimer)\n reloadTimer = setTimeout(() => {\n reloadTimer = undefined\n void load()\n }, REFERENCE_RELOAD_DEBOUNCE_MS)\n }\n void getStudioOperations().then((studio) => {\n if (disposed) return\n unsubscribe = studio.operations.events.subscribe((event) => {\n if (isReferenceRefreshEvent(event)) scheduleReload()\n })\n })\n return () => {\n disposed = true\n epochRef.current += 1\n if (reloadTimer) clearTimeout(reloadTimer)\n unsubscribe?.()\n }\n }, [load])\n\n const refresh = useCallback(() => {\n setState((previous) => ({ ...previous, status: 'loading' }))\n void load()\n }, [load])\n\n return { ...state, refresh }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytracms/studio",
|
|
3
3
|
"author": "finaldream <hi@finaldream.de>",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.25",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "The Elytra studio engine (UI + lib + routes + data-only Convex schema) as an importable package. Extraction staged in EXTRACTION.md (EC-223); Stages 1-2 = UI primitives + router-free lib leaves.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"lucide-react": "^1.17.0",
|
|
118
118
|
"tailwind-merge": "^3.6.0",
|
|
119
119
|
"zod": "^4.0.0",
|
|
120
|
-
"@elytracms/core": "^0.0.
|
|
120
|
+
"@elytracms/core": "^0.0.25"
|
|
121
121
|
},
|
|
122
122
|
"peerDependencies": {
|
|
123
123
|
"@tanstack/react-router": "^1.170.0",
|