@elytracms/studio 0.0.51 → 0.0.53
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/convex/auth.ts +135 -62
- package/convex/invites.ts +77 -0
- package/dist/components/account/account-settings.js +4 -4
- package/dist/components/account/credential-panel.js +1 -1
- package/dist/components/account/self-account-view-source.js +3 -3
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel.js +4 -4
- package/dist/components/ai/ai-cms-panel.js +1 -1
- package/dist/components/ai/proposal-card.js +4 -4
- package/dist/components/ai/translate-document-action.js +1 -1
- package/dist/components/ai/translate-document-dialog.d.ts +1 -1
- package/dist/components/app-views/default-modules.d.ts +3 -3
- package/dist/components/app-views/default-modules.js +20 -20
- package/dist/components/app-views/sidebar.d.ts +1 -1
- package/dist/components/app-views/sidebar.js +2 -2
- package/dist/components/app-views/studio-config.d.ts +1 -1
- package/dist/components/assets/asset-detail.js +9 -9
- package/dist/components/assets/asset-grid.js +7 -7
- package/dist/components/assets/asset-thumb.d.ts +1 -1
- package/dist/components/assets/asset-thumb.js +2 -2
- package/dist/components/assets/assets-collection-view-source.js +3 -3
- package/dist/components/assets/assets-module.js +8 -8
- package/dist/components/assets/assets-store.d.ts +1 -1
- package/dist/components/assets/assets-store.js +1 -1
- package/dist/components/builder/blocks-model.js +1 -1
- package/dist/components/builder/blocks-panel.d.ts +1 -1
- package/dist/components/builder/blocks-panel.js +6 -6
- package/dist/components/builder/canvas.js +11 -11
- package/dist/components/builder/clipboard-actions.js +2 -2
- package/dist/components/builder/composition-field-editor-lazy.d.ts +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +1 -1
- package/dist/components/builder/composition-field-editor.js +9 -9
- package/dist/components/builder/insert-context.d.ts +2 -2
- package/dist/components/builder/insert-context.js +2 -2
- package/dist/components/builder/insert-menu.d.ts +2 -2
- package/dist/components/builder/insert-menu.js +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/page-tree.js +5 -5
- package/dist/components/builder/property-inspector.js +3 -3
- package/dist/components/builder/reuse-context.d.ts +1 -1
- package/dist/components/builder/reuse-context.js +3 -3
- package/dist/components/builder/spacing-handles.js +2 -2
- package/dist/components/cms/asset-picker.js +4 -4
- package/dist/components/cms/bulk-actions.js +1 -1
- package/dist/components/cms/bulk-bar.d.ts +1 -1
- package/dist/components/cms/bulk-bar.js +3 -3
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell.js +2 -2
- package/dist/components/cms/cms-index.js +1 -1
- package/dist/components/cms/cms-store.d.ts +2 -2
- package/dist/components/cms/cms-store.js +6 -6
- package/dist/components/cms/collection-table.js +17 -17
- package/dist/components/cms/detail-editor.js +31 -31
- package/dist/components/cms/document-diff-view.js +4 -4
- package/dist/components/cms/editable-cell.d.ts +1 -1
- package/dist/components/cms/editable-cell.js +6 -6
- package/dist/components/cms/field-control.js +15 -15
- package/dist/components/cms/field-renderer.d.ts +2 -2
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/import-export.js +1 -1
- package/dist/components/cms/issues-pane.js +1 -1
- package/dist/components/cms/prop-fields.js +3 -3
- package/dist/components/cms/reference-confirm.js +1 -1
- package/dist/components/cms/relation-picker.js +1 -1
- package/dist/components/cms/rich-text/block-commands.d.ts +1 -1
- package/dist/components/cms/rich-text/component-embed.js +2 -2
- package/dist/components/cms/rich-text/editor.js +8 -8
- package/dist/components/cms/rich-text/embed-inspector.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-inspector.js +2 -2
- package/dist/components/cms/rich-text/embed-picker.js +1 -1
- package/dist/components/cms/rich-text/embed-props.js +1 -1
- package/dist/components/cms/rich-text/extensions.d.ts +1 -1
- package/dist/components/cms/rich-text/extensions.js +4 -4
- package/dist/components/cms/rich-text/index.d.ts +1 -1
- package/dist/components/cms/rich-text/index.js +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +1 -1
- package/dist/components/cms/rich-text/lazy.d.ts +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/slash-command.js +1 -1
- package/dist/components/cms/rich-text/toolbar.d.ts +1 -1
- package/dist/components/cms/rich-text/toolbar.js +4 -4
- package/dist/components/cms/use-cell-edit.d.ts +1 -1
- package/dist/components/cms/use-cell-edit.js +2 -2
- package/dist/components/cms/use-compare-selection.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +2 -2
- package/dist/components/cms/use-document-versions.d.ts +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-embed-inspector-host.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +2 -2
- package/dist/components/cms/use-published-perspective.js +2 -2
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare.js +6 -6
- package/dist/components/cms/versions-model.d.ts +1 -1
- package/dist/components/cms/versions-pane.js +5 -5
- package/dist/components/cms/view-bar.js +2 -2
- package/dist/components/collab/collab-context.js +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +2 -2
- package/dist/components/components/component-preview.js +3 -3
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/registry-browser.js +2 -2
- package/dist/components/components/transfer-dialog.js +5 -5
- package/dist/components/components/transfer.js +4 -4
- package/dist/components/current-user-menu.js +2 -2
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/operation-error.js +1 -1
- package/dist/components/project-list.js +5 -5
- package/dist/components/project-session.js +1 -1
- package/dist/components/project-switcher.js +3 -3
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/redirects/redirects-collection-view-source.js +3 -3
- package/dist/components/redirects/redirects-module.js +6 -6
- package/dist/components/section-nav.js +3 -3
- package/dist/components/section-surface.js +1 -1
- package/dist/components/studio-project-screen.js +7 -7
- package/dist/components/studio-shell.js +9 -9
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -4
- package/dist/components/users/users-module.js +9 -9
- package/dist/components/workspace-projects.js +1 -1
- package/dist/customization.d.ts +9 -9
- package/dist/customization.js +5 -5
- package/dist/lib/ai/anthropic-provider.d.ts +1 -1
- package/dist/lib/ai/apply-content.d.ts +1 -1
- package/dist/lib/ai/apply-content.js +3 -3
- package/dist/lib/ai/chat-state.d.ts +2 -2
- package/dist/lib/ai/config.d.ts +1 -1
- package/dist/lib/ai/create-flow.d.ts +2 -2
- package/dist/lib/ai/create-flow.js +3 -3
- package/dist/lib/ai/fake-provider.d.ts +1 -1
- package/dist/lib/ai/index.d.ts +15 -15
- package/dist/lib/ai/index.js +15 -15
- package/dist/lib/ai/knowledge.d.ts +1 -1
- package/dist/lib/ai/provenance.d.ts +1 -1
- package/dist/lib/ai/route-provider.d.ts +1 -1
- package/dist/lib/ai/route.d.ts +1 -1
- package/dist/lib/ai/shape.d.ts +1 -1
- package/dist/lib/ai/translate-document.d.ts +1 -1
- package/dist/lib/ai/translate-document.js +3 -3
- package/dist/lib/ai-panel.js +1 -1
- package/dist/lib/assets/index.d.ts +4 -4
- package/dist/lib/assets/index.js +4 -4
- package/dist/lib/assets/upload.d.ts +2 -2
- package/dist/lib/assets/upload.js +2 -2
- package/dist/lib/auth/convex-auth-adapter.d.ts +53 -2
- package/dist/lib/auth/convex-auth-adapter.js +149 -68
- package/dist/lib/auth/convex-auth-adapter.js.map +1 -1
- package/dist/lib/auth/dev-auto-login-adapter.d.ts +1 -1
- package/dist/lib/auth/index.d.ts +9 -9
- package/dist/lib/auth/index.js +10 -10
- package/dist/lib/auth/local-auth-adapter.d.ts +1 -1
- package/dist/lib/auth/route-guard.d.ts +1 -1
- package/dist/lib/auth-errors.d.ts +1 -1
- package/dist/lib/clipboard/index.d.ts +3 -3
- package/dist/lib/clipboard/index.js +3 -3
- package/dist/lib/cms/published-documents.d.ts +1 -1
- package/dist/lib/cms-views/index.d.ts +2 -2
- package/dist/lib/cms-views/index.js +2 -2
- package/dist/lib/collection-view/collection-view-source.d.ts +4 -4
- package/dist/lib/data/collection-content.js +1 -1
- package/dist/lib/data/data-store.js +6 -6
- package/dist/lib/data/index.d.ts +4 -4
- package/dist/lib/data/index.js +4 -4
- package/dist/lib/data/sample-capture.js +4 -4
- package/dist/lib/data/sources-store.d.ts +1 -1
- package/dist/lib/diff-view-preference.js +1 -1
- package/dist/lib/dnd/gap-drag.d.ts +1 -1
- package/dist/lib/i18n/i18n.d.ts +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/index.d.ts +4 -4
- package/dist/lib/i18n/index.js +3 -3
- package/dist/lib/i18n/messages.en.d.ts +1 -1
- package/dist/lib/i18n/messages.en.js +1 -1
- package/dist/lib/locale/binding-fallback.d.ts +1 -1
- package/dist/lib/locale/binding-fallback.js +2 -2
- package/dist/lib/locale/index.d.ts +6 -6
- package/dist/lib/locale/index.js +6 -6
- package/dist/lib/locale/locale-switcher.js +3 -3
- package/dist/lib/locale/use-studio-locale.js +1 -1
- package/dist/lib/operations/asset-repository.d.ts +1 -1
- package/dist/lib/operations/cms-persistence.d.ts +1 -1
- package/dist/lib/operations/cms-persistence.js +1 -1
- package/dist/lib/operations/index.d.ts +6 -6
- package/dist/lib/operations/index.js +6 -6
- package/dist/lib/operations/session-hydration.d.ts +1 -1
- package/dist/lib/operations/session-hydration.js +1 -1
- package/dist/lib/operations/studio-operations.d.ts +1 -1
- package/dist/lib/operations/studio-operations.js +3 -3
- package/dist/lib/persistence/convex-adapter.js +2 -2
- package/dist/lib/persistence/convex-blob-storage.d.ts +1 -1
- package/dist/lib/persistence/convex-blob-storage.js +1 -1
- package/dist/lib/persistence/deployment-readiness.d.ts +2 -2
- package/dist/lib/persistence/index.d.ts +6 -6
- package/dist/lib/persistence/index.js +7 -7
- package/dist/lib/persona/labels.js +1 -1
- package/dist/lib/query/index.d.ts +2 -2
- package/dist/lib/query/index.js +1 -1
- package/dist/lib/rail.js +1 -1
- package/dist/lib/references/impact.d.ts +1 -1
- package/dist/lib/references/impact.js +1 -1
- package/dist/lib/references/index.d.ts +3 -3
- package/dist/lib/references/index.js +3 -3
- package/dist/lib/references/references-store.d.ts +2 -2
- package/dist/lib/references/references-store.js +3 -3
- package/dist/lib/registry/index.js +2 -2
- package/dist/lib/theme.js +1 -1
- package/dist/lib/webhooks/config.d.ts +1 -1
- package/dist/lib/webhooks/config.js +2 -2
- package/dist/lib/webhooks/dispatcher.js +2 -2
- package/dist/lib/webhooks/index.d.ts +6 -6
- package/dist/lib/webhooks/index.js +4 -4
- package/dist/lib/workspace/index.d.ts +7 -7
- package/dist/lib/workspace/index.js +3 -3
- package/dist/lib/workspace/switcher-source.d.ts +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.js +1 -1
- package/dist/ui/avatar.js +1 -1
- package/dist/ui/badge.js +1 -1
- package/dist/ui/button.js +1 -1
- package/dist/ui/card.js +1 -1
- package/dist/ui/collapsible-section.js +2 -2
- package/dist/ui/dialog.js +1 -1
- package/dist/ui/dropdown-menu.js +1 -1
- package/dist/ui/field.js +1 -1
- package/dist/ui/index.d.ts +19 -19
- package/dist/ui/index.js +19 -19
- package/dist/ui/input.js +1 -1
- package/dist/ui/inspector.js +1 -1
- package/dist/ui/layout.js +1 -1
- package/dist/ui/panel.js +2 -2
- package/dist/ui/popover.js +1 -1
- package/dist/ui/row-item.js +2 -2
- package/dist/ui/segmented-control.js +1 -1
- package/dist/ui/separator.js +1 -1
- package/dist/ui/toolbar.js +2 -2
- package/dist/ui/typography.js +1 -1
- package/package.json +3 -3
- package/dist/components/assets/asset-library.d.ts +0 -14
- package/dist/components/assets/asset-library.js +0 -202
- package/dist/components/assets/asset-library.js.map +0 -1
- package/dist/components/backend-gate.d.ts +0 -19
- package/dist/components/backend-gate.js +0 -64
- package/dist/components/backend-gate.js.map +0 -1
- package/dist/components/builder/composition-scope.d.ts +0 -16
- package/dist/components/builder/composition-scope.js +0 -7
- package/dist/components/builder/composition-scope.js.map +0 -1
- package/dist/components/cms/alias-selector.d.ts +0 -20
- package/dist/components/cms/alias-selector.js +0 -34
- package/dist/components/cms/alias-selector.js.map +0 -1
- package/dist/components/cms/collections-sidebar.d.ts +0 -18
- package/dist/components/cms/collections-sidebar.js +0 -36
- package/dist/components/cms/collections-sidebar.js.map +0 -1
- package/dist/components/cms/new-page-dialog.d.ts +0 -22
- package/dist/components/cms/new-page-dialog.js +0 -42
- package/dist/components/cms/new-page-dialog.js.map +0 -1
- package/dist/components/cms/rich-text/embed-control.d.ts +0 -17
- package/dist/components/cms/rich-text/embed-control.js +0 -62
- package/dist/components/cms/rich-text/embed-control.js.map +0 -1
- package/dist/components/cms/rich-text/image-control.d.ts +0 -16
- package/dist/components/cms/rich-text/image-control.js +0 -36
- package/dist/components/cms/rich-text/image-control.js.map +0 -1
- package/dist/components/components/fixture-runner.d.ts +0 -10
- package/dist/components/components/fixture-runner.js +0 -27
- package/dist/components/components/fixture-runner.js.map +0 -1
- package/dist/components/references/builder-deep-link.d.ts +0 -12
- package/dist/components/references/builder-deep-link.js +0 -53
- package/dist/components/references/builder-deep-link.js.map +0 -1
- package/dist/components/routes/redirect-chain.d.ts +0 -9
- package/dist/components/routes/redirect-chain.js +0 -16
- package/dist/components/routes/redirect-chain.js.map +0 -1
- package/dist/components/routes/redirect-manager.d.ts +0 -13
- package/dist/components/routes/redirect-manager.js +0 -24
- package/dist/components/routes/redirect-manager.js.map +0 -1
- package/dist/components/routes/route-list.d.ts +0 -35
- package/dist/components/routes/route-list.js +0 -69
- package/dist/components/routes/route-list.js.map +0 -1
- package/dist/components/routes/routes-shell.d.ts +0 -14
- package/dist/components/routes/routes-shell.js +0 -68
- package/dist/components/routes/routes-shell.js.map +0 -1
- package/dist/components/sections.d.ts +0 -37
- package/dist/components/sections.js +0 -62
- package/dist/components/sections.js.map +0 -1
- package/dist/components/users/user-search.d.ts +0 -8
- package/dist/components/users/user-search.js +0 -13
- package/dist/components/users/user-search.js.map +0 -1
- package/dist/components/workspace-gate.d.ts +0 -12
- package/dist/components/workspace-gate.js +0 -28
- package/dist/components/workspace-gate.js.map +0 -1
- package/dist/lib/cms-views/views-store.d.ts +0 -70
- package/dist/lib/cms-views/views-store.js +0 -129
- package/dist/lib/cms-views/views-store.js.map +0 -1
- package/dist/lib/operations/graph-commands.d.ts +0 -136
- package/dist/lib/operations/graph-commands.js +0 -85
- package/dist/lib/operations/graph-commands.js.map +0 -1
- package/dist/lib/operations/graph-persistence.d.ts +0 -27
- package/dist/lib/operations/graph-persistence.js +0 -35
- package/dist/lib/operations/graph-persistence.js.map +0 -1
- package/dist/lib/routing/index.d.ts +0 -10
- package/dist/lib/routing/index.js +0 -11
- package/dist/lib/routing/index.js.map +0 -1
- package/dist/lib/routing/routing-analysis.d.ts +0 -26
- package/dist/lib/routing/routing-analysis.js +0 -37
- package/dist/lib/routing/routing-analysis.js.map +0 -1
- package/dist/lib/routing/routing-store.d.ts +0 -50
- package/dist/lib/routing/routing-store.js +0 -72
- package/dist/lib/routing/routing-store.js.map +0 -1
- package/dist/lib/working-graph.d.ts +0 -82
- package/dist/lib/working-graph.js +0 -113
- package/dist/lib/working-graph.js.map +0 -1
package/dist/ui/badge.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
|
-
import { cn } from '../lib/utils';
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
4
4
|
export const badgeVariants = cva('inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs font-medium leading-none transition-colors [&_svg]:size-3', {
|
|
5
5
|
variants: {
|
|
6
6
|
variant: {
|
package/dist/ui/button.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
3
|
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
-
import { cn } from '../lib/utils';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
5
|
export const buttonVariants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow] outline-none focus-visible:ring-2 focus-visible:ring-ring/60 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', {
|
|
6
6
|
variants: {
|
|
7
7
|
variant: {
|
package/dist/ui/card.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
3
|
/** Named card surface — use instead of repeated bordered/shadowed divs. */
|
|
4
4
|
export function Card({ interactive, className, ...props }) {
|
|
5
5
|
return (_jsx("div", { className: cn('rounded-lg border border-border bg-card text-card-foreground shadow-sm', interactive &&
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDown } from 'lucide-react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import { cn } from '../lib/utils';
|
|
5
|
-
import { Panel, PanelHeader } from './panel';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
|
+
import { Panel, PanelHeader } from './panel.js';
|
|
6
6
|
/**
|
|
7
7
|
* A `Panel`-backed section whose header can collapse its body (EC-306). No
|
|
8
8
|
* existing collapsible/accordion primitive existed; this is the reusable one
|
package/dist/ui/dialog.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
3
3
|
import { X } from 'lucide-react';
|
|
4
|
-
import { cn } from '../lib/utils';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
5
|
export const Dialog = DialogPrimitive.Root;
|
|
6
6
|
export const DialogTrigger = DialogPrimitive.Trigger;
|
|
7
7
|
export const DialogClose = DialogPrimitive.Close;
|
package/dist/ui/dropdown-menu.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
3
3
|
import { Check } from 'lucide-react';
|
|
4
|
-
import { cn } from '../lib/utils';
|
|
4
|
+
import { cn } from '../lib/utils.js';
|
|
5
5
|
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
6
6
|
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
7
7
|
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
package/dist/ui/field.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ChevronDown } from 'lucide-react';
|
|
3
|
-
import { cn } from '../lib/utils';
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
4
4
|
export function Field({ label, htmlFor, hint, error, required, children, className }) {
|
|
5
5
|
return (_jsxs("div", { className: cn('flex flex-col gap-1.5', className), children: [_jsxs(FieldLabel, { htmlFor: htmlFor, children: [label, required ? _jsx("span", { className: "text-destructive", children: " *" }) : null] }), children, error ? (_jsx("p", { className: "text-xs font-medium text-destructive", role: "alert", children: error })) : hint ? (_jsx("p", { className: "text-xs text-muted-foreground", children: hint })) : null] }));
|
|
6
6
|
}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export * from './typography';
|
|
2
|
-
export * from './layout';
|
|
3
|
-
export * from './avatar';
|
|
4
|
-
export * from './button';
|
|
5
|
-
export * from './input';
|
|
6
|
-
export * from './badge';
|
|
7
|
-
export * from './card';
|
|
8
|
-
export * from './panel';
|
|
9
|
-
export * from './row-item';
|
|
10
|
-
export * from './collapsible-section';
|
|
11
|
-
export * from './separator';
|
|
12
|
-
export * from './toolbar';
|
|
13
|
-
export * from './inspector';
|
|
14
|
-
export * from './dialog';
|
|
15
|
-
export * from './dropdown-menu';
|
|
16
|
-
export * from './popover';
|
|
17
|
-
export * from './field';
|
|
18
|
-
export * from './toast';
|
|
19
|
-
export * from './segmented-control';
|
|
1
|
+
export * from './typography.js';
|
|
2
|
+
export * from './layout.js';
|
|
3
|
+
export * from './avatar.js';
|
|
4
|
+
export * from './button.js';
|
|
5
|
+
export * from './input.js';
|
|
6
|
+
export * from './badge.js';
|
|
7
|
+
export * from './card.js';
|
|
8
|
+
export * from './panel.js';
|
|
9
|
+
export * from './row-item.js';
|
|
10
|
+
export * from './collapsible-section.js';
|
|
11
|
+
export * from './separator.js';
|
|
12
|
+
export * from './toolbar.js';
|
|
13
|
+
export * from './inspector.js';
|
|
14
|
+
export * from './dialog.js';
|
|
15
|
+
export * from './dropdown-menu.js';
|
|
16
|
+
export * from './popover.js';
|
|
17
|
+
export * from './field.js';
|
|
18
|
+
export * from './toast.js';
|
|
19
|
+
export * from './segmented-control.js';
|
package/dist/ui/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
export * from './typography';
|
|
2
|
-
export * from './layout';
|
|
3
|
-
export * from './avatar';
|
|
4
|
-
export * from './button';
|
|
5
|
-
export * from './input';
|
|
6
|
-
export * from './badge';
|
|
7
|
-
export * from './card';
|
|
8
|
-
export * from './panel';
|
|
9
|
-
export * from './row-item';
|
|
10
|
-
export * from './collapsible-section';
|
|
11
|
-
export * from './separator';
|
|
12
|
-
export * from './toolbar';
|
|
13
|
-
export * from './inspector';
|
|
14
|
-
export * from './dialog';
|
|
15
|
-
export * from './dropdown-menu';
|
|
16
|
-
export * from './popover';
|
|
17
|
-
export * from './field';
|
|
18
|
-
export * from './toast';
|
|
19
|
-
export * from './segmented-control';
|
|
1
|
+
export * from './typography.js';
|
|
2
|
+
export * from './layout.js';
|
|
3
|
+
export * from './avatar.js';
|
|
4
|
+
export * from './button.js';
|
|
5
|
+
export * from './input.js';
|
|
6
|
+
export * from './badge.js';
|
|
7
|
+
export * from './card.js';
|
|
8
|
+
export * from './panel.js';
|
|
9
|
+
export * from './row-item.js';
|
|
10
|
+
export * from './collapsible-section.js';
|
|
11
|
+
export * from './separator.js';
|
|
12
|
+
export * from './toolbar.js';
|
|
13
|
+
export * from './inspector.js';
|
|
14
|
+
export * from './dialog.js';
|
|
15
|
+
export * from './dropdown-menu.js';
|
|
16
|
+
export * from './popover.js';
|
|
17
|
+
export * from './field.js';
|
|
18
|
+
export * from './toast.js';
|
|
19
|
+
export * from './segmented-control.js';
|
|
20
20
|
//# sourceMappingURL=index.js.map
|
package/dist/ui/input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
3
|
/** Named text input primitive — shadcn-style, token-driven. */
|
|
4
4
|
export function Input({ className, type = 'text', ...props }) {
|
|
5
5
|
return (_jsx("input", { type: type, className: cn('flex h-8 w-full rounded-md border border-input bg-background px-2.5 text-sm text-foreground shadow-sm transition-colors', 'placeholder:text-muted-foreground/60', 'focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 outline-none', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
|
package/dist/ui/inspector.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Named inspector section — the building block of the property inspector (brief §8.2).
|
|
5
5
|
* Use instead of ad-hoc labelled div groups.
|
package/dist/ui/layout.js
CHANGED
package/dist/ui/panel.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
3
|
-
import { Row } from './layout';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
|
+
import { Row } from './layout.js';
|
|
4
4
|
/**
|
|
5
5
|
* Panel — the primary surface primitive for Studio regions (sidebars, inspector,
|
|
6
6
|
* canvas chrome). Use instead of stray bordered divs.
|
package/dist/ui/popover.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
|
3
|
-
import { cn } from '../lib/utils';
|
|
3
|
+
import { cn } from '../lib/utils.js';
|
|
4
4
|
/**
|
|
5
5
|
* Anchored popover primitive — the same visual language as the dropdown menu,
|
|
6
6
|
* but with popover (not menu) semantics, so it can hold arbitrary interactive
|
package/dist/ui/row-item.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
3
|
-
import { Row } from './layout';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
|
+
import { Row } from './layout.js';
|
|
4
4
|
/**
|
|
5
5
|
* A full-width list row (EC-246c): calm bordered chrome with a leading icon slot,
|
|
6
6
|
* a truncating main slot, and a trailing slot for actions or badges. Replaces the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
3
|
/** The "segmented pill" tab family — see the module doc comment. */
|
|
4
4
|
export function SegmentedControl({ items, ariaLabel, equalWidth, className, testId, }) {
|
|
5
5
|
return (_jsx("div", { role: "group", ...(ariaLabel ? { 'aria-label': ariaLabel } : {}), ...(testId ? { 'data-testid': testId } : {}), className: cn('flex items-center gap-1 rounded-md bg-muted/50 p-0.5', className), children: items.map((item) => (_jsxs("button", { type: "button", "aria-pressed": item.active, onClick: item.onClick, ...(item.ariaLabel ? { 'aria-label': item.ariaLabel, title: item.ariaLabel } : {}), ...(item.testId ? { 'data-testid': item.testId } : {}), className: cn('items-center justify-center gap-1.5 rounded-[5px] px-2.5 py-1 text-xs font-medium transition-colors', equalWidth ? 'flex flex-1' : 'inline-flex', item.active
|
package/dist/ui/separator.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
3
|
/** Named separator primitive. */
|
|
4
4
|
export function Separator({ orientation = 'horizontal', className, ...props }) {
|
|
5
5
|
return (_jsx("div", { role: "separator", "aria-orientation": orientation, className: cn('shrink-0 bg-border', orientation === 'horizontal' ? 'h-px w-full' : 'h-full w-px', className), ...props }));
|
package/dist/ui/toolbar.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
3
|
-
import { Separator } from './separator';
|
|
2
|
+
import { cn } from '../lib/utils.js';
|
|
3
|
+
import { Separator } from './separator.js';
|
|
4
4
|
/** Named toolbar primitive — a horizontal control strip for Studio surfaces. */
|
|
5
5
|
export function Toolbar({ className, ...props }) {
|
|
6
6
|
return (_jsx("div", { role: "toolbar", className: cn('flex h-11 shrink-0 items-center gap-1 border border-border bg-panel px-2 rounded-md', className), ...props }));
|
package/dist/ui/typography.js
CHANGED
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.53",
|
|
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",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
"lucide-react": "^1.23.0",
|
|
137
137
|
"tailwind-merge": "^3.6.0",
|
|
138
138
|
"zod": "^4.4.3",
|
|
139
|
-
"@elytracms/core": "^0.0.
|
|
139
|
+
"@elytracms/core": "^0.0.53"
|
|
140
140
|
},
|
|
141
141
|
"peerDependencies": {
|
|
142
142
|
"@tanstack/react-router": "^1.170.0",
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
"typescript": "^6.0.3"
|
|
155
155
|
},
|
|
156
156
|
"scripts": {
|
|
157
|
-
"build": "tsc -p tsconfig.build.json",
|
|
157
|
+
"build": "tsc -p tsconfig.build.json && node ../../scripts/fix-esm-extensions.mjs dist && node ../../scripts/check-esm-extensions.mjs dist",
|
|
158
158
|
"typecheck": "tsc --noEmit && tsc -p convex/tsconfig.json --noEmit",
|
|
159
159
|
"lint": "eslint src"
|
|
160
160
|
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
|
|
3
|
-
* with visible progress/failure/retry, a searchable grid of stored assets
|
|
4
|
-
* (filename/alt/title), metadata editing (alt, title — persisted through the
|
|
5
|
-
* operations asset facade), and reference-respecting deletion (usage count +
|
|
6
|
-
* block-or-confirm). All storage access goes through the persistence adapter
|
|
7
|
-
* via the assets store. Each card answers "where is this asset used?" via the
|
|
8
|
-
* shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
|
|
9
|
-
* param) scrolls that card into view and opens its references dialog.
|
|
10
|
-
*/
|
|
11
|
-
export declare function AssetLibrary({ projectId, focusAssetId, }: {
|
|
12
|
-
projectId: string;
|
|
13
|
-
focusAssetId?: string;
|
|
14
|
-
}): import("react").JSX.Element;
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { AlertTriangle, ImageIcon, Link2, Pencil, RotateCcw, Search, Trash2, Upload, X, } from 'lucide-react';
|
|
3
|
-
import { useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { translate } from '@elytracms/studio/lib/i18n';
|
|
5
|
-
import { buildAssetReferenceImpact } from '@elytracms/studio/lib/references';
|
|
6
|
-
import { cn } from '@elytracms/studio/lib/utils';
|
|
7
|
-
import { ReferenceImpactRows } from '../cms/reference-confirm';
|
|
8
|
-
import { ReferencesPanel } from '../references/references-panel';
|
|
9
|
-
import { Badge, Button, Code, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, Grid, Heading, Input, Panel, PanelBody, PanelHeader, Row, Spacer, Stack, Text, } from '@elytracms/studio/ui';
|
|
10
|
-
import { filterAssets } from './asset-search';
|
|
11
|
-
import { useAssets } from './assets-store';
|
|
12
|
-
/**
|
|
13
|
-
* Asset library surface (EC-151, EC-152): file picker + drag-and-drop uploads
|
|
14
|
-
* with visible progress/failure/retry, a searchable grid of stored assets
|
|
15
|
-
* (filename/alt/title), metadata editing (alt, title — persisted through the
|
|
16
|
-
* operations asset facade), and reference-respecting deletion (usage count +
|
|
17
|
-
* block-or-confirm). All storage access goes through the persistence adapter
|
|
18
|
-
* via the assets store. Each card answers "where is this asset used?" via the
|
|
19
|
-
* shared references panel (EC-172); `focusAssetId` (the `?asset=` deep-link
|
|
20
|
-
* param) scrolls that card into view and opens its references dialog.
|
|
21
|
-
*/
|
|
22
|
-
export function AssetLibrary({ projectId, focusAssetId, }) {
|
|
23
|
-
const store = useAssets();
|
|
24
|
-
const fileInputRef = useRef(null);
|
|
25
|
-
const [dragging, setDragging] = useState(false);
|
|
26
|
-
const [query, setQuery] = useState('');
|
|
27
|
-
const onPick = (event) => {
|
|
28
|
-
if (event.target.files)
|
|
29
|
-
store.upload([...event.target.files]);
|
|
30
|
-
event.target.value = '';
|
|
31
|
-
};
|
|
32
|
-
const onDrop = (event) => {
|
|
33
|
-
event.preventDefault();
|
|
34
|
-
setDragging(false);
|
|
35
|
-
if (event.dataTransfer.files.length > 0)
|
|
36
|
-
store.upload([...event.dataTransfer.files]);
|
|
37
|
-
};
|
|
38
|
-
if (store.status === 'error') {
|
|
39
|
-
return (_jsxs(Stack, { gap: "2", className: "p-6", "data-testid": "assets-error", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-destructive" }), _jsx(Text, { weight: "semibold", children: translate('assets.error.loadFailed') })] }), _jsx(Text, { size: "sm", tone: "muted", children: store.errorMessage ?? translate('common.backendUnreachable') }), _jsx(Row, { children: _jsxs(Button, { variant: "outline", size: "sm", onClick: () => void store.refresh(), children: [_jsx(RotateCcw, {}), " ", translate('common.retry')] }) })] }));
|
|
40
|
-
}
|
|
41
|
-
return (_jsxs(Stack, { gap: "4", className: cn('flex-1 p-4 sm:p-6', dragging && 'outline-2 outline-dashed outline-primary/60 -outline-offset-4'), onDragOver: (event) => {
|
|
42
|
-
event.preventDefault();
|
|
43
|
-
setDragging(true);
|
|
44
|
-
}, onDragLeave: () => setDragging(false), onDrop: onDrop, "data-testid": "asset-library", children: [_jsxs(Row, { gap: "2", children: [_jsx(Heading, { level: 2, size: "h2", children: translate('assets.title') }), _jsx(Badge, { variant: "outline", children: store.assets.length }), _jsx(Spacer, {}), _jsx(AssetSearchInput, { value: query, onChange: setQuery }), _jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: "hidden", onChange: onPick, "data-testid": "asset-file-input" }), _jsxs(Button, { size: "sm", onClick: () => fileInputRef.current?.click(), children: [_jsx(Upload, {}), " ", translate('common.upload')] })] }), _jsx(DropHint, { visible: dragging || store.assets.length === 0 }), store.uploads.length > 0 ? (_jsxs(Panel, { "data-testid": "upload-queue", children: [_jsx(PanelHeader, { children: _jsx(Text, { size: "xs", weight: "semibold", tone: "muted", children: translate('assets.uploads.heading') }) }), _jsx(PanelBody, { className: "divide-y divide-border p-0", children: store.uploads.map((entry) => (_jsx(UploadRow, { entry: entry, onRetry: () => store.retryUpload(entry.id), onDismiss: () => store.dismissUpload(entry.id) }, entry.id))) })] })) : null, store.status === 'loading' ? (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.loading') })) : (_jsx(AssetGrid, { assets: filterAssets(store.assets, query), filtered: query.trim().length > 0, projectId: projectId, focusAssetId: focusAssetId }))] }));
|
|
45
|
-
}
|
|
46
|
-
/** Named search primitive over filename / alt / title (EC-152). */
|
|
47
|
-
function AssetSearchInput({ value, onChange, }) {
|
|
48
|
-
return (_jsxs("span", { className: "relative", 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, { value: value, onChange: (e) => onChange(e.target.value), placeholder: translate('assets.searchPlaceholder'), "aria-label": translate('assets.searchLabel'), className: "w-56 pl-8", "data-testid": "asset-search" })] }));
|
|
49
|
-
}
|
|
50
|
-
/** Persistent drop affordance; doubles as the empty state. */
|
|
51
|
-
function DropHint({ visible }) {
|
|
52
|
-
return (_jsxs(Stack, { gap: "1", align: "center", className: cn('rounded-lg border border-dashed border-border p-6 text-center', !visible && 'hidden sm:flex'), children: [_jsx(ImageIcon, { className: "size-5 text-muted-foreground" }), _jsx(Text, { size: "sm", tone: "muted", children: translate('assets.dropHint') })] }));
|
|
53
|
-
}
|
|
54
|
-
/** Upload phase → the `assets.upload.phase.*` catalog key suffix (idle → queued). */
|
|
55
|
-
const PHASE_KEY = {
|
|
56
|
-
idle: 'queued',
|
|
57
|
-
preparing: 'preparing',
|
|
58
|
-
uploading: 'uploading',
|
|
59
|
-
confirming: 'confirming',
|
|
60
|
-
done: 'done',
|
|
61
|
-
failed: 'failed',
|
|
62
|
-
};
|
|
63
|
-
/** One in-flight upload: progress while transferring, explicit failure + retry. */
|
|
64
|
-
function UploadRow({ entry, onRetry, onDismiss, }) {
|
|
65
|
-
const { state } = entry;
|
|
66
|
-
const failed = state.phase === 'failed';
|
|
67
|
-
return (_jsxs(Stack, { gap: "1", className: "px-3 py-2", "data-testid": "upload-row", children: [_jsxs(Row, { gap: "2", children: [_jsx(Text, { size: "sm", weight: "medium", className: "min-w-0 flex-1 truncate", children: entry.filename }), _jsxs(Badge, { variant: failed ? 'error' : state.phase === 'done' ? 'success' : 'info', children: [translate('assets.upload.phase.' + PHASE_KEY[state.phase]), state.attempt > 1
|
|
68
|
-
? ' · ' + translate('assets.upload.attempt', { count: state.attempt })
|
|
69
|
-
: ''] }), failed ? (_jsxs(_Fragment, { children: [state.failure?.retryable !== false ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, "data-testid": "upload-retry", children: [_jsx(RotateCcw, {}), " Retry"] })) : null, _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: onDismiss, "aria-label": translate('assets.upload.dismissLabel'), children: _jsx(X, {}) })] })) : null] }), failed ? (_jsxs(Text, { size: "xs", tone: "muted", "data-testid": "upload-error", children: [state.failure?.stage, ": ", state.failure?.message] })) : (_jsx(ProgressBar, { value: state.progress }))] }));
|
|
70
|
-
}
|
|
71
|
-
/** Named progress primitive for byte transfer. */
|
|
72
|
-
function ProgressBar({ value }) {
|
|
73
|
-
return (_jsx("span", { className: "block h-1.5 w-full overflow-hidden rounded-full bg-muted", children: _jsx("span", { className: "block h-full rounded-full bg-primary transition-[width]", style: { width: `${Math.round(value * 100)}%` } }) }));
|
|
74
|
-
}
|
|
75
|
-
function AssetGrid({ assets, filtered, projectId, focusAssetId, }) {
|
|
76
|
-
if (assets.length === 0) {
|
|
77
|
-
// The unfiltered empty state is the DropHint above; the *filtered* empty
|
|
78
|
-
// state must say so explicitly (deterministic, reproducible).
|
|
79
|
-
return filtered ? (_jsx(Text, { size: "sm", tone: "muted", "data-testid": "asset-search-empty", children: translate('assets.searchEmpty') })) : null;
|
|
80
|
-
}
|
|
81
|
-
return (_jsx(Grid, { minItemWidth: "220px", gap: "3", "data-testid": "asset-grid", children: assets.map((asset) => (_jsx(AssetCard, { asset: asset, projectId: projectId, focused: asset.id === focusAssetId }, asset.id))) }));
|
|
82
|
-
}
|
|
83
|
-
function formatBytes(byteSize) {
|
|
84
|
-
if (byteSize < 1024)
|
|
85
|
-
return `${byteSize} B`;
|
|
86
|
-
if (byteSize < 1024 * 1024)
|
|
87
|
-
return `${(byteSize / 1024).toFixed(1)} KB`;
|
|
88
|
-
return `${(byteSize / (1024 * 1024)).toFixed(1)} MB`;
|
|
89
|
-
}
|
|
90
|
-
function AssetCard({ asset, projectId, focused, }) {
|
|
91
|
-
const cardRef = useRef(null);
|
|
92
|
-
useEffect(() => {
|
|
93
|
-
if (focused)
|
|
94
|
-
cardRef.current?.scrollIntoView({ block: 'center', behavior: 'smooth' });
|
|
95
|
-
}, [focused]);
|
|
96
|
-
return (_jsx("div", { ref: cardRef, children: _jsxs(Panel, { className: cn('overflow-hidden', focused && 'ring-2 ring-primary/70'), "data-testid": "asset-card", children: [_jsx(AssetThumbnail, { asset: asset }), _jsxs(Stack, { gap: "1", className: "p-3", children: [_jsx(Text, { size: "sm", weight: "medium", className: "truncate", title: asset.filename, children: asset.title ?? asset.filename }), asset.title ? (_jsx(Text, { size: "xs", tone: "muted", className: "truncate", children: asset.filename })) : null, _jsx(Text, { size: "xs", tone: asset.alt ? 'muted' : 'subtle', className: "truncate", "data-testid": "asset-alt", children: asset.alt
|
|
97
|
-
? translate('assets.card.altPrefix', { alt: asset.alt })
|
|
98
|
-
: translate('assets.card.noAlt') }), _jsxs(Row, { gap: "1", wrap: true, children: [_jsx(Code, { tone: "muted", children: asset.contentType }), asset.width && asset.height ? (_jsxs(Code, { tone: "muted", children: [asset.width, "\u00D7", asset.height] })) : null, _jsx(Code, { tone: "muted", children: formatBytes(asset.byteSize) })] }), _jsxs(Row, { gap: "1", children: [_jsx(Code, { tone: "muted", className: "min-w-0 flex-1 truncate", children: asset.id }), _jsx(AssetReferencesButton, { asset: asset, projectId: projectId, autoOpen: focused }), _jsx(EditAssetButton, { asset: asset }), _jsx(DeleteAssetButton, { asset: asset })] })] })] }) }));
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* "Where is this asset used?" (EC-172): the shared references panel, inbound
|
|
102
|
-
* over the EC-155 index, hosted in a dialog per asset card.
|
|
103
|
-
*/
|
|
104
|
-
function AssetReferencesButton({ asset, projectId, autoOpen, }) {
|
|
105
|
-
const [open, setOpen] = useState(false);
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
if (autoOpen)
|
|
108
|
-
setOpen(true);
|
|
109
|
-
}, [autoOpen]);
|
|
110
|
-
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": translate('assets.card.referencesLabel', { filename: asset.filename }), onClick: () => setOpen(true), "data-testid": "asset-references", children: _jsx(Link2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-lg", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.references.title', { name: asset.title ?? asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.references.description') })] }), open ? (_jsx(ReferencesPanel, { projectId: projectId, target: { type: 'asset', id: asset.id } })) : null] }) })] }));
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Metadata editing (EC-152): alt + title, persisted through the operations
|
|
114
|
-
* asset facade (`assets.upsert`). Failures render in the dialog — never a
|
|
115
|
-
* silent loss.
|
|
116
|
-
*/
|
|
117
|
-
function EditAssetButton({ asset }) {
|
|
118
|
-
const store = useAssets();
|
|
119
|
-
const [open, setOpen] = useState(false);
|
|
120
|
-
const [alt, setAlt] = useState('');
|
|
121
|
-
const [title, setTitle] = useState('');
|
|
122
|
-
const [error, setError] = useState(null);
|
|
123
|
-
const [busy, setBusy] = useState(false);
|
|
124
|
-
const openDialog = () => {
|
|
125
|
-
setAlt(asset.alt ?? '');
|
|
126
|
-
setTitle(asset.title ?? '');
|
|
127
|
-
setError(null);
|
|
128
|
-
setOpen(true);
|
|
129
|
-
};
|
|
130
|
-
const save = async () => {
|
|
131
|
-
setBusy(true);
|
|
132
|
-
setError(null);
|
|
133
|
-
const result = await store.updateMetadata(asset.id, { alt, title });
|
|
134
|
-
setBusy(false);
|
|
135
|
-
if (result.ok)
|
|
136
|
-
setOpen(false);
|
|
137
|
-
else
|
|
138
|
-
setError(result.message);
|
|
139
|
-
};
|
|
140
|
-
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": translate('assets.card.editLabel', { filename: asset.filename }), onClick: openDialog, "data-testid": "asset-edit", children: _jsx(Pencil, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.edit.title', { filename: asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.edit.description') })] }), _jsx(Field, { label: translate('assets.edit.altLabel'), htmlFor: `asset-alt-${asset.id}`, children: _jsx(Input, { id: `asset-alt-${asset.id}`, value: alt, onChange: (e) => setAlt(e.target.value), placeholder: translate('assets.edit.altPlaceholder'), "data-testid": "asset-edit-alt" }) }), _jsx(Field, { label: translate('assets.edit.titleLabel'), htmlFor: `asset-title-${asset.id}`, children: _jsx(Input, { id: `asset-title-${asset.id}`, value: title, onChange: (e) => setTitle(e.target.value), placeholder: asset.filename, "data-testid": "asset-edit-title" }) }), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-edit-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: translate('common.cancel') }), _jsx(Button, { size: "sm", onClick: () => void save(), disabled: busy, "data-testid": "asset-edit-save", children: translate('common.save') })] })] }) })] }));
|
|
141
|
-
}
|
|
142
|
-
/** Image preview from the stored serve URL; icon fallback for other types. */
|
|
143
|
-
function AssetThumbnail({ asset }) {
|
|
144
|
-
const isImage = asset.contentType.startsWith('image/');
|
|
145
|
-
// In-memory/opaque locators (memory://, convex://) are not fetchable by <img>.
|
|
146
|
-
const resolvable = asset.url?.startsWith('http') ?? false;
|
|
147
|
-
if (isImage && asset.url && resolvable) {
|
|
148
|
-
return (_jsx("img", { src: asset.url, alt: asset.alt ?? asset.filename, className: "aspect-video w-full bg-muted object-cover" }));
|
|
149
|
-
}
|
|
150
|
-
return (_jsx("span", { className: "grid aspect-video w-full place-items-center bg-muted text-muted-foreground", children: _jsx(ImageIcon, { className: "size-6" }) }));
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Reference-respecting delete (EC-151, EC-202): the dialog enumerates the exact
|
|
154
|
-
* inbound references — index-accurate (EC-155), with deep links to the field or
|
|
155
|
-
* node — using the shared EC-173 impact rows. Deleting a referenced asset is
|
|
156
|
-
* weak-by-default (allowed, with an explicit "delete anyway"): every reference
|
|
157
|
-
* dangles into a visible validation issue rather than silently breaking.
|
|
158
|
-
*/
|
|
159
|
-
function DeleteAssetButton({ asset }) {
|
|
160
|
-
const store = useAssets();
|
|
161
|
-
const [open, setOpen] = useState(false);
|
|
162
|
-
const [usages, setUsages] = useState(null);
|
|
163
|
-
const [error, setError] = useState(null);
|
|
164
|
-
const [busy, setBusy] = useState(false);
|
|
165
|
-
const openDialog = async () => {
|
|
166
|
-
setOpen(true);
|
|
167
|
-
setError(null);
|
|
168
|
-
setUsages(null);
|
|
169
|
-
try {
|
|
170
|
-
setUsages(await store.usagesOf(asset.id));
|
|
171
|
-
}
|
|
172
|
-
catch (cause) {
|
|
173
|
-
setError(cause instanceof Error ? cause.message : String(cause));
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
const confirm = async () => {
|
|
177
|
-
setBusy(true);
|
|
178
|
-
setError(null);
|
|
179
|
-
// The dialog has already enumerated usages (or shown there are none) and the user
|
|
180
|
-
// is confirming deletion either way — so confirm unconditionally, letting
|
|
181
|
-
// deleteAsset skip its redundant inbound re-check (EC-301 perf). Weak-by-default:
|
|
182
|
-
// a still-referenced asset deletes anyway, dangling into a visible issue.
|
|
183
|
-
const result = await store.remove(asset.id, { confirmInUse: true });
|
|
184
|
-
setBusy(false);
|
|
185
|
-
if (result.outcome === 'deleted' || result.outcome === 'not-found') {
|
|
186
|
-
setOpen(false);
|
|
187
|
-
}
|
|
188
|
-
else if (result.outcome === 'blocked') {
|
|
189
|
-
// A reference appeared between the pre-flight read and confirm (rare race).
|
|
190
|
-
setError(translate(result.usageCount === 1 ? 'assets.delete.raceError.one' : 'assets.delete.raceError.other', { count: result.usageCount }));
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
setError(result.message);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
const impact = usages ? buildAssetReferenceImpact(usages) : null;
|
|
197
|
-
const usageCount = impact?.broken.length ?? 0;
|
|
198
|
-
return (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": `Delete ${asset.filename}`, onClick: () => void openDialog(), "data-testid": "asset-delete", children: _jsx(Trash2, {}) }), _jsx(Dialog, { open: open, onOpenChange: setOpen, children: _jsxs(DialogContent, { className: "max-h-[80vh] max-w-md overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: translate('assets.delete.title', { filename: asset.filename }) }), _jsx(DialogDescription, { children: translate('assets.delete.description') })] }), usages === null && error === null ? (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.delete.checking') })) : usageCount > 0 ? (_jsxs(Stack, { gap: "2", "data-testid": "asset-usage-warning", children: [_jsxs(Row, { gap: "2", children: [_jsx(AlertTriangle, { className: "size-4 text-warning" }), _jsx(Text, { size: "sm", weight: "semibold", children: translate(usageCount === 1 ? 'assets.delete.inUse.one' : 'assets.delete.inUse.other', {
|
|
199
|
-
count: usageCount,
|
|
200
|
-
}) })] }), _jsx(ReferenceImpactRows, { projectId: store.projectId, broken: impact.broken, onNavigate: () => setOpen(false) }), _jsx(Text, { size: "xs", tone: "muted", children: translate('assets.delete.brokenWarning') })] })) : (_jsx(Text, { size: "sm", tone: "muted", children: translate('assets.delete.noReferences') })), error ? (_jsx(Text, { size: "sm", className: "text-destructive", "data-testid": "asset-delete-error", children: error })) : null, _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: () => setOpen(false), disabled: busy, children: translate('common.cancel') }), _jsx(Button, { variant: "destructive", size: "sm", onClick: () => void confirm(), disabled: busy || usages === null, "data-testid": "asset-delete-confirm", children: usageCount > 0 ? translate('assets.delete.confirmAnyway') : translate('common.delete') })] })] }) })] }));
|
|
201
|
-
}
|
|
202
|
-
//# sourceMappingURL=asset-library.js.map
|
|
@@ -1 +0,0 @@
|
|
|
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 { translate } from '@elytracms/studio/lib/i18n'\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\">{translate('assets.error.loadFailed')}</Text>\n </Row>\n <Text size=\"sm\" tone=\"muted\">\n {store.errorMessage ?? translate('common.backendUnreachable')}\n </Text>\n <Row>\n <Button variant=\"outline\" size=\"sm\" onClick={() => void store.refresh()}>\n <RotateCcw /> {translate('common.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 {translate('assets.title')}\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 /> {translate('common.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 {translate('assets.uploads.heading')}\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 {translate('assets.loading')}\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={translate('assets.searchPlaceholder')}\n aria-label={translate('assets.searchLabel')}\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 {translate('assets.dropHint')}\n </Text>\n </Stack>\n )\n}\n\n/** Upload phase → the `assets.upload.phase.*` catalog key suffix (idle → queued). */\nconst PHASE_KEY: Record<UploadState['phase'], string> = {\n idle: 'queued',\n preparing: 'preparing',\n uploading: 'uploading',\n confirming: 'confirming',\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 {translate('assets.upload.phase.' + PHASE_KEY[state.phase])}\n {state.attempt > 1\n ? ' · ' + translate('assets.upload.attempt', { count: state.attempt })\n : ''}\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\n variant=\"ghost\"\n size=\"icon-sm\"\n onClick={onDismiss}\n aria-label={translate('assets.upload.dismissLabel')}\n >\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 {translate('assets.searchEmpty')}\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\n ? translate('assets.card.altPrefix', { alt: asset.alt })\n : translate('assets.card.noAlt')}\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={translate('assets.card.referencesLabel', { filename: 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>\n {translate('assets.references.title', { name: asset.title ?? asset.filename })}\n </DialogTitle>\n <DialogDescription>{translate('assets.references.description')}</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={translate('assets.card.editLabel', { filename: 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>{translate('assets.edit.title', { filename: asset.filename })}</DialogTitle>\n <DialogDescription>{translate('assets.edit.description')}</DialogDescription>\n </DialogHeader>\n <Field label={translate('assets.edit.altLabel')} 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={translate('assets.edit.altPlaceholder')}\n data-testid=\"asset-edit-alt\"\n />\n </Field>\n <Field label={translate('assets.edit.titleLabel')} 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 {translate('common.cancel')}\n </Button>\n <Button\n size=\"sm\"\n onClick={() => void save()}\n disabled={busy}\n data-testid=\"asset-edit-save\"\n >\n {translate('common.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(\n translate(\n result.usageCount === 1 ? 'assets.delete.raceError.one' : 'assets.delete.raceError.other',\n { count: result.usageCount },\n ),\n )\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>{translate('assets.delete.title', { filename: asset.filename })}</DialogTitle>\n <DialogDescription>{translate('assets.delete.description')}</DialogDescription>\n </DialogHeader>\n {usages === null && error === null ? (\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.delete.checking')}\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 {translate(usageCount === 1 ? 'assets.delete.inUse.one' : 'assets.delete.inUse.other', {\n count: usageCount,\n })}\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 {translate('assets.delete.brokenWarning')}\n </Text>\n </Stack>\n ) : (\n <Text size=\"sm\" tone=\"muted\">\n {translate('assets.delete.noReferences')}\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 {translate('common.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 ? translate('assets.delete.confirmAnyway') : translate('common.delete')}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </>\n )\n}\n"]}
|