@agent-native/core 0.80.10 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +53 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
- package/corpus/core/src/client/chat-view-transition.ts +33 -2
- package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
- package/corpus/core/src/client/sse-event-processor.ts +9 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
- package/corpus/core/src/db/client.ts +29 -2
- package/corpus/core/src/provider-api/index.ts +29 -0
- package/corpus/core/src/server/core-routes-plugin.ts +58 -20
- package/corpus/core/src/styles/agent-native.css +54 -0
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
- package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
- package/corpus/templates/analytics/app/global.css +8 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
- package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
- package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
- package/corpus/templates/assets/AGENTS.md +4 -0
- package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
- package/corpus/templates/assets/actions/generate-image.ts +118 -67
- package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
- package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
- package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
- package/corpus/templates/assets/app/global.css +15 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/assets/app/i18n-data.ts +61 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +233 -52
- package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +135 -11
- package/corpus/templates/assets/shared/api.ts +4 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
- package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
- package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
- package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
- package/corpus/templates/design/AGENTS.md +27 -0
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +76 -25
- package/corpus/templates/design/actions/apply-design-state.ts +17 -4
- package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
- package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
- package/corpus/templates/design/actions/capture-design-state.ts +27 -4
- package/corpus/templates/design/actions/connect-localhost.ts +59 -22
- package/corpus/templates/design/actions/delete-design.ts +20 -0
- package/corpus/templates/design/actions/export-zip.ts +23 -8
- package/corpus/templates/design/actions/generate-design.ts +53 -1
- package/corpus/templates/design/actions/get-component-details.ts +21 -26
- package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +18 -5
- package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
- package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
- package/corpus/templates/design/actions/list-design-states.ts +19 -1
- package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
- package/corpus/templates/design/actions/navigate.ts +18 -3
- package/corpus/templates/design/actions/open-component-source.ts +7 -2
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
- package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
- package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
- package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
- package/corpus/templates/design/actions/provider-api-request.ts +3 -3
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
- package/corpus/templates/design/actions/run-design-audit.ts +74 -10
- package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
- package/corpus/templates/design/actions/view-screen.ts +1 -1
- package/corpus/templates/design/actions/write-local-file.ts +213 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +244 -3197
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
- package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
- package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
- package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
- package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
- package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
- package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/global.css +7 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
- package/corpus/templates/design/app/i18n-data.ts +369 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
- package/corpus/templates/design/app/pages/Index.tsx +5 -1
- package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +94 -2
- package/corpus/templates/design/e2e/helpers.ts +168 -65
- package/corpus/templates/design/package.json +3 -1
- package/corpus/templates/design/server/db/schema.ts +23 -0
- package/corpus/templates/design/server/handlers/uploads.ts +22 -1
- package/corpus/templates/design/server/lib/provider-api.ts +7 -4
- package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
- package/corpus/templates/design/server/plugins/db.ts +39 -0
- package/corpus/templates/design/server/register-secrets.ts +38 -0
- package/corpus/templates/design/shared/board-file.ts +228 -0
- package/corpus/templates/design/shared/board-objects.ts +288 -0
- package/corpus/templates/design/shared/code-layer.ts +52 -0
- package/corpus/templates/design/shared/component-model.ts +35 -0
- package/corpus/templates/design/shared/motion-compiler.ts +137 -15
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- package/corpus/templates/design/tsconfig.json +2 -1
- package/corpus/templates/slides/AGENTS.md +9 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
- package/corpus/templates/slides/app/vite-env.d.ts +18 -0
- package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/vite.config.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +94 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +25 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +357 -18
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -0
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +5 -5
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat-view-transition.d.ts +6 -0
- package/dist/client/chat-view-transition.d.ts.map +1 -1
- package/dist/client/chat-view-transition.js +23 -2
- package/dist/client/chat-view-transition.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +3 -2
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +11 -4
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -3
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +20 -6
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +28 -2
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/index.d.ts +5 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +28 -0
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +43 -18
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +54 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Matches the motion artboard at
|
|
6
6
|
* https://plan.agent-native.com/plans/plan-88dc4a09fb0c46bc:
|
|
7
|
-
* - Full-width
|
|
7
|
+
* - Full-width dock beneath the canvas when opened from the Layers footer.
|
|
8
8
|
* - Left sidebar: animated layer rows with property sub-rows.
|
|
9
9
|
* - Center: time ruler + diamond keyframes on a track grid.
|
|
10
10
|
* - Playhead: draggable; scrubbing sends a preview-only `motion-preview`
|
|
11
11
|
* postMessage to the canvas iframe — NEVER writes to DB.
|
|
12
|
-
* - Top toolbar: play/pause, duration input, auto-keyframe toggle,
|
|
12
|
+
* - Top toolbar: play/pause, duration input, auto-keyframe toggle, autosave.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
* `apply-motion-edit
|
|
14
|
+
* Track and duration edits notify the parent; the parent persists through
|
|
15
|
+
* `apply-motion-edit`. Scrubbing/playback stays preview-only.
|
|
16
16
|
*
|
|
17
17
|
* All times are normalised to [0, 1] internally; the ruler maps them to px.
|
|
18
18
|
*/
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
IconChevronRight,
|
|
38
38
|
IconPlus,
|
|
39
39
|
IconTrash,
|
|
40
|
-
IconCode,
|
|
41
40
|
IconRefresh,
|
|
42
41
|
IconBolt,
|
|
43
42
|
IconLayersSubtract,
|
|
@@ -108,17 +107,12 @@ export interface MotionDockProps {
|
|
|
108
107
|
onTracksChange?: (tracks: MotionDockTrack[]) => void;
|
|
109
108
|
/** Called when durationMs is edited. */
|
|
110
109
|
onDurationChange?: (ms: number) => void;
|
|
111
|
-
/**
|
|
112
|
-
* Called when "Write to CSS" is clicked. The parent should call
|
|
113
|
-
* `apply-motion-edit` via useActionMutation.
|
|
114
|
-
*/
|
|
115
|
-
onApply?: (tracks: MotionDockTrack[], durationMs: number) => void;
|
|
116
110
|
/**
|
|
117
111
|
* Reference to the canvas iframe element. Used to send preview postMessages.
|
|
118
112
|
* If not provided, preview messages are skipped (no crash).
|
|
119
113
|
*/
|
|
120
114
|
canvasIframeRef?: React.RefObject<HTMLIFrameElement | null>;
|
|
121
|
-
/** Whether the parent
|
|
115
|
+
/** Whether the parent autosave mutation is in flight. */
|
|
122
116
|
applying?: boolean;
|
|
123
117
|
/**
|
|
124
118
|
* The currently-selected canvas element, if any. Required to create the FIRST
|
|
@@ -139,7 +133,6 @@ export function MotionDock({
|
|
|
139
133
|
onOpenChange,
|
|
140
134
|
onTracksChange,
|
|
141
135
|
onDurationChange,
|
|
142
|
-
onApply,
|
|
143
136
|
canvasIframeRef,
|
|
144
137
|
applying = false,
|
|
145
138
|
selectedTarget = null,
|
|
@@ -174,6 +167,14 @@ export function MotionDock({
|
|
|
174
167
|
() => new Set(tracks.map((t) => t.targetNodeId)),
|
|
175
168
|
);
|
|
176
169
|
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
setExpandedNodeIds((current) => {
|
|
172
|
+
const next = new Set(current);
|
|
173
|
+
for (const track of tracks) next.add(track.targetNodeId);
|
|
174
|
+
return next.size === current.size ? current : next;
|
|
175
|
+
});
|
|
176
|
+
}, [tracks]);
|
|
177
|
+
|
|
177
178
|
// Ruler / track area ref for pointer math.
|
|
178
179
|
const trackAreaRef = useRef<HTMLDivElement>(null);
|
|
179
180
|
|
|
@@ -332,8 +333,8 @@ export function MotionDock({
|
|
|
332
333
|
// ── Create a brand-new track (the "first track" path) ──────────────────────
|
|
333
334
|
// This is the entry point that turns the dock from a dead end into a working
|
|
334
335
|
// editor: with an element selected and no track yet, the user picks a property
|
|
335
|
-
// preset and we seed a two-keyframe track.
|
|
336
|
-
//
|
|
336
|
+
// preset and we seed a two-keyframe track. The parent autosaves that valid
|
|
337
|
+
// track into managed CSS. Idempotent per (nodeId, property) — picking the same
|
|
337
338
|
// property twice just re-expands the existing track instead of duplicating.
|
|
338
339
|
const createTrack = useCallback(
|
|
339
340
|
(preset: MotionPropertyPreset) => {
|
|
@@ -403,298 +404,275 @@ export function MotionDock({
|
|
|
403
404
|
// ── Render ────────────────────────────────────────────────────────────────
|
|
404
405
|
return (
|
|
405
406
|
<div
|
|
407
|
+
aria-label="Motion dock"
|
|
406
408
|
className={cn(
|
|
407
|
-
"flex flex-col
|
|
408
|
-
isOpen
|
|
409
|
+
"flex flex-col overflow-hidden border-t bg-background transition-[height,opacity,transform,border-color] duration-200 ease-out select-none will-change-transform",
|
|
410
|
+
isOpen
|
|
411
|
+
? "translate-y-0 border-border opacity-100"
|
|
412
|
+
: "translate-y-full border-transparent opacity-0 pointer-events-none",
|
|
409
413
|
)}
|
|
410
|
-
style={
|
|
414
|
+
style={{ height: isOpen ? dockHeight : 0 }}
|
|
411
415
|
>
|
|
412
416
|
{/* Resize handle */}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
/>
|
|
420
|
-
)}
|
|
417
|
+
<div
|
|
418
|
+
className="absolute -top-1 left-0 right-0 h-2 cursor-ns-resize z-10"
|
|
419
|
+
onPointerDown={handleResizePointerDown}
|
|
420
|
+
onPointerMove={handleResizePointerMove}
|
|
421
|
+
onPointerUp={handleResizePointerUp}
|
|
422
|
+
/>
|
|
421
423
|
|
|
422
424
|
{/* Dock toolbar */}
|
|
423
425
|
<div className="flex h-8 shrink-0 items-center gap-1 border-b border-border px-2">
|
|
424
|
-
{/* Collapse toggle */}
|
|
425
|
-
<
|
|
426
|
+
{/* Collapse toggle. The label and nearby space are part of the target. */}
|
|
427
|
+
<button
|
|
426
428
|
type="button"
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
onClick={() => setOpen(!isOpen)}
|
|
431
|
-
aria-label={isOpen ? "Collapse motion dock" : "Expand motion dock"}
|
|
429
|
+
className="-ml-1 flex h-7 shrink-0 cursor-pointer items-center gap-1.5 rounded-md px-1.5 text-[11px] font-medium uppercase tracking-wide text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]"
|
|
430
|
+
onClick={() => setOpen(false)}
|
|
431
|
+
aria-label="Collapse motion dock"
|
|
432
432
|
>
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
433
|
+
<IconChevronDown className="size-3.5" />
|
|
434
|
+
<span>Motion</span>
|
|
435
|
+
</button>
|
|
436
|
+
|
|
437
|
+
<>
|
|
438
|
+
{/* Play / Pause */}
|
|
439
|
+
<Tooltip>
|
|
440
|
+
<TooltipTrigger asChild>
|
|
441
|
+
<Button
|
|
442
|
+
type="button"
|
|
443
|
+
variant="ghost"
|
|
444
|
+
size="icon"
|
|
445
|
+
className="size-6 shrink-0"
|
|
446
|
+
onClick={playing ? stopPlayback : startPlayback}
|
|
447
|
+
aria-label={playing ? "Pause" : "Play"}
|
|
448
|
+
>
|
|
449
|
+
{playing ? (
|
|
450
|
+
<IconPlayerPause className="size-3.5" />
|
|
451
|
+
) : (
|
|
452
|
+
<IconPlayerPlay className="size-3.5" />
|
|
453
|
+
)}
|
|
454
|
+
</Button>
|
|
455
|
+
</TooltipTrigger>
|
|
456
|
+
<TooltipContent side="top">
|
|
457
|
+
{playing ? "Pause" : "Play"}
|
|
458
|
+
</TooltipContent>
|
|
459
|
+
</Tooltip>
|
|
460
|
+
|
|
461
|
+
{/* Stop / reset */}
|
|
462
|
+
<Tooltip>
|
|
463
|
+
<TooltipTrigger asChild>
|
|
464
|
+
<Button
|
|
465
|
+
type="button"
|
|
466
|
+
variant="ghost"
|
|
467
|
+
size="icon"
|
|
468
|
+
className="size-6 shrink-0"
|
|
469
|
+
onClick={() => {
|
|
470
|
+
stopPlayback();
|
|
471
|
+
setPlayhead(0);
|
|
472
|
+
sendPreview(0);
|
|
473
|
+
}}
|
|
474
|
+
aria-label="Reset playhead"
|
|
475
|
+
>
|
|
476
|
+
<IconPlayerStop className="size-3.5" />
|
|
477
|
+
</Button>
|
|
478
|
+
</TooltipTrigger>
|
|
479
|
+
<TooltipContent side="top">Reset</TooltipContent>
|
|
480
|
+
</Tooltip>
|
|
481
|
+
|
|
482
|
+
{/* Duration */}
|
|
483
|
+
<div className="flex items-center gap-1 ml-1">
|
|
484
|
+
<span className="text-[10px] text-muted-foreground">Duration</span>
|
|
485
|
+
<Input
|
|
486
|
+
type="number"
|
|
487
|
+
min={50}
|
|
488
|
+
step={50}
|
|
489
|
+
value={durationInput}
|
|
490
|
+
onChange={(e) => setDurationInput(e.target.value)}
|
|
491
|
+
onBlur={() => {
|
|
492
|
+
const ms = parseInt(durationInput, 10);
|
|
493
|
+
if (!isNaN(ms) && ms >= 50) {
|
|
494
|
+
onDurationChange?.(ms);
|
|
495
|
+
} else {
|
|
496
|
+
setDurationInput(String(durationMs));
|
|
497
|
+
}
|
|
498
|
+
}}
|
|
499
|
+
className="h-5 w-16 px-1 text-[11px]"
|
|
500
|
+
aria-label="Duration in ms"
|
|
501
|
+
/>
|
|
502
|
+
<span className="text-[10px] text-muted-foreground">ms</span>
|
|
503
|
+
</div>
|
|
439
504
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
505
|
+
{/* Add track — the "create first track" entry point. */}
|
|
506
|
+
<div className="ml-2">
|
|
507
|
+
<AddTrackMenu
|
|
508
|
+
selectedTarget={selectedTarget}
|
|
509
|
+
onCreateTrack={createTrack}
|
|
510
|
+
/>
|
|
511
|
+
</div>
|
|
443
512
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
{/* Play / Pause */}
|
|
513
|
+
<div className="ml-auto flex items-center gap-1">
|
|
514
|
+
{/* Auto-keyframe toggle */}
|
|
447
515
|
<Tooltip>
|
|
448
516
|
<TooltipTrigger asChild>
|
|
449
517
|
<Button
|
|
450
518
|
type="button"
|
|
451
|
-
variant="ghost"
|
|
519
|
+
variant={autoKeyframe ? "secondary" : "ghost"}
|
|
452
520
|
size="icon"
|
|
453
|
-
className=
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
>
|
|
457
|
-
{playing ? (
|
|
458
|
-
<IconPlayerPause className="size-3.5" />
|
|
459
|
-
) : (
|
|
460
|
-
<IconPlayerPlay className="size-3.5" />
|
|
521
|
+
className={cn(
|
|
522
|
+
"size-6 shrink-0",
|
|
523
|
+
autoKeyframe && "text-primary",
|
|
461
524
|
)}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
{playing ? "Pause" : "Play"}
|
|
466
|
-
</TooltipContent>
|
|
467
|
-
</Tooltip>
|
|
468
|
-
|
|
469
|
-
{/* Stop / reset */}
|
|
470
|
-
<Tooltip>
|
|
471
|
-
<TooltipTrigger asChild>
|
|
472
|
-
<Button
|
|
473
|
-
type="button"
|
|
474
|
-
variant="ghost"
|
|
475
|
-
size="icon"
|
|
476
|
-
className="size-6 shrink-0"
|
|
477
|
-
onClick={() => {
|
|
478
|
-
stopPlayback();
|
|
479
|
-
setPlayhead(0);
|
|
480
|
-
sendPreview(0);
|
|
481
|
-
}}
|
|
482
|
-
aria-label="Reset playhead"
|
|
525
|
+
onClick={() => setAutoKeyframe((v) => !v)}
|
|
526
|
+
aria-label="Toggle auto-keyframe"
|
|
527
|
+
aria-pressed={autoKeyframe}
|
|
483
528
|
>
|
|
484
|
-
<
|
|
529
|
+
<IconBolt className="size-3.5" />
|
|
485
530
|
</Button>
|
|
486
531
|
</TooltipTrigger>
|
|
487
|
-
<TooltipContent side="top">
|
|
532
|
+
<TooltipContent side="top">Auto-keyframe</TooltipContent>
|
|
488
533
|
</Tooltip>
|
|
489
534
|
|
|
490
|
-
{
|
|
491
|
-
<div className="flex items-center gap-1 ml-1">
|
|
492
|
-
<span className="text-[10px] text-muted-foreground">
|
|
493
|
-
Duration
|
|
494
|
-
</span>
|
|
495
|
-
<Input
|
|
496
|
-
type="number"
|
|
497
|
-
min={50}
|
|
498
|
-
step={50}
|
|
499
|
-
value={durationInput}
|
|
500
|
-
onChange={(e) => setDurationInput(e.target.value)}
|
|
501
|
-
onBlur={() => {
|
|
502
|
-
const ms = parseInt(durationInput, 10);
|
|
503
|
-
if (!isNaN(ms) && ms >= 50) {
|
|
504
|
-
onDurationChange?.(ms);
|
|
505
|
-
} else {
|
|
506
|
-
setDurationInput(String(durationMs));
|
|
507
|
-
}
|
|
508
|
-
}}
|
|
509
|
-
className="h-5 w-16 px-1 text-[11px]"
|
|
510
|
-
aria-label="Duration in ms"
|
|
511
|
-
/>
|
|
512
|
-
<span className="text-[10px] text-muted-foreground">ms</span>
|
|
513
|
-
</div>
|
|
514
|
-
|
|
515
|
-
{/* Add track — the "create first track" entry point. */}
|
|
516
|
-
<div className="ml-2">
|
|
517
|
-
<AddTrackMenu
|
|
518
|
-
selectedTarget={selectedTarget}
|
|
519
|
-
onCreateTrack={createTrack}
|
|
520
|
-
/>
|
|
521
|
-
</div>
|
|
522
|
-
|
|
523
|
-
<div className="ml-auto flex items-center gap-1">
|
|
524
|
-
{/* Auto-keyframe toggle */}
|
|
525
|
-
<Tooltip>
|
|
526
|
-
<TooltipTrigger asChild>
|
|
527
|
-
<Button
|
|
528
|
-
type="button"
|
|
529
|
-
variant={autoKeyframe ? "secondary" : "ghost"}
|
|
530
|
-
size="icon"
|
|
531
|
-
className={cn(
|
|
532
|
-
"size-6 shrink-0",
|
|
533
|
-
autoKeyframe && "text-primary",
|
|
534
|
-
)}
|
|
535
|
-
onClick={() => setAutoKeyframe((v) => !v)}
|
|
536
|
-
aria-label="Toggle auto-keyframe"
|
|
537
|
-
aria-pressed={autoKeyframe}
|
|
538
|
-
>
|
|
539
|
-
<IconBolt className="size-3.5" />
|
|
540
|
-
</Button>
|
|
541
|
-
</TooltipTrigger>
|
|
542
|
-
<TooltipContent side="top">Auto-keyframe</TooltipContent>
|
|
543
|
-
</Tooltip>
|
|
544
|
-
|
|
545
|
-
{/* Write to CSS */}
|
|
535
|
+
{applying ? (
|
|
546
536
|
<Tooltip>
|
|
547
537
|
<TooltipTrigger asChild>
|
|
548
|
-
<
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
className="
|
|
552
|
-
disabled={applying || tracks.length === 0}
|
|
553
|
-
onClick={() => onApply?.(tracks, durationMs)}
|
|
538
|
+
<span
|
|
539
|
+
role="status"
|
|
540
|
+
aria-label="Saving motion"
|
|
541
|
+
className="flex size-6 items-center justify-center rounded text-muted-foreground"
|
|
554
542
|
>
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
) : (
|
|
558
|
-
<IconCode className="size-3" />
|
|
559
|
-
)}
|
|
560
|
-
Write to CSS
|
|
561
|
-
</Button>
|
|
543
|
+
<IconRefresh className="size-3 animate-spin" />
|
|
544
|
+
</span>
|
|
562
545
|
</TooltipTrigger>
|
|
563
|
-
<TooltipContent side="top">
|
|
564
|
-
Compile timeline → managed <style
|
|
565
|
-
data-agent-native-motion> block
|
|
566
|
-
</TooltipContent>
|
|
546
|
+
<TooltipContent side="top">Saving motion</TooltipContent>
|
|
567
547
|
</Tooltip>
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
548
|
+
) : null}
|
|
549
|
+
</div>
|
|
550
|
+
</>
|
|
571
551
|
</div>
|
|
572
552
|
|
|
573
553
|
{/* Dock body */}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
554
|
+
<div className="flex flex-1 overflow-hidden">
|
|
555
|
+
{/* Layer sidebar */}
|
|
556
|
+
<div
|
|
557
|
+
className="flex flex-col shrink-0 border-r border-border overflow-y-auto"
|
|
558
|
+
style={{ width: LAYER_SIDEBAR_WIDTH }}
|
|
559
|
+
>
|
|
560
|
+
{/* Ruler spacer */}
|
|
577
561
|
<div
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
{/* Ruler spacer */}
|
|
582
|
-
<div
|
|
583
|
-
style={{ height: RULER_HEIGHT }}
|
|
584
|
-
className="border-b border-border"
|
|
585
|
-
/>
|
|
562
|
+
style={{ height: RULER_HEIGHT }}
|
|
563
|
+
className="border-b border-border"
|
|
564
|
+
/>
|
|
586
565
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
<p className="text-[11px] text-muted-foreground/70 leading-snug">
|
|
593
|
-
Animate{" "}
|
|
594
|
-
<span className="font-medium text-foreground/80">
|
|
595
|
-
{selectedTarget.label}
|
|
596
|
-
</span>
|
|
597
|
-
. Pick a property to add the first track.
|
|
598
|
-
</p>
|
|
599
|
-
<AddTrackMenu
|
|
600
|
-
selectedTarget={selectedTarget}
|
|
601
|
-
onCreateTrack={createTrack}
|
|
602
|
-
variant="cta"
|
|
603
|
-
/>
|
|
604
|
-
</>
|
|
605
|
-
) : (
|
|
566
|
+
{layers.length === 0 ? (
|
|
567
|
+
<div className="flex flex-col items-center justify-center flex-1 gap-3 text-center px-4 py-6">
|
|
568
|
+
<IconLayersSubtract className="size-5 text-muted-foreground/40" />
|
|
569
|
+
{selectedTarget ? (
|
|
570
|
+
<>
|
|
606
571
|
<p className="text-[11px] text-muted-foreground/70 leading-snug">
|
|
607
|
-
|
|
608
|
-
|
|
572
|
+
Animate{" "}
|
|
573
|
+
<span className="font-medium text-foreground/80">
|
|
574
|
+
{selectedTarget.label}
|
|
575
|
+
</span>
|
|
576
|
+
. Pick a property to add the first track.
|
|
609
577
|
</p>
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
578
|
+
<AddTrackMenu
|
|
579
|
+
selectedTarget={selectedTarget}
|
|
580
|
+
onCreateTrack={createTrack}
|
|
581
|
+
variant="cta"
|
|
582
|
+
/>
|
|
583
|
+
</>
|
|
584
|
+
) : (
|
|
585
|
+
<p className="text-[11px] text-muted-foreground/70 leading-snug">
|
|
586
|
+
Select an element on the canvas, then add a track to animate
|
|
587
|
+
it.
|
|
588
|
+
</p>
|
|
589
|
+
)}
|
|
590
|
+
</div>
|
|
591
|
+
) : (
|
|
592
|
+
layers.map((layer) => (
|
|
593
|
+
<LayerGroup
|
|
594
|
+
key={layer.nodeId}
|
|
595
|
+
layer={layer}
|
|
596
|
+
expanded={expandedNodeIds.has(layer.nodeId)}
|
|
597
|
+
onToggleExpand={() =>
|
|
598
|
+
setExpandedNodeIds((prev) => {
|
|
599
|
+
const next = new Set(prev);
|
|
600
|
+
if (next.has(layer.nodeId)) next.delete(layer.nodeId);
|
|
601
|
+
else next.add(layer.nodeId);
|
|
602
|
+
return next;
|
|
603
|
+
})
|
|
604
|
+
}
|
|
605
|
+
onAddKeyframe={(track) => addKeyframe(track)}
|
|
606
|
+
/>
|
|
607
|
+
))
|
|
608
|
+
)}
|
|
609
|
+
</div>
|
|
631
610
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
611
|
+
{/* Timeline / track area */}
|
|
612
|
+
<div className="flex flex-col flex-1 overflow-hidden">
|
|
613
|
+
{/* Ruler + playhead drag */}
|
|
614
|
+
<div
|
|
615
|
+
ref={trackAreaRef}
|
|
616
|
+
className="relative shrink-0 border-b border-border cursor-col-resize"
|
|
617
|
+
style={{ height: RULER_HEIGHT }}
|
|
618
|
+
onPointerDown={handleRulerPointerDown}
|
|
619
|
+
onPointerMove={handleRulerPointerMove}
|
|
620
|
+
onPointerUp={handleRulerPointerUp}
|
|
621
|
+
>
|
|
622
|
+
{/* Tick marks */}
|
|
623
|
+
{rulerTicks().map(({ t, label }) => (
|
|
624
|
+
<div
|
|
625
|
+
key={t}
|
|
626
|
+
className="absolute top-0 flex flex-col items-center pointer-events-none"
|
|
627
|
+
style={{ left: `${t * 100}%`, transform: "translateX(-50%)" }}
|
|
628
|
+
>
|
|
629
|
+
<span className="text-[9px] text-muted-foreground/60 leading-none mt-1">
|
|
630
|
+
{label}
|
|
631
|
+
</span>
|
|
632
|
+
<div className="w-px h-2 bg-border mt-0.5" />
|
|
633
|
+
</div>
|
|
634
|
+
))}
|
|
656
635
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
636
|
+
{/* Playhead */}
|
|
637
|
+
<PlayheadLine t={playhead} height={RULER_HEIGHT} inRuler />
|
|
638
|
+
</div>
|
|
660
639
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
/>
|
|
679
|
-
))}
|
|
680
|
-
|
|
681
|
-
{/* Playhead across track rows */}
|
|
682
|
-
<PlayheadLine
|
|
683
|
-
t={playhead}
|
|
684
|
-
height={layers.reduce(
|
|
685
|
-
(sum, layer) =>
|
|
686
|
-
sum +
|
|
687
|
-
ROW_HEIGHT +
|
|
688
|
-
(expandedNodeIds.has(layer.nodeId)
|
|
689
|
-
? layer.tracks.length * ROW_HEIGHT
|
|
690
|
-
: 0),
|
|
691
|
-
0,
|
|
692
|
-
)}
|
|
640
|
+
{/* Track rows with keyframe diamonds */}
|
|
641
|
+
<div className="relative flex-1 overflow-y-auto">
|
|
642
|
+
{layers.map((layer) => (
|
|
643
|
+
<LayerTrackRows
|
|
644
|
+
key={layer.nodeId}
|
|
645
|
+
layer={layer}
|
|
646
|
+
expanded={expandedNodeIds.has(layer.nodeId)}
|
|
647
|
+
playhead={playhead}
|
|
648
|
+
onDeleteKeyframe={deleteKeyframe}
|
|
649
|
+
onMoveKeyframe={(track, kf, newT) => {
|
|
650
|
+
updateTrack(track.targetNodeId, track.property, (tr) => ({
|
|
651
|
+
...tr,
|
|
652
|
+
keyframes: tr.keyframes.map((k) =>
|
|
653
|
+
k === kf ? { ...k, t: newT } : k,
|
|
654
|
+
),
|
|
655
|
+
}));
|
|
656
|
+
}}
|
|
693
657
|
/>
|
|
694
|
-
|
|
658
|
+
))}
|
|
659
|
+
|
|
660
|
+
{/* Playhead across track rows */}
|
|
661
|
+
<PlayheadLine
|
|
662
|
+
t={playhead}
|
|
663
|
+
height={layers.reduce(
|
|
664
|
+
(sum, layer) =>
|
|
665
|
+
sum +
|
|
666
|
+
ROW_HEIGHT +
|
|
667
|
+
(expandedNodeIds.has(layer.nodeId)
|
|
668
|
+
? layer.tracks.length * ROW_HEIGHT
|
|
669
|
+
: 0),
|
|
670
|
+
0,
|
|
671
|
+
)}
|
|
672
|
+
/>
|
|
695
673
|
</div>
|
|
696
674
|
</div>
|
|
697
|
-
|
|
675
|
+
</div>
|
|
698
676
|
</div>
|
|
699
677
|
);
|
|
700
678
|
}
|