Share the parts the sound devices and timelines repeated (#85)
feuerwerk / gate (push) Successful in 5m5s
feuerwerk / suite (push) Failing after 18m39s

Round 12 of the clean-up. Duplicate code had grown from 33 clones
(249 lines) after round 11 to 140 clones (1263 lines), almost all of
it in the panels and faces of the sound FX devices (#379 to #401).
It is down to 57 clones (416 lines) now, and src is 816 lines
shorter.

- deviceParts.ts holds what the device panels built by hand: the
  head, rows of knobs, the screen style and the row layout for
  groups of knobs. Twelve panels use it. panel.knobs stays because
  the debug hooks read it.
- DeviceFace is the base of the twelve faces. It finds the node and
  does wired, preview, commit, set, hide, reload and audition.
  compressorWired lives there as paramWired.
- TextPool replaces ten copies of "make texts as needed, hide the
  rest" in the wave and video tabs, the rulers, the piano roll and
  the song and composition timelines. clipStrip.ts is the clip label
  strip the wave and video tabs share.
- core/util/bytes.ts has the concat that ogg, matroska and avcColour
  each wrote out.
- Dead code is gone: clear on the video players (never called since
  #402), posterFileOf and videoTimeAt (only tests called them) and
  easeOut (the same as glide(t, 3)). Values only used in their own
  file lose their export.
- Comments over 125 columns are wrapped, and comments that told the
  history ("as before", "used to") say what is, in src and in the
  e2e modules.
- Left alone on purpose: AnimationEditor and CompositionEditor share
  only the play button, and the curve panels are built differently.
- Chip now places its boxes through put, which rounds. They were
  whole numbers already. The three row layouts are byte-identical
  before and after.
- README lists the video and composition assets and the Videos and
  Compositions pages.

Tests:
- Gate (melektaus-atx): CHECK EXIT 0, 313 files, 4835 tests.
- e2e 25, 34, 64, 65, 66, 69 (melektaus-atx): ALL PASSED, 330 checks.
- TextPool without keeping its texts and framesToRoom without making
  room both go red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-14 20:40:05 +02:00
1 parent df6dbe97c5
commit 0701fe3856
94 files changed
+1602 -2380

No files matched your search

+1 -1
View File
@@ -38,7 +38,7 @@ export async function topbar(t) {
);
// The group is centred on the bar itself. The row cannot do that on its own: the spacers around
// the buttons share the free space with the flexible notice and so get a third of it each, which
// used to leave the group well left of the middle.
// would leave the group well left of the middle.
const play = await json('__gm.toolbarAt("play")');
const step = await json('__gm.toolbarAt("step")');
const barCentre = await json('window.innerWidth / 2');
+4 -4
View File
@@ -191,7 +191,7 @@ export async function video(t) {
// 66e-iii. With sound the video has a node in the Master routing (#351), linked to the master.
// Sent into a bus there, the tab still plays it straight into the master, as every
// tab's preview does (#368): heard at the output, where a recording of the editor
// listens, and nothing on the bus. It used to play past the mix altogether.
// listens, and nothing on the bus.
const graphOf = (expr) => json(`((g) => ${expr})(__gm.routing.graph())`);
const routed = await graphOf(
`({ node: g.nodes.find((n) => n.id === ${JSON.stringify(assetId)}) ?? null,
@@ -589,9 +589,9 @@ export async function video(t) {
JSON.stringify({ readied, firstShown }),
);
// 66n. The same in a run: the world used to leave a switched-off object out of what the Game tab
// draws, so a cut to it loaded the video only then and showed white meanwhile. Nor did wearing
// the player count as asking for it: let go after 2 to 4 s, it was opened again.
// 66n. The same in a run: a switched-off object stays in what the Game tab draws, so a cut to it
// finds its video loaded and shows no white. Wearing the player counts as asking for it, so it
// is not let go after 2 to 4 s and opened again.
await switchLate(false);
const sceneKeys = new Set((await json('__gm.videos()')).map((v) => v.key));
await play();
+1 -1
View File
@@ -506,7 +506,7 @@ export async function recording(t) {
// it records (resolution 1.8) rather than scaled up, here at 30 fps without sound. Without
// Convert, Import goes grey, the file is only kept on the server and nothing comes into the
// project. The video picker downloads it as it is, and converts it on asking at its full size,
// above the 1080p the server used to shrink every video to.
// even above 1080p.
const assetsBefore = await json('__gm.state.project.assets.length');
await startDialog({ resolution: '1440p', fps: 30, pointer: false, sound: false });
const convertAt = (await json('__gm.recordingDialog()')).switches.convert;
+2 -1
View File
@@ -51,7 +51,8 @@ export async function composition(t) {
],
};
// Red from the start, a larger blue behind it from the next frame – built after red, it still goes
// behind – and green after them, moved by a clip; ten frames a second, so the cut lasts 1.2 s. Placed where the camera looks, in front of what is there.
// behind – and green after them, moved by a clip; ten frames a second, so the cut lasts 1.2 s.
// Placed where the camera looks, in front of what is there.
await mutate(`
const p = __gm.state.project;
const scene = __gm.state.activeScene;
+11 -10
View File
@@ -2,16 +2,17 @@
* The Composition tab (#417). A sprite dragged in from the Assets panel becomes an item, 3 s long
* and picked, so the inspector shows its TIMING; a second one dropped onto the first lands there,
* over it. The end of the first, covered by the second, lights up under the pointer and is dragged
* past it. An animation dragged in becomes a clip that moves the item it overlaps in the picture. The hierarchy lists what is alive at the playhead, Delete
* takes an item with its object, and Play runs the playhead to the end. The video of an item about
* to begin is opened ahead, so the cut to it shows its picture at once, and Play waits for a video
* whose player is still loading. A copied item is pasted under the pointer or at the playhead. A
* video dragged in wears a sprite the composition keeps to itself. An item dragged in the picture
* follows the pointer while the button is down. A band picks items, with Alt clips, and copied clips
* are pasted where they fit. A sound dragged in is an audio item, heard while the playhead runs;
* the handle of its fade in drags out a fade its bus rises along. A video item's sprite reads as
* driven by the composition and goes into its bus, which has a node in the master routing; after a
* hitch the sound is started again where the playhead is.
* past it. An animation dragged in becomes a clip that moves the item it overlaps in the picture.
* The hierarchy lists what is alive at the playhead, Delete takes an item with its object, and Play
* runs the playhead to the end. The video of an item about to begin is opened ahead, so the cut to
* it shows its picture at once, and Play waits for a video whose player is still loading. A copied
* item is pasted under the pointer or at the playhead. A video dragged in wears a sprite the
* composition keeps to itself. An item dragged in the picture follows the pointer while the button
* is down. A band picks items, with Alt clips, and copied clips are pasted where they fit. A sound
* dragged in is an audio item, heard while the playhead runs; the handle of its fade in drags out a
* fade its bus rises along. A video item's sprite reads as driven by the composition and goes into
* its bus, which has a node in the master routing; after a hitch the sound is started again where
* the playhead is.
*
* Everything it adds carries a `ct69` id and goes again at the end.
*/