flomotion 0.2.0 → 0.4.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/CHANGELOG.md +58 -2
- package/LICENSE +1 -1
- package/README.md +29 -20
- package/dist/bin.mjs +6279 -2582
- package/dist/chunks/{chunk-QG2MOGKO.mjs → chunk-IGG2VZB6.mjs} +178 -14
- package/dist/chunks/{chunk-AVP5XX4G.mjs → chunk-PEC2UVQY.mjs} +138 -108
- package/dist/chunks/{chunk-IUSYVGP6.mjs → chunk-TTHAZGRG.mjs} +7 -7
- package/dist/chunks/{chunk-JCLRAREG.mjs → chunk-XWY6CUY6.mjs} +16 -16
- package/dist/chunks/{lock-MLQUU7X3.mjs → lock-WGPR7CJ2.mjs} +3 -3
- package/dist/components.mjs +19 -13
- package/dist/core.mjs +12 -12
- package/dist/hooks.mjs +8 -12
- package/dist/jsx-dev-runtime.mjs +1 -1
- package/dist/jsx-runtime.mjs +1 -1
- package/docs/components/Adjustment.md +2 -2
- package/docs/components/Audio.md +2 -2
- package/docs/components/B.md +3 -3
- package/docs/components/Captions.md +2 -2
- package/docs/components/Card.md +2 -2
- package/docs/components/Center.md +2 -2
- package/docs/components/Chart.md +2 -2
- package/docs/components/Chip.md +2 -2
- package/docs/components/Circle.md +2 -2
- package/docs/components/Clip.md +2 -2
- package/docs/components/CodeBlock.md +2 -2
- package/docs/components/Composition.md +2 -2
- package/docs/components/Counter.md +2 -2
- package/docs/components/Cursor.md +2 -2
- package/docs/components/Fonts.md +2 -2
- package/docs/components/GlassCard.md +2 -2
- package/docs/components/Grid.md +2 -2
- package/docs/components/Group.md +2 -2
- package/docs/components/Highlight.md +3 -3
- package/docs/components/I.md +3 -3
- package/docs/components/Image.md +2 -2
- package/docs/components/Line.md +2 -2
- package/docs/components/Marker.md +2 -2
- package/docs/components/Path.md +2 -2
- package/docs/components/Polygon.md +2 -2
- package/docs/components/Precomp.md +2 -2
- package/docs/components/ProgressRing.md +2 -2
- package/docs/components/Rect.md +2 -2
- package/docs/components/RoundRect.md +2 -2
- package/docs/components/SafeArea.md +2 -2
- package/docs/components/Scene.md +2 -2
- package/docs/components/Sequence.md +2 -2
- package/docs/components/Span.md +3 -3
- package/docs/components/SplitText.md +2 -2
- package/docs/components/Stack.md +2 -2
- package/docs/components/Stagger.md +2 -2
- package/docs/components/Strike.md +3 -3
- package/docs/components/Svg.md +2 -2
- package/docs/components/Text.md +2 -2
- package/docs/components/Title.md +2 -2
- package/docs/components/Track.md +2 -2
- package/docs/components/UIMock.md +2 -2
- package/docs/components/Underline.md +3 -3
- package/docs/components/Video.md +2 -2
- package/docs/gate2-manual-checklist.md +10 -9
- package/docs/hyperframes-notes.md +20 -20
- package/docs/later.md +23 -16
- package/docs/spec.md +83 -22
- package/package.json +1 -2
- package/skills/flomotion-studio/CHANGELOG.md +39 -0
- package/skills/flomotion-studio/README.md +81 -0
- package/skills/flomotion-studio/SKILL.md +90 -0
- package/skills/flomotion-studio/config/thresholds.md +191 -0
- package/skills/flomotion-studio/dataset/annotations/ansarada.md +53 -0
- package/skills/flomotion-studio/dataset/annotations/canva.md +49 -0
- package/skills/flomotion-studio/dataset/annotations/clickup.md +60 -0
- package/skills/flomotion-studio/dataset/annotations/gemini-stub.md +39 -0
- package/skills/flomotion-studio/dataset/annotations/monday.md +51 -0
- package/skills/flomotion-studio/dataset/annotations/phantom.md +53 -0
- package/skills/flomotion-studio/dataset/annotations/slack.md +54 -0
- package/skills/flomotion-studio/dataset/annotations/unknown-7.md +39 -0
- package/skills/flomotion-studio/dataset/annotations/vaylens.md +53 -0
- package/skills/flomotion-studio/dataset/brief-template.md +110 -0
- package/skills/flomotion-studio/dataset/rating-sheet.md +28 -0
- package/skills/flomotion-studio/dataset/schema.md +44 -0
- package/skills/flomotion-studio/evals/README.md +33 -0
- package/skills/flomotion-studio/evals/briefs/briefs.md +25 -0
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/kinetic.scene.tsx +1 -1
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/minimal.scene.tsx +1 -1
- package/skills/{motion-studio/looks → flomotion-studio/examples/engine-looks}/sketch.scene.tsx +1 -1
- package/skills/flomotion-studio/examples/flomotion-ad/LOG.md +39 -0
- package/skills/flomotion-studio/examples/flomotion-ad/brief.md +41 -0
- package/skills/flomotion-studio/examples/flomotion-ad/ideas.md +12 -0
- package/skills/flomotion-studio/examples/flomotion-ad/project.tsx +1694 -0
- package/skills/flomotion-studio/examples/flomotion-ad/script.md +28 -0
- package/skills/flomotion-studio/examples/flomotion-ad/storyboard.md +93 -0
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/README.md +1 -1
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/make-assets.sh +1 -1
- package/skills/{motion-studio → flomotion-studio}/examples/saas-promo/project.tsx +3 -3
- package/skills/flomotion-studio/looks/bold-kinetic.md +122 -0
- package/skills/flomotion-studio/looks/clean-light.md +122 -0
- package/skills/flomotion-studio/looks/dark-product.md +120 -0
- package/skills/flomotion-studio/references/anti-patterns.md +36 -0
- package/skills/flomotion-studio/references/assets.md +42 -0
- package/skills/flomotion-studio/references/block-recipes.md +249 -0
- package/skills/flomotion-studio/references/blocks.md +114 -0
- package/skills/flomotion-studio/references/catalogue.md +53 -0
- package/skills/flomotion-studio/references/checks.md +87 -0
- package/skills/flomotion-studio/references/clean-styles.md +56 -0
- package/skills/{motion-studio → flomotion-studio}/references/components.md +94 -94
- package/skills/flomotion-studio/references/copywriting.md +64 -0
- package/skills/flomotion-studio/references/decision-tables.md +80 -0
- package/skills/flomotion-studio/references/interview.md +145 -0
- package/skills/flomotion-studio/references/looks.md +45 -0
- package/skills/flomotion-studio/references/mcp-tools.md +70 -0
- package/skills/flomotion-studio/references/motion.md +68 -0
- package/skills/flomotion-studio/references/patterns.md +45 -0
- package/skills/flomotion-studio/references/review.md +56 -0
- package/skills/flomotion-studio/references/script-craft.md +59 -0
- package/skills/flomotion-studio/references/sound.md +57 -0
- package/skills/flomotion-studio/references/story-templates.md +52 -0
- package/skills/flomotion-studio/references/storyboard.md +62 -0
- package/skills/flomotion-studio/references/styles.md +70 -0
- package/skills/flomotion-studio/references/transitions.md +62 -0
- package/skills/flomotion-studio/references/typography.md +46 -0
- package/skills/flomotion-studio/references/workflow.md +96 -0
- package/skills/motion-studio/SKILL.md +0 -87
- package/skills/motion-studio/looks/kinetic.json +0 -89
- package/skills/motion-studio/looks/minimal.json +0 -91
- package/skills/motion-studio/looks/sketch.json +0 -83
- package/skills/motion-studio/references/motion.md +0 -73
- package/skills/motion-studio/references/review.md +0 -32
- package/skills/motion-studio/references/saas-promo.md +0 -29
- package/skills/motion-studio/references/sound.md +0 -47
- package/skills/motion-studio/references/typography.md +0 -50
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
FlomotionError,
|
|
4
4
|
__export,
|
|
5
5
|
cloneNode,
|
|
6
6
|
findById,
|
|
7
7
|
flattenChildren,
|
|
8
8
|
isNode,
|
|
9
9
|
walk
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TTHAZGRG.mjs";
|
|
11
11
|
|
|
12
12
|
// packages/core/src/config.ts
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var ID_PREFIX = "
|
|
13
|
+
var FLOMOTION_NAME = "flomotion";
|
|
14
|
+
var FLOMOTION_SCOPE = "@flomotion";
|
|
15
|
+
var FLOMOTION_DIR = ".flomotion";
|
|
16
|
+
var ID_PREFIX = "fm_";
|
|
17
17
|
|
|
18
18
|
// packages/core/src/values/time.ts
|
|
19
19
|
var round6 = (n) => Math.round(n * 1e6) / 1e6;
|
|
@@ -45,7 +45,7 @@ function parseTimeLiteral(input2, fps) {
|
|
|
45
45
|
throw bad(input2);
|
|
46
46
|
}
|
|
47
47
|
function bad(input2) {
|
|
48
|
-
return new
|
|
48
|
+
return new FlomotionError("invalid_time", `Invalid time: ${JSON.stringify(input2)}`, {
|
|
49
49
|
fix: 'Use seconds (1.5), "1.5s", "36f" or "00:00:01:12".'
|
|
50
50
|
});
|
|
51
51
|
}
|
|
@@ -89,7 +89,7 @@ function parseLength(input2) {
|
|
|
89
89
|
if (typeof input2 === "number") return { unit: "px", value: input2 };
|
|
90
90
|
const m = input2.trim().match(/^(-?\d+(?:\.\d+)?|-?\.\d+)\s*(px|%|vw|vh)?$/);
|
|
91
91
|
if (!m) {
|
|
92
|
-
throw new
|
|
92
|
+
throw new FlomotionError("invalid_value", `Invalid length: ${JSON.stringify(input2)}`, {
|
|
93
93
|
fix: 'Use pixels (120), "50%", "10vw" or "10vh".'
|
|
94
94
|
});
|
|
95
95
|
}
|
|
@@ -114,7 +114,7 @@ function parseAngle(input2) {
|
|
|
114
114
|
if (typeof input2 === "number") return input2;
|
|
115
115
|
const m = input2.trim().match(/^(-?\d+(?:\.\d+)?|-?\.\d+)\s*(deg|turn|rad|grad)?$/);
|
|
116
116
|
if (!m) {
|
|
117
|
-
throw new
|
|
117
|
+
throw new FlomotionError("invalid_value", `Invalid angle: ${JSON.stringify(input2)}`, {
|
|
118
118
|
fix: 'Use degrees (45) or "0.25turn".'
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -136,7 +136,7 @@ var clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
|
|
|
136
136
|
function parseColor(input2) {
|
|
137
137
|
const s = input2.trim().toLowerCase();
|
|
138
138
|
if (s.startsWith("$")) {
|
|
139
|
-
throw new
|
|
139
|
+
throw new FlomotionError("invalid_value", `Unresolved color token ${input2}`, {
|
|
140
140
|
fix: "Define the token in the Theme/Look or resolve the project first."
|
|
141
141
|
});
|
|
142
142
|
}
|
|
@@ -188,7 +188,7 @@ var NAMED = {
|
|
|
188
188
|
grey: "#808080"
|
|
189
189
|
};
|
|
190
190
|
function badColor(input2) {
|
|
191
|
-
return new
|
|
191
|
+
return new FlomotionError("invalid_value", `Invalid color: ${JSON.stringify(input2)}`, {
|
|
192
192
|
fix: 'Use "#rrggbb", "rgb(...)", "oklch(L C H)" or a "$token".'
|
|
193
193
|
});
|
|
194
194
|
}
|
|
@@ -333,7 +333,7 @@ function parseEase(spec) {
|
|
|
333
333
|
}
|
|
334
334
|
var evalEase = (spec, p) => parseEase(spec)(clamp01(p));
|
|
335
335
|
function badEase(spec) {
|
|
336
|
-
throw new
|
|
336
|
+
throw new FlomotionError("invalid_value", `Unknown ease: ${JSON.stringify(spec)}`, {
|
|
337
337
|
fix: 'Use e.g. "expo.out", "back.out(1.7)", "bezier(.2,.8,.2,1)" or "spring(180,18)".'
|
|
338
338
|
});
|
|
339
339
|
}
|
|
@@ -539,7 +539,7 @@ function normalizeTrack(raw, opts) {
|
|
|
539
539
|
keys.sort((a, b) => a.t - b.t);
|
|
540
540
|
for (let i = 1; i < keys.length; i++) {
|
|
541
541
|
if (keys[i].t === keys[i - 1].t) {
|
|
542
|
-
throw new
|
|
542
|
+
throw new FlomotionError(
|
|
543
543
|
"duplicate_keyframe",
|
|
544
544
|
`Two keyframes share time ${keys[i].t}s on one property`,
|
|
545
545
|
{ fix: "Give each keyframe on a property a unique time (move one by at least one frame)." }
|
|
@@ -653,7 +653,7 @@ function numSlopes(track, comp, mode) {
|
|
|
653
653
|
}
|
|
654
654
|
function evalNumber(track, tIn) {
|
|
655
655
|
const ks = track.keys;
|
|
656
|
-
if (ks.length === 0) throw new
|
|
656
|
+
if (ks.length === 0) throw new FlomotionError("invalid_value", "Keyframe track has no keys");
|
|
657
657
|
const t = loopTime(track, tIn);
|
|
658
658
|
if (t <= ks[0].t || ks.length === 1) return ks[0].v;
|
|
659
659
|
const last = ks[ks.length - 1];
|
|
@@ -673,7 +673,7 @@ function evalNumber(track, tIn) {
|
|
|
673
673
|
}
|
|
674
674
|
function evalVec(track, tIn) {
|
|
675
675
|
const ks = track.keys;
|
|
676
|
-
if (ks.length === 0) throw new
|
|
676
|
+
if (ks.length === 0) throw new FlomotionError("invalid_value", "Keyframe track has no keys");
|
|
677
677
|
const t = loopTime(track, tIn);
|
|
678
678
|
if (t <= ks[0].t || ks.length === 1) return [...ks[0].v];
|
|
679
679
|
const last = ks[ks.length - 1];
|
|
@@ -752,7 +752,7 @@ function makeCtx(base, t, value) {
|
|
|
752
752
|
value,
|
|
753
753
|
prop: (ref2) => {
|
|
754
754
|
if (!base.prop) {
|
|
755
|
-
throw new
|
|
755
|
+
throw new FlomotionError(
|
|
756
756
|
"unresolved_ref",
|
|
757
757
|
`ctx.prop(${JSON.stringify(ref2)}) is not available here`
|
|
758
758
|
);
|
|
@@ -762,12 +762,12 @@ function makeCtx(base, t, value) {
|
|
|
762
762
|
noise: (seed = 1, freq = 1) => noise1D(t * freq, seed),
|
|
763
763
|
beat: (n) => {
|
|
764
764
|
if (!base.beat)
|
|
765
|
-
throw new
|
|
765
|
+
throw new FlomotionError("unresolved_ref", "ctx.beat needs a tempo or beat map");
|
|
766
766
|
return base.beat(n);
|
|
767
767
|
},
|
|
768
768
|
word: (voiceId, i) => {
|
|
769
769
|
if (!base.word)
|
|
770
|
-
throw new
|
|
770
|
+
throw new FlomotionError("unresolved_ref", "ctx.word needs a Voiceover with timings");
|
|
771
771
|
return base.word(voiceId, i);
|
|
772
772
|
},
|
|
773
773
|
lerp,
|
|
@@ -20973,7 +20973,7 @@ function group(fields, o = {}) {
|
|
|
20973
20973
|
function parseControlValue(c, v, name = "value") {
|
|
20974
20974
|
const r = c.prop.safeParse(v);
|
|
20975
20975
|
if (!r.success) {
|
|
20976
|
-
throw new
|
|
20976
|
+
throw new FlomotionError(
|
|
20977
20977
|
"schema_violation",
|
|
20978
20978
|
`${name}: ${r.error.issues.map((i) => i.message).join("; ")}`
|
|
20979
20979
|
);
|
|
@@ -21022,7 +21022,7 @@ var BLEND_MODES = [
|
|
|
21022
21022
|
"add"
|
|
21023
21023
|
];
|
|
21024
21024
|
var timingControls = () => ({
|
|
21025
|
-
id: text({ label: "Id", description: "Stable unique id. Written back as
|
|
21025
|
+
id: text({ label: "Id", description: "Stable unique id. Written back as fm_\u2026 when missing." }),
|
|
21026
21026
|
label: text({ label: "Label" }),
|
|
21027
21027
|
start: time3({
|
|
21028
21028
|
label: "Start",
|
|
@@ -21144,10 +21144,10 @@ function zodShape(controls, form) {
|
|
|
21144
21144
|
}
|
|
21145
21145
|
function makeDef(kind, name, base) {
|
|
21146
21146
|
if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) {
|
|
21147
|
-
throw new
|
|
21147
|
+
throw new FlomotionError("schema_violation", `Component name "${name}" must be PascalCase`);
|
|
21148
21148
|
}
|
|
21149
21149
|
if (!base.own || typeof base.own !== "object") {
|
|
21150
|
-
throw new
|
|
21150
|
+
throw new FlomotionError(
|
|
21151
21151
|
"schema_violation",
|
|
21152
21152
|
`${name}: components without a schema are rejected`,
|
|
21153
21153
|
{
|
|
@@ -21159,7 +21159,7 @@ function makeDef(kind, name, base) {
|
|
|
21159
21159
|
if (!controls.id)
|
|
21160
21160
|
controls.id = text({
|
|
21161
21161
|
label: "Id",
|
|
21162
|
-
description: "Stable unique id. Written back as
|
|
21162
|
+
description: "Stable unique id. Written back as fm_\u2026 when missing."
|
|
21163
21163
|
});
|
|
21164
21164
|
const def = {
|
|
21165
21165
|
kind,
|
|
@@ -21233,7 +21233,7 @@ function defineEffect(name, spec) {
|
|
|
21233
21233
|
});
|
|
21234
21234
|
return makeTag(def);
|
|
21235
21235
|
}
|
|
21236
|
-
var Fragment = /* @__PURE__ */ Symbol.for("
|
|
21236
|
+
var Fragment = /* @__PURE__ */ Symbol.for("flomotion.fragment");
|
|
21237
21237
|
function lev(a, b) {
|
|
21238
21238
|
const d = Array.from({ length: a.length + 1 }, (_, i) => [
|
|
21239
21239
|
i,
|
|
@@ -21321,7 +21321,7 @@ function assertProps(def, props, id) {
|
|
|
21321
21321
|
const issues = validateProps(def, props);
|
|
21322
21322
|
if (issues.length) {
|
|
21323
21323
|
const head = issues[0];
|
|
21324
|
-
throw new
|
|
21324
|
+
throw new FlomotionError("schema_violation", `${id ? `#${id} ` : ""}${head.message}`, {
|
|
21325
21325
|
fix: head.fix,
|
|
21326
21326
|
details: issues
|
|
21327
21327
|
});
|
|
@@ -21380,7 +21380,7 @@ function mcpToolSchemas(reg) {
|
|
|
21380
21380
|
type: { type: "string", enum: comps.map((d) => d.name) },
|
|
21381
21381
|
props: {
|
|
21382
21382
|
type: "object",
|
|
21383
|
-
description: "Validated against the component schema (see
|
|
21383
|
+
description: "Validated against the component schema (see flomotion://schema/{component})."
|
|
21384
21384
|
},
|
|
21385
21385
|
at: { type: "integer", description: "Child index to insert at (default: append)." }
|
|
21386
21386
|
},
|
|
@@ -21613,7 +21613,7 @@ var transition = () => group(
|
|
|
21613
21613
|
}
|
|
21614
21614
|
);
|
|
21615
21615
|
var ident = () => ({
|
|
21616
|
-
id: text({ label: "Id", description: "Stable unique id. Written back as
|
|
21616
|
+
id: text({ label: "Id", description: "Stable unique id. Written back as fm_\u2026 when missing." })
|
|
21617
21617
|
});
|
|
21618
21618
|
var Composition = defineComponent("Composition", {
|
|
21619
21619
|
category: "structure",
|
|
@@ -21833,7 +21833,7 @@ var err = (env, i) => env.issues.push(i);
|
|
|
21833
21833
|
function resolveProject(source, opts = {}) {
|
|
21834
21834
|
const reg = opts.registry ?? registry2;
|
|
21835
21835
|
if (source.name !== "Composition") {
|
|
21836
|
-
throw new
|
|
21836
|
+
throw new FlomotionError(
|
|
21837
21837
|
"schema_violation",
|
|
21838
21838
|
`Project root must be <Composition>, got <${source.name}>`,
|
|
21839
21839
|
{
|
|
@@ -21885,7 +21885,7 @@ function resolveProject(source, opts = {}) {
|
|
|
21885
21885
|
function build3(env, node2, parent, depth, generatedIds) {
|
|
21886
21886
|
const def = env.reg.get(node2.name);
|
|
21887
21887
|
if (!def) {
|
|
21888
|
-
throw new
|
|
21888
|
+
throw new FlomotionError("unknown_component", `Unknown component <${node2.name}>`, {
|
|
21889
21889
|
fix: `Known: ${env.reg.names().slice(0, 40).join(", ")}\u2026`
|
|
21890
21890
|
});
|
|
21891
21891
|
}
|
|
@@ -22150,7 +22150,7 @@ var timed = /* @__PURE__ */ new WeakSet();
|
|
|
22150
22150
|
function timeNode(env, n, visiting) {
|
|
22151
22151
|
if (timed.has(n)) return;
|
|
22152
22152
|
if (visiting.has(n.id)) {
|
|
22153
|
-
throw new
|
|
22153
|
+
throw new FlomotionError("unresolved_ref", `Timing reference cycle through #${n.id}`, {
|
|
22154
22154
|
fix: "Break the cycle: a start/end cannot depend on itself."
|
|
22155
22155
|
});
|
|
22156
22156
|
}
|
|
@@ -22314,7 +22314,7 @@ function resolveProps(env, n) {
|
|
|
22314
22314
|
const words = env.opts.words?.(voice);
|
|
22315
22315
|
const w = words?.[i];
|
|
22316
22316
|
if (!w)
|
|
22317
|
-
throw new
|
|
22317
|
+
throw new FlomotionError(
|
|
22318
22318
|
"unresolved_ref",
|
|
22319
22319
|
`ctx.word(${JSON.stringify(voice)}, ${i}) has no timing`,
|
|
22320
22320
|
{ fix: "Run transcribe for this voice first." }
|
|
@@ -22474,7 +22474,7 @@ function resolveKeyTime(env, n, t) {
|
|
|
22474
22474
|
case "marker": {
|
|
22475
22475
|
const m = env.markers.find((x) => x.id === spec.name || x.label === spec.name);
|
|
22476
22476
|
if (!m)
|
|
22477
|
-
throw new
|
|
22477
|
+
throw new FlomotionError("unresolved_ref", `Unknown marker "${spec.name}"`, {
|
|
22478
22478
|
fix: "Add a <Marker label=\u2026/>."
|
|
22479
22479
|
});
|
|
22480
22480
|
return m.at + spec.offset - n.parentStart;
|
|
@@ -22482,7 +22482,7 @@ function resolveKeyTime(env, n, t) {
|
|
|
22482
22482
|
case "ref": {
|
|
22483
22483
|
const target = env.byId.get(spec.id);
|
|
22484
22484
|
if (!target)
|
|
22485
|
-
throw new
|
|
22485
|
+
throw new FlomotionError("unresolved_ref", `Unknown element #${spec.id}`, {
|
|
22486
22486
|
fix: "Check the id."
|
|
22487
22487
|
});
|
|
22488
22488
|
const v = spec.edge === "start" ? target.start : spec.edge === "end" ? target.end : target.duration;
|
|
@@ -22520,11 +22520,11 @@ function normFill(v, tok) {
|
|
|
22520
22520
|
function readRef(env, ref2, t) {
|
|
22521
22521
|
const m = ref2.match(/^#([\w-]+)\.([\w.]+)$/);
|
|
22522
22522
|
if (!m)
|
|
22523
|
-
throw new
|
|
22523
|
+
throw new FlomotionError("unresolved_ref", `Bad ref ${JSON.stringify(ref2)}`, {
|
|
22524
22524
|
fix: 'Use "#id.prop".'
|
|
22525
22525
|
});
|
|
22526
22526
|
const node2 = env.byId.get(m[1]);
|
|
22527
|
-
if (!node2) throw new
|
|
22527
|
+
if (!node2) throw new FlomotionError("unresolved_ref", `Unknown element #${m[1]}`);
|
|
22528
22528
|
const v = node2.props[m[2]];
|
|
22529
22529
|
if (v && typeof v === "object" && "kind" in v) return evalSignal(v, t);
|
|
22530
22530
|
return v;
|
|
@@ -22709,7 +22709,7 @@ function transitionPlan(scene, prev, spec, comp) {
|
|
|
22709
22709
|
const overlay = (suffix, color2, opacityAt) => {
|
|
22710
22710
|
const id = `${scene.id}-${suffix}`;
|
|
22711
22711
|
plan.overlays.push({
|
|
22712
|
-
html: `<div id="${id}" class="
|
|
22712
|
+
html: `<div id="${id}" class="fm-tx" style="position:absolute;inset:0;background:${color2};opacity:0;z-index:9000;pointer-events:none"></div>`,
|
|
22713
22713
|
channels: [{ target: `#${id}`, prop: "opacity", signal: num4(opacityAt), span }]
|
|
22714
22714
|
});
|
|
22715
22715
|
};
|
|
@@ -23085,7 +23085,7 @@ function buildIR(project, opts = {}) {
|
|
|
23085
23085
|
else html += kids;
|
|
23086
23086
|
layer.html = html;
|
|
23087
23087
|
if (n === rootNode) {
|
|
23088
|
-
return `<div id="${n.id}-xf" class="
|
|
23088
|
+
return `<div id="${n.id}-xf" class="fm-xf" style="position:absolute;inset:0">${layer.html}${layer.overlays.join("")}</div>`;
|
|
23089
23089
|
}
|
|
23090
23090
|
return hostHtml(layer, n);
|
|
23091
23091
|
};
|
|
@@ -23306,21 +23306,21 @@ function hostHtml(layer, node2) {
|
|
|
23306
23306
|
const host = styleAttr(
|
|
23307
23307
|
layer.inFlow ? { position: "relative", "z-index": String(z2), ...layer.hostStyle } : { position: "absolute", inset: "0", "z-index": String(z2), ...layer.hostStyle }
|
|
23308
23308
|
);
|
|
23309
|
-
const attrs = `id="${node2.id}" class="clip
|
|
23309
|
+
const attrs = `id="${node2.id}" class="clip fm-l fm-${node2.name.toLowerCase()}" data-start="${layer.start}" data-duration="${Math.round(dur * 1e6) / 1e6}"${layer.trackIndex !== void 0 ? ` data-track-index="${layer.trackIndex}"` : ""} data-cc="${node2.name}"`;
|
|
23310
23310
|
if (!layer.visual) {
|
|
23311
23311
|
return `<div ${attrs} style="${host}">${layer.html}${layer.overlays.join("")}</div>`;
|
|
23312
23312
|
}
|
|
23313
23313
|
const xfBase = layer.inFlow ? { position: "relative" } : layer.xfStyle["left"] || layer.xfStyle["top"] || layer.xfStyle["width"] ? { position: "absolute" } : { position: "absolute", inset: "0" };
|
|
23314
23314
|
const xf = styleAttr({ ...xfBase, ...layer.xfStyle });
|
|
23315
|
-
return `<div ${attrs} style="${host}"><div id="${node2.id}-xf" class="
|
|
23315
|
+
return `<div ${attrs} style="${host}"><div id="${node2.id}-xf" class="fm-xf" style="${xf}">${layer.html}${layer.overlays.join("")}</div></div>`;
|
|
23316
23316
|
}
|
|
23317
23317
|
|
|
23318
23318
|
// packages/core/src/compile/html.ts
|
|
23319
23319
|
var BASE_CSS = `*{margin:0;padding:0;box-sizing:border-box}
|
|
23320
23320
|
html,body{margin:0;overflow:hidden}
|
|
23321
|
-
.
|
|
23322
|
-
.
|
|
23323
|
-
.
|
|
23321
|
+
.fm-l{pointer-events:none}
|
|
23322
|
+
.fm-xf{will-change:auto}
|
|
23323
|
+
.fm-xf>*{max-width:none}`;
|
|
23324
23324
|
function fontFaceCss(fonts) {
|
|
23325
23325
|
const rules = [];
|
|
23326
23326
|
for (const f of fonts) {
|
|
@@ -23353,7 +23353,7 @@ function walkAll(layer, fn) {
|
|
|
23353
23353
|
layer.children.forEach((c) => walkAll(c, fn));
|
|
23354
23354
|
}
|
|
23355
23355
|
var RUNTIME = `(function(){
|
|
23356
|
-
var G=window.gsap, S=window.
|
|
23356
|
+
var G=window.gsap, S=window.__fmStatic||[], B=window.__fmBaked||[], D=window.__fmDur||1, els={};
|
|
23357
23357
|
function el(sel){return els[sel]||(els[sel]=document.querySelector(sel));}
|
|
23358
23358
|
function put(target,props){var e=el(target); if(!e)return; if(props.__text!==undefined){e.textContent=props.__text; delete props.__text;} var at=props.__attr; if(at){for(var k in at)e.setAttribute(k,at[k]); delete props.__attr;} if(Object.keys(props).length)G.set(e,props);}
|
|
23359
23359
|
function groupBy(list){var m={};list.forEach(function(c){(m[c[0]]=m[c[0]]||[]).push(c);});return m;}
|
|
@@ -23381,9 +23381,9 @@ var RUNTIME = `(function(){
|
|
|
23381
23381
|
var tl=G.timeline({paused:true});
|
|
23382
23382
|
tl.to(proxy,{t:D,duration:D,ease:"none",onUpdate:function(){renderAt(proxy.t);}},0);
|
|
23383
23383
|
renderAt(0);
|
|
23384
|
-
window.
|
|
23384
|
+
window.__fmRenderAt=renderAt;
|
|
23385
23385
|
window.__timelines=window.__timelines||{};
|
|
23386
|
-
window.__timelines[window.
|
|
23386
|
+
window.__timelines[window.__fmId]=tl;
|
|
23387
23387
|
tl.seek(0);
|
|
23388
23388
|
})();`;
|
|
23389
23389
|
function compileProject(project, opts = {}) {
|
|
@@ -23415,10 +23415,10 @@ function compileProject(project, opts = {}) {
|
|
|
23415
23415
|
const bakedRows = acc.baked.map((b) => [b.target, b.prop, b.t0, b.hz, b.samples, b.unit ?? ""]);
|
|
23416
23416
|
const gsap = opts.gsapPath ?? "vendor/gsap.min.js";
|
|
23417
23417
|
const res = comp.width >= comp.height ? comp.width === comp.height ? "square" : "landscape" : "portrait";
|
|
23418
|
-
const mix = opts.mix ? `<audio id="
|
|
23418
|
+
const mix = opts.mix ? `<audio id="fm-mix" src="${opts.mix.src}" data-start="0" data-duration="${numFmt(opts.mix.duration ?? comp.duration)}" data-volume="1"></audio>` : "";
|
|
23419
23419
|
const defs = acc.defs.length ? `<svg width="0" height="0" style="position:absolute" aria-hidden="true"><defs>${acc.defs.join("")}</defs></svg>` : "";
|
|
23420
23420
|
const html = `<!doctype html>
|
|
23421
|
-
<html lang="en" data-resolution="${res}" data-generator="${
|
|
23421
|
+
<html lang="en" data-resolution="${res}" data-generator="${FLOMOTION_NAME}">
|
|
23422
23422
|
<head>
|
|
23423
23423
|
<meta charset="UTF-8">
|
|
23424
23424
|
<meta name="viewport" content="width=${comp.width}, height=${comp.height}">
|
|
@@ -23437,10 +23437,10 @@ ${root.html}
|
|
|
23437
23437
|
${mix}
|
|
23438
23438
|
</div>
|
|
23439
23439
|
<script>
|
|
23440
|
-
window.
|
|
23441
|
-
window.
|
|
23442
|
-
window.
|
|
23443
|
-
window.
|
|
23440
|
+
window.__fmId=${JSON.stringify(comp.id)};
|
|
23441
|
+
window.__fmDur=${numFmt(comp.duration)};
|
|
23442
|
+
window.__fmStatic=${JSON.stringify(staticRows)};
|
|
23443
|
+
window.__fmBaked=${JSON.stringify(bakedRows)};
|
|
23444
23444
|
</script>
|
|
23445
23445
|
<script>
|
|
23446
23446
|
${RUNTIME}
|
|
@@ -23608,7 +23608,7 @@ function lintProject(project, ctx = {}) {
|
|
|
23608
23608
|
rule: "font-no-ledger",
|
|
23609
23609
|
message: `Font "${f.id}" (${f.family}) has no ledger entry`,
|
|
23610
23610
|
id: f.id,
|
|
23611
|
-
fix: "Run `
|
|
23611
|
+
fix: "Run `flomotion fonts sync`."
|
|
23612
23612
|
});
|
|
23613
23613
|
else {
|
|
23614
23614
|
const lic = ctx.ledger.entries.find(
|
|
@@ -23630,7 +23630,7 @@ function lintProject(project, ctx = {}) {
|
|
|
23630
23630
|
rule: "font-network-url",
|
|
23631
23631
|
message: `Font "${f.id}" is loaded from a URL at render time`,
|
|
23632
23632
|
id: f.id,
|
|
23633
|
-
fix: "Run `
|
|
23633
|
+
fix: "Run `flomotion fonts sync` so the file is local."
|
|
23634
23634
|
});
|
|
23635
23635
|
}
|
|
23636
23636
|
for (const m of ctx.fontLockProblems?.(declIds) ?? [])
|
|
@@ -23638,7 +23638,7 @@ function lintProject(project, ctx = {}) {
|
|
|
23638
23638
|
severity: "error",
|
|
23639
23639
|
rule: "font-lock",
|
|
23640
23640
|
message: m,
|
|
23641
|
-
fix: "Run `
|
|
23641
|
+
fix: "Run `flomotion fonts sync`."
|
|
23642
23642
|
});
|
|
23643
23643
|
const minSize = Math.min(comp.width, comp.height) * cfg.minTextSize;
|
|
23644
23644
|
const safePreset = cfg.safe ?? (comp.height > comp.width ? "reels" : "none");
|
|
@@ -23720,8 +23720,8 @@ function lintProject(project, ctx = {}) {
|
|
|
23720
23720
|
});
|
|
23721
23721
|
else if (ctx.ledger) {
|
|
23722
23722
|
const e = ctx.ledger.entries.find((x) => `${x.source}:${x.id}` === src);
|
|
23723
|
-
const family = /^CC0/i.test(e?.license ?? "") ? "CC0" : /^CC-BY(-\d|$)/i.test(e?.license ?? "") ? "CC-BY" : e?.license;
|
|
23724
|
-
if (e && !cfg.soundLicenses.includes(family))
|
|
23723
|
+
const family = /^CC0/i.test(e?.license ?? "") ? "CC0" : /^CC-BY(-\d|$)/i.test(e?.license ?? "") ? "CC-BY" : e?.source === "local" || e?.source === "own" || /^(user-supplied|owned|licensed|used with permission)/i.test(e?.license ?? "") ? "own" : e?.license;
|
|
23724
|
+
if (e && family !== "own" && !cfg.soundLicenses.includes(family))
|
|
23725
23725
|
out.push({
|
|
23726
23726
|
severity: "error",
|
|
23727
23727
|
rule: "license-blocked",
|
|
@@ -23742,17 +23742,42 @@ function lintProject(project, ctx = {}) {
|
|
|
23742
23742
|
});
|
|
23743
23743
|
}
|
|
23744
23744
|
}
|
|
23745
|
-
|
|
23745
|
+
const MANAGED = /^(\.\/)?assets\/(third-party|captures|brand|imported)\//;
|
|
23746
|
+
for (const n of nodes.filter((x) => ["Image", "Svg", "Video", "Clip"].includes(x.name))) {
|
|
23746
23747
|
const src = String(n.raw.src ?? "");
|
|
23747
|
-
if (src
|
|
23748
|
+
if (!src || /^data:|^</.test(src.trim())) continue;
|
|
23749
|
+
if (ctx.fileExists && !ctx.fileExists(src)) {
|
|
23748
23750
|
out.push({
|
|
23749
23751
|
severity: "error",
|
|
23750
23752
|
rule: "asset-missing",
|
|
23751
23753
|
message: `#${n.id}: file ${src} does not exist`,
|
|
23752
23754
|
id: n.id,
|
|
23753
23755
|
prop: "src",
|
|
23754
|
-
fix: "Add the file to the project."
|
|
23756
|
+
fix: "Add the file to the project, or fetch it with asset_get / asset_capture / asset_import."
|
|
23755
23757
|
});
|
|
23758
|
+
continue;
|
|
23759
|
+
}
|
|
23760
|
+
if (ctx.ledger && MANAGED.test(src)) {
|
|
23761
|
+
const rel = src.replace(/^\.\//, "");
|
|
23762
|
+
const e = ctx.ledger.entries.find((x) => x.file === rel);
|
|
23763
|
+
if (!e)
|
|
23764
|
+
out.push({
|
|
23765
|
+
severity: "error",
|
|
23766
|
+
rule: "asset-no-ledger",
|
|
23767
|
+
message: `#${n.id}: ${src} has no ledger entry`,
|
|
23768
|
+
id: n.id,
|
|
23769
|
+
prop: "src",
|
|
23770
|
+
fix: "Fetch it with asset_get, asset_capture or asset_import so its licence is recorded."
|
|
23771
|
+
});
|
|
23772
|
+
else if (!e.commercialOk)
|
|
23773
|
+
out.push({
|
|
23774
|
+
severity: "error",
|
|
23775
|
+
rule: "license-blocked",
|
|
23776
|
+
message: `#${n.id}: ${src} (${e.license}) is not cleared for commercial use`,
|
|
23777
|
+
id: n.id,
|
|
23778
|
+
fix: "Pick an asset with a licence that allows it, or get permission and re-import it."
|
|
23779
|
+
});
|
|
23780
|
+
}
|
|
23756
23781
|
}
|
|
23757
23782
|
return unique(out);
|
|
23758
23783
|
}
|
|
@@ -23888,7 +23913,7 @@ __export(edit_exports, {
|
|
|
23888
23913
|
function elementOrThrow(root, id) {
|
|
23889
23914
|
const hit = findById(root, id);
|
|
23890
23915
|
if (!hit)
|
|
23891
|
-
throw new
|
|
23916
|
+
throw new FlomotionError("unknown_element", `No element with id "${id}"`, {
|
|
23892
23917
|
fix: "Use project_read to list ids."
|
|
23893
23918
|
});
|
|
23894
23919
|
return hit;
|
|
@@ -23925,7 +23950,7 @@ function applyPatches(root, patches) {
|
|
|
23925
23950
|
}
|
|
23926
23951
|
case "remove": {
|
|
23927
23952
|
const { node: node2, parent, index } = elementOrThrow(project, p.id);
|
|
23928
|
-
if (!parent) throw new
|
|
23953
|
+
if (!parent) throw new FlomotionError("invalid_edit", "Cannot remove the root element");
|
|
23929
23954
|
parent.children.splice(index, 1);
|
|
23930
23955
|
inverse.unshift({
|
|
23931
23956
|
op: "insert",
|
|
@@ -23967,14 +23992,14 @@ var fmt = (t) => `${Math.round(t * 1e6) / 1e6}s`;
|
|
|
23967
23992
|
var snap = (root, t) => snapToFrame(t, fpsOf(root));
|
|
23968
23993
|
function lit(root, v, what, id) {
|
|
23969
23994
|
if (v === void 0)
|
|
23970
|
-
throw new
|
|
23995
|
+
throw new FlomotionError("invalid_edit", `#${id} has no explicit ${what}`, {
|
|
23971
23996
|
fix: `Give #${id} a literal ${what} (e.g. ${what}="2s") before editing its timing.`
|
|
23972
23997
|
});
|
|
23973
23998
|
try {
|
|
23974
23999
|
if (typeof v === "string" && /^[@#]/.test(v)) throw new Error("reference");
|
|
23975
24000
|
return parseTimeLiteral(v, fpsOf(root));
|
|
23976
24001
|
} catch {
|
|
23977
|
-
throw new
|
|
24002
|
+
throw new FlomotionError(
|
|
23978
24003
|
"opaque_expression",
|
|
23979
24004
|
`#${id}.${what} is ${JSON.stringify(v)}, which is not a literal time`,
|
|
23980
24005
|
{ fix: "Replace the reference/expression with a literal time first." }
|
|
@@ -23988,7 +24013,7 @@ function timingOf(root, node2) {
|
|
|
23988
24013
|
if (node2.props.duration !== void 0) duration3 = lit(root, node2.props.duration, "duration", id);
|
|
23989
24014
|
else if (node2.props.end !== void 0) duration3 = lit(root, node2.props.end, "end", id) - start;
|
|
23990
24015
|
else
|
|
23991
|
-
throw new
|
|
24016
|
+
throw new FlomotionError("invalid_edit", `#${id} has no explicit duration`, {
|
|
23992
24017
|
fix: `Give #${id} a duration (e.g. duration="2s") before editing its timing.`
|
|
23993
24018
|
});
|
|
23994
24019
|
return { node: node2, id, start, duration: duration3, end: start + duration3 };
|
|
@@ -24002,7 +24027,7 @@ function itemsOf(root, container) {
|
|
|
24002
24027
|
function trackOf(root, id) {
|
|
24003
24028
|
const { node: node2 } = elementOrThrow(root, id);
|
|
24004
24029
|
if (node2.name !== "Track" && node2.name !== "Scene" && node2.name !== "Composition" && node2.name !== "Group") {
|
|
24005
|
-
throw new
|
|
24030
|
+
throw new FlomotionError("invalid_edit", `#${id} is a ${node2.name}, not a Track`, {
|
|
24006
24031
|
fix: "Pass the id of a Track."
|
|
24007
24032
|
});
|
|
24008
24033
|
}
|
|
@@ -24010,14 +24035,14 @@ function trackOf(root, id) {
|
|
|
24010
24035
|
}
|
|
24011
24036
|
function assertWritable(track) {
|
|
24012
24037
|
if (track.name === "Track" && track.props.locked === true) {
|
|
24013
|
-
throw new
|
|
24038
|
+
throw new FlomotionError("track_locked", `Track #${track.props.id} is locked`, {
|
|
24014
24039
|
fix: `Unlock it first with edit lockTrack {track:"${track.props.id}", value:false}.`
|
|
24015
24040
|
});
|
|
24016
24041
|
}
|
|
24017
24042
|
}
|
|
24018
24043
|
function parentTrackOf(root, id) {
|
|
24019
24044
|
const { parent } = elementOrThrow(root, id);
|
|
24020
|
-
if (!parent) throw new
|
|
24045
|
+
if (!parent) throw new FlomotionError("invalid_edit", "The root has no track");
|
|
24021
24046
|
return parent;
|
|
24022
24047
|
}
|
|
24023
24048
|
function overlapCheck(items, ignore, start, end, allow) {
|
|
@@ -24025,7 +24050,7 @@ function overlapCheck(items, ignore, start, end, allow) {
|
|
|
24025
24050
|
for (const o of items) {
|
|
24026
24051
|
if (ignore.includes(o.id)) continue;
|
|
24027
24052
|
if (start < o.end - 1e-9 && end > o.start + 1e-9) {
|
|
24028
|
-
throw new
|
|
24053
|
+
throw new FlomotionError(
|
|
24029
24054
|
"overlap_rejected",
|
|
24030
24055
|
`[${fmt(start)}, ${fmt(end)}) overlaps #${o.id} [${fmt(o.start)}, ${fmt(o.end)})`,
|
|
24031
24056
|
{ fix: "Move it, trim the neighbour, or pass overlap:true." }
|
|
@@ -24131,7 +24156,7 @@ function overwrite(root, a) {
|
|
|
24131
24156
|
}
|
|
24132
24157
|
function replace(root, a) {
|
|
24133
24158
|
const { node: old, parent, index } = elementOrThrow(root, a.id);
|
|
24134
|
-
if (!parent) throw new
|
|
24159
|
+
if (!parent) throw new FlomotionError("invalid_edit", "Cannot replace the root");
|
|
24135
24160
|
assertWritable(parent);
|
|
24136
24161
|
const node2 = cloneNode(a.node);
|
|
24137
24162
|
const keep = {};
|
|
@@ -24170,7 +24195,7 @@ function extract(root, a) {
|
|
|
24170
24195
|
assertWritable(track);
|
|
24171
24196
|
const from = snap(root, lit(root, a.from, "from", a.track));
|
|
24172
24197
|
const to = snap(root, lit(root, a.to, "to", a.track));
|
|
24173
|
-
if (to <= from) throw new
|
|
24198
|
+
if (to <= from) throw new FlomotionError("invalid_edit", "extract needs to > from");
|
|
24174
24199
|
const span = to - from;
|
|
24175
24200
|
const fps = fpsOf(root);
|
|
24176
24201
|
const patches = [];
|
|
@@ -24216,7 +24241,7 @@ function split(root, a) {
|
|
|
24216
24241
|
const it = timingOf(root, elementOrThrow(root, a.id).node);
|
|
24217
24242
|
const at = snap(root, lit(root, a.at, "at", a.id));
|
|
24218
24243
|
if (at <= it.start + 1e-9 || at >= it.end - 1e-9)
|
|
24219
|
-
throw new
|
|
24244
|
+
throw new FlomotionError(
|
|
24220
24245
|
"invalid_edit",
|
|
24221
24246
|
`split time ${fmt(at)} is outside #${a.id} (${fmt(it.start)} to ${fmt(it.end)})`
|
|
24222
24247
|
);
|
|
@@ -24232,7 +24257,7 @@ function trim(root, a) {
|
|
|
24232
24257
|
const fps = fpsOf(root);
|
|
24233
24258
|
const start = a.start === void 0 ? it.start : snap(root, lit(root, a.start, "start", a.id));
|
|
24234
24259
|
const end = a.end === void 0 ? it.end : snap(root, lit(root, a.end, "end", a.id));
|
|
24235
|
-
if (end <= start) throw new
|
|
24260
|
+
if (end <= start) throw new FlomotionError("invalid_edit", "trim would make the clip empty");
|
|
24236
24261
|
overlapCheck(itemsOf(root, parent), [a.id], start, end, a.overlap);
|
|
24237
24262
|
const patches = [setTime(a.id, "start", start), setTime(a.id, "duration", end - start)];
|
|
24238
24263
|
if (it.node.props.end !== void 0) patches.push(dropProp(a.id, "end"));
|
|
@@ -24252,12 +24277,12 @@ function roll(root, a) {
|
|
|
24252
24277
|
const A = timingOf(root, elementOrThrow(root, a.a).node);
|
|
24253
24278
|
const B = timingOf(root, elementOrThrow(root, a.b).node);
|
|
24254
24279
|
if (Math.abs(A.end - B.start) > 1e-6)
|
|
24255
|
-
throw new
|
|
24280
|
+
throw new FlomotionError("invalid_edit", `#${a.a} and #${a.b} are not adjacent`, {
|
|
24256
24281
|
fix: "Roll needs clips that touch."
|
|
24257
24282
|
});
|
|
24258
24283
|
const d = snap(root, lit(root, a.delta, "delta", a.a));
|
|
24259
24284
|
if (A.duration + d <= 0 || B.duration - d <= 0)
|
|
24260
|
-
throw new
|
|
24285
|
+
throw new FlomotionError("invalid_edit", "roll would collapse a clip");
|
|
24261
24286
|
const fps = fpsOf(root);
|
|
24262
24287
|
const patches = [
|
|
24263
24288
|
setTime(a.a, "duration", A.duration + d),
|
|
@@ -24286,7 +24311,8 @@ function slide(root, a) {
|
|
|
24286
24311
|
assertWritable(parent);
|
|
24287
24312
|
const items = itemsOf(root, parent);
|
|
24288
24313
|
const i = items.findIndex((x) => x.id === a.id);
|
|
24289
|
-
if (i < 0)
|
|
24314
|
+
if (i < 0)
|
|
24315
|
+
throw new FlomotionError("unknown_element", `#${a.id} is not a timed item of its track`);
|
|
24290
24316
|
const it = items[i];
|
|
24291
24317
|
const d = snap(root, lit(root, a.delta, "delta", a.id));
|
|
24292
24318
|
const prev = items[i - 1];
|
|
@@ -24295,12 +24321,12 @@ function slide(root, a) {
|
|
|
24295
24321
|
const patches = [setTime(a.id, "start", it.start + d)];
|
|
24296
24322
|
if (prev && Math.abs(prev.end - it.start) < 1e-6) {
|
|
24297
24323
|
if (prev.duration + d <= 0)
|
|
24298
|
-
throw new
|
|
24324
|
+
throw new FlomotionError("invalid_edit", "slide would collapse the previous clip");
|
|
24299
24325
|
patches.push(setTime(prev.id, "duration", prev.duration + d));
|
|
24300
24326
|
}
|
|
24301
24327
|
if (next && Math.abs(it.end - next.start) < 1e-6) {
|
|
24302
24328
|
if (next.duration - d <= 0)
|
|
24303
|
-
throw new
|
|
24329
|
+
throw new FlomotionError("invalid_edit", "slide would collapse the next clip");
|
|
24304
24330
|
patches.push(
|
|
24305
24331
|
setTime(next.id, "start", next.start + d),
|
|
24306
24332
|
setTime(next.id, "duration", next.duration - d)
|
|
@@ -24314,7 +24340,7 @@ function slide(root, a) {
|
|
|
24314
24340
|
}
|
|
24315
24341
|
function move(root, a) {
|
|
24316
24342
|
const { node: node2, parent, index } = elementOrThrow(root, a.id);
|
|
24317
|
-
if (!parent) throw new
|
|
24343
|
+
if (!parent) throw new FlomotionError("invalid_edit", "Cannot move the root");
|
|
24318
24344
|
assertWritable(parent);
|
|
24319
24345
|
const it = timingOf(root, node2);
|
|
24320
24346
|
const to = snap(root, lit(root, a.to, "to", a.id));
|
|
@@ -24356,11 +24382,11 @@ function unlink(root, a) {
|
|
|
24356
24382
|
}
|
|
24357
24383
|
function wrap(root, ids, name, id) {
|
|
24358
24384
|
if (!ids.length)
|
|
24359
|
-
throw new
|
|
24385
|
+
throw new FlomotionError("invalid_edit", `${name.toLowerCase()} needs at least one id`);
|
|
24360
24386
|
const els = ids.map((i) => elementOrThrow(root, i));
|
|
24361
24387
|
const parent = els[0].parent;
|
|
24362
24388
|
if (els.some((e) => e.parent !== parent))
|
|
24363
|
-
throw new
|
|
24389
|
+
throw new FlomotionError("invalid_edit", "All elements must share one parent");
|
|
24364
24390
|
assertWritable(parent);
|
|
24365
24391
|
const times = els.map((e) => timingOf(root, e.node));
|
|
24366
24392
|
const start = Math.min(...times.map((t) => t.start));
|
|
@@ -24393,10 +24419,10 @@ var groupItems = (root, a) => wrap(root, a.ids, "Group", a.id);
|
|
|
24393
24419
|
var nest = (root, a) => wrap(root, a.ids, "Precomp", a.id);
|
|
24394
24420
|
function ungroup(root, a) {
|
|
24395
24421
|
const { node: node2, parent, index } = elementOrThrow(root, a.id);
|
|
24396
|
-
if (!parent) throw new
|
|
24422
|
+
if (!parent) throw new FlomotionError("invalid_edit", "Cannot ungroup the root");
|
|
24397
24423
|
assertWritable(parent);
|
|
24398
24424
|
if (node2.name !== "Group" && node2.name !== "Precomp")
|
|
24399
|
-
throw new
|
|
24425
|
+
throw new FlomotionError("invalid_edit", `#${a.id} is a ${node2.name}, not a Group`);
|
|
24400
24426
|
const gs = timingOf(root, node2).start;
|
|
24401
24427
|
const patches = [{ op: "remove", id: a.id }];
|
|
24402
24428
|
node2.children.filter(isNode).forEach((c, k) => {
|
|
@@ -24411,7 +24437,7 @@ function ungroup(root, a) {
|
|
|
24411
24437
|
}
|
|
24412
24438
|
function setSpeed(root, a) {
|
|
24413
24439
|
assertWritable(parentTrackOf(root, a.id));
|
|
24414
|
-
if (!(a.speed > 0)) throw new
|
|
24440
|
+
if (!(a.speed > 0)) throw new FlomotionError("invalid_edit", "speed must be > 0");
|
|
24415
24441
|
const it = timingOf(root, elementOrThrow(root, a.id).node);
|
|
24416
24442
|
const old = speedOf(it.node);
|
|
24417
24443
|
const dur = snap(root, it.duration * old / a.speed);
|
|
@@ -24423,7 +24449,7 @@ function setSpeed(root, a) {
|
|
|
24423
24449
|
}
|
|
24424
24450
|
function snapOp(root, a) {
|
|
24425
24451
|
const { node: node2, parent } = elementOrThrow(root, a.id);
|
|
24426
|
-
if (!parent) throw new
|
|
24452
|
+
if (!parent) throw new FlomotionError("invalid_edit", "Cannot snap the root");
|
|
24427
24453
|
assertWritable(parent);
|
|
24428
24454
|
const it = timingOf(root, node2);
|
|
24429
24455
|
const fps = fpsOf(root);
|
|
@@ -24490,7 +24516,7 @@ function addMarker(root, a) {
|
|
|
24490
24516
|
}
|
|
24491
24517
|
function removeMarker(root, a) {
|
|
24492
24518
|
const { node: node2 } = elementOrThrow(root, a.id);
|
|
24493
|
-
if (node2.name !== "Marker") throw new
|
|
24519
|
+
if (node2.name !== "Marker") throw new FlomotionError("invalid_edit", `#${a.id} is not a Marker`);
|
|
24494
24520
|
return makeResult(root, [{ op: "remove", id: a.id }], `remove marker #${a.id}`);
|
|
24495
24521
|
}
|
|
24496
24522
|
var EDIT_OPS = {
|
|
@@ -24522,7 +24548,7 @@ var EDIT_OPS = {
|
|
|
24522
24548
|
function runEdit(root, op, args) {
|
|
24523
24549
|
const fn = EDIT_OPS[op];
|
|
24524
24550
|
if (!fn)
|
|
24525
|
-
throw new
|
|
24551
|
+
throw new FlomotionError("invalid_edit", `Unknown edit op "${op}"`, {
|
|
24526
24552
|
fix: `Ops: ${Object.keys(EDIT_OPS).join(", ")}`
|
|
24527
24553
|
});
|
|
24528
24554
|
return fn(root, args);
|
|
@@ -24535,7 +24561,7 @@ var num2 = (t, id) => {
|
|
|
24535
24561
|
if (typeof t === "number") return t;
|
|
24536
24562
|
const m = /^(-?\d+(?:\.\d+)?)s?$/.exec(t.trim());
|
|
24537
24563
|
if (!m)
|
|
24538
|
-
throw new
|
|
24564
|
+
throw new FlomotionError(
|
|
24539
24565
|
"opaque_expression",
|
|
24540
24566
|
`Keyframe time ${JSON.stringify(t)} on #${id} is a reference; structured keyframe edits need literal times`,
|
|
24541
24567
|
{ fix: "Replace it with a literal time first." }
|
|
@@ -24547,20 +24573,20 @@ function read(root, id, prop, create = false) {
|
|
|
24547
24573
|
const raw = node2.props[prop];
|
|
24548
24574
|
if (raw === void 0) {
|
|
24549
24575
|
if (!create)
|
|
24550
|
-
throw new
|
|
24576
|
+
throw new FlomotionError("invalid_edit", `#${id}.${prop} has no keyframes`, {
|
|
24551
24577
|
fix: "Use keyAdd to create the first key."
|
|
24552
24578
|
});
|
|
24553
24579
|
return { set: { keys: [], opts: {} }, node: node2, raw };
|
|
24554
24580
|
}
|
|
24555
24581
|
if (typeof raw === "function")
|
|
24556
|
-
throw new
|
|
24582
|
+
throw new FlomotionError(
|
|
24557
24583
|
"opaque_expression",
|
|
24558
24584
|
`#${id}.${prop} is an expression; it cannot be edited as keyframes`,
|
|
24559
24585
|
{ fix: "Replace the expression with keyframes first." }
|
|
24560
24586
|
);
|
|
24561
24587
|
if (!isKeyframes(raw)) {
|
|
24562
24588
|
if (!create)
|
|
24563
|
-
throw new
|
|
24589
|
+
throw new FlomotionError("invalid_edit", `#${id}.${prop} is a static value, not keyframes`, {
|
|
24564
24590
|
fix: "Use keyAdd to turn it into keyframes."
|
|
24565
24591
|
});
|
|
24566
24592
|
return { set: { keys: [], opts: {} }, node: node2, raw };
|
|
@@ -24584,7 +24610,7 @@ function write(root, id, prop, set2, desc, data) {
|
|
|
24584
24610
|
const keys = [...set2.keys].sort((a, b) => a.t - b.t);
|
|
24585
24611
|
for (let i = 1; i < keys.length; i++) {
|
|
24586
24612
|
if (keys[i].t === keys[i - 1].t)
|
|
24587
|
-
throw new
|
|
24613
|
+
throw new FlomotionError(
|
|
24588
24614
|
"duplicate_keyframe",
|
|
24589
24615
|
`Two keyframes share time ${keys[i].t}s on #${id}.${prop}`,
|
|
24590
24616
|
{ fix: "Give each key a unique time (at least one frame apart)." }
|
|
@@ -24610,9 +24636,13 @@ function keyAdd(root, a) {
|
|
|
24610
24636
|
if (t > 0) set2.keys.push({ t: 0, v: node2.props[a.prop] });
|
|
24611
24637
|
}
|
|
24612
24638
|
if (set2.keys.some((k) => same(k.t, t)))
|
|
24613
|
-
throw new
|
|
24614
|
-
|
|
24615
|
-
|
|
24639
|
+
throw new FlomotionError(
|
|
24640
|
+
"duplicate_keyframe",
|
|
24641
|
+
`#${a.id}.${a.prop} already has a key at ${t}s`,
|
|
24642
|
+
{
|
|
24643
|
+
fix: "Use keySet to change it."
|
|
24644
|
+
}
|
|
24645
|
+
);
|
|
24616
24646
|
set2.keys.push({ t, v: a.value, ...a.ease ? { ease: a.ease } : {} });
|
|
24617
24647
|
return write(root, a.id, a.prop, set2, `add key ${a.prop} @ ${t}s on #${a.id}`);
|
|
24618
24648
|
}
|
|
@@ -24621,7 +24651,7 @@ function keySet(root, a) {
|
|
|
24621
24651
|
const t = snap2(root, num2(a.t, a.id));
|
|
24622
24652
|
const k = set2.keys.find((x) => same(x.t, t));
|
|
24623
24653
|
if (!k)
|
|
24624
|
-
throw new
|
|
24654
|
+
throw new FlomotionError("invalid_edit", `#${a.id}.${a.prop} has no key at ${t}s`, {
|
|
24625
24655
|
fix: "Use keyAdd to create one."
|
|
24626
24656
|
});
|
|
24627
24657
|
if (a.value !== void 0) k.v = a.value;
|
|
@@ -24635,9 +24665,9 @@ function keyMove(root, a) {
|
|
|
24635
24665
|
const f = snap2(root, num2(a.from, a.id));
|
|
24636
24666
|
const t = snap2(root, num2(a.to, a.id));
|
|
24637
24667
|
const k = set2.keys.find((x) => same(x.t, f));
|
|
24638
|
-
if (!k) throw new
|
|
24668
|
+
if (!k) throw new FlomotionError("invalid_edit", `#${a.id}.${a.prop} has no key at ${f}s`);
|
|
24639
24669
|
if (set2.keys.some((x) => x !== k && same(x.t, t)))
|
|
24640
|
-
throw new
|
|
24670
|
+
throw new FlomotionError("duplicate_keyframe", `#${a.id}.${a.prop} already has a key at ${t}s`);
|
|
24641
24671
|
k.t = t;
|
|
24642
24672
|
return write(root, a.id, a.prop, set2, `move key ${a.prop} ${f}s \u2192 ${t}s on #${a.id}`);
|
|
24643
24673
|
}
|
|
@@ -24647,7 +24677,7 @@ function keyDelete(root, a) {
|
|
|
24647
24677
|
const before = set2.keys.length;
|
|
24648
24678
|
set2.keys = set2.keys.filter((x) => !same(x.t, t));
|
|
24649
24679
|
if (set2.keys.length === before)
|
|
24650
|
-
throw new
|
|
24680
|
+
throw new FlomotionError("invalid_edit", `#${a.id}.${a.prop} has no key at ${t}s`);
|
|
24651
24681
|
if (set2.keys.length === 0)
|
|
24652
24682
|
return makeResult(
|
|
24653
24683
|
root,
|
|
@@ -24664,13 +24694,13 @@ function keyCopy(root, a) {
|
|
|
24664
24694
|
}
|
|
24665
24695
|
function keyPaste(root, a) {
|
|
24666
24696
|
const { set: set2 } = read(root, a.id, a.prop, true);
|
|
24667
|
-
if (!a.keys.length) throw new
|
|
24697
|
+
if (!a.keys.length) throw new FlomotionError("invalid_edit", "nothing to paste");
|
|
24668
24698
|
const base = Math.min(...a.keys.map((k) => num2(k.t, a.id)));
|
|
24669
24699
|
const at = snap2(root, num2(a.at, a.id));
|
|
24670
24700
|
for (const k of a.keys) {
|
|
24671
24701
|
const t = snap2(root, at + num2(k.t, a.id) - base);
|
|
24672
24702
|
if (set2.keys.some((x) => same(x.t, t)))
|
|
24673
|
-
throw new
|
|
24703
|
+
throw new FlomotionError(
|
|
24674
24704
|
"duplicate_keyframe",
|
|
24675
24705
|
`paste would put two keys at ${t}s on #${a.id}.${a.prop}`
|
|
24676
24706
|
);
|
|
@@ -24685,7 +24715,7 @@ function keyPaste(root, a) {
|
|
|
24685
24715
|
);
|
|
24686
24716
|
}
|
|
24687
24717
|
function keyScale(root, a) {
|
|
24688
|
-
if (!(a.factor > 0)) throw new
|
|
24718
|
+
if (!(a.factor > 0)) throw new FlomotionError("invalid_edit", "factor must be > 0");
|
|
24689
24719
|
const { set: set2 } = read(root, a.id, a.prop);
|
|
24690
24720
|
const origin = a.origin === void 0 ? set2.keys[0].t : num2(a.origin, a.id);
|
|
24691
24721
|
for (const k of set2.keys) k.t = snap2(root, origin + (k.t - origin) * a.factor);
|
|
@@ -24788,7 +24818,7 @@ function keySimplify(root, a) {
|
|
|
24788
24818
|
const tol = a.tolerance ?? 0.01;
|
|
24789
24819
|
const vecs = set2.keys.map((k) => Array.isArray(k.v) ? k.v : [k.v]);
|
|
24790
24820
|
if (vecs.some((v) => v.some((x) => typeof x !== "number")))
|
|
24791
|
-
throw new
|
|
24821
|
+
throw new FlomotionError("invalid_edit", "keySimplify works on numeric or vector keys");
|
|
24792
24822
|
const t = set2.keys.map((k) => k.t);
|
|
24793
24823
|
const keep = /* @__PURE__ */ new Set([0, set2.keys.length - 1]);
|
|
24794
24824
|
const rec = (i, j) => {
|
|
@@ -24825,7 +24855,7 @@ var AXES = ["x", "y", "z"];
|
|
|
24825
24855
|
function keySeparate(root, a) {
|
|
24826
24856
|
const { set: set2 } = read(root, a.id, a.prop);
|
|
24827
24857
|
const dim = Array.isArray(set2.keys[0].v) ? set2.keys[0].v.length : 0;
|
|
24828
|
-
if (!dim) throw new
|
|
24858
|
+
if (!dim) throw new FlomotionError("invalid_edit", `#${a.id}.${a.prop} is not a vector property`);
|
|
24829
24859
|
const value = {};
|
|
24830
24860
|
for (let c = 0; c < dim; c++) {
|
|
24831
24861
|
value[AXES[c]] = set2.keys.map(
|
|
@@ -24842,7 +24872,7 @@ function keyCombine(root, a) {
|
|
|
24842
24872
|
const { node: node2 } = elementOrThrow(root, a.id);
|
|
24843
24873
|
const raw = node2.props[a.prop];
|
|
24844
24874
|
if (!raw || Array.isArray(raw) || typeof raw !== "object")
|
|
24845
|
-
throw new
|
|
24875
|
+
throw new FlomotionError("invalid_edit", `#${a.id}.${a.prop} is not per-axis`);
|
|
24846
24876
|
const axes = AXES.filter((x) => raw[x]);
|
|
24847
24877
|
const lists = axes.map(
|
|
24848
24878
|
(x) => raw[x].map(
|
|
@@ -24886,16 +24916,16 @@ var KEY_OPS = {
|
|
|
24886
24916
|
function runKeyOp(root, op, args) {
|
|
24887
24917
|
const fn = KEY_OPS[op];
|
|
24888
24918
|
if (!fn)
|
|
24889
|
-
throw new
|
|
24919
|
+
throw new FlomotionError("invalid_edit", `Unknown keyframe op "${op}"`, {
|
|
24890
24920
|
fix: `Ops: ${Object.keys(KEY_OPS).join(", ")}`
|
|
24891
24921
|
});
|
|
24892
24922
|
return fn(root, args);
|
|
24893
24923
|
}
|
|
24894
24924
|
|
|
24895
24925
|
export {
|
|
24896
|
-
|
|
24897
|
-
|
|
24898
|
-
|
|
24926
|
+
FLOMOTION_NAME,
|
|
24927
|
+
FLOMOTION_SCOPE,
|
|
24928
|
+
FLOMOTION_DIR,
|
|
24899
24929
|
ID_PREFIX,
|
|
24900
24930
|
snapToFrame,
|
|
24901
24931
|
secondsToFrames,
|