@agent-native/core 0.80.11 → 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 +47 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/design-connect.ts +419 -19
- 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/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/actions/open-asset-picker.ts +9 -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/routes/library.tsx +90 -16
- package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
- 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 +63 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +67 -69
- 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/export-zip.ts +23 -8
- package/corpus/templates/design/actions/generate-design.ts +53 -1
- package/corpus/templates/design/actions/get-component-details.ts +5 -19
- 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 +10 -1
- 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-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/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 +53 -4
- 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 +230 -3308
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +776 -287
- package/corpus/templates/design/app/components/design/EditPanel.tsx +212 -208
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +14 -2
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +235 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +490 -55
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +43 -62
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +34 -61
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +251 -0
- 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 +21 -0
- package/corpus/templates/design/app/i18n-data.ts +358 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2402 -499
- 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-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/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/motion-compiler.ts +58 -7
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- 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/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/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/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 +2 -2
- 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/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,
|
|
@@ -340,8 +333,8 @@ export function MotionDock({
|
|
|
340
333
|
// ── Create a brand-new track (the "first track" path) ──────────────────────
|
|
341
334
|
// This is the entry point that turns the dock from a dead end into a working
|
|
342
335
|
// editor: with an element selected and no track yet, the user picks a property
|
|
343
|
-
// preset and we seed a two-keyframe track.
|
|
344
|
-
//
|
|
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
|
|
345
338
|
// property twice just re-expands the existing track instead of duplicating.
|
|
346
339
|
const createTrack = useCallback(
|
|
347
340
|
(preset: MotionPropertyPreset) => {
|
|
@@ -413,297 +406,273 @@ export function MotionDock({
|
|
|
413
406
|
<div
|
|
414
407
|
aria-label="Motion dock"
|
|
415
408
|
className={cn(
|
|
416
|
-
"flex flex-col
|
|
417
|
-
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",
|
|
418
413
|
)}
|
|
419
|
-
style={
|
|
414
|
+
style={{ height: isOpen ? dockHeight : 0 }}
|
|
420
415
|
>
|
|
421
416
|
{/* Resize handle */}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
/>
|
|
429
|
-
)}
|
|
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
|
+
/>
|
|
430
423
|
|
|
431
424
|
{/* Dock toolbar */}
|
|
432
425
|
<div className="flex h-8 shrink-0 items-center gap-1 border-b border-border px-2">
|
|
433
|
-
{/* Collapse toggle */}
|
|
434
|
-
<
|
|
426
|
+
{/* Collapse toggle. The label and nearby space are part of the target. */}
|
|
427
|
+
<button
|
|
435
428
|
type="button"
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
onClick={() => setOpen(!isOpen)}
|
|
440
|
-
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"
|
|
441
432
|
>
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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>
|
|
448
504
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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>
|
|
452
512
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
{/* Play / Pause */}
|
|
513
|
+
<div className="ml-auto flex items-center gap-1">
|
|
514
|
+
{/* Auto-keyframe toggle */}
|
|
456
515
|
<Tooltip>
|
|
457
516
|
<TooltipTrigger asChild>
|
|
458
517
|
<Button
|
|
459
518
|
type="button"
|
|
460
|
-
variant="ghost"
|
|
519
|
+
variant={autoKeyframe ? "secondary" : "ghost"}
|
|
461
520
|
size="icon"
|
|
462
|
-
className=
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
>
|
|
466
|
-
{playing ? (
|
|
467
|
-
<IconPlayerPause className="size-3.5" />
|
|
468
|
-
) : (
|
|
469
|
-
<IconPlayerPlay className="size-3.5" />
|
|
521
|
+
className={cn(
|
|
522
|
+
"size-6 shrink-0",
|
|
523
|
+
autoKeyframe && "text-primary",
|
|
470
524
|
)}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
{playing ? "Pause" : "Play"}
|
|
475
|
-
</TooltipContent>
|
|
476
|
-
</Tooltip>
|
|
477
|
-
|
|
478
|
-
{/* Stop / reset */}
|
|
479
|
-
<Tooltip>
|
|
480
|
-
<TooltipTrigger asChild>
|
|
481
|
-
<Button
|
|
482
|
-
type="button"
|
|
483
|
-
variant="ghost"
|
|
484
|
-
size="icon"
|
|
485
|
-
className="size-6 shrink-0"
|
|
486
|
-
onClick={() => {
|
|
487
|
-
stopPlayback();
|
|
488
|
-
setPlayhead(0);
|
|
489
|
-
sendPreview(0);
|
|
490
|
-
}}
|
|
491
|
-
aria-label="Reset playhead"
|
|
525
|
+
onClick={() => setAutoKeyframe((v) => !v)}
|
|
526
|
+
aria-label="Toggle auto-keyframe"
|
|
527
|
+
aria-pressed={autoKeyframe}
|
|
492
528
|
>
|
|
493
|
-
<
|
|
529
|
+
<IconBolt className="size-3.5" />
|
|
494
530
|
</Button>
|
|
495
531
|
</TooltipTrigger>
|
|
496
|
-
<TooltipContent side="top">
|
|
532
|
+
<TooltipContent side="top">Auto-keyframe</TooltipContent>
|
|
497
533
|
</Tooltip>
|
|
498
534
|
|
|
499
|
-
{
|
|
500
|
-
<div className="flex items-center gap-1 ml-1">
|
|
501
|
-
<span className="text-[10px] text-muted-foreground">
|
|
502
|
-
Duration
|
|
503
|
-
</span>
|
|
504
|
-
<Input
|
|
505
|
-
type="number"
|
|
506
|
-
min={50}
|
|
507
|
-
step={50}
|
|
508
|
-
value={durationInput}
|
|
509
|
-
onChange={(e) => setDurationInput(e.target.value)}
|
|
510
|
-
onBlur={() => {
|
|
511
|
-
const ms = parseInt(durationInput, 10);
|
|
512
|
-
if (!isNaN(ms) && ms >= 50) {
|
|
513
|
-
onDurationChange?.(ms);
|
|
514
|
-
} else {
|
|
515
|
-
setDurationInput(String(durationMs));
|
|
516
|
-
}
|
|
517
|
-
}}
|
|
518
|
-
className="h-5 w-16 px-1 text-[11px]"
|
|
519
|
-
aria-label="Duration in ms"
|
|
520
|
-
/>
|
|
521
|
-
<span className="text-[10px] text-muted-foreground">ms</span>
|
|
522
|
-
</div>
|
|
523
|
-
|
|
524
|
-
{/* Add track — the "create first track" entry point. */}
|
|
525
|
-
<div className="ml-2">
|
|
526
|
-
<AddTrackMenu
|
|
527
|
-
selectedTarget={selectedTarget}
|
|
528
|
-
onCreateTrack={createTrack}
|
|
529
|
-
/>
|
|
530
|
-
</div>
|
|
531
|
-
|
|
532
|
-
<div className="ml-auto flex items-center gap-1">
|
|
533
|
-
{/* Auto-keyframe toggle */}
|
|
535
|
+
{applying ? (
|
|
534
536
|
<Tooltip>
|
|
535
537
|
<TooltipTrigger asChild>
|
|
536
|
-
<
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
className={cn(
|
|
541
|
-
"size-6 shrink-0",
|
|
542
|
-
autoKeyframe && "text-primary",
|
|
543
|
-
)}
|
|
544
|
-
onClick={() => setAutoKeyframe((v) => !v)}
|
|
545
|
-
aria-label="Toggle auto-keyframe"
|
|
546
|
-
aria-pressed={autoKeyframe}
|
|
538
|
+
<span
|
|
539
|
+
role="status"
|
|
540
|
+
aria-label="Saving motion"
|
|
541
|
+
className="flex size-6 items-center justify-center rounded text-muted-foreground"
|
|
547
542
|
>
|
|
548
|
-
<
|
|
549
|
-
</
|
|
550
|
-
</TooltipTrigger>
|
|
551
|
-
<TooltipContent side="top">Auto-keyframe</TooltipContent>
|
|
552
|
-
</Tooltip>
|
|
553
|
-
|
|
554
|
-
{/* Write to CSS */}
|
|
555
|
-
<Tooltip>
|
|
556
|
-
<TooltipTrigger asChild>
|
|
557
|
-
<Button
|
|
558
|
-
type="button"
|
|
559
|
-
size="sm"
|
|
560
|
-
className="h-6 px-2.5 text-[11px] gap-1"
|
|
561
|
-
disabled={applying || tracks.length === 0}
|
|
562
|
-
onClick={() => onApply?.(tracks, durationMs)}
|
|
563
|
-
>
|
|
564
|
-
{applying ? (
|
|
565
|
-
<IconRefresh className="size-3 animate-spin" />
|
|
566
|
-
) : (
|
|
567
|
-
<IconCode className="size-3" />
|
|
568
|
-
)}
|
|
569
|
-
Write to CSS
|
|
570
|
-
</Button>
|
|
543
|
+
<IconRefresh className="size-3 animate-spin" />
|
|
544
|
+
</span>
|
|
571
545
|
</TooltipTrigger>
|
|
572
|
-
<TooltipContent side="top">
|
|
573
|
-
Compile timeline → managed <style
|
|
574
|
-
data-agent-native-motion> block
|
|
575
|
-
</TooltipContent>
|
|
546
|
+
<TooltipContent side="top">Saving motion</TooltipContent>
|
|
576
547
|
</Tooltip>
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
548
|
+
) : null}
|
|
549
|
+
</div>
|
|
550
|
+
</>
|
|
580
551
|
</div>
|
|
581
552
|
|
|
582
553
|
{/* Dock body */}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
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 */}
|
|
586
561
|
<div
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
{/* Ruler spacer */}
|
|
591
|
-
<div
|
|
592
|
-
style={{ height: RULER_HEIGHT }}
|
|
593
|
-
className="border-b border-border"
|
|
594
|
-
/>
|
|
562
|
+
style={{ height: RULER_HEIGHT }}
|
|
563
|
+
className="border-b border-border"
|
|
564
|
+
/>
|
|
595
565
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
<p className="text-[11px] text-muted-foreground/70 leading-snug">
|
|
602
|
-
Animate{" "}
|
|
603
|
-
<span className="font-medium text-foreground/80">
|
|
604
|
-
{selectedTarget.label}
|
|
605
|
-
</span>
|
|
606
|
-
. Pick a property to add the first track.
|
|
607
|
-
</p>
|
|
608
|
-
<AddTrackMenu
|
|
609
|
-
selectedTarget={selectedTarget}
|
|
610
|
-
onCreateTrack={createTrack}
|
|
611
|
-
variant="cta"
|
|
612
|
-
/>
|
|
613
|
-
</>
|
|
614
|
-
) : (
|
|
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
|
+
<>
|
|
615
571
|
<p className="text-[11px] text-muted-foreground/70 leading-snug">
|
|
616
|
-
|
|
617
|
-
|
|
572
|
+
Animate{" "}
|
|
573
|
+
<span className="font-medium text-foreground/80">
|
|
574
|
+
{selectedTarget.label}
|
|
575
|
+
</span>
|
|
576
|
+
. Pick a property to add the first track.
|
|
618
577
|
</p>
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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>
|
|
640
610
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
+
))}
|
|
665
635
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
636
|
+
{/* Playhead */}
|
|
637
|
+
<PlayheadLine t={playhead} height={RULER_HEIGHT} inRuler />
|
|
638
|
+
</div>
|
|
669
639
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
/>
|
|
688
|
-
))}
|
|
689
|
-
|
|
690
|
-
{/* Playhead across track rows */}
|
|
691
|
-
<PlayheadLine
|
|
692
|
-
t={playhead}
|
|
693
|
-
height={layers.reduce(
|
|
694
|
-
(sum, layer) =>
|
|
695
|
-
sum +
|
|
696
|
-
ROW_HEIGHT +
|
|
697
|
-
(expandedNodeIds.has(layer.nodeId)
|
|
698
|
-
? layer.tracks.length * ROW_HEIGHT
|
|
699
|
-
: 0),
|
|
700
|
-
0,
|
|
701
|
-
)}
|
|
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
|
+
}}
|
|
702
657
|
/>
|
|
703
|
-
|
|
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
|
+
/>
|
|
704
673
|
</div>
|
|
705
674
|
</div>
|
|
706
|
-
|
|
675
|
+
</div>
|
|
707
676
|
</div>
|
|
708
677
|
);
|
|
709
678
|
}
|