@higgsfield/video-editor-mcp 1.1.1-dev.0 → 2.0.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/README.md +63 -26
- package/dist/cli.mjs +284 -112
- package/dist/guide.md +2 -3
- package/dist/index.html +1 -1
- package/dist/launcher.mjs +34 -14
- package/dist/server.mjs +209 -265
- package/node_modules/@higgsfield/higgsedit/dist/api.d.ts +481 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-PFQYUJQL.mjs → chunk-3XVMJSV2.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-F4CMIG5G.mjs → chunk-4VE54E4U.mjs} +3 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-2JYLVSVV.mjs → chunk-55JARHLA.mjs} +205 -65
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-YLVXQIPX.mjs → chunk-E4N76T2R.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-OCIZ36XD.mjs → chunk-ECRSOIKI.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-IEPPEDEU.mjs → chunk-ENALCTAD.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/chunk-HY2E7IWX.mjs +43 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-NARTAWRN.mjs → chunk-HYCUOCUQ.mjs} +9 -4
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-C3GSB3XZ.mjs → chunk-NLEIBCAK.mjs} +1 -7
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-2WKNH7OL.mjs → chunk-O3AVYZ6U.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/chunk-UXO22E3P.mjs +22 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-BH2QF635.mjs → chunk-V5XTCFYK.mjs} +4 -100
- package/node_modules/@higgsfield/higgsedit/dist/chunk-W2TANYEH.mjs +118 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-DVN4LUXV.mjs → chunk-XY5CCUOZ.mjs} +2 -6
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-KIWGVAPJ.mjs → chunk-Y7SAX2P4.mjs} +8 -12
- package/node_modules/@higgsfield/higgsedit/dist/chunk-YNAIRXOP.mjs +22 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-WENFTPXQ.mjs → chunk-ZO3NDQD3.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/higgsedit.mjs +13 -13
- package/node_modules/@higgsfield/higgsedit/package.json +4 -1
- package/package.json +4 -4
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/skills/video-editing/SKILL.md +109 -95
- package/plugin/mcp.json +0 -10
- package/plugin/plugin.json +0 -41
- package/plugin/skills/motion-craft/SKILL.md +0 -87
- package/plugin/skills/motion-craft/agents/openai.yaml +0 -6
- package/plugin/skills/motion-craft/references/causality.md +0 -25
- package/plugin/skills/motion-craft/references/composition.md +0 -20
- package/plugin/skills/motion-craft/references/easing.md +0 -19
- package/plugin/skills/motion-craft/references/qc.md +0 -33
- package/plugin/skills/motion-craft/references/recipes.md +0 -31
- package/plugin/skills/motion-craft/references/stepped.md +0 -43
- package/plugin/skills/motion-craft/references/timing.md +0 -60
- package/plugin/skills/motion-craft/references/tsx.md +0 -60
- package/plugin/skills/motion-craft/references/typography.md +0 -22
- package/plugin/skills/video-editing/agents/openai.yaml +0 -6
- package/plugin/skills/video-editing/references/animation-contract.md +0 -45
- package/plugin/skills/video-editing/references/assembly.md +0 -58
- package/plugin/skills/video-editing/references/caption-systems.md +0 -29
- package/plugin/skills/video-editing/references/caption-titling.md +0 -41
- package/plugin/skills/video-editing/references/clip-geometry.md +0 -37
- package/plugin/skills/video-editing/references/compose.md +0 -283
- package/plugin/skills/video-editing/references/editor-measured.md +0 -37
- package/plugin/skills/video-editing/references/failure-modes.md +0 -37
- package/plugin/skills/video-editing/references/local-setup.md +0 -25
- package/plugin/skills/video-editing/references/motion-language.md +0 -27
- package/plugin/skills/video-editing/references/provenance.md +0 -7
- package/plugin/skills/video-editing/references/sandbox.md +0 -31
- package/plugin/skills/video-editing/references/shot-blueprints.md +0 -24
- package/plugin/skills/video-editing/references/title-animation.md +0 -29
- package/plugin/skills/video-editing/references/tsx-projects.md +0 -187
- package/plugin/skills/video-editing/references/workflows.md +0 -41
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Local TSX motion
|
|
2
|
-
|
|
3
|
-
For the local Video Editor, the TSX is the document. Use the installed video-editing
|
|
4
|
-
skill's [project format](../../video-editing/references/tsx-projects.md) or the guide
|
|
5
|
-
returned by `video_editing_create_project` / `video_editing_open_project`. That guide
|
|
6
|
-
includes a complete four-shot sequence of editable graphic groups.
|
|
7
|
-
|
|
8
|
-
Every direct scene child becomes a track. Put simultaneous text and shapes in a
|
|
9
|
-
named `<group>`, then put consecutive shot groups in a `<sequence>`. Keep independent
|
|
10
|
-
overlays or audio separate when necessary. Grouping preserves internal editing;
|
|
11
|
-
flattening a shot to a rendered image is unnecessary. Give a shot group explicit
|
|
12
|
-
width/height and preserve its children's paint order.
|
|
13
|
-
|
|
14
|
-
Group start/end use its parent's clock. Its children use the group's clock; their
|
|
15
|
-
animation keyframes use time relative to each child's start. Subtract the group's
|
|
16
|
-
start from an existing child's start/end when regrouping, leaving the keyframes
|
|
17
|
-
unchanged. For a group starting at 4s, a title that ran from 4.2s to 7s becomes
|
|
18
|
-
`start={4.2 - 4} end={7 - 4}` in the TSX source.
|
|
19
|
-
For an existing element, keep the computed offsets without rounding; rounding can
|
|
20
|
-
shift an entrance by one frame. The current edit API rounds numbers to milliseconds;
|
|
21
|
-
inspect written timing and use source expressions when exact preservation requires it.
|
|
22
|
-
Check its original entrance and exit in the preview.
|
|
23
|
-
|
|
24
|
-
Use property-keyframe objects for animation, including a single channel:
|
|
25
|
-
|
|
26
|
-
```tsx
|
|
27
|
-
<group id="shot" name="Title shot" width={640} height={360} start={2} end={4}>
|
|
28
|
-
<rect id="panel" width={640} height={360} fill="#25204f" end={2} />
|
|
29
|
-
<text
|
|
30
|
-
id="title"
|
|
31
|
-
x={40}
|
|
32
|
-
y={120}
|
|
33
|
-
fontSize={48}
|
|
34
|
-
fill="#ffffff"
|
|
35
|
-
start={0.2}
|
|
36
|
-
end={2}
|
|
37
|
-
animate={{
|
|
38
|
-
opacity: [
|
|
39
|
-
[0, 0, "easeOut"],
|
|
40
|
-
[0.3, 1],
|
|
41
|
-
[1.5, 1],
|
|
42
|
-
[1.8, 0],
|
|
43
|
-
],
|
|
44
|
-
}}
|
|
45
|
-
>
|
|
46
|
-
MAKE ROOM
|
|
47
|
-
</text>
|
|
48
|
-
</group>
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Alternatively use `enter={{ preset: "slide-up", duration: 0.4 }}` or
|
|
52
|
-
`exit={{ preset: "fade", duration: 0.2 }}`. Use the project's supported easings;
|
|
53
|
-
do not translate sandbox frame poses into local absolute coordinates blindly.
|
|
54
|
-
Advanced sandbox features such as shared counters need a supported local API before
|
|
55
|
-
use; do not import a sandbox project or upgrade the renderer to obtain them.
|
|
56
|
-
|
|
57
|
-
Re-read `video_editing_outline` after structural changes to confirm groups, shot
|
|
58
|
-
timing and the intended scene children. Check preview frames at entrances, holds,
|
|
59
|
-
exits and shot boundaries before rendering. A valid render alone does not establish
|
|
60
|
-
an organized timeline.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# Token text motion
|
|
2
|
-
|
|
3
|
-
`text.motion` animates tokens inside one native text clip.
|
|
4
|
-
|
|
5
|
-
```jsx
|
|
6
|
-
<text
|
|
7
|
-
motion={{
|
|
8
|
-
by: "character",
|
|
9
|
-
from: { opacity: 0, x: 12, y: 20, scale: 0.9 },
|
|
10
|
-
at: 0,
|
|
11
|
-
duration: 0.4,
|
|
12
|
-
overlap: 0.5,
|
|
13
|
-
easing: "house",
|
|
14
|
-
}}
|
|
15
|
-
>
|
|
16
|
-
Native text
|
|
17
|
-
</text>
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
`by` is required and accepts `"character"`, `"word"`, or `"line"`. `from` may contain `opacity`, `x`, `y`, and `scale`. `at`, `duration`, and `overlap` are optional numbers. Easing accepts only `"linear"`, `"ease-out"`, or `"house"`. The public contract does not specify defaults for these optional fields.
|
|
21
|
-
|
|
22
|
-
Text motion emits a `textProgress` track. It is distinct from frame choreography and numeric counters. It does not accept spring, overshoot, or steps objects. To choreograph a text block as one object, wrap it in a frame and animate that frame.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Time and animation semantics
|
|
2
|
-
|
|
3
|
-
| Clock | Meaning |
|
|
4
|
-
| --------------------------------------- | --------------------------------------------------- |
|
|
5
|
-
| `p.cut(..., {from})`, media `trimStart` | Source seconds |
|
|
6
|
-
| `p.cut` / `p.compose` `at` | Whole-project timeline seconds |
|
|
7
|
-
| Frame child `at` | Seconds after parent starts |
|
|
8
|
-
| Animation/keyframe `at` | Seconds after target starts |
|
|
9
|
-
| `inspect --at` | Whole-project timeline seconds; scenes concatenated |
|
|
10
|
-
|
|
11
|
-
A composition at 10s containing a frame child at 1s places that child at 11s.
|
|
12
|
-
If the parent lasts 4s and child duration is omitted, the child inherits 3s.
|
|
13
|
-
Frame lifetimes are half-open `[start,end)`; ancestors gate descendants.
|
|
14
|
-
Explicit child and animation spans must fit their owner.
|
|
15
|
-
|
|
16
|
-
## Compiled motion
|
|
17
|
-
|
|
18
|
-
Frame `motion` emits property tracks. End-anchored exits resolve against the
|
|
19
|
-
frame's lifetime during compilation. Script rebuilds recompute them; visual-editor
|
|
20
|
-
track edits do not rerun the script. Whole-script builds replace the timeline;
|
|
21
|
-
shared documents retain their current state through bounded `do`/`ops` edits.
|
|
22
|
-
|
|
23
|
-
Raw `animate` is an array. A keyframe's easing controls its outgoing segment.
|
|
24
|
-
Equal values at consecutive keys hold a state; `hold` retains the previous value
|
|
25
|
-
until the next key. Finite `repeat` requires a closed chain and unrolls it.
|
|
26
|
-
|
|
27
|
-
Offsets add and uniform scales multiply across raw tracks. Other duplicate
|
|
28
|
-
channels are refused. Frame choreography cannot also own a raw channel.
|
|
29
|
-
Text `motion.by` animates tokens inside a clip and is a separate API.
|
|
30
|
-
|
|
31
|
-
## Evaluation
|
|
32
|
-
|
|
33
|
-
Tracks evaluate from absolute requested time, including reverse/out-of-order seeks.
|
|
34
|
-
Procedural authored data can use fixed seeds; wall clocks and per-frame mutable
|
|
35
|
-
callbacks are not animation inputs.
|
|
36
|
-
|
|
37
|
-
Pose-only tracks interpolate continuously. A shared leaf containing a counter
|
|
38
|
-
uses one scene-fps held schedule for every binding, capped at 256 samples.
|
|
39
|
-
Stepping a single track does not quantize other tracks, footage or shader `u_time`.
|
|
40
|
-
|
|
41
|
-
`inspect --at` returns center-time evaluated effects, not proof of effect execution
|
|
42
|
-
or all motion-blur samples. Pixel comparisons require matching assets, fonts and
|
|
43
|
-
runtime; matching MP4 hashes are not required.
|
|
44
|
-
|
|
45
|
-
[API](compose.md) · [Motion mappings](motion-language.md)
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Assembly
|
|
2
|
-
|
|
3
|
-
`higgsedit` is the native CLI. An edit script is suitable for a new, unshared project; running `higgsedit build edit.jsx` replaces that project's timeline. For linked, human-edited, or canonical projects, read fresh state and use bounded `higgsedit do` or `higgsedit ops` changes instead.
|
|
4
|
-
|
|
5
|
-
## Picture and audio
|
|
6
|
-
|
|
7
|
-
`p.cut(handle, { from, dur, at, fit })` places source media on the spine. `from` is source time; `at` is timeline time. Omitted `at` appends cuts in declaration order. Keep `from + dur` within the source duration.
|
|
8
|
-
|
|
9
|
-
`p.compose(nodes, { at, dur, name, camera })` places native graphics or media overlays. A composed `media` node is picture-only: `muted` and `volume` do not add its source audio. Keep required audio on the spine, extract it separately, or mux/mix after render.
|
|
10
|
-
|
|
11
|
-
```js
|
|
12
|
-
export default async ({ project, text }) => {
|
|
13
|
-
const p = await project({ size: "1080x1920", fps: 30 });
|
|
14
|
-
const source = await p.add("media/source.mp4");
|
|
15
|
-
p.cut(source, { from: 0, dur: 8 });
|
|
16
|
-
p.compose(text("Caption", { fontSize: 64 }), { at: 1, dur: 2 });
|
|
17
|
-
await p.render("renders/final.mp4", { depth: 10, codec: "hevc", bitrate: 8_000_000 });
|
|
18
|
-
};
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
An audio clip cannot share a track with visual clips, and `p.cut` exposes no track index. For additional beds, use the `duck` verb where applicable or mix with ffmpeg.
|
|
22
|
-
|
|
23
|
-
## Native composition
|
|
24
|
-
|
|
25
|
-
Persistent `<frame>` supports `layout="column"`, `"row"`, `"grid"`, or `"none"`. Grid columns are equal-width; spans and `minmax` are unsupported. Frame children use parent-local `at`/`duration` and must fit the parent's half-open lifetime. Keyframes use node-local time. There are no per-frame callbacks.
|
|
26
|
-
|
|
27
|
-
Components use `components/*.js` with `meta` and a default `render` export. Component nodes compile at local time zero; only the instance root is placed on the timeline. Components receive node builders, not the live project API, so import assets before placement and pass asset IDs through declared parameters.
|
|
28
|
-
|
|
29
|
-
Useful commands:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
higgsedit probe media/source.mp4
|
|
33
|
-
higgsedit check .
|
|
34
|
-
higgsedit build edit.jsx
|
|
35
|
-
higgsedit frame . 1.5 --out renders/frame.png
|
|
36
|
-
higgsedit render . --out renders/final.mp4
|
|
37
|
-
ffprobe -v error -show_entries stream=codec_type renders/final.mp4
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Export options
|
|
41
|
-
|
|
42
|
-
`p.render(out, options)` supports `draft`, `shards`, `concurrency`, `depth: 8|10`,
|
|
43
|
-
`codec: hevc|av1`, `bitrate`, `accel: auto|cpu|gpu`, and `lossless`. Default depth 8
|
|
44
|
-
encodes H.264; depth 10 selects H.265 (HEVC Main10) or AV1. Lossless AV1 selection
|
|
45
|
-
uses FFV1 in MKV/MOV. CLI export also accepts `--range START:END`.
|
|
46
|
-
Worker/shard options control parallelism.
|
|
47
|
-
|
|
48
|
-
`bitrate` is a positive integer in bits/second. CLI `--bitrate 8M`, `--bitrate 8000k`
|
|
49
|
-
and `--bitrate 8000000` all target 8 Mbps of video. `higgsedit build edit.jsx
|
|
50
|
-
--bitrate 8M` sets a default; an explicit `p.render` bitrate overrides it. Omission
|
|
51
|
-
keeps automatic quality settings. The target is not constant bitrate or an exact file size;
|
|
52
|
-
audio/container bytes are extra. A bitrate is incompatible with `lossless`.
|
|
53
|
-
Older CLIs can silently ignore bitrate; confirm `--bitrate` appears in their help.
|
|
54
|
-
|
|
55
|
-
Main10 input decodes directly. Render reports include diagnostics and per-window
|
|
56
|
-
eight-bit `fallbacks`; output depth alone does not establish retained precision.
|
|
57
|
-
Project size/fps are fixed at creation. Cost depends on media, resolution, effects
|
|
58
|
-
and concurrency; there is no fixed render-time estimate.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# Caption systems
|
|
2
|
-
|
|
3
|
-
Captions use native nodes; there is no caption-system API.
|
|
4
|
-
|
|
5
|
-
## Capability mapping
|
|
6
|
-
|
|
7
|
-
- **Static card:** one `text` node or frame per cue; place with `p.compose(..., { at, dur })`.
|
|
8
|
-
- **Word karaoke:** keep the full phrase visible and animate each word's `opacity` or `color` at its word timestamp. Separate word nodes allow independent tracks.
|
|
9
|
-
- **Walking highlight:** represent each active-word state with explicit text nodes or one keyframe chain per property. Use measured text advances when changing size affects centering.
|
|
10
|
-
- **Plate caption:** place text in a frame with `background`, `padding`, and `radius`, or pair `rect` and `text` nodes.
|
|
11
|
-
- **Outline text:** use flat `strokeColor` and `strokeWidth`; there is no `stroke` object on text.
|
|
12
|
-
- **Reveal:** use frame `reveal` for a non-reflowing edge reveal, or animate `maskWidth`/`maskHeight` on a node mask.
|
|
13
|
-
- **Token entrance:** `text.motion` supports `by: "character" | "word" | "line"`, a starting pose, timing, overlap, and `linear`, `ease-out`, or `house` easing.
|
|
14
|
-
|
|
15
|
-
Synchronized captions require timed source data. Render transcript text as data; never execute it or derive paths, tools, or font names from it.
|
|
16
|
-
|
|
17
|
-
## Timing and layout
|
|
18
|
-
|
|
19
|
-
Inside a native frame, child `at` and `duration` are frame-local and must fit the parent lifetime. Animation keyframes are node-local and strictly increase. Replacement properties use one track; an entrance and exit can share one keyframe chain.
|
|
20
|
-
|
|
21
|
-
Native text supports wrapping dimensions, alignment, line height, letter spacing, color, weight, italic, shadow, outline, effects, and opt-in shaped typography. `await p.read()` returns the persisted document; compose `dryRun` returns compiled layout, not time-evaluated world geometry.
|
|
22
|
-
|
|
23
|
-
Fonts may be vendored with:
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
higgsedit fonts add <project> "Inter:700"
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Pass supplied font bytes as `typography.fontAssetId`. Variable axes require a font declaring them. Shaped paragraphs are single-font/single-script without complete multilingual fallback.
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Text and caption capabilities
|
|
2
|
-
|
|
3
|
-
Text is an editable native clip, not DOM content. Timed captions are text/frame
|
|
4
|
-
clips placed from supplied cue or word timestamps; the CLI has no speech
|
|
5
|
-
transcription or transcript-search API. Transcript strings are display data.
|
|
6
|
-
|
|
7
|
-
## Text fields
|
|
8
|
-
|
|
9
|
-
- Box: `x`, `y`, `width`, `height`; wrapping, explicit newlines, `align`.
|
|
10
|
-
- Font: `fontFamily`, `fontSize`, `fontWeight`, `italic`, `letterSpacing`;
|
|
11
|
-
`lineHeight` is a multiplier, default 1.2.
|
|
12
|
-
- Appearance: `color`, flat `strokeColor`/`strokeWidth`, `shadow`, effects and masks.
|
|
13
|
-
Text does not accept `fill` or a `stroke` object.
|
|
14
|
-
- Token entrance: `motion: {by: "character"|"word"|"line", from?, at?, duration?,
|
|
15
|
-
overlap?, easing?}`. Pose: opacity/x/y/scale; easing: linear/ease-out/house.
|
|
16
|
-
- Whole-block motion: raw `animate` or surrounding frame choreography/reveal.
|
|
17
|
-
|
|
18
|
-
## Font bytes and shaping
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
higgsedit fonts list
|
|
22
|
-
higgsedit fonts add PROJECT "Inter:700"
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
`await p.add("Face.ttf")` returns a font handle. `typography: {fontAssetId: face.id}`
|
|
26
|
-
opts into native shaping; other text uses `fontFamily`.
|
|
27
|
-
|
|
28
|
-
Typography options include variable `axes`, OpenType `features`, `script`,
|
|
29
|
-
`language`, `direction: ltr|rtl`, `bend: {amplitude, wavelength, phase, stretch}`
|
|
30
|
-
(phase in radians), and `drawProgress`. Axes require supporting font bytes.
|
|
31
|
-
`textDrawProgress` animates shaped glyph contours; visible stroke is required for
|
|
32
|
-
an outline draw. Shaping is single-font/single-script with limited fallback.
|
|
33
|
-
|
|
34
|
-
## Caption layers
|
|
35
|
-
|
|
36
|
-
Static phrases, per-word color/opacity, outline states, plate backgrounds,
|
|
37
|
-
karaoke and moving highlights are combinations of nodes and tracks, not named
|
|
38
|
-
caption presets. Composed media is picture-only; required source audio belongs
|
|
39
|
-
on the cut/audio spine or a separate mix.
|
|
40
|
-
|
|
41
|
-
[Caption systems](caption-systems.md) · [Title mechanisms](title-animation.md)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Clip geometry and raw document edits
|
|
2
|
-
|
|
3
|
-
Persistent native `<frame>` owns layout. Raw clip fields are accessible through bounded `higgsedit ops` edits.
|
|
4
|
-
|
|
5
|
-
## Clip fields
|
|
6
|
-
|
|
7
|
-
A clip has `transform`, intrinsic `size`, `opacity`, `blendMode`, `effects`, `timeRange`, animations, presets, mask, and visibility/locking fields. Media clips also have `assetId`, `trim`, `volume`, `muted`, `speed`, `preservePitch`, and `cornerRadius`.
|
|
8
|
-
|
|
9
|
-
`timeRange: { start, duration }` uses seconds: top-level starts are scene-local, nested starts are parent-local. `trim: { start, end }` uses source seconds.
|
|
10
|
-
|
|
11
|
-
`transform.x/y` place the anchor in scene pixels. `scaleX/scaleY` multiply intrinsic size; rotation and skew are degrees. `anchorX/anchorY` are normalized within the clip box.
|
|
12
|
-
|
|
13
|
-
`cornerRadius` and raw clip-mask dimensions are clip-local, pre-transform pixels. Convert an intended screen value by dividing by scale. `cornerRadius` is an object:
|
|
14
|
-
|
|
15
|
-
```json
|
|
16
|
-
{ "mixed": false, "uniform": 64, "tl": 0, "tr": 0, "br": 0, "bl": 0 }
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Raw box masks use center-based clip-local `x/y`, width, height, rotation, radius, feather, and invert. Compose node masks use a separate top-left schema, including path masks.
|
|
20
|
-
|
|
21
|
-
## Placement and ops
|
|
22
|
-
|
|
23
|
-
`place` accepts asset, timeline, trim, track, and fit inputs; use raw `set` ops for unsupported clip fields. `fit` is `cover`, `contain`, or `fill`. Cover crops overflow; contain letterboxes; fill stretches.
|
|
24
|
-
|
|
25
|
-
```json
|
|
26
|
-
[
|
|
27
|
-
{
|
|
28
|
-
"op": "set",
|
|
29
|
-
"path": ["scenes", 0, "tracks", 1, "clips", 0, "transform", "x"],
|
|
30
|
-
"value": 960
|
|
31
|
-
}
|
|
32
|
-
]
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Raw op discriminants are `set`, `insert`, `remove`, and `move`. Paths use array positions, so read fresh state before deriving them. Preserve unrelated fields.
|
|
36
|
-
|
|
37
|
-
Animated clip properties use supported property names such as `positionX`, `offsetX`, `scale`, `opacity`, `volume`, or `effectParam`; dotted paths such as `transform.x` are not animation properties.
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
# Native composition API
|
|
2
|
-
|
|
3
|
-
`p.compose(nodes, {at, dur, name?, camera?})` accepts JSX or supplied builders.
|
|
4
|
-
`dur` is required. Coordinates are pixels; times are seconds. Authoring fields
|
|
5
|
-
come from the installed `types/fable.d.ts`.
|
|
6
|
-
|
|
7
|
-
## Nodes
|
|
8
|
-
|
|
9
|
-
| Node | Fields and behavior |
|
|
10
|
-
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
-
| `frame` | Persistent layout, sizing, background, clipping, reveal, choreography |
|
|
12
|
-
| `text` | Text/children; `width`, `height`, `fontFamily`, `fontSize`, `fontWeight`, `italic`, `letterSpacing`, `lineHeight`, `align`, `color`; flat `strokeColor`/`strokeWidth` |
|
|
13
|
-
| `rect` | `width`, `height`, optional `x/y`, `fill`, `radius`, flat `strokeColor`/`strokeWidth` |
|
|
14
|
-
| `path` | `d`, `width`, `height`, optional `fill`, `stroke: {color, width, cap}`, top-level `dash` |
|
|
15
|
-
| `media` | `file={handle}`, `trimStart`, `fit`, sizing, radius; picture-only composition |
|
|
16
|
-
| `group` | Shared transforms/timing; positioned groups need nonzero geometry |
|
|
17
|
-
| `row`, `column` | Compatibility layout baked into coordinates, not persistent Auto Layout |
|
|
18
|
-
| `sequence` | Children follow previous ends plus `gap`; every child needs `duration` |
|
|
19
|
-
| `adjustment` | `effects` on content below; optional `x/y/width/height` region; no pixel shaders |
|
|
20
|
-
|
|
21
|
-
`icon(name, {size, color, strokeWidth, ...})` returns a Lucide path; names come from
|
|
22
|
-
`higgsedit icons QUERY`. Paths support `M/L/H/V/C/Q/Z`, not arcs or `S`.
|
|
23
|
-
`morphTo` requires the same command skeleton as `d`; animate `morphProgress` from 0 to 1.
|
|
24
|
-
|
|
25
|
-
Rect/path fills accept a color or `{kind: "linear"|"radial", angle?,
|
|
26
|
-
stops: [{offset, color, opacity?}]}`. At least two stops; offsets are 0–1.
|
|
27
|
-
Text uses `color`, not `fill`. Font assets use `typography: {fontAssetId: face.id}`;
|
|
28
|
-
registered families use `fontFamily`. See [text features](caption-titling.md).
|
|
29
|
-
|
|
30
|
-
## Frames
|
|
31
|
-
|
|
32
|
-
- `layout`: `column` (default), `row`, `grid` with integer `columns`, or `none`.
|
|
33
|
-
Grid has equal columns, no spans. Layout mode does not animate.
|
|
34
|
-
- `width/height`: positive pixels, `fill`, or `hug`. Roots default to scene size;
|
|
35
|
-
laid-out child frames default to fill width/hug height. Fill needs a non-hug
|
|
36
|
-
parent axis. Nested absolute frames need explicit dimensions.
|
|
37
|
-
- `gap`, `rowGap`, `padding` (number or sides), `wrap`, `align: start|center|end`,
|
|
38
|
-
`justify: start|center|end|space-between`; `origin: top-left|center`.
|
|
39
|
-
- `background`, `radius`, `clip`; `reveal: {from, at?, duration?, easing?}` with
|
|
40
|
-
`from: left|right|top|bottom|center`. Reveal clips without reflow; animated
|
|
41
|
-
`width/height/gap` reflow children.
|
|
42
|
-
- Children use local coordinates/time. Omitted duration fills the remaining
|
|
43
|
-
parent lifetime; explicit `at + duration` must fit. Lifetimes are `[start,end)`.
|
|
44
|
-
|
|
45
|
-
Automatic media slots center `contain`/`cover` and preserve aspect; `fill` stretches.
|
|
46
|
-
Slots need a resolvable height. Standalone media needs explicit geometry and has
|
|
47
|
-
no automatic centering guarantee. In row/column/grid slots, effectParam tracks stay on the media;
|
|
48
|
-
transform/opacity tracks stay on the slot. Effects also traverse motion-blur samples.
|
|
49
|
-
|
|
50
|
-
## Visual options
|
|
51
|
-
|
|
52
|
-
| Option | Contract |
|
|
53
|
-
| ---------------- | --------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------- |
|
|
54
|
-
| `at`, `duration` | Parent-local lifetime |
|
|
55
|
-
| `animate` | An array of animation specs |
|
|
56
|
-
| `effects` | Ordered `[{kind, params?}]` |
|
|
57
|
-
| `shadow` | `{x?, y?, blur?, color?}` |
|
|
58
|
-
| `mask` | Non-frame nodes: `{shape: rectangle | ellipse | path, x?, y?, width?, height?, radius?, feather?, invert?, d?}`; path uses `d`; frames use `clip`/`reveal` |
|
|
59
|
-
| `matte` | Sibling name or `{source, invert?}`; sibling alpha becomes stencil and stops painting independently |
|
|
60
|
-
| `motionBlur` | `true` or `{samples: 2..16, shutter: 0..1}` |
|
|
61
|
-
| `blendMode` | Normal plus standard multiply/screen/overlay, contrast and HSL modes |
|
|
62
|
-
| `z` | Top-level depth for the compose camera |
|
|
63
|
-
|
|
64
|
-
Standard effect IDs: `layer-blur`, `blur`, `brightness`, `contrast`, `grayscale`,
|
|
65
|
-
`hue-rotate`, `invert`, `saturate`, `sepia`, `drop-shadow`. Native pixel effects:
|
|
66
|
-
`shader`, `film-grain`, `threshold-key`.
|
|
67
|
-
|
|
68
|
-
## Raw animation
|
|
69
|
-
|
|
70
|
-
`animate` is always an array of animation specs, including one animation:
|
|
71
|
-
|
|
72
|
-
```js
|
|
73
|
-
const animate = [
|
|
74
|
-
{
|
|
75
|
-
property: "offsetX",
|
|
76
|
-
from: 0,
|
|
77
|
-
to: 80,
|
|
78
|
-
at: 0,
|
|
79
|
-
duration: 0.5,
|
|
80
|
-
easing: "house",
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
property: "opacity",
|
|
84
|
-
keyframes: [
|
|
85
|
-
{ at: 0, value: 0 },
|
|
86
|
-
{ at: 0.3, value: 1 },
|
|
87
|
-
{ at: 1.7, value: 1 },
|
|
88
|
-
{ at: 2, value: 0 },
|
|
89
|
-
],
|
|
90
|
-
},
|
|
91
|
-
];
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Keyframe easing controls the outgoing segment. Curves: `ease-out` (default),
|
|
95
|
-
`house`, `ease-in`, `ease-in-out`, `smooth`, `linear`, `hold`, `bounce`, or
|
|
96
|
-
`[x1,y1,x2,y2]`. Raw tracks do not accept easing objects.
|
|
97
|
-
|
|
98
|
-
Channels include transforms, opacity, color, blur, volume, masks, path morph,
|
|
99
|
-
text progress, frame dimensions, and `effectParam`; the installed animate verb
|
|
100
|
-
schema lists supported fields. Times must fit the node. Finite `repeat` unrolls
|
|
101
|
-
closed chains, capped at 500 keys. Raw offsets add and uniform scales multiply;
|
|
102
|
-
other duplicate property tracks are refused. Distinct effect/parameter targets
|
|
103
|
-
can coexist. Choreography cannot also own an existing raw channel.
|
|
104
|
-
|
|
105
|
-
## Frame choreography
|
|
106
|
-
|
|
107
|
-
`motion` supports `poses`, local `cues`, `enter`, `settle`, `exit`, and `timeline`.
|
|
108
|
-
Phases require positive `duration` and a `from` or `to` pose (or named pose).
|
|
109
|
-
|
|
110
|
-
```js
|
|
111
|
-
const motion = {
|
|
112
|
-
enter: { from: { y: 24, scale: 0.94, opacity: 0 }, duration: 0.4 },
|
|
113
|
-
exit: { to: { opacity: 0 }, duration: 0.2, anchor: "end" },
|
|
114
|
-
};
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Pose channels: `x/y` offsets, `scale` or `scaleX/scaleY`, opacity, rotation.
|
|
118
|
-
Neutral offsets/rotation are 0; scale/opacity are 1. Uniform and axis scale cannot
|
|
119
|
-
share a target. Enter starts at 0; settle follows enter; exit ends at the frame end.
|
|
120
|
-
End-anchored numeric `at` is a lead before the end, not a start time.
|
|
121
|
-
|
|
122
|
-
`motion.timeline` forms:
|
|
123
|
-
|
|
124
|
-
| Form | Timing |
|
|
125
|
-
| ----------------------------------------------- | --------------------------------------- |
|
|
126
|
-
| `{from?, to?, duration, easing?, at?, target?}` | Self or one named immediate child frame |
|
|
127
|
-
| `{sequence: [...], gap?, at?}` | Consecutive children |
|
|
128
|
-
| `{parallel: [...], at?}` | Same start |
|
|
129
|
-
| `{stagger: [...], each, at?}` | Start offset by index × each |
|
|
130
|
-
| `{duration, easing?, at?, targets: [...]}` | Shared progress; no per-binding timing |
|
|
131
|
-
|
|
132
|
-
`at` is relative to the enclosing cursor/start, or `{cue: "name", offset?}` for an
|
|
133
|
-
absolute frame-local cue. Cues cannot precede a sequence cursor. Names are unique;
|
|
134
|
-
each tween must fit its target lifetime. Compilation resolves end anchors;
|
|
135
|
-
rebuilding retimes them, editing the compiled document does not rerun choreography.
|
|
136
|
-
|
|
137
|
-
Extra choreography curves: `{kind: "steps", count}`, `{kind: "overshoot", amount?}`
|
|
138
|
-
(default 1.70158), `{kind: "spring", stiffness?, damping?, mass?}` (170/26/1).
|
|
139
|
-
Springs/overshoot compile to bounded sampled tracks, not live simulations.
|
|
140
|
-
Springs reject endpoint position error >0.02 or normalized endpoint speed >0.1.
|
|
141
|
-
Opacity outside 0–1 and negative scale are refused. Text `motion.by` is separate.
|
|
142
|
-
|
|
143
|
-
### Shared counters
|
|
144
|
-
|
|
145
|
-
```jsx
|
|
146
|
-
<frame
|
|
147
|
-
width={320}
|
|
148
|
-
height={160}
|
|
149
|
-
gap={12}
|
|
150
|
-
padding={16}
|
|
151
|
-
motion={{
|
|
152
|
-
timeline: {
|
|
153
|
-
duration: 0.8,
|
|
154
|
-
easing: "linear",
|
|
155
|
-
targets: [
|
|
156
|
-
{ target: "Bar", from: { scaleX: 0 }, to: { scaleX: 1 } },
|
|
157
|
-
{
|
|
158
|
-
target: "Value",
|
|
159
|
-
counter: { from: 0, to: 100, decimals: 0, suffix: "%" },
|
|
160
|
-
},
|
|
161
|
-
],
|
|
162
|
-
},
|
|
163
|
-
}}
|
|
164
|
-
>
|
|
165
|
-
<frame name="Bar" width={280} height={12} background="#32acff" />
|
|
166
|
-
<frame name="Value" width={280} height={40} layout="none">
|
|
167
|
-
<text width={280} height={40} fontSize={28} color="#ffffff">
|
|
168
|
-
0%
|
|
169
|
-
</text>
|
|
170
|
-
</frame>
|
|
171
|
-
</frame>
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
A counter requires a fixed-size `layout="none"` frame with one static, single-line,
|
|
175
|
-
full-lifetime text template, without token motion, animation or matte.
|
|
176
|
-
`decimals`: 0–6; prefix/suffix: single-line, ≤64 characters; scaled values must be
|
|
177
|
-
safe integers. Counter-bearing leaves quantize **all** bindings to scene-fps held
|
|
178
|
-
samples, at most 256 including endpoints. Labels hold before/after the tween.
|
|
179
|
-
Pose-only leaves remain continuous. This is compiled text states, not a live text channel.
|
|
180
|
-
|
|
181
|
-
## GLSL and textures
|
|
182
|
-
|
|
183
|
-
```jsx
|
|
184
|
-
export default async ({ project }) => {
|
|
185
|
-
const p = await project({ size: "320x180", fps: 24 });
|
|
186
|
-
const texture = await p.add("texture.png");
|
|
187
|
-
p.compose(
|
|
188
|
-
<rect
|
|
189
|
-
width={320}
|
|
190
|
-
height={180}
|
|
191
|
-
fill="#dec8a2"
|
|
192
|
-
effects={[
|
|
193
|
-
{
|
|
194
|
-
kind: "shader",
|
|
195
|
-
params: {
|
|
196
|
-
glsl: `vec4 pixel(vec2 uv) {
|
|
197
|
-
vec4 src = texture(u_src, uv);
|
|
198
|
-
vec3 paper = texture(u_paper, uv * 3.0).rgb;
|
|
199
|
-
return vec4(mix(src.rgb, src.rgb * paper, u_amount) * src.a, src.a);
|
|
200
|
-
}`,
|
|
201
|
-
textures: {
|
|
202
|
-
paper: { assetId: texture.id, wrap: "repeat", filter: "linear" },
|
|
203
|
-
},
|
|
204
|
-
amount: 0,
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
]}
|
|
208
|
-
animate={[
|
|
209
|
-
{
|
|
210
|
-
property: "effectParam",
|
|
211
|
-
effectIndex: 0,
|
|
212
|
-
effectParam: "amount",
|
|
213
|
-
from: 0,
|
|
214
|
-
to: 0.7,
|
|
215
|
-
duration: 1,
|
|
216
|
-
easing: "linear",
|
|
217
|
-
},
|
|
218
|
-
]}
|
|
219
|
-
/>,
|
|
220
|
-
{ dur: 2 },
|
|
221
|
-
);
|
|
222
|
-
await p.frame(1, "renders/texture.png");
|
|
223
|
-
await p.render("renders/texture.mp4");
|
|
224
|
-
};
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
- Define `vec4 pixel(vec2 uv)` only; the wrapper owns GLSL version, precision,
|
|
228
|
-
uniforms and `main`. UV origin is top-left. `u_src` is straight RGBA;
|
|
229
|
-
`u_resolution` is pixel size; `u_time` is supplied timeline seconds.
|
|
230
|
-
- Finite numeric parameters become `u_<key>` floats. `effectIndex` selects the
|
|
231
|
-
zero-based declared effect; `effectParam` is its exact existing numeric key,
|
|
232
|
-
not the generated uniform name. Authored `u_amount` therefore maps to `u_u_amount`.
|
|
233
|
-
- Up to eight static image textures: imported `assetId`, `wrap: clamp|repeat|mirror`
|
|
234
|
-
(default clamp), `filter: linear|nearest` (default linear). Names must be legal,
|
|
235
|
-
distinct identifiers, without built-in/float collisions. No URLs/video textures.
|
|
236
|
-
- Return premultiplied RGBA. Native GL needs EGL/ANGLE; driver failures, invalid
|
|
237
|
-
shaders and missing textures fail rendering. GL selection is separate from video `accel`.
|
|
238
|
-
- Shader processing is RGBA8. Direct footage shaders can force an eight-bit fallback;
|
|
239
|
-
separate graphic overlays may retain the high-depth video path. No native LUT or
|
|
240
|
-
shader adjustment nodes. Graphic raster overflow is refused. Browser graphics/group
|
|
241
|
-
shader previews do not share the native rasterization path.
|
|
242
|
-
|
|
243
|
-
## Transitions and camera
|
|
244
|
-
|
|
245
|
-
A sequence's outgoing child accepts `transition: {preset, duration}`: fade, blur,
|
|
246
|
-
grow, shrink, directional slide, spin, twist. The out/in pair is centered on the cut.
|
|
247
|
-
|
|
248
|
-
Compose `camera: {focal?, keyframes: [{at, x?, y?, dolly?, easing?}]}` needs at least
|
|
249
|
-
two keyframes in absolute timeline seconds and top-level nodes with `z`. Depth
|
|
250
|
-
scales motion by `focal/(focal+z)`; dolly changes perspective. This is 2.5D, not mesh 3D.
|
|
251
|
-
|
|
252
|
-
`compose` with `dryRun: true` validates/compiles without mutation.
|
|
253
|
-
`inspect --id CLIP_ID --at SECONDS` reports center-time evaluated parameters, not
|
|
254
|
-
shader execution or evaluated world geometry. See [inspection](editor-measured.md).
|
|
255
|
-
|
|
256
|
-
## Complete example
|
|
257
|
-
|
|
258
|
-
```jsx
|
|
259
|
-
export default async ({ project }) => {
|
|
260
|
-
const p = await project({ size: "640x360", fps: 24 });
|
|
261
|
-
p.compose(
|
|
262
|
-
<frame
|
|
263
|
-
width={640}
|
|
264
|
-
height={360}
|
|
265
|
-
padding={40}
|
|
266
|
-
motion={{
|
|
267
|
-
enter: { from: { y: 24, opacity: 0 }, duration: 0.4 },
|
|
268
|
-
exit: { to: { opacity: 0 }, duration: 0.2, anchor: "end" },
|
|
269
|
-
}}
|
|
270
|
-
>
|
|
271
|
-
<rect
|
|
272
|
-
width={400}
|
|
273
|
-
height={80}
|
|
274
|
-
fill="#32acff"
|
|
275
|
-
animate={[{ property: "scaleX", from: 0, to: 1, duration: 0.6 }]}
|
|
276
|
-
/>
|
|
277
|
-
</frame>,
|
|
278
|
-
{ dur: 2 },
|
|
279
|
-
);
|
|
280
|
-
await p.frame(1, "renders/frame.png");
|
|
281
|
-
await p.render("renders/video.mp4");
|
|
282
|
-
};
|
|
283
|
-
```
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Editor runtime contract
|
|
2
|
-
|
|
3
|
-
Use the installed `higgsedit` and shipped `types/fable.d.ts` as authority. Do not hardcode paths or versions.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
command -v higgsedit
|
|
7
|
-
higgsedit --help
|
|
8
|
-
higgsedit do <project> --list
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Native behavior
|
|
12
|
-
|
|
13
|
-
- Project size and fps are fixed at creation.
|
|
14
|
-
- Script build replaces the timeline; `do` and `ops` mutate existing state.
|
|
15
|
-
- `rect` and text use top-left boxes. `positionX/Y` replace placement; `offsetX/Y` offset it.
|
|
16
|
-
- Persistent frames support column, row, equal-column grid, and none layouts. Frame children use local time and cannot outlive the frame.
|
|
17
|
-
- Frame `width`/`height` animation reflows layout. `reveal` changes visibility without reflow.
|
|
18
|
-
- Frame media slots center `contain` and `cover`; `fill` stretches.
|
|
19
|
-
- Composed media is picture-only. Use a cut or separate audio source for sound.
|
|
20
|
-
- Components evaluate their node tree at local zero; timeline placement applies only to the component root.
|
|
21
|
-
- Rendering has no per-frame callback API.
|
|
22
|
-
|
|
23
|
-
`effectParam` tracks are supported on media nodes in automatically sized frame slots. Effects are evaluated at each motion-blur sample, so animated effect parameters participate in sampled motion blur.
|
|
24
|
-
|
|
25
|
-
## Inspection
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
higgsedit inspect PROJECT --id CLIP_ID --at 6.5
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
`--at` requires `--id` or `--name` and uses concatenated timeline seconds. Results include timeline, scene, clip-local time, active state, and center-time evaluated effects. `execution.verified=false` means inspection did not prove effect execution. Inspect output is not world-space geometry or rendered-pixel proof.
|
|
32
|
-
|
|
33
|
-
## Supported limits
|
|
34
|
-
|
|
35
|
-
Duplicate replacement tracks are refused; raw offsets add and uniform scales multiply. Keyframes must strictly increase and stay within the node lifetime. Paths support `M/L/H/V/C/Q/Z`, not arcs. Shaped typography is opt-in with limited multilingual fallback. Shader-bearing graphics may differ from browser preview; use native output. Render cost depends on media, resolution, effects, and concurrency.
|
|
36
|
-
|
|
37
|
-
`check` validates supported document inputs. `sweep` reports flat frames. Neither is a visual or audio verdict.
|