@nodetool-ai/agents 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.nodetool-build-stamp +1 -1
- package/dist/analysis/audio-dsp.d.ts +3 -2
- package/dist/analysis/audio-dsp.d.ts.map +1 -1
- package/dist/analysis/audio-dsp.js +11 -8
- package/dist/analysis/audio-dsp.js.map +1 -1
- package/dist/analysis/media-decode.d.ts +13 -0
- package/dist/analysis/media-decode.d.ts.map +1 -1
- package/dist/analysis/media-decode.js +33 -6
- package/dist/analysis/media-decode.js.map +1 -1
- package/dist/author-graph.d.ts +2 -3
- package/dist/author-graph.d.ts.map +1 -1
- package/dist/author-graph.js +3 -5
- package/dist/author-graph.js.map +1 -1
- package/dist/browser-gate.d.ts +2 -2
- package/dist/browser-gate.js +2 -2
- package/dist/capabilities/GamePublishRequest.d.ts +14 -0
- package/dist/capabilities/GamePublishRequest.d.ts.map +1 -0
- package/dist/capabilities/GamePublishRequest.js +2 -0
- package/dist/capabilities/GamePublishRequest.js.map +1 -0
- package/dist/capabilities/adapters.d.ts +4 -13
- package/dist/capabilities/adapters.d.ts.map +1 -1
- package/dist/capabilities/adapters.js +9 -14
- package/dist/capabilities/adapters.js.map +1 -1
- package/dist/capabilities/agent-activity.d.ts +27 -0
- package/dist/capabilities/agent-activity.d.ts.map +1 -0
- package/dist/capabilities/agent-activity.js +103 -0
- package/dist/capabilities/agent-activity.js.map +1 -0
- package/dist/capabilities/agentic-storyboard-finish.d.ts +67 -0
- package/dist/capabilities/agentic-storyboard-finish.d.ts.map +1 -0
- package/dist/capabilities/agentic-storyboard-finish.js +1321 -0
- package/dist/capabilities/agentic-storyboard-finish.js.map +1 -0
- package/dist/capabilities/agents.d.ts +5 -2
- package/dist/capabilities/agents.d.ts.map +1 -1
- package/dist/capabilities/agents.js +14 -4
- package/dist/capabilities/agents.js.map +1 -1
- package/dist/capabilities/agents.specs.d.ts +3 -0
- package/dist/capabilities/agents.specs.d.ts.map +1 -1
- package/dist/capabilities/agents.specs.js +71 -1
- package/dist/capabilities/agents.specs.js.map +1 -1
- package/dist/capabilities/analysis.d.ts.map +1 -1
- package/dist/capabilities/analysis.js +33 -22
- package/dist/capabilities/analysis.js.map +1 -1
- package/dist/capabilities/analysis.specs.js +2 -2
- package/dist/capabilities/analysis.specs.js.map +1 -1
- package/dist/capabilities/apps.d.ts.map +1 -1
- package/dist/capabilities/apps.js +5 -3
- package/dist/capabilities/apps.js.map +1 -1
- package/dist/capabilities/args.d.ts +2 -5
- package/dist/capabilities/args.d.ts.map +1 -1
- package/dist/capabilities/args.js +2 -5
- package/dist/capabilities/args.js.map +1 -1
- package/dist/capabilities/browser.d.ts +1 -1
- package/dist/capabilities/browser.js +1 -1
- package/dist/capabilities/browser.specs.d.ts +1 -1
- package/dist/capabilities/browser.specs.js +1 -1
- package/dist/capabilities/code.d.ts +5 -0
- package/dist/capabilities/code.d.ts.map +1 -1
- package/dist/capabilities/code.js +128 -89
- package/dist/capabilities/code.js.map +1 -1
- package/dist/capabilities/errors.d.ts +19 -0
- package/dist/capabilities/errors.d.ts.map +1 -0
- package/dist/capabilities/errors.js +109 -0
- package/dist/capabilities/errors.js.map +1 -0
- package/dist/capabilities/errors.specs.d.ts +11 -0
- package/dist/capabilities/errors.specs.d.ts.map +1 -0
- package/dist/capabilities/errors.specs.js +100 -0
- package/dist/capabilities/errors.specs.js.map +1 -0
- package/dist/capabilities/example-timelines.d.ts.map +1 -1
- package/dist/capabilities/example-timelines.js +52 -10
- package/dist/capabilities/example-timelines.js.map +1 -1
- package/dist/capabilities/example-timelines.specs.d.ts +1 -0
- package/dist/capabilities/example-timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/example-timelines.specs.js +21 -3
- package/dist/capabilities/example-timelines.specs.js.map +1 -1
- package/dist/capabilities/finish-storyboard.d.ts +5 -0
- package/dist/capabilities/finish-storyboard.d.ts.map +1 -0
- package/dist/capabilities/finish-storyboard.js +278 -0
- package/dist/capabilities/finish-storyboard.js.map +1 -0
- package/dist/capabilities/game-asset-source.js +1 -1
- package/dist/capabilities/game-asset-source.js.map +1 -1
- package/dist/capabilities/game.d.ts +14 -2
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +209 -39
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.d.ts +2 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -1
- package/dist/capabilities/game.specs.js +25 -11
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts +17 -0
- package/dist/capabilities/game3d.d.ts.map +1 -0
- package/dist/capabilities/game3d.js +433 -0
- package/dist/capabilities/game3d.js.map +1 -0
- package/dist/capabilities/gate-from-context.d.ts +1 -1
- package/dist/capabilities/gate-from-context.js +1 -1
- package/dist/capabilities/generations.d.ts +2 -0
- package/dist/capabilities/generations.d.ts.map +1 -1
- package/dist/capabilities/generations.js +7 -2
- package/dist/capabilities/generations.js.map +1 -1
- package/dist/capabilities/index.d.ts +2 -2
- package/dist/capabilities/index.d.ts.map +1 -1
- package/dist/capabilities/index.js +2 -2
- package/dist/capabilities/index.js.map +1 -1
- package/dist/capabilities/invoke.d.ts +14 -25
- package/dist/capabilities/invoke.d.ts.map +1 -1
- package/dist/capabilities/invoke.js +72 -35
- package/dist/capabilities/invoke.js.map +1 -1
- package/dist/capabilities/jobs.d.ts.map +1 -1
- package/dist/capabilities/jobs.js +47 -3
- package/dist/capabilities/jobs.js.map +1 -1
- package/dist/capabilities/jobs.specs.d.ts.map +1 -1
- package/dist/capabilities/jobs.specs.js +3 -1
- package/dist/capabilities/jobs.specs.js.map +1 -1
- package/dist/capabilities/js-scripts.d.ts +19 -1
- package/dist/capabilities/js-scripts.d.ts.map +1 -1
- package/dist/capabilities/js-scripts.js +109 -2
- package/dist/capabilities/js-scripts.js.map +1 -1
- package/dist/capabilities/js-scripts.specs.d.ts +2 -0
- package/dist/capabilities/js-scripts.specs.d.ts.map +1 -1
- package/dist/capabilities/js-scripts.specs.js +50 -0
- package/dist/capabilities/js-scripts.specs.js.map +1 -1
- package/dist/capabilities/lazy-tool.d.ts +23 -24
- package/dist/capabilities/lazy-tool.d.ts.map +1 -1
- package/dist/capabilities/lazy-tool.js +29 -35
- package/dist/capabilities/lazy-tool.js.map +1 -1
- package/dist/capabilities/legacy-tools.d.ts +15 -0
- package/dist/capabilities/legacy-tools.d.ts.map +1 -0
- package/dist/capabilities/legacy-tools.js +98 -0
- package/dist/capabilities/legacy-tools.js.map +1 -0
- package/dist/capabilities/media.d.ts +0 -9
- package/dist/capabilities/media.d.ts.map +1 -1
- package/dist/capabilities/media.js +301 -260
- package/dist/capabilities/media.js.map +1 -1
- package/dist/capabilities/models.d.ts +2 -2
- package/dist/capabilities/models.d.ts.map +1 -1
- package/dist/capabilities/models.js +5 -3
- package/dist/capabilities/models.js.map +1 -1
- package/dist/capabilities/registry.d.ts +2 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +28 -0
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/capabilities/run-agent.d.ts +19 -0
- package/dist/capabilities/run-agent.d.ts.map +1 -0
- package/dist/capabilities/run-agent.js +183 -0
- package/dist/capabilities/run-agent.js.map +1 -0
- package/dist/capabilities/runs.d.ts +9 -0
- package/dist/capabilities/runs.d.ts.map +1 -0
- package/dist/capabilities/runs.js +46 -0
- package/dist/capabilities/runs.js.map +1 -0
- package/dist/capabilities/runs.specs.d.ts +39 -0
- package/dist/capabilities/runs.specs.d.ts.map +1 -0
- package/dist/capabilities/runs.specs.js +52 -0
- package/dist/capabilities/runs.specs.js.map +1 -0
- package/dist/capabilities/scripts.d.ts.map +1 -1
- package/dist/capabilities/scripts.js +11 -5
- package/dist/capabilities/scripts.js.map +1 -1
- package/dist/capabilities/sketches.d.ts.map +1 -1
- package/dist/capabilities/sketches.js +4 -2
- package/dist/capabilities/sketches.js.map +1 -1
- package/dist/capabilities/skills.d.ts.map +1 -1
- package/dist/capabilities/skills.js +22 -2
- package/dist/capabilities/skills.js.map +1 -1
- package/dist/capabilities/storyboard-layout-check.d.ts +33 -0
- package/dist/capabilities/storyboard-layout-check.d.ts.map +1 -0
- package/dist/capabilities/storyboard-layout-check.js +229 -0
- package/dist/capabilities/storyboard-layout-check.js.map +1 -0
- package/dist/capabilities/storyboard-measured-sources.d.ts +10 -0
- package/dist/capabilities/storyboard-measured-sources.d.ts.map +1 -0
- package/dist/capabilities/storyboard-measured-sources.js +67 -0
- package/dist/capabilities/storyboard-measured-sources.js.map +1 -0
- package/dist/capabilities/storyboard-review-times.d.ts +4 -0
- package/dist/capabilities/storyboard-review-times.d.ts.map +1 -0
- package/dist/capabilities/storyboard-review-times.js +39 -0
- package/dist/capabilities/storyboard-review-times.js.map +1 -0
- package/dist/capabilities/storyboards.d.ts +2 -18
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +315 -94
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts +3 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +42 -3
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/timeline-bridge.d.ts +359 -0
- package/dist/capabilities/timeline-bridge.d.ts.map +1 -0
- package/dist/capabilities/timeline-bridge.js +1016 -0
- package/dist/capabilities/timeline-bridge.js.map +1 -0
- package/dist/capabilities/timeline-clip-frames.d.ts +17 -0
- package/dist/capabilities/timeline-clip-frames.d.ts.map +1 -0
- package/dist/capabilities/timeline-clip-frames.js +84 -0
- package/dist/capabilities/timeline-clip-frames.js.map +1 -0
- package/dist/capabilities/timeline-media-edit.d.ts +8 -0
- package/dist/capabilities/timeline-media-edit.d.ts.map +1 -0
- package/dist/capabilities/timeline-media-edit.js +94 -0
- package/dist/capabilities/timeline-media-edit.js.map +1 -0
- package/dist/capabilities/timeline-native-media.d.ts.map +1 -1
- package/dist/capabilities/timeline-native-media.js +40 -0
- package/dist/capabilities/timeline-native-media.js.map +1 -1
- package/dist/capabilities/timelines.d.ts +64 -4
- package/dist/capabilities/timelines.d.ts.map +1 -1
- package/dist/capabilities/timelines.js +566 -67
- package/dist/capabilities/timelines.js.map +1 -1
- package/dist/capabilities/timelines.specs.d.ts +8 -0
- package/dist/capabilities/timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/timelines.specs.js +173 -8
- package/dist/capabilities/timelines.specs.js.map +1 -1
- package/dist/capabilities/types.d.ts +3 -1
- package/dist/capabilities/types.d.ts.map +1 -1
- package/dist/capabilities/types.js.map +1 -1
- package/dist/capabilities/ui.d.ts.map +1 -1
- package/dist/capabilities/ui.js +24 -2
- package/dist/capabilities/ui.js.map +1 -1
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +53 -26
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +7 -1
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/codeact/action-diagnostics.d.ts.map +1 -1
- package/dist/codeact/action-diagnostics.js +51 -1
- package/dist/codeact/action-diagnostics.js.map +1 -1
- package/dist/codeact/codeact-executor.d.ts.map +1 -1
- package/dist/codeact/codeact-executor.js +16 -8
- package/dist/codeact/codeact-executor.js.map +1 -1
- package/dist/codeact/compact-ids.d.ts.map +1 -1
- package/dist/codeact/compact-ids.js +5 -1
- package/dist/codeact/compact-ids.js.map +1 -1
- package/dist/codeact/graph-model.d.ts +1 -1
- package/dist/codeact/graph-model.d.ts.map +1 -1
- package/dist/codeact/graph-model.js +2 -3
- package/dist/codeact/graph-model.js.map +1 -1
- package/dist/codeact/nodetool-api.d.ts +1 -1
- package/dist/codeact/nodetool-api.d.ts.map +1 -1
- package/dist/codeact/nodetool-api.js +202 -284
- package/dist/codeact/nodetool-api.js.map +1 -1
- package/dist/codeact/tool-api.d.ts +6 -3
- package/dist/codeact/tool-api.d.ts.map +1 -1
- package/dist/codeact/tool-api.js +22 -15
- package/dist/codeact/tool-api.js.map +1 -1
- package/dist/evals/codeact-api-cases.d.ts.map +1 -1
- package/dist/evals/codeact-api-cases.js +2 -0
- package/dist/evals/codeact-api-cases.js.map +1 -1
- package/dist/evals/codeact-api-runs.d.ts +3 -0
- package/dist/evals/codeact-api-runs.d.ts.map +1 -0
- package/dist/evals/codeact-api-runs.js +33 -0
- package/dist/evals/codeact-api-runs.js.map +1 -0
- package/dist/evals/surfaces/sketch.d.ts +2 -0
- package/dist/evals/surfaces/sketch.d.ts.map +1 -1
- package/dist/evals/surfaces/sketch.js +11 -3
- package/dist/evals/surfaces/sketch.js.map +1 -1
- package/dist/evals/surfaces/timeline.d.ts +4 -357
- package/dist/evals/surfaces/timeline.d.ts.map +1 -1
- package/dist/evals/surfaces/timeline.js +9 -2621
- package/dist/evals/surfaces/timeline.js.map +1 -1
- package/dist/evals/tool-loop-bridge.d.ts +4 -0
- package/dist/evals/tool-loop-bridge.d.ts.map +1 -1
- package/dist/evals/tool-loop-bridge.js.map +1 -1
- package/dist/evals/tool-loop-eval.d.ts +2 -0
- package/dist/evals/tool-loop-eval.d.ts.map +1 -1
- package/dist/evals/tool-loop-eval.js +6 -1
- package/dist/evals/tool-loop-eval.js.map +1 -1
- package/dist/evals/tool-loop-permission.d.ts +1 -15
- package/dist/evals/tool-loop-permission.d.ts.map +1 -1
- package/dist/evals/tool-loop-permission.js +23 -41
- package/dist/evals/tool-loop-permission.js.map +1 -1
- package/dist/game-authoring.d.ts +4737 -0
- package/dist/game-authoring.d.ts.map +1 -0
- package/dist/game-authoring.js +146 -0
- package/dist/game-authoring.js.map +1 -0
- package/dist/game-code-bake.d.ts +26 -0
- package/dist/game-code-bake.d.ts.map +1 -0
- package/dist/game-code-bake.js +98 -0
- package/dist/game-code-bake.js.map +1 -0
- package/dist/game-examples.d.ts +872 -16
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/game-examples.js +5 -4
- package/dist/game-examples.js.map +1 -1
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox-worker/engine.d.ts +4 -0
- package/dist/js-sandbox-worker/engine.d.ts.map +1 -0
- package/dist/js-sandbox-worker/engine.js +45 -0
- package/dist/js-sandbox-worker/engine.js.map +1 -0
- package/dist/js-sandbox-worker/host.js +1 -1
- package/dist/js-sandbox-worker/host.js.map +1 -1
- package/dist/js-sandbox-worker/interpreter.d.ts +1 -1
- package/dist/js-sandbox-worker/interpreter.d.ts.map +1 -1
- package/dist/js-sandbox-worker/interpreter.js +94 -2
- package/dist/js-sandbox-worker/interpreter.js.map +1 -1
- package/dist/js-sandbox-worker/worker-entry.d.ts.map +1 -1
- package/dist/js-sandbox-worker/worker-entry.js +3 -2
- package/dist/js-sandbox-worker/worker-entry.js.map +1 -1
- package/dist/js-sandbox.d.ts +2 -0
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +56 -11
- package/dist/js-sandbox.js.map +1 -1
- package/dist/js-script-app-runner.d.ts +2 -0
- package/dist/js-script-app-runner.d.ts.map +1 -1
- package/dist/js-script-app-runner.js +8 -3
- package/dist/js-script-app-runner.js.map +1 -1
- package/dist/js-script-sandbox.d.ts +12 -18
- package/dist/js-script-sandbox.d.ts.map +1 -1
- package/dist/js-script-sandbox.js +3 -49
- package/dist/js-script-sandbox.js.map +1 -1
- package/dist/prompts/product-knowledge.d.ts +1 -1
- package/dist/prompts/product-knowledge.d.ts.map +1 -1
- package/dist/prompts/product-knowledge.js +82 -41
- package/dist/prompts/product-knowledge.js.map +1 -1
- package/dist/sandbox-pack-resolution.d.ts +21 -0
- package/dist/sandbox-pack-resolution.d.ts.map +1 -0
- package/dist/sandbox-pack-resolution.js +42 -0
- package/dist/sandbox-pack-resolution.js.map +1 -0
- package/dist/sandbox-toolbelt.d.ts +3 -1
- package/dist/sandbox-toolbelt.d.ts.map +1 -1
- package/dist/sandbox-toolbelt.js +8 -2
- package/dist/sandbox-toolbelt.js.map +1 -1
- package/dist/skill-prompt.d.ts +11 -1
- package/dist/skill-prompt.d.ts.map +1 -1
- package/dist/skill-prompt.js +17 -6
- package/dist/skill-prompt.js.map +1 -1
- package/dist/system-skills.d.ts +7 -1
- package/dist/system-skills.d.ts.map +1 -1
- package/dist/system-skills.js +2 -2
- package/dist/system-skills.js.map +1 -1
- package/dist/timeline-capture.d.ts +21 -0
- package/dist/timeline-capture.d.ts.map +1 -0
- package/dist/timeline-capture.js +564 -0
- package/dist/timeline-capture.js.map +1 -0
- package/dist/timeline-code-bake.d.ts +75 -0
- package/dist/timeline-code-bake.d.ts.map +1 -0
- package/dist/timeline-code-bake.js +327 -0
- package/dist/timeline-code-bake.js.map +1 -0
- package/dist/timeline-preview/frames.d.ts +26 -0
- package/dist/timeline-preview/frames.d.ts.map +1 -1
- package/dist/timeline-preview/frames.js +155 -70
- package/dist/timeline-preview/frames.js.map +1 -1
- package/dist/tools/base-tool.d.ts +1 -0
- package/dist/tools/base-tool.d.ts.map +1 -1
- package/dist/tools/base-tool.js +1 -1
- package/dist/tools/base-tool.js.map +1 -1
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +13 -0
- package/dist/tools/builtin-tools.js.map +1 -1
- package/dist/tools/external-capability-tools.d.ts +1 -1
- package/dist/tools/external-capability-tools.js +1 -1
- package/dist/tools/mcp-tool-support.d.ts +10 -9
- package/dist/tools/mcp-tool-support.d.ts.map +1 -1
- package/dist/tools/mcp-tool-support.js +14 -13
- package/dist/tools/mcp-tool-support.js.map +1 -1
- package/dist/tools/mcp-tools.d.ts +6 -4
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +65 -62
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/tools/tool-permissions.d.ts +1 -1
- package/dist/tools/tool-permissions.js +1 -1
- package/package.json +24 -22
- package/dist/capabilities/gate-tools.d.ts +0 -18
- package/dist/capabilities/gate-tools.d.ts.map +0 -1
- package/dist/capabilities/gate-tools.js +0 -66
- package/dist/capabilities/gate-tools.js.map +0 -1
- package/dist/tsconfig.tests.tmp.tsbuildinfo +0 -1
|
@@ -17,8 +17,16 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Design: docs/tool-class-retirement-design.md § "Migration".
|
|
19
19
|
*/
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { applyTimelineTrackOp } from "@nodetool-ai/timeline/ops";
|
|
22
|
+
import { STAGGER_UNITS, ANIMATED_PROPERTIES, DEFAULT_BEAT_TOLERANCE_MS } from "@nodetool-ai/timeline";
|
|
23
|
+
import { buildTimelineToolContracts } from "@nodetool-ai/protocol/api-schemas/timeline-tool-contracts.js";
|
|
24
|
+
import { resolveMoveTrackArgs, resolveDeleteTrackArgs } from "@nodetool-ai/protocol/api-schemas/timeline-tool-params.js";
|
|
25
|
+
import { parseWithTypeCoercion } from "@nodetool-ai/runtime";
|
|
26
|
+
import { listTimelinesSpec, createTimelineSpec, getTimelineSpec, listTimelineVersionsSpec, getTimelineVersionSpec, createTimelineVersionSpec, restoreTimelineVersionSpec, deleteTimelineVersionSpec, editTimelineSpec, validateTimelineSpec, setTimelineDocumentSpec, previewTimelineFrameSpec, compareTimelineFramesSpec, renderTimelineSpec, bakeAudioAnimationSpec, isolateSubjectSpec, ISOLATE_SUBJECT_MODELS, ISOLATE_SUBJECT_RESOLUTIONS, DEFAULT_ISOLATE_SUBJECT_MODEL, DEFAULT_ISOLATE_SUBJECT_RESOLUTION, DEFAULT_BAKE_ATTACK_MS, DEFAULT_BAKE_RELEASE_MS, DEFAULT_BAKE_TOLERANCE, DEFAULT_BAKE_MAX_POINTS, DEFAULT_BAKE_MAX_SECONDS, MAX_BAKE_POINTS, DEFAULT_RENDER_TIMEOUT_MS, MAX_RENDER_TIMEOUT_MS, DEFAULT_PREVIEW_COUNT, DEFAULT_COMPARE_WIDTH, MAX_PREVIEW_TIMES, MAX_PREVIEW_RANGE_COUNT, DEFAULT_VERSION_LIMIT, MAX_VERSION_LIMIT, trackObjectSpec, TRACK_OBJECT_DIRECTIONS, getTimelineCodeSpec, setTimelineCodeSpec, editTimelineCodeSpec, rebakeTimelineCodeSpec, detachTimelineCodeSpec, deleteTimelineSpec } from "./timelines.specs.js";
|
|
27
|
+
import { AUDIO_BAKED_ANIMATION_KIND, normalizeAuthoredDocument, hashSceneSubtree, mergeTimelineSource } from "@nodetool-ai/timeline";
|
|
28
|
+
import { bakeTimelineCode, bakedDocumentMatchesSaved } from "../timeline-code-bake.js";
|
|
29
|
+
import { applyJsScriptEditOps, parseJsScriptEditOps } from "./js-scripts.js";
|
|
22
30
|
import { clipSourceWindowMs } from "./timeline-audio-bake.js";
|
|
23
31
|
import { TIMELINE_NATIVE_MEDIA_CAPABILITIES } from "./timeline-native-media.js";
|
|
24
32
|
import { isFiniteNumber, isRecord, isString } from "../utils/type-guards.js";
|
|
@@ -193,7 +201,7 @@ const getTimeline = {
|
|
|
193
201
|
const seq = await loadTimeline(run, params["timeline_id"]);
|
|
194
202
|
if (isError(seq))
|
|
195
203
|
return seq;
|
|
196
|
-
return { timeline: seq.toTimelineSequence() };
|
|
204
|
+
return { timeline: seq.toTimelineSequence(), revision: seq.revision };
|
|
197
205
|
}
|
|
198
206
|
};
|
|
199
207
|
const listTimelineVersions = {
|
|
@@ -367,20 +375,9 @@ const CAS_ATTEMPTS = 2;
|
|
|
367
375
|
/** Operations one call may apply, so a runaway script cannot rewrite a cut. */
|
|
368
376
|
const MAX_OPS = 60;
|
|
369
377
|
const TOOL_PREFIX = "ui_timeline_";
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
* `edit_timeline` persists document mutations but has no durable media-edit
|
|
374
|
-
* generation adapter or callback path. Exposing generative editing here would
|
|
375
|
-
* require it to invent an asset id, so it remains browser-only until a server
|
|
376
|
-
* generation runner can create and attach the candidate take. `apply_take`
|
|
377
|
-
* stays available because it only promotes an existing candidate.
|
|
378
|
-
*/
|
|
379
|
-
const EXCLUDED_OPS = new Set([
|
|
380
|
-
`${TOOL_PREFIX}get_clip_frames`,
|
|
381
|
-
`${TOOL_PREFIX}generate_clip`,
|
|
382
|
-
`${TOOL_PREFIX}generatively_edit_clip`
|
|
383
|
-
]);
|
|
378
|
+
/** Browser generation without a real provider adapter remains excluded. */
|
|
379
|
+
const EXCLUDED_OPS = new Set([`${TOOL_PREFIX}generate_clip`]);
|
|
380
|
+
const READ_ONLY_OPS = new Set([`${TOOL_PREFIX}get_clip_frames`]);
|
|
384
381
|
/** `add_track`, `ui_add_track`, and `ui_timeline_add_track` all name one tool. */
|
|
385
382
|
function normalizeOpName(name) {
|
|
386
383
|
const trimmed = name.trim();
|
|
@@ -392,7 +389,7 @@ function normalizeOpName(name) {
|
|
|
392
389
|
return `${TOOL_PREFIX}${bare}`;
|
|
393
390
|
}
|
|
394
391
|
/** An op is `{op, ...args}` — the arguments sit alongside the verb. */
|
|
395
|
-
function parseOps(raw) {
|
|
392
|
+
export function parseOps(raw) {
|
|
396
393
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
397
394
|
return {
|
|
398
395
|
error: 'ops must be a non-empty array, e.g. [{"op": "add_track", "type": "audio"}].'
|
|
@@ -506,34 +503,11 @@ async function bakeTimelineAnimation(run, request) {
|
|
|
506
503
|
return result;
|
|
507
504
|
}
|
|
508
505
|
/**
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* A failing op is recorded and the script continues: stopping at the first
|
|
512
|
-
* error hides every problem behind it, and the caller wants the whole picture.
|
|
506
|
+
* The bridge hooks that write outside the document: a Blender render of a 3D
|
|
507
|
+
* clip, and the new sequence `retarget_format` creates.
|
|
513
508
|
*/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const bridge = createTimelineToolBridge({
|
|
517
|
-
sequenceId: sequence.id,
|
|
518
|
-
sequenceName: sequence.name,
|
|
519
|
-
projectId: sequence.project_id,
|
|
520
|
-
sequence: {
|
|
521
|
-
fps: sequence.fps,
|
|
522
|
-
width: sequence.width,
|
|
523
|
-
height: sequence.height,
|
|
524
|
-
tracks: document.tracks,
|
|
525
|
-
clips: document.clips,
|
|
526
|
-
markers: document.markers,
|
|
527
|
-
transcript: document.transcript,
|
|
528
|
-
scriptEnabled: document.scriptEnabled,
|
|
529
|
-
templateId: document.templateId,
|
|
530
|
-
tempo: document.tempo,
|
|
531
|
-
camera2d: document.camera2d,
|
|
532
|
-
setup: document.setup,
|
|
533
|
-
mediaTracks: document.mediaTracks
|
|
534
|
-
},
|
|
535
|
-
resolveAsset: (ref) => resolveTimelineAsset(run, ref),
|
|
536
|
-
bakeAnimation: (request) => bakeTimelineAnimation(run, request),
|
|
509
|
+
function writingHooks(run, sequence) {
|
|
510
|
+
return {
|
|
537
511
|
bakeModel3DClip: async (request) => {
|
|
538
512
|
const { bakeModel3DClipOnServer } = await import("./timeline-bake.js");
|
|
539
513
|
return bakeModel3DClipOnServer(run.context, request);
|
|
@@ -576,7 +550,156 @@ async function applyOps(run, sequence, document, ops) {
|
|
|
576
550
|
document: JSON.stringify(derivedDocument)
|
|
577
551
|
});
|
|
578
552
|
return { sequenceId: created.id, name };
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
/** Track-only batches need no simulated editor or external hooks. */
|
|
557
|
+
function applyTrackOps(sequence, document, ops) {
|
|
558
|
+
const contracts = buildTimelineToolContracts({
|
|
559
|
+
staggerUnits: STAGGER_UNITS,
|
|
560
|
+
animatedProperties: ANIMATED_PROPERTIES,
|
|
561
|
+
beatToleranceMs: DEFAULT_BEAT_TOLERANCE_MS
|
|
562
|
+
});
|
|
563
|
+
let state = {
|
|
564
|
+
fps: sequence.fps,
|
|
565
|
+
width: sequence.width,
|
|
566
|
+
height: sequence.height,
|
|
567
|
+
tracks: document.tracks,
|
|
568
|
+
clips: document.clips,
|
|
569
|
+
markers: document.markers,
|
|
570
|
+
mediaTracks: document.mediaTracks,
|
|
571
|
+
tempo: document.tempo,
|
|
572
|
+
playheadMs: 0,
|
|
573
|
+
selectedClipIds: []
|
|
574
|
+
};
|
|
575
|
+
const usedIds = new Set([
|
|
576
|
+
...[
|
|
577
|
+
...document.tracks,
|
|
578
|
+
...document.clips,
|
|
579
|
+
...document.markers,
|
|
580
|
+
...(document.mediaTracks ?? [])
|
|
581
|
+
].map((unit) => unit.id),
|
|
582
|
+
...document.clips.flatMap((clip) => (clip.animations ?? []).map((animation) => animation.id))
|
|
583
|
+
]);
|
|
584
|
+
let nextId = 0;
|
|
585
|
+
const newId = () => {
|
|
586
|
+
let id;
|
|
587
|
+
do {
|
|
588
|
+
id = `track_${++nextId}`;
|
|
589
|
+
} while (usedIds.has(id));
|
|
590
|
+
usedIds.add(id);
|
|
591
|
+
return id;
|
|
592
|
+
};
|
|
593
|
+
const records = [];
|
|
594
|
+
for (const entry of ops) {
|
|
595
|
+
try {
|
|
596
|
+
let op;
|
|
597
|
+
if (entry.op === "ui_timeline_add_track") {
|
|
598
|
+
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_add_track.shape), entry.input);
|
|
599
|
+
op = { op: "add_track", ...args };
|
|
600
|
+
}
|
|
601
|
+
else if (entry.op === "ui_timeline_move_track") {
|
|
602
|
+
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_move_track.shape), entry.input);
|
|
603
|
+
op = { op: "move_track", ...resolveMoveTrackArgs(args) };
|
|
604
|
+
}
|
|
605
|
+
else {
|
|
606
|
+
const args = parseWithTypeCoercion(z.object(contracts.ui_timeline_delete_track.shape), entry.input);
|
|
607
|
+
op = { op: "delete_track", ...resolveDeleteTrackArgs(args) };
|
|
608
|
+
}
|
|
609
|
+
const outcome = applyTimelineTrackOp(state, op, { newId });
|
|
610
|
+
if (outcome.error)
|
|
611
|
+
throw new Error(outcome.error);
|
|
612
|
+
state = outcome.state;
|
|
613
|
+
records.push({ op: entry.op, ok: true, result: outcome.result });
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
records.push({
|
|
617
|
+
op: entry.op,
|
|
618
|
+
ok: false,
|
|
619
|
+
error: error instanceof Error ? error.message : String(error)
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return {
|
|
624
|
+
records,
|
|
625
|
+
state: {
|
|
626
|
+
fps: state.fps,
|
|
627
|
+
width: state.width,
|
|
628
|
+
height: state.height,
|
|
629
|
+
durationMs: state.clips.reduce((end, clip) => Math.max(end, clip.startMs + clip.durationMs), 0),
|
|
630
|
+
playheadMs: state.playheadMs,
|
|
631
|
+
tracks: state.tracks.map(({ id, name, type, index }) => ({
|
|
632
|
+
id,
|
|
633
|
+
name,
|
|
634
|
+
type,
|
|
635
|
+
index
|
|
636
|
+
})),
|
|
637
|
+
clips: state.clips.map((clip) => ({
|
|
638
|
+
id: clip.id,
|
|
639
|
+
name: clip.name,
|
|
640
|
+
trackId: clip.trackId,
|
|
641
|
+
mediaType: clip.mediaType,
|
|
642
|
+
startMs: clip.startMs,
|
|
643
|
+
durationMs: clip.durationMs,
|
|
644
|
+
prompt: clip.prompt,
|
|
645
|
+
animations: (clip.animations ?? []).map(({ role, preset }) => ({
|
|
646
|
+
role,
|
|
647
|
+
preset
|
|
648
|
+
}))
|
|
649
|
+
})),
|
|
650
|
+
documentTracks: state.tracks,
|
|
651
|
+
documentClips: state.clips,
|
|
652
|
+
markers: state.markers,
|
|
653
|
+
mediaTracks: state.mediaTracks ?? [],
|
|
654
|
+
derivedSequences: [],
|
|
655
|
+
tempo: state.tempo,
|
|
656
|
+
camera2d: document.camera2d ?? null,
|
|
657
|
+
setup: document.setup ?? null,
|
|
658
|
+
transitionCandidates: [],
|
|
659
|
+
appliedTransitionCandidates: [],
|
|
660
|
+
toolLog: ops.map((entry) => entry.op),
|
|
661
|
+
previewTimesMs: [],
|
|
662
|
+
previewedLayerKinds: []
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Run `ops` against a bridge seeded from `document`.
|
|
668
|
+
*
|
|
669
|
+
* A failing op is recorded and the script continues: stopping at the first
|
|
670
|
+
* error hides every problem behind it, and the caller wants the whole picture.
|
|
671
|
+
*
|
|
672
|
+
* `hermetic` leaves out the hooks that write outside the document: the 3D
|
|
673
|
+
* render and the new sequence a retarget creates. A code bake sets it. The
|
|
674
|
+
* bridge then uses its in-memory fallbacks for those ops.
|
|
675
|
+
*/
|
|
676
|
+
export async function applyOps(run, sequence, document, ops, options = {}) {
|
|
677
|
+
if (ops.every(({ op }) => op === "ui_timeline_add_track" || op === "ui_timeline_move_track" || op === "ui_timeline_delete_track")) {
|
|
678
|
+
return applyTrackOps(sequence, document, ops);
|
|
679
|
+
}
|
|
680
|
+
const { createTimelineToolBridge } = await import("./timeline-bridge.js");
|
|
681
|
+
let operationIndex = 0;
|
|
682
|
+
const init = {
|
|
683
|
+
sequenceId: sequence.id,
|
|
684
|
+
sequenceName: sequence.name,
|
|
685
|
+
projectId: sequence.project_id,
|
|
686
|
+
sequence: {
|
|
687
|
+
fps: sequence.fps,
|
|
688
|
+
width: sequence.width,
|
|
689
|
+
height: sequence.height,
|
|
690
|
+
tracks: document.tracks,
|
|
691
|
+
clips: document.clips,
|
|
692
|
+
markers: document.markers,
|
|
693
|
+
transcript: document.transcript,
|
|
694
|
+
scriptEnabled: document.scriptEnabled,
|
|
695
|
+
templateId: document.templateId,
|
|
696
|
+
tempo: document.tempo,
|
|
697
|
+
camera2d: document.camera2d,
|
|
698
|
+
setup: document.setup,
|
|
699
|
+
mediaTracks: document.mediaTracks
|
|
579
700
|
},
|
|
701
|
+
resolveAsset: (ref) => resolveTimelineAsset(run, ref),
|
|
702
|
+
bakeAnimation: (request) => bakeTimelineAnimation(run, request),
|
|
580
703
|
loadComposition: {
|
|
581
704
|
get: async (id) => {
|
|
582
705
|
const { loadComposition } = await import("./compositions.js");
|
|
@@ -592,12 +715,31 @@ async function applyOps(run, sequence, document, ops) {
|
|
|
592
715
|
];
|
|
593
716
|
}
|
|
594
717
|
}
|
|
595
|
-
}
|
|
718
|
+
};
|
|
719
|
+
if (!options.hermetic) {
|
|
720
|
+
Object.assign(init, writingHooks(run, sequence));
|
|
721
|
+
const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
|
|
722
|
+
const generate = options.generateMediaEdit ?? timelineMediaEditGenerator(run);
|
|
723
|
+
init.generateMediaEdit = (request) => generate(request, operationIndex);
|
|
724
|
+
init.getClipFrames = async (clip, frameOptions) => {
|
|
725
|
+
const current = bridge.finalState();
|
|
726
|
+
const { inspectTimelineClipFrames } = await import("./timeline-clip-frames.js");
|
|
727
|
+
return inspectTimelineClipFrames(run.context, { ...sequence.toDocument(),
|
|
728
|
+
tracks: current.documentTracks, clips: current.documentClips, markers: current.markers,
|
|
729
|
+
mediaTracks: current.mediaTracks, tempo: current.tempo, camera2d: current.camera2d,
|
|
730
|
+
id: sequence.id, projectId: sequence.project_id ?? "default", name: sequence.name,
|
|
731
|
+
fps: sequence.fps, width: sequence.width, height: sequence.height,
|
|
732
|
+
durationMs: sequence.duration_ms, createdAt: sequence.created_at, updatedAt: sequence.updated_at
|
|
733
|
+
}, clip, frameOptions);
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
const bridge = createTimelineToolBridge(init);
|
|
596
737
|
const byName = new Map(bridge.tools
|
|
597
|
-
.filter((tool) => !EXCLUDED_OPS.has(tool.name))
|
|
738
|
+
.filter((tool) => !EXCLUDED_OPS.has(tool.name) && !(options.hermetic && (tool.name === `${TOOL_PREFIX}generatively_edit_clip` || READ_ONLY_OPS.has(tool.name))))
|
|
598
739
|
.map((tool) => [tool.name, tool]));
|
|
599
740
|
const records = [];
|
|
600
|
-
for (const { op, input } of ops) {
|
|
741
|
+
for (const [index, { op, input }] of ops.entries()) {
|
|
742
|
+
operationIndex = index;
|
|
601
743
|
const tool = byName.get(op);
|
|
602
744
|
if (!tool) {
|
|
603
745
|
const known = [...byName.keys()]
|
|
@@ -741,7 +883,9 @@ const editTimeline = {
|
|
|
741
883
|
"the returned timeline id."
|
|
742
884
|
};
|
|
743
885
|
}
|
|
744
|
-
const { TimelineSequence } = await import("@nodetool-ai/models");
|
|
886
|
+
const { TimelineSequence, TimelineSequenceVersion } = await import("@nodetool-ai/models");
|
|
887
|
+
const { timelineMediaEditGenerator } = await import("./timeline-media-edit.js");
|
|
888
|
+
const generateMediaEdit = timelineMediaEditGenerator(run);
|
|
745
889
|
for (let attempt = 0; attempt < CAS_ATTEMPTS; attempt++) {
|
|
746
890
|
const sequence = await TimelineSequence.findById(timelineId);
|
|
747
891
|
// A sequence owned by someone else reads as missing — the rule the tRPC
|
|
@@ -750,7 +894,7 @@ const editTimeline = {
|
|
|
750
894
|
return { error: `Timeline ${timelineId} was not found.` };
|
|
751
895
|
}
|
|
752
896
|
const document = sequence.toDocument();
|
|
753
|
-
const { records, state } = await applyOps(run, sequence, document, ops);
|
|
897
|
+
const { records, state } = await applyOps(run, sequence, document, ops, { generateMediaEdit });
|
|
754
898
|
// The transcript rides along untouched — no timeline operation edits it,
|
|
755
899
|
// so the stored copy stays authoritative. Markers come back from the
|
|
756
900
|
// bridge, which seeded them and which the marker ops write to.
|
|
@@ -784,12 +928,12 @@ const editTimeline = {
|
|
|
784
928
|
// hand an open editor a merge with nothing in it.
|
|
785
929
|
const appliedOps = ops
|
|
786
930
|
.map((parsed, index) => ({ parsed, index }))
|
|
787
|
-
.filter(({ index }) => records[index]?.ok);
|
|
931
|
+
.filter(({ parsed, index }) => records[index]?.ok && !READ_ONLY_OPS.has(parsed.op));
|
|
788
932
|
if (appliedOps.length === 0) {
|
|
789
933
|
return {
|
|
790
934
|
timeline_id: timelineId,
|
|
791
935
|
updated_at: sequence.updated_at,
|
|
792
|
-
applied:
|
|
936
|
+
applied: records.filter((record) => record.ok).length,
|
|
793
937
|
failed: failed.length,
|
|
794
938
|
ops: records,
|
|
795
939
|
tracks: state.tracks,
|
|
@@ -826,15 +970,23 @@ const editTimeline = {
|
|
|
826
970
|
}))
|
|
827
971
|
};
|
|
828
972
|
}
|
|
973
|
+
const undo = appliedOps.some(({ parsed }) => parsed.op === `${TOOL_PREFIX}apply_take`)
|
|
974
|
+
? await TimelineSequenceVersion.snapshot(sequence, { saveType: "manual", name: "Before applying AI edit take" })
|
|
975
|
+
: undefined;
|
|
829
976
|
const saved = await TimelineSequence.updateDocumentIfUnchanged(sequence.id, sequence.updated_at, next, {
|
|
830
977
|
ops: appliedOps.map(({ parsed, index }) => ({
|
|
831
978
|
tool: parsed.op,
|
|
832
979
|
input: resolveTimelineOpInput(parsed.input, document, state, records[index]?.result)
|
|
833
980
|
}))
|
|
981
|
+
}).catch(async (error) => {
|
|
982
|
+
await undo?.delete();
|
|
983
|
+
throw error;
|
|
834
984
|
});
|
|
835
|
-
if (!saved)
|
|
985
|
+
if (!saved) {
|
|
986
|
+
await undo?.delete();
|
|
836
987
|
continue;
|
|
837
|
-
|
|
988
|
+
}
|
|
989
|
+
const response = {
|
|
838
990
|
timeline_id: timelineId,
|
|
839
991
|
updated_at: saved.updated_at,
|
|
840
992
|
applied: records.length - failed.length,
|
|
@@ -851,6 +1003,10 @@ const editTimeline = {
|
|
|
851
1003
|
animations: clip.animations
|
|
852
1004
|
}))
|
|
853
1005
|
};
|
|
1006
|
+
if (undo) {
|
|
1007
|
+
Object.assign(response, { undo_version: undo.version });
|
|
1008
|
+
}
|
|
1009
|
+
return response;
|
|
854
1010
|
}
|
|
855
1011
|
return {
|
|
856
1012
|
error: `Timeline ${timelineId} is being modified concurrently; nothing was saved. Retry the call.`
|
|
@@ -1333,6 +1489,7 @@ const previewTimelineFrame = {
|
|
|
1333
1489
|
mime: "image/png"
|
|
1334
1490
|
});
|
|
1335
1491
|
sheet = {
|
|
1492
|
+
complete: result.complete,
|
|
1336
1493
|
columns: tiled.columns,
|
|
1337
1494
|
rows: tiled.rows,
|
|
1338
1495
|
cells: tiled.cells,
|
|
@@ -1346,12 +1503,15 @@ const previewTimelineFrame = {
|
|
|
1346
1503
|
const frames = [];
|
|
1347
1504
|
for (const frame of result.frames) {
|
|
1348
1505
|
const entry = {
|
|
1506
|
+
complete: frame.complete,
|
|
1349
1507
|
time_ms: frame.time_ms,
|
|
1350
1508
|
width: frame.width,
|
|
1351
1509
|
height: frame.height,
|
|
1352
1510
|
layers: frame.layers,
|
|
1511
|
+
failures: frame.failures,
|
|
1353
1512
|
dropped: frame.dropped,
|
|
1354
|
-
degraded: frame.degraded
|
|
1513
|
+
degraded: frame.degraded,
|
|
1514
|
+
degradations: frame.degraded
|
|
1355
1515
|
};
|
|
1356
1516
|
if (!wantSheet) {
|
|
1357
1517
|
const saved = await persistOutput(run.context, frame.png, {
|
|
@@ -1363,6 +1523,8 @@ const previewTimelineFrame = {
|
|
|
1363
1523
|
frames.push(entry);
|
|
1364
1524
|
}
|
|
1365
1525
|
return {
|
|
1526
|
+
complete: result.complete,
|
|
1527
|
+
visual_output_verified: false,
|
|
1366
1528
|
timeline_id: isString(timelineId) ? timelineId : undefined,
|
|
1367
1529
|
name,
|
|
1368
1530
|
fps: meta.fps,
|
|
@@ -1372,12 +1534,15 @@ const previewTimelineFrame = {
|
|
|
1372
1534
|
frames,
|
|
1373
1535
|
sheet,
|
|
1374
1536
|
effects_not_applied: result.effectsNotApplied,
|
|
1375
|
-
|
|
1537
|
+
fonts_unavailable: result.fontsUnavailable,
|
|
1538
|
+
hint: (!result.complete
|
|
1539
|
+
? "Incomplete preview: requested content was skipped or approximated. Inspect failures, dropped, degradations, skipped layers, effects_not_applied and fonts_unavailable. This image cannot verify the complete composition. "
|
|
1540
|
+
: "") + (wantSheet
|
|
1376
1541
|
? "Call view_image with the sheet's asset_id to see every frame at " +
|
|
1377
1542
|
"once; the cells run left to right, labelled with their timecode. " +
|
|
1378
1543
|
"Each frame's layers are listed top of the stack first."
|
|
1379
1544
|
: "Call view_image with a frame's asset_id to see it. The layers are " +
|
|
1380
|
-
"listed top of the stack first — the first one covers the rest."
|
|
1545
|
+
"listed top of the stack first — the first one covers the rest.")
|
|
1381
1546
|
};
|
|
1382
1547
|
}
|
|
1383
1548
|
};
|
|
@@ -1510,10 +1675,15 @@ const compareTimelineFrames = {
|
|
|
1510
1675
|
const frames = [];
|
|
1511
1676
|
const cells = [];
|
|
1512
1677
|
for (let i = 0; i < times.length; i++) {
|
|
1513
|
-
const
|
|
1514
|
-
|
|
1678
|
+
const complete = left.frames[i].complete && right.frames[i].complete;
|
|
1679
|
+
const difference = complete ? Number((await frameDifference(left.frames[i].png, right.frames[i].png)).toFixed(4)) : null;
|
|
1680
|
+
frames.push({
|
|
1681
|
+
time_ms: times[i], complete, difference,
|
|
1682
|
+
a: { failures: left.frames[i].failures, dropped: left.frames[i].dropped, degradations: left.frames[i].degraded, skipped: left.frames[i].layers.filter((layer) => layer.skipped) },
|
|
1683
|
+
b: { failures: right.frames[i].failures, dropped: right.frames[i].dropped, degradations: right.frames[i].degraded, skipped: right.frames[i].layers.filter((layer) => layer.skipped) }
|
|
1684
|
+
});
|
|
1515
1685
|
cells.push({
|
|
1516
|
-
label: `${times[i]}ms ${difference.toFixed(3)}`,
|
|
1686
|
+
label: `${times[i]}ms ${difference === null ? "incomplete" : difference.toFixed(3)}`,
|
|
1517
1687
|
png: await sideBySide(left.frames[i].png, right.frames[i].png)
|
|
1518
1688
|
});
|
|
1519
1689
|
}
|
|
@@ -1522,16 +1692,20 @@ const compareTimelineFrames = {
|
|
|
1522
1692
|
namePrefix: "timeline-compare",
|
|
1523
1693
|
mime: "image/png"
|
|
1524
1694
|
});
|
|
1525
|
-
const
|
|
1526
|
-
const
|
|
1695
|
+
const complete = left.complete && right.complete;
|
|
1696
|
+
const differences = frames.flatMap((frame) => frame.difference === null ? [] : [frame.difference]);
|
|
1697
|
+
const changed = frames.filter((frame) => frame.difference !== null && frame.difference > 0);
|
|
1527
1698
|
return {
|
|
1699
|
+
complete,
|
|
1700
|
+
visual_output_verified: false,
|
|
1528
1701
|
a: a.source,
|
|
1529
1702
|
b: b.source,
|
|
1530
1703
|
frames,
|
|
1531
|
-
changed_times_ms: changed.map((frame) => frame.time_ms),
|
|
1532
|
-
max_difference: Math.max(...differences),
|
|
1533
|
-
mean_difference: Number((differences.reduce((sum, d) => sum + d, 0) / differences.length).toFixed(4)),
|
|
1704
|
+
changed_times_ms: complete ? changed.map((frame) => frame.time_ms) : null,
|
|
1705
|
+
max_difference: complete ? Math.max(...differences) : null,
|
|
1706
|
+
mean_difference: complete ? Number((differences.reduce((sum, d) => sum + d, 0) / differences.length).toFixed(4)) : null,
|
|
1534
1707
|
sheet: {
|
|
1708
|
+
complete,
|
|
1535
1709
|
columns: tiled.columns,
|
|
1536
1710
|
rows: tiled.rows,
|
|
1537
1711
|
cells: tiled.cells,
|
|
@@ -1542,7 +1716,11 @@ const compareTimelineFrames = {
|
|
|
1542
1716
|
effects_not_applied: [
|
|
1543
1717
|
...new Set([...left.effectsNotApplied, ...right.effectsNotApplied])
|
|
1544
1718
|
].sort(),
|
|
1545
|
-
|
|
1719
|
+
fonts_unavailable: [
|
|
1720
|
+
...new Set([...left.fontsUnavailable, ...right.fontsUnavailable])
|
|
1721
|
+
].sort(),
|
|
1722
|
+
hint: (!complete ? "Incomplete previews cannot establish visual equivalence. Difference scores are null for incomplete pairs and aggregate metrics are unavailable. " : "") +
|
|
1723
|
+
"difference is the mean absolute RGB difference, 0 (identical) to 1. " +
|
|
1546
1724
|
"Each cell of the sheet is a pair: `a` on the left, `b` on the right, " +
|
|
1547
1725
|
"labelled with its timecode and score."
|
|
1548
1726
|
};
|
|
@@ -2283,6 +2461,322 @@ const trackObject = {
|
|
|
2283
2461
|
return result;
|
|
2284
2462
|
}
|
|
2285
2463
|
};
|
|
2464
|
+
/**
|
|
2465
|
+
* The report shape `get_timeline_code`, `set_timeline_code`, `edit_timeline_code`
|
|
2466
|
+
* and `rebake_timeline_code` all answer for `scenes`: `edited` means "kept
|
|
2467
|
+
* as-is rather than reflecting the code" — either the code has never been
|
|
2468
|
+
* baked into a current merge (`get`), or this call's own merge kept it as a
|
|
2469
|
+
* conflict (`set`/`edit`/`rebake`).
|
|
2470
|
+
*/
|
|
2471
|
+
function sceneReport(scenes, editedNames) {
|
|
2472
|
+
return Object.entries(scenes).map(([name, recorded]) => ({
|
|
2473
|
+
name,
|
|
2474
|
+
group_id: recorded.groupId,
|
|
2475
|
+
edited: editedNames.has(name)
|
|
2476
|
+
}));
|
|
2477
|
+
}
|
|
2478
|
+
/** `force` is a boolean or an array of scene names; anything else is refused. */
|
|
2479
|
+
function normalizeForceOption(value) {
|
|
2480
|
+
if (value === undefined)
|
|
2481
|
+
return undefined;
|
|
2482
|
+
if (value === true)
|
|
2483
|
+
return true;
|
|
2484
|
+
if (value === false)
|
|
2485
|
+
return undefined;
|
|
2486
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
2487
|
+
return value;
|
|
2488
|
+
}
|
|
2489
|
+
return { error: "force must be a boolean or an array of scene names." };
|
|
2490
|
+
}
|
|
2491
|
+
/**
|
|
2492
|
+
* Validate, snapshot, and CAS-write a document the way `set_timeline_document`
|
|
2493
|
+
* does (see its own docstring for why each step is there): validate first so
|
|
2494
|
+
* a document that would not render never reaches the database, snapshot the
|
|
2495
|
+
* state being replaced so the write is undoable, then a CAS write so a
|
|
2496
|
+
* concurrent edit is reported rather than clobbered.
|
|
2497
|
+
*/
|
|
2498
|
+
export async function writeTimelineCodeDocument(sequence, document, snapshotName) {
|
|
2499
|
+
const { validateTimelineSequence } = await import("@nodetool-ai/execution/timeline-debug");
|
|
2500
|
+
const before = validateTimelineSequence(document, { fps: sequence.fps, width: sequence.width, height: sequence.height });
|
|
2501
|
+
const errors = before.errors.map((issue) => issue.message);
|
|
2502
|
+
const warnings = before.warnings.map((issue) => issue.message);
|
|
2503
|
+
if (!before.ok) {
|
|
2504
|
+
return {
|
|
2505
|
+
written: false,
|
|
2506
|
+
error: `The baked document has ${before.errors.length} error${before.errors.length === 1 ? "" : "s"} and was not written to timeline ${sequence.id}.`,
|
|
2507
|
+
errors,
|
|
2508
|
+
warnings
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
const { TimelineSequence: TimelineSequenceModel, TimelineSequenceVersion } = await import("@nodetool-ai/models");
|
|
2512
|
+
await TimelineSequenceVersion.snapshot(sequence, {
|
|
2513
|
+
saveType: "manual",
|
|
2514
|
+
name: snapshotName
|
|
2515
|
+
});
|
|
2516
|
+
const saved = await TimelineSequenceModel.updateFieldsIfUnchanged(sequence.id, sequence.updated_at, {
|
|
2517
|
+
document: JSON.stringify(document),
|
|
2518
|
+
duration_ms: documentDurationMs(document)
|
|
2519
|
+
});
|
|
2520
|
+
if (!saved) {
|
|
2521
|
+
return {
|
|
2522
|
+
written: false,
|
|
2523
|
+
error: `Timeline ${sequence.id} was modified concurrently; nothing was written. Read it again and try again.`,
|
|
2524
|
+
errors,
|
|
2525
|
+
warnings
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
return { written: true, timeline_id: saved.id, errors, warnings };
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Bake `code`, merge the result into `sequence`'s current document (see
|
|
2532
|
+
* `mergeTimelineSource` in `@nodetool-ai/timeline` for the merge rule), and
|
|
2533
|
+
* write it through the same path `set_timeline_document` uses. Shared by
|
|
2534
|
+
* `set_timeline_code`, `edit_timeline_code` and `rebake_timeline_code` — they
|
|
2535
|
+
* differ only in where `code` comes from.
|
|
2536
|
+
*/
|
|
2537
|
+
async function bakeAndMergeTimelineCode(run, sequence, code, force) {
|
|
2538
|
+
const current = sequence.toDocument();
|
|
2539
|
+
const baked = await bakeTimelineCode(run.context, code);
|
|
2540
|
+
if (!baked.ok || !baked.document) {
|
|
2541
|
+
return {
|
|
2542
|
+
timeline_id: sequence.id,
|
|
2543
|
+
errors: [baked.error ?? "The code failed to build a document."],
|
|
2544
|
+
warnings: [],
|
|
2545
|
+
conflicts: [],
|
|
2546
|
+
scenes: []
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
const previousScenes = current.source?.scenes ?? {};
|
|
2550
|
+
const merged = mergeTimelineSource(current, baked.document, previousScenes, force !== undefined ? { force } : {});
|
|
2551
|
+
const mergedWithSource = {
|
|
2552
|
+
...merged.document,
|
|
2553
|
+
source: {
|
|
2554
|
+
lang: "js",
|
|
2555
|
+
code,
|
|
2556
|
+
bakedAt: new Date().toISOString(),
|
|
2557
|
+
scenes: merged.scenes
|
|
2558
|
+
}
|
|
2559
|
+
};
|
|
2560
|
+
const editedNames = new Set(merged.conflicts.map((c) => c.scene));
|
|
2561
|
+
const scenes = sceneReport(merged.scenes, editedNames);
|
|
2562
|
+
const write = await writeTimelineCodeDocument(sequence, mergedWithSource, "Before writing timeline code");
|
|
2563
|
+
if (!write.written) {
|
|
2564
|
+
return {
|
|
2565
|
+
timeline_id: sequence.id,
|
|
2566
|
+
errors: [write.error, ...write.errors],
|
|
2567
|
+
warnings: write.warnings,
|
|
2568
|
+
conflicts: merged.conflicts,
|
|
2569
|
+
scenes
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
return {
|
|
2573
|
+
timeline_id: write.timeline_id,
|
|
2574
|
+
errors: write.errors,
|
|
2575
|
+
warnings: write.warnings,
|
|
2576
|
+
conflicts: merged.conflicts,
|
|
2577
|
+
scenes
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Attach `code` as `sequence`'s authoring source only when a hermetic bake
|
|
2582
|
+
* of it gives exactly the document that is saved now. This is
|
|
2583
|
+
* `set_timeline_code` with `require_match: true`, which `v.save()` calls
|
|
2584
|
+
* with the retained program it printed (docs/timeline-code-capture.md).
|
|
2585
|
+
*
|
|
2586
|
+
* A mismatch writes nothing and returns a warning. The document stays as it
|
|
2587
|
+
* was saved, only without code, because code that does not rebuild it would
|
|
2588
|
+
* make a later rebake diverge from what the timeline shows.
|
|
2589
|
+
*/
|
|
2590
|
+
async function attachMatchingTimelineCode(run, sequence, code) {
|
|
2591
|
+
const current = sequence.toDocument();
|
|
2592
|
+
const notEmbedded = (warning) => ({
|
|
2593
|
+
timeline_id: sequence.id,
|
|
2594
|
+
embedded: false,
|
|
2595
|
+
errors: [],
|
|
2596
|
+
warnings: [warning],
|
|
2597
|
+
conflicts: [],
|
|
2598
|
+
scenes: []
|
|
2599
|
+
});
|
|
2600
|
+
const baked = await bakeTimelineCode(run.context, code);
|
|
2601
|
+
if (!baked.ok || !baked.document) {
|
|
2602
|
+
return notEmbedded(`Code not embedded: the retained program failed to rebake (${baked.error ?? "unknown error"}).`);
|
|
2603
|
+
}
|
|
2604
|
+
if (!bakedDocumentMatchesSaved(current, baked.document)) {
|
|
2605
|
+
return notEmbedded("Code not embedded: the retained program rebakes to a different " +
|
|
2606
|
+
"timeline than the one saved. A scene clip probably changed after " +
|
|
2607
|
+
"v.series(), or a value came from Math.random() or Date.now().");
|
|
2608
|
+
}
|
|
2609
|
+
// Every scene is new here: the saved document was just written from this
|
|
2610
|
+
// same run, so there are no hand edits to keep.
|
|
2611
|
+
const merged = mergeTimelineSource(current, baked.document, {}, { force: true });
|
|
2612
|
+
const write = await writeTimelineCodeDocument(sequence, {
|
|
2613
|
+
...merged.document,
|
|
2614
|
+
source: {
|
|
2615
|
+
lang: "js",
|
|
2616
|
+
code,
|
|
2617
|
+
bakedAt: new Date().toISOString(),
|
|
2618
|
+
scenes: merged.scenes
|
|
2619
|
+
}
|
|
2620
|
+
}, "Attach authoring code");
|
|
2621
|
+
if (!write.written) {
|
|
2622
|
+
return notEmbedded(`Code not embedded: ${write.error}`);
|
|
2623
|
+
}
|
|
2624
|
+
return {
|
|
2625
|
+
timeline_id: write.timeline_id,
|
|
2626
|
+
embedded: true,
|
|
2627
|
+
errors: write.errors,
|
|
2628
|
+
warnings: write.warnings,
|
|
2629
|
+
conflicts: [],
|
|
2630
|
+
scenes: sceneReport(merged.scenes, new Set())
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
const getTimelineCode = {
|
|
2634
|
+
spec: getTimelineCodeSpec,
|
|
2635
|
+
impl: async (run, params) => {
|
|
2636
|
+
const sequence = await loadTimeline(run, params["timeline_id"]);
|
|
2637
|
+
if (isError(sequence))
|
|
2638
|
+
return sequence;
|
|
2639
|
+
const doc = sequence.toDocument();
|
|
2640
|
+
const source = doc.source;
|
|
2641
|
+
if (!source)
|
|
2642
|
+
return { code: null, baked_at: null, scenes: [] };
|
|
2643
|
+
const groupIdByScene = new Map();
|
|
2644
|
+
for (const clip of doc.clips) {
|
|
2645
|
+
if (typeof clip.sourceScene === "string" && clip.sourceScene) {
|
|
2646
|
+
groupIdByScene.set(clip.sourceScene, clip.id);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
const editedNames = new Set();
|
|
2650
|
+
for (const [name, recorded] of Object.entries(source.scenes)) {
|
|
2651
|
+
const groupId = groupIdByScene.get(name);
|
|
2652
|
+
const edited = groupId === undefined ||
|
|
2653
|
+
hashSceneSubtree(doc.clips, groupId) !== recorded.hash;
|
|
2654
|
+
if (edited)
|
|
2655
|
+
editedNames.add(name);
|
|
2656
|
+
}
|
|
2657
|
+
return {
|
|
2658
|
+
code: source.code,
|
|
2659
|
+
baked_at: source.bakedAt,
|
|
2660
|
+
scenes: sceneReport(source.scenes, editedNames)
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
};
|
|
2664
|
+
const setTimelineCode = {
|
|
2665
|
+
spec: setTimelineCodeSpec,
|
|
2666
|
+
impl: async (run, params) => {
|
|
2667
|
+
const sequence = await loadTimeline(run, params["timeline_id"]);
|
|
2668
|
+
if (isError(sequence))
|
|
2669
|
+
return sequence;
|
|
2670
|
+
const code = params["code"];
|
|
2671
|
+
if (!isString(code) || !code.trim()) {
|
|
2672
|
+
return { error: "code is required and must be a non-empty script." };
|
|
2673
|
+
}
|
|
2674
|
+
if (params["require_match"] === true) {
|
|
2675
|
+
return attachMatchingTimelineCode(run, sequence, code);
|
|
2676
|
+
}
|
|
2677
|
+
const force = normalizeForceOption(params["force"]);
|
|
2678
|
+
if (isError(force))
|
|
2679
|
+
return force;
|
|
2680
|
+
return bakeAndMergeTimelineCode(run, sequence, code, force);
|
|
2681
|
+
}
|
|
2682
|
+
};
|
|
2683
|
+
const editTimelineCode = {
|
|
2684
|
+
spec: editTimelineCodeSpec,
|
|
2685
|
+
impl: async (run, params) => {
|
|
2686
|
+
const sequence = await loadTimeline(run, params["timeline_id"]);
|
|
2687
|
+
if (isError(sequence))
|
|
2688
|
+
return sequence;
|
|
2689
|
+
const source = sequence.toDocument().source;
|
|
2690
|
+
if (!source) {
|
|
2691
|
+
return {
|
|
2692
|
+
error: `Timeline ${sequence.id} has no authoring code to edit. Call set_timeline_code first.`
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
const ops = parseJsScriptEditOps(params["edits"]);
|
|
2696
|
+
if (isError(ops))
|
|
2697
|
+
return ops;
|
|
2698
|
+
const applied = applyJsScriptEditOps(source.code, ops);
|
|
2699
|
+
if (isError(applied))
|
|
2700
|
+
return applied;
|
|
2701
|
+
const force = normalizeForceOption(params["force"]);
|
|
2702
|
+
if (isError(force))
|
|
2703
|
+
return force;
|
|
2704
|
+
return bakeAndMergeTimelineCode(run, sequence, applied.code, force);
|
|
2705
|
+
}
|
|
2706
|
+
};
|
|
2707
|
+
const rebakeTimelineCode = {
|
|
2708
|
+
spec: rebakeTimelineCodeSpec,
|
|
2709
|
+
impl: async (run, params) => {
|
|
2710
|
+
const sequence = await loadTimeline(run, params["timeline_id"]);
|
|
2711
|
+
if (isError(sequence))
|
|
2712
|
+
return sequence;
|
|
2713
|
+
const source = sequence.toDocument().source;
|
|
2714
|
+
if (!source) {
|
|
2715
|
+
return { error: `Timeline ${sequence.id} has no authoring code to rebake.` };
|
|
2716
|
+
}
|
|
2717
|
+
const force = normalizeForceOption(params["force"]);
|
|
2718
|
+
if (isError(force))
|
|
2719
|
+
return force;
|
|
2720
|
+
return bakeAndMergeTimelineCode(run, sequence, source.code, force);
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
const detachTimelineCode = {
|
|
2724
|
+
spec: detachTimelineCodeSpec,
|
|
2725
|
+
impl: async (run, params) => {
|
|
2726
|
+
const sequence = await loadTimeline(run, params["timeline_id"]);
|
|
2727
|
+
if (isError(sequence))
|
|
2728
|
+
return sequence;
|
|
2729
|
+
const current = sequence.toDocument();
|
|
2730
|
+
const source = current.source;
|
|
2731
|
+
if (!source)
|
|
2732
|
+
return { scenes: [] };
|
|
2733
|
+
const scenesParam = params["scenes"];
|
|
2734
|
+
// "all" drops the code entirely — there is nothing left to track, and
|
|
2735
|
+
// nothing left worth rebaking. Naming every currently-tracked scene one
|
|
2736
|
+
// by one is a different request: those scenes stop being tracked, but
|
|
2737
|
+
// the code stays, so a later edit that reintroduces a scene (or adds a
|
|
2738
|
+
// new one) still has somewhere to bake into.
|
|
2739
|
+
const dropCode = scenesParam === "all";
|
|
2740
|
+
let detachedNames;
|
|
2741
|
+
if (dropCode) {
|
|
2742
|
+
detachedNames = Object.keys(source.scenes);
|
|
2743
|
+
}
|
|
2744
|
+
else if (Array.isArray(scenesParam) &&
|
|
2745
|
+
scenesParam.length > 0 &&
|
|
2746
|
+
scenesParam.every((s) => typeof s === "string")) {
|
|
2747
|
+
detachedNames = scenesParam.filter((name) => name in source.scenes);
|
|
2748
|
+
}
|
|
2749
|
+
else {
|
|
2750
|
+
return {
|
|
2751
|
+
error: 'scenes must be "all" or a non-empty array of scene names.'
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
if (detachedNames.length === 0)
|
|
2755
|
+
return { scenes: [] };
|
|
2756
|
+
const remaining = { ...source.scenes };
|
|
2757
|
+
for (const name of detachedNames)
|
|
2758
|
+
delete remaining[name];
|
|
2759
|
+
const nextDocument = { ...current };
|
|
2760
|
+
if (dropCode) {
|
|
2761
|
+
delete nextDocument["source"];
|
|
2762
|
+
}
|
|
2763
|
+
else {
|
|
2764
|
+
nextDocument["source"] = { ...source, scenes: remaining };
|
|
2765
|
+
}
|
|
2766
|
+
const { TimelineSequence: TimelineSequenceModel, TimelineSequenceVersion } = await import("@nodetool-ai/models");
|
|
2767
|
+
await TimelineSequenceVersion.snapshot(sequence, {
|
|
2768
|
+
saveType: "manual",
|
|
2769
|
+
name: "Before detaching timeline code"
|
|
2770
|
+
});
|
|
2771
|
+
const saved = await TimelineSequenceModel.updateFieldsIfUnchanged(sequence.id, sequence.updated_at, { document: JSON.stringify(nextDocument) });
|
|
2772
|
+
if (!saved) {
|
|
2773
|
+
return {
|
|
2774
|
+
error: `Timeline ${sequence.id} was modified concurrently; nothing was written. Try again.`
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
return { timeline_id: saved.id, scenes: detachedNames };
|
|
2778
|
+
}
|
|
2779
|
+
};
|
|
2286
2780
|
const deleteTimeline = {
|
|
2287
2781
|
spec: deleteTimelineSpec,
|
|
2288
2782
|
impl: async (run, params) => {
|
|
@@ -2316,11 +2810,16 @@ export const TIMELINE_CAPABILITIES = [
|
|
|
2316
2810
|
isolateSubject,
|
|
2317
2811
|
trackObject,
|
|
2318
2812
|
...TIMELINE_NATIVE_MEDIA_CAPABILITIES,
|
|
2813
|
+
getTimelineCode,
|
|
2814
|
+
setTimelineCode,
|
|
2815
|
+
editTimelineCode,
|
|
2816
|
+
rebakeTimelineCode,
|
|
2817
|
+
detachTimelineCode,
|
|
2319
2818
|
deleteTimeline
|
|
2320
2819
|
];
|
|
2321
2820
|
export const module = {
|
|
2322
2821
|
module: "timelines",
|
|
2323
2822
|
exports: TIMELINE_CAPABILITIES
|
|
2324
2823
|
};
|
|
2325
|
-
export { listTimelines, createTimeline, getTimeline, listTimelineVersions, getTimelineVersion, createTimelineVersion, restoreTimelineVersion, deleteTimelineVersion, editTimeline, validateTimeline, setTimelineDocument, previewTimelineFrame, compareTimelineFrames, renderTimeline, bakeAudioAnimation, isolateSubject, trackObject, deleteTimeline };
|
|
2824
|
+
export { listTimelines, createTimeline, getTimeline, listTimelineVersions, getTimelineVersion, createTimelineVersion, restoreTimelineVersion, deleteTimelineVersion, editTimeline, validateTimeline, setTimelineDocument, previewTimelineFrame, compareTimelineFrames, renderTimeline, bakeAudioAnimation, isolateSubject, trackObject, getTimelineCode, setTimelineCode, editTimelineCode, rebakeTimelineCode, detachTimelineCode, deleteTimeline };
|
|
2326
2825
|
//# sourceMappingURL=timelines.js.map
|