70402aa19a4011c4bef17cf131ab5e47d6efb4ae
192
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
70402aa19a |
Let the agent hold the game at a given game time (#435)
The agent could not look at a short moment of a running game. Its
calls took longer than the moment lasted, so an intro window from
0.5 to 2 s slipped past twice in the 5d test. Now the editor itself
stops the clock at the time the agent names.
- play takes until: play, resume or pause let the game run on to
that game time and hold it there. The call answers once the game
is paused, so get_runtime and screenshot show that moment.
- The frame that would step past the time is cut short
(frameWithin in src/core/time.ts, used in PlaySession). The game
stands at 0.5 s, not one frame later.
- play_input takes a last step {"pause": true}. After a wait it
holds the game at the time of the wait, after a key or click at
once. Its answer names the game time and whether it is paused.
- A pause that comes first (toolbar or a Break node), a Load scene
or a Restart game call the until off, and the answer says so.
- When the clock stands still, the call gives up after 25 s with
the time it is at. A tab in the background is refused at once.
- A recording that does not run in real time is not cut. There the
game stops in the first frame that reaches the time.
- get_runtime and play_input give times to the millisecond, not to
the hundredth, so 0.505 no longer reads as 0.51.
- The relay's instructions name both ways.
Tests:
- Unit agent-play (6 new) and time (2 new): 19/19, related editor
and relay tests 1158/1158.
- 6 counter-checks, all compile, all red. One stayed green at
first: 0.3 s is exactly 18 frames. The test now takes 0.31 s.
- e2e 71 (atx): ALL PASSED, 27 checks. New 71h3 holds at 0.505 s
and 0.8 s and after a wait of 0.31 s. Without the cut in
PlaySession it went red at 0.5166 s.
- Gate (atx): CHECK EXIT 0, 327 files, 5033 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
e4ccf5f72f |
Keep other nodes out of a group the agent makes round new nodes
A group made in an agent call is fitted round its nodes only after the call has arranged them, and the arrangement knew nothing of it. With On key held, Move and Owner placed fresh, a group round key and move and one round the owner, the Owner landed beside the key. Move is taller than the key, so the Owner stood in the first group and the two groups lay on each other. lint_graph counted none of it. Found while testing copy_nodes (#434). - arrange takes the new option blocks: sets of arranged nodes a group is fitted round afterwards. Once the columns are laid out, whatever would stand in such a group goes below it, a node or the group of another set, with everything arranged under it. - The flow keeps its columns. A first try laid each set out as one block instead, but then the link from the Owner ran over the key inside the group (throughNodes 1). - AgentEdit passes every group made in the call whose nodes are all new. Groups round nodes already there and the Arrange button stay as they were. Tests: - agent-edit: new test with the setup that showed the fault. No node stands in a group not its own, the groups lie apart, lint_graph has 0 overlaps, throughNodes, backwards and coveredNotes. - Counter-check: without the new step exactly that test is red. - Agent, graph-arrange and graph-follow tests 120/120. - e2e 71 on atx: ALL PASSED, 26 checks. - Gate on atx: CHECK EXIT 0, 327 files, 5025 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fb981b5b0f |
Let the agent copy nodes, also into another graph (#434)
The agent built every part again that already stood in a graph, node by node, while the editor has Ctrl+C and Ctrl+V for it. The new command `copy_nodes` in the area graphs copies nodes into the same graph or another one, in one undo step with the rest of the call. - The links between the copied nodes come along, and so do groups and comments that are only about copied nodes. A node belongs to a group by its middle, as when it is dragged. `boxesAbout` in annotationFollow.ts decides this. - `made` names each copy by the id of its original, so later steps link to it with `$copy.<id>`. `$copy` alone is the first one. - Without x and y the copies are arranged at the end of the call like new nodes. With them they keep their layout, top left corner there. - Nodes the target graph does not take are refused by name, not left out as Ctrl+V does. The rule is the graph tab's: `fitsGraph` and `fittingPart` moved from clipboard.ts to paletteEntries.ts. - A copied Variables node takes the variables of its behavior along into the target behavior (`CarryVariablesCommand`), with default, value, Keep and Private. A variable of that name of another kind there is refused. - The relay's instructions name `copy_nodes`. Found on the way: - Groups made by the agent can take in a node of another group once the nodes are arranged. lint_graph does not count that. - Undo after a paste leaves an empty `groups: []`, as AddAnnotationCommand does. It is the same as no list. Tests: - agent-copy (new, 3 tests) with the agent, relay, clipboard and palette tests: 118/118. - Counter-probes: 6 bendings, all compile, all red. - e2e 71 (atx): ALL PASSED, 26 checks. - Gate (atx): CHECK EXIT 0, 327 files, 5024 tests. - Live in the MCP Test Project: 5 nodes of Arrow keys walk into a new behavior, 6 links, group, comment and speed (300) along, lint_graph 0 everywhere, taken back with undo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
24cd9fdeaa |
Let the agent use stored files, presets and usages (#431 #432 #433)
The agent got stuck where the editor has it easy: it could not make a
Sample play without a sound asset, it built instruments node by node
although the Preset menu has them ready, and it could not tell what
names an asset before reworking or deleting it.
- list_files is a new tool. It lists the pictures, fonts, sounds or
videos the server holds, with size, video state and the asset that
already wears a file. Bakes and stream copies of songs are left out.
- add_file_asset makes an asset of such a file, as the picker does.
It reuses the create rules of the import pipeline, so the four
import kinds are exported now. It refuses a file already in the
project (naming that asset), a picture without a size, a video not
yet converted and a file the server does not hold.
- run_commands fetches the file lists its steps need before it runs,
so the commands themselves stay synchronous.
- apply_preset sets one of the 16 instrument presets or 7 sfxr
presets, like the Preset menu. Presets take every note, so the
C4 to B4 range of a new Notes node is no trap there.
- get_asset gains usedBy: every asset naming the asset or one of its
frames or clips, with the object, node, item or key that does, plus
start scene, master FX and master routing of the project.
- usesOf in src/core/model/usages.ts walks the project data instead of
a list of fields, so no new field can be missed. The node every
asset has in the master routing does not count as a use.
- The relay tells the agent about all three ways.
Found on the way: suite 740 for
|
||
|
|
2e4c0a09ea |
Open clips and frames from the asset panel by double click (#430)
A double click on a clip row did not open its tab. The first click of the double click had already selected the clip for the inspector, so the second click selected the same thing again and the editor saw no change. Frames of textures and pixel art had no rows at all. - A double click now asks for the tab with openSelection, even when the selection is already right. - Textures with frames and pixel art with more than one frame get a chevron and one row per frame, like sounds and videos. Which rows an asset has is decided in one place, src/core/model/assetParts.ts, used by the panel and the inspector link. - A texture frame shows in the inspector on click and opens the texture with the frame selected on double click. - A pixel art frame selects the pixel art on click, so an open tab paints on that frame. A double click opens the tab on the frame. - A frame can be dragged onto a composition track like a video clip. The item carries the sprite of the whole image, the frame becomes the texture of its object, and it is 3 s long. The agent can do the same with add_composition_item. - A texture label that names a frame now points at the frame row. Found on the way: the e2e check from #425 only double clicked the texture label, where the first click is a ping, so the bug never showed there. Tests: - Unit: asset-parts (new, 6), composition-sprites and references extended, related tests 303/303, agent tests 21/21 - 5 mutations, all red in the matching test - e2e 69 (atx): ALL PASSED, 26 checks, 69t new - Gate (atx): CHECK EXIT 0, 325 files, 5017 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
37779a2070 |
Fold object groups in the timeline, with summary keys (#414)
Long animations with many objects fill the timeline with rows. A group per object can now fold away, and its header row still shows where the group has keys, so they stay reachable while folded. - A click on an object header or its triangle folds the group. The groups of the objects below it fold along. - Which groups fold along follows the real parent in the hierarchy, not the indent. The root shows at the level of its children, so folding it hides only its own rows. - Every header shows a summary diamond on each frame where a row of the group or of a group below has a key. Click selects all those keys, drag moves them, Ctrl+drag duplicates, a band catches them and Delete removes them, each through the existing commands. - The fold state is a view of the session per animation. It is no undo step and is not saved. - Selecting a row without keys inside a folded group unfolds it, so a fresh row from + Row does not stay hidden. - The fold rules live in the new timelineFold.ts. keyAt became keysAt, which returns every key behind a summary diamond. Found on the way: the triangle moved the object name to x 22 while its rows stayed at x 20, left of the name (#408 check in e2e 25). Rows now sit one step behind the name, as in the hierarchy. Tests: - Unit animation + timeline-view 114/114, 6 new - 6 counter-checks, all red in the matching test - e2e 25 (atx) ALL PASSED, 41 checks, 4 new (25k) - Gate (atx) CHECK EXIT 0, 324 files, 5008 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
19902a9f02 |
Keep groups, comments and silent music in view for the agent (#429)
A live test of the music areas built a pattern, a song and a composition that stayed silent: no instrument at first, and later notes outside the C4 to B4 range of a new Notes node. The agent got no hint of either. The same test showed that new nodes left groups behind, that a comment could end up under other nodes, and that an end node like Output stayed left of the devices put in front of it. - get_asset lists under silent what of a pattern, song or composition is not heard. That is a missing instrument, an instrument that sounds no note, or notes outside what its Notes nodes take in, in a song after the transpose. - soundingNotes moves from the pattern tab to src/core/instrument/compile.ts. The tab and the agent use the same rule. - When new nodes go between two that have no room, everything from the fed node on moves right together. This holds for Arrange with a selection too, so no link runs backwards. - A new node linked to a node in a group joins that group, and the group grows in the same undo step. A group that would take in a foreign node stays as it is. - Arrange takes groups and comments along: a group whose nodes all moved the same keeps its size, otherwise it is fitted anew. The rules live in the new src/editor/graph/annotationFollow.ts. - add_comment and fit_group put a comment below its nodes when a node or a group title is in the way above. lint_graph counts covered comments as coveredNotes. - add_notes, get_asset, lint_graph and the relay instructions name the default range and how to mend a covered comment. Tests: - graph-follow (new, 12), agent-edit, agent-music (11) and the other agent, graph, pattern and relay tests green - 10 deliberate breakages, each one red in its own test - e2e 71: 25/25 - Gate: CHECK EXIT 0, 324 files, 5002 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
10f5e3ba8f |
Let the agent write music and build compositions (#429)
The agent could already draw, animate and wire graphs, but songs, patterns, sound clips and compositions were out of its reach. This last step of #429 adds the two missing catalog areas, so the agent can now use 168 of the editor's commands. - Area music (15 commands): notes, pattern and song loops, song tracks and items, sends, and clips of sounds and videos. - Area composition (9 commands): tracks, items for objects, images and audio, changes with fades, volume and bus, removal, and animation clips. - A pitch is a MIDI number or a name like C4 or F#3. A note past the end of its pattern is refused. - Song and master routing stay graphs. The agent links them with connect, and set_send sets level and tap. get_graph now shows the send of every link. - Lengths follow the same rules as dragging in the tab. A sound has a length only after the editor loaded it. get_asset reports it as seconds, and without it the agent has to give a length. - Moving a composition item takes its clips along, as dragging does. A clip that would then overlap another one is refused. Fades are only allowed on items with sound. - get_asset reads notes back with their names and gives the length of a pattern in ticks. Found while testing live: two song tracks in the same column made lint_graph report a backwards link. arrange_graph cleared it, as the agent instructions ask. Tests: - agent-music (new, 9 tests) and the other agent and relay tests: 109/109. - 9 mutations of the new code, each one red in its own test. - e2e 71 on melektaus-atx: 25/25. New check: notes are read back by name, and a composition with a pattern and a sprite opens in its tab. - Gate on melektaus-atx: CHECK EXIT 0, 323 files, 4987 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0684e76dc5 |
Let the agent draw pixel art and vector art (#429)
Stage 5c of the MCP work. The agent could place sprites and edit graphs, but it could not draw: pixel art, palettes and vector art had no commands in its catalog. Two new areas bring 29 commands, so the catalog now holds 144. - pixelart (13): paint, layers (add, copy, remove, change, move, merge down), frames (add, also as a copy, remove, move), canvas size with an anchor, palette colors set and removed. - paint_pixels edits one layer on one frame. Its steps run in order: rows (a small picture as characters, a space keeps the pixel), pixels, line, rect, ellipse, fill, clear. An index the palette does not have, a pixel off the canvas and an unknown character are refused. - vectorart (16): shapes from rings or as rect and ellipse, outline, style (fill, opacity, gradient or image fill, stroke), move, scale, rotate and flip, copy, remove, combine, rename, order, move into groups, group, layers. - get_asset gives the palette of pixel art as colors. Up to 16384 pixels over all layers and frames each layer comes as rows, above that only the number of painted pixels. Palette and vector colors come as #rrggbb. - Merge down, canvas size and removing a color are now builders in pixelActions.ts. The panel and the agent share the same code. - Color and point helpers moved into catalog/shared.ts, and graphs.ts uses them too. Found while testing: - The ellipse tests only checked the four extreme points, which stay right even with wrong handle lengths. The test now checks that every point of the drawn curve lies on the ellipse. Tests: - agent-drawing (new, 9) and the other agent, pixel and relay tests: 133/133. - 7 mutations, all red in the matching test. - e2e 71 on melektaus-atx: 24/24. New check: the agent's pixel art opens in its tab, reads back as rows, and its cel is on the server after saving (status 200). - Live in a test project: an 8x8 sprite and a copied second frame read back as painted, a rotated square and a radial gradient disc with their corners where computed. - Gate on melektaus-atx: CHECK EXIT 0, 322 files, 4978 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d01e2b832e |
Let the agent edit animations, frames, tiles and tilemaps (#429)
After stage 5a the agent could build behaviors and scenes, but not animate an object, cut a texture into frames or paint a tilemap. Stage 5b adds three areas with 30 commands, 115 in all. - animation (12): set the root, add, retarget and make rows relative, set, move, copy and delete keys, easing, insert or remove frames, and curve points. - textures (8): add, change and delete frames, slice a grid, nine slice, sorting line and collision outline. - tiles (10): the images of a tile, painting tilemaps, layers (add, copy, move, change) and the border of a placed tilemap. - get_asset names the targets of an animation with their animatable properties. A tilemap up to 400 cells shows its cells, a bigger one only their count. - After such a call the editor opens the tab of the animation, curve, texture, tile or tilemap. A frame edit selects the frame there. - One paint step takes rectangles and single cells. Rectangles go first, so a cell can cut a hole into a rectangle. Cells outside the grid, ids that are no tile and more than 20 000 cells are refused. The grid is the tilemap's largest, a grown placement included. Left out on purpose: Generate collision, because it reads the pixels of the texture, and pasting keys, which goes through the clipboard. Found on the way: - Nine slice without fill now keeps the fill the image has. The model's default is repeat, not stretch as first described. - Painting ran the other way round, rectangles overwrote cells. Tests: - agent-art (new, 11) and the other agent tests green, 64/64. 5 bends of the code, all red. - e2e 71 on atx: 23/23, the new check opens the agent's animation in its tab and reads its targets. - Gate on atx: 321 files, 4969 tests. - Live on a test project: a Spin animation of 360 degrees in 30 frames matched 12 degrees per frame to 0.01 frames in the game. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0d8fd6ff52 |
Let the agent use the graph, scene and project commands (#429)
The agent knew 23 of the editor's commands. It could not point a Variables node at a variable, group nodes, edit ports, transitions, containers, colliders or the project settings. Stage 5a brings the catalog to 85 commands in five areas and keeps every answer short. - The catalog lists names and descriptions only. With a query (an area or command names) it adds the arguments. All schemas at once would pass the 60 000 character limit of one answer. - add_port points a Variables node at a variable. The port takes the variable's kind, and an unknown name is refused with the list of the ones that exist. - add_group and add_comment take nodes instead of coordinates. They follow the nodes after the arrange step, in the same undo step. - The rule for which list a port picks from moved into portChoices.ts. Inspector and agent now share it. - The frame of a group around nodes moved into annotationDrag.ts, so the button and the agent get the same frame. - list_assets returns the project settings. get_asset shows the content of placed containers with the ids overrides need. get_graph shows declared ports and frame colors. - A new node is placed at the height of its port, in the free spot whose links cross the fewest others. A source goes left of the node it feeds. Before, new nodes always went right of the rest. - Arrange breaks a tie in crossings by putting the flow on top, and exec links pull harder when a column is sorted. - A comment over a group keeps clear of the group's title bar. - The agent's instructions and lint_graph ask to look at crossings too and to call arrange_graph when there are more than needed. Left out on purpose: file assets (no upload), paste from the clipboard, internal wrapper commands and switching plugins on. Found on the way: - Variable commands on a Struct asset did not run through WithStructTypes. New fields did not reach variables of that type. - A query "variables" also matched set_variables_scope. A word that names an area now means only that area. Tests: - agent-catalog (new, 10), agent-edit, graph-arrange (3 new) and relay green. 11 bends of the code, all red. - e2e 71 on atx: 22/22, the new check sees port speed and a group around all 4 drawn nodes. - Live on a test project: a speed variable of 300 moved the player to x 150.03 in 0.5 s. A re-placed Variables node landed below Axis input with 4 crossings instead of 7. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1943539a7a |
Let the agent play the game and read its state through MCP (#429)
An agent could build a game object but not see whether it works. It had to ask the user to press the keys. Now it can play the game in the editor itself, send keys and mouse input and read what happened. - play_input sends steps into the running game: hold or tap a key, keep it down or let it go, click, press, release or move the mouse, turn the wheel, wait. Holding and waiting count game seconds. - A mouse target is a point in world units or an object by id or name. A click on an object reports whether it landed on it. - The answer names the game time used, the keys still held, the mouse and the new console lines. - One tool instead of press_keys and pointer from the plan. A step list can then mix both, like holding Shift during a click. - get_runtime reads the running game: objects with id, type and position, the own variables of their behaviors, the Variables assets, the camera, keys, mouse and the console. Named objects come with all their properties. - A hidden editor tab gets no frames. play_input says so at once. If game time stands still for 25 s it stops, names the step and lets go of every key it held. - get_view and play say when the scene has no active camera. The first agent test missed that and only saw it on a screenshot. - The relay instructions tell the agent to test what it built. Found in the live test on a real project: - A click on an object by its name reported hit false. The hit test looked the target up by id only. - get_runtime listed the built-in switches of a behavior (enabled, runInEditor) as its variables. Tests: - agent-play (new), agent-edit, agent-view, relay green. - 5 counter-checks red: hold counts one frame, key never released, click ignores the object, hidden tab not refused, no variable filter. - e2e 71: 21/21. The agent holds ArrowLeft 0.5 s (x -120 to -270) and clicks the object (hit true). - Gate: CHECK EXIT 0, 319 files, 4944 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
97e0d93168 |
Let the editor follow the agent's edits on screen (#429)
The user asked to see what the agent is working on through MCP. Until now the agent had to call open for that, and mostly did not. - After run_commands the editor shows what the call worked on last. A graph opens its tab with the touched nodes selected, deleted ones left out. A scene or container shows with its objects selected. Any other asset shows in the inspector. - Objects in another scene switch the scene inside the same undo step. A switch of its own would be its own step, and Ctrl+Z would take back the switch before the agent's edit. - A graph tab that is open already moves its camera after the nodes. It stays put while they are in view, moves only as far as needed with the zoom kept, and frames them like Fit only when they do not fit. This is revealBoxes in framing.ts and the state's reveal event. - A tab that has just opened frames itself first and moves after that. - The account menu has "Follow agent" below "Agent", on by default and kept in the browser. While the game plays the editor never follows. - The run_commands description and the relay's instructions tell the agent about it. Tests: - agent-edit.test.ts: 3 new tests, each red with its bent-back fix (scene switch outside the undo step, deleted nodes selected, switch ignored) - graph-framing.test.ts 29/29, 6 of them new - e2e 71 on melektaus-atx: ALL PASSED, 20 checks. 71g4 adds a node 3000 px to the right of an open tab: it ends 24 px inside the edge, zoom still 1 - Tried live on a test project through a real MCP session: the tab opened, the nodes and the object were selected. With the old build a far node stayed out of view, after the reload the camera went there - Gate on melektaus-atx: CHECK EXIT 0, 318 files, 4931 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
732989a452 |
Keep Fit inside the song tab's header again (#429)
Suite 719 for |
||
|
|
b9d1602b94 |
Arrange graph nodes for the agent and with a button (#429)
An agent that built a graph through MCP left every new node in one row. In the first real test that put nine nodes on a line 2080 px long, with the Owner's links running straight through all the others. Placing nodes by hand cost nine move_node calls and guessed heights. The agent now gives no coordinates, and the editor lays the nodes out. - arrange.ts is a pure function. It puts the flow into columns from left to right and sets an Owner or Get right before the first node it feeds. It orders each column for the fewest crossings and lines nodes up by their linked ports without overlaps. - Node sizes come from the drawn NodeView in the browser and from an estimate in unit tests. Both share the constants in nodeBox.ts. - run_commands arranges the nodes that came without x and y when the call ends, inside the same undo step. Nodes that were there stay. - New command arrange_graph and new tool lint_graph. The lint counts overlaps, crossings, links through foreign nodes, backward links and the longest links, with example ids. - The relay instructions tell the agent not to give coordinates and to fetch far values with a second Get or Owner node. - An Arrange button next to the snap magnet in all eight graph tabs. It arranges the selected nodes, or all of them, as one undo step. Found while testing on a real project: - lint_graph missed a crossing where two links swap rows exactly mirrored. Both curves then share a sample point in the middle, and only a crossing between two sample points was counted. Links that meet on a point count now (segmentsMeet). - The Arrow-key graph with one Owner for four Moves cannot get below 8 crossings. The Owner feeds object in row 2 of Move, between exec and dt of the key, so it always crosses one of them. Tests: - graph-arrange, agent-edit, snap-toggle: 33/33. The swapped-rows test was red before the fix (0 instead of 1). - Four counter-checks that bend arrange or the edit plan, all red. - e2e 71 on melektaus-atx: 18/18. Lint after the build shows 0 overlaps, 0 links through nodes, 0 backward. Arrange takes a tangled graph from 3 backward links and 2 crossings to 0, and one Ctrl+Z restores it. - Gate on melektaus-atx: CHECK EXIT 0, 318 files, 4917 tests, measured before the lint fix. - Live through MCP on the user's project: lint_graph reports 8 crossings after Arrange, matching the screenshot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
25af064249 |
Let an AI agent read and edit the open project through MCP (#429)
Stage 2 of #429. Until now an agent could only look at the editor. Now it reads the project and changes it through the same commands the user's own edits run, so every change shows up live and one Ctrl+Z takes back a whole agent call. - Read tools: list_assets, get_asset, get_graph and catalog. The catalog lists the commands with their arguments, the nodes of a graph from the same palette the user sees, asset types and variable kinds. - run_commands runs its steps as one undo step named "Agent: ...". A failing step rolls back the steps before it. The answer names the step and the reason the editor gave. - A step can name what it made ("as") and later steps use "$name". Each step is built after the one before has run, so one call can make a behavior and fill it. - 23 commands cover assets, variables, scene objects and graphs. Textures, fonts, sounds and videos need a file and are left out. - open, select, play and undo/redo drive the editor. - Deleting asks the user with the same text as their own delete. The tab sends "wait", and the relay then gives the call 2 minutes instead of 30 seconds. - tools.json is the only source of the argument schemas. argCheck checks every call against them before anything runs. - An open menu now names its first entries in get_view, instead of an empty title. - The new host functions live in editorToolHost.ts, so Editor.ts does not grow further. Found while testing: e2e 71 hung for 30 s in Page.navigate after a failure. The beforeunload guard of an unsaved project held the page and hid the real error. The module now reports the error first and waits for the save before it navigates. Tests: - Gate on melektaus-atx: CHECK EXIT 0, 317 files, 4903 tests. - agent-edit 14/14, agent-view 10/10, relay 24/24. - e2e 71 16/16 on atx and against the live site. One call builds a sprite with an arrow-key behavior, play moves it from x 0 to -125, and one undo restores the assets exactly. - Counter-checks turned red: no rollback, a partial undo, no delete question, a relay that ignores "wait". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
48ec722660 |
Let an AI agent see the open editor through MCP (#429)
Claude Code should be able to work inside the user's open editor tab. This first stage connects the two and lets the agent look: what the user sees and a picture of it. Changing the project comes in stage 2. - An agent token per account opens only /mcp, no API route. The dialog "Agent tokens..." in the account menu creates it once, shows the ready "claude mcp add" line and revokes it. The server keeps only its sha256 (api/lib/AgentTokens.php). - Wrong tokens count into the sign-in brake from #427. Changing the password, renaming or deleting an account ends its tokens. - The relay (tools/mcp/) is a small Node service without packages, WebSocket server included. It runs in the container feuerwerk-mcp because PHP-FPM cannot hold connections. Whether a token or a tab ticket is valid is still decided by the PHP API, through two routes that answer only with the relay key. - The tab joins over WebSocket on the same origin with the normal session, an Origin check and a one-time ticket. It connects only after "Agent" is switched on in the account menu. One tab per account is active, a new one takes over. Guests get nothing. - Tools: get_view (project, tab, document, selection with names, camera, play state, open dialog) and screenshot. The resource feuerwerk://view can be subscribed. Claude Code does not act on that reliably, so a changed view is also noted on the next answer. - A badge next to the account menu shows the connected agents. It counts only agents with an open stream or a request in the last two minutes. - CI starts the relay in front of php -S (tools/ci/serve.sh), and e2e 71 runs the whole path in the browser. Found on the live test: the badge showed four agents because old sessions of revoked tokens were still counted. Tests: - Gate on atx: 316 files, 4888 tests - relay protocol test with fake API and fake tabs: 23/23 - get_view, tools, socketUrl: 16/16 - PHP agents 29, rights 103 checks - e2e 71 on atx 10/10, against the live site 10/10, no CSP violation - real Claude Code against the live site: get_view answered - ZAP against /mcp: nothing at High or Medium - every new check fails when the guard it covers is taken out (6) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0701fe3856 |
Share the parts the sound devices and timelines repeated (#85)
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> |
||
|
|
043c224cc3 |
Rename Game Maker to Feuerwerk and serve it at /feuerwerk/ (#422)
The editor gets a name of its own that fits beside Brandmauer, Glutspeicher and Feuerwehr. This is stage 1, 2 and 4 of #422: the name, the URL and the stored state in the browser. Repository, CI runners, containers and the GAME_MAKER_* variables follow in stage 3. - All paths come from SITE_PATH in src/core/site.ts: the API endpoint, the data folders, the play page and the base in vite.config.ts. A later move is one line instead of fifteen. - The window title, the sign-in screen, the playground and the README say Feuerwerk. The package, __feuerwerkPlay, the take worklet and the writing app in Ogg and Matroska follow. - The session cookie is now feuerwerk_session, so everyone signs in once more. - localStorage keys are now feuerwerk.*. moveOldStoredKeys copies the old game-maker.* keys over on the first start, so settings, panel sizes and save games survive. - tools/ci/serve.sh serves the build under /feuerwerk/. The variable GAME_MAKER_BASE_URL keeps its name until stage 3. Outside the repository, nockal.com/feuerwerk/ is a second symlink to the same tree. nginx sends the bare /game-maker/ folder there with a 301. Everything else under /game-maker/ is still served directly, so shared play links and the API keep working. Finding: when nginx redirects a path without a trailing slash or with a query, the Brandmauer answers with its own 307 to the same URL plus a slash, placed after the query. A play link would lose its token that way, which is why only the bare folder is redirected. Tests: - Gate on melektaus-atx: CHECK EXIT 0, 307 files, 4756 tests (4 new in site.test.ts). - Counter-check: when the migration misses the old prefix, 3 of the 4 new tests fail. - e2e 60 under /feuerwerk/: ALL PASSED, 20 checks. - From outside: /feuerwerk/ 200, /game-maker/ 301, an old play link 200. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
480db9c4e4 |
Scroll the rows of the animation timeline up and down (#413)
An animation with more rows than the panel is tall cut the bottom rows off. There was no way to reach them, their fields or their keys. - The view has a new `scrollY`. Rows scroll up under the ruler, which stays where it is together with the tab head. - The wheel over the header column scrolls the rows. Over the cells it still zooms, and `Shift`+wheel still scrolls across. - A scrollbar at the right edge shows up only when the rows do not fit. Its thumb can be dragged, a click beside it jumps there. - The middle button pans both ways. `Shift`+drag stays across only. - A row picked or added out of view is scrolled just far enough to be seen whole. Value fields out of view are not drawn. - The tab keeps its scroll like its zoom when switching. Found on the way: `zoomCells` built a new view from `panX` and `cellWidth` only, so every zoom would have dropped the scroll. The type check caught it, and a unit test now holds it. Tests: - tsc, eslint, Prettier clean - Unit timeline-view: 20 green, 6 new. Bending `rowAt` to ignore the scroll or `revealRow` to do nothing turns exactly those tests red. - e2e 25 on melektaus-atx: ALL PASSED, 37 checks (25j, 25j-ii new). 32 rows, the wheel brings the last field into view and typing keys 77 there. Dragging the thumb up goes back to 0. - Gate on melektaus-atx: green, 305 files, 4730 tests. - Not checked in the browser: the middle button, revealing a picked row, the wheel right over a value field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d4fc425233 |
Draw the editor's mouse pointer from SVG files, on screen too
The pointer drawn into an editor recording lived as paths in code, so it could not be looked at or edited by hand. The shapes are now SVG files, and the editor can show the same pointer on screen, so what is recorded is what was seen. - src/core/recording/cursors/ holds one file per shape: arrow, hand, open-hand, fist, move, crosshair, text and resize. Resize is turned for the vertical and diagonal directions. - data-hot="x y" on the <svg> is the hotspot in viewBox units. Width and height in the file are ignored, and anything before <svg>, such as <?xml ...> from a drawing program, is cut off. - The open hand had six fingers. It has a thumb and four now, the middle one longest. - CURSOR_SIZE = 0.8 scales every pointer the same on screen and in a recording, so the files keep their units. - General settings has "Custom mouse pointer", on by default. It hangs the files into Pixi's cursorStyles as CSS cursors with the keyword as fallback, so the system still draws the pointer and the canvas does not. Off gives Pixi's own values back at once. - A recording draws the pointer from the same files at its own size, so it stays sharp in 4K. It still reads the shape from the canvas when that holds a url(...) cursor. Tests: - Unit recording and view-tween: 3 new, green. They read viewBox and data-hot in an edited form, the boxes of turned shapes, the default size and old settings without the switch. Red without data-hot and without cutting before <svg>. - e2e 46i-ii: the canvas cursor is a url(...) ending in default and the image loads. Off gives inherit and stores false, on brings the file back. Red without useCursorFiles. - e2e 67e: the recording is still black at the mouse position. - The README names 2046 e2e checks. - Gate on melektaus-atx: 304 files, 4702 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
0334d2508d |
Place compositions in scenes and play them from behaviors (#417)
A composition can now sit in a scene like a container and play there. Its items become real objects while they run, so behaviors, colliders and events work on them as on any other object. - A composition is placeable with its own symbol, transform and render layer. The scene view shows its picture at frame 0. A click on that picture selects the placement, not a single item. - CompositionSystem spawns each item as an object under the placement when it begins, with the id <placement>~<item>. On start and the other events run. The object is destroyed when the item ends. - The top track lies in front no matter which item started first. World.arrange() orders the entity list and the scene objects. - Clips and driven videos follow the same rules as compositionAt(). Loop, restart, stop and the game pause work. - New nodes: Play composition (with restart), Stop composition and the event On composition finished. They were planned for step 4 but come now, because without autoplay nothing could play otherwise. - The editor run ignores Play composition and says so in the log. It writes into the saved project, and the items would stay behind. - contentId() moved to instance.ts. The animation system uses it for nested ids too. - Compositions get no "Sort with parent" for now. Tests: the gate passed with 301 files and 4643 tests, 18 more than step 1. 13 of them are in tests/core/composition-play.test.ts. The first gate run failed in event-order.test.ts, which counts the event nodes and now finds 19. Five counter-checks each turned exactly their test red. Two order tests stayed green without the fix at first and were rebuilt. e2e 68 is new and passed with 5 own checks. Its counter-check without arrange() failed as expected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
597367c804 |
Keep a transition's pace through new steps and pans (#309)
A wheel step in the middle of a transition restarted the ease-out curve at full speed, so a run of quick steps kicked at each one. A pan called finish() first and made the zoom jump to its target. Both made the zoom and the gliding lists feel jerky. - A new step starts from the speed the view already has and then eases out. Quick steps in one direction move in one stroke. A step the other way still starts from rest. - A pan takes the transition along. The zoom goes on while dragging and lands where it was heading, moved by the pan. This covers all eight pan places: scene and container, graph, plugin board, texture and pixel art, timeline, pattern, song and sound. - finish() is gone, it has no callers left. Dragging the scrollbar, a jump to a row, another document or a new view size still end the transition as before. Found: on a second step the old curve jumped to 2.47 times the speed the view had just before. Tests: - npm run check on stein2: 299 files, 4598 tests (5 new) - e2e 46 on stein2: ALL PASSED, 24 checks (1 new). A pan by 40/30 px during a 1000 ms zoom step leaves it moving (2.33 on the way from 2 to 3), it lands on 3 with the world point moved by the pan. - Counter-checks: with the pace reset to 3 the new unit test fails with 2.47 instead of at most 1.1. Without carry the pan test fails. - melektaus-atx went offline mid-run, so both ran on stein2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
be41b1f0d7 |
Glide the asset and object lists and scroll past their end (#309)
The asset panel stopped with its last group flush on the bottom edge, and both lists on the left jumped on every wheel step while the tabs next to them already zoom with a transition. - ScrollContainer gets tailSpace, empty room always kept below the content. Content that fits only without it scrolls by the rest. The content is made that much taller, so clicks and the context menu on that room work as on any empty part of the list. - The asset panel keeps six rows of it. The other lists keep none. - ScrollContainer gets wheelStep, which by default scrolls at once. - scrollTween() in viewTween.ts hands the wheel step to an axisTween. The duration is the zoom transition's from the General settings, quick steps add up and stop at the end of the list, and dragging the bar or a jump to a row ends the glide where it stands. - Only the asset panel and Scene objects use it. Inspector, menus and the console still jump. - Wiki: Concepts describes the glide and the room below the assets. Not checked in the browser by me. The user picked the six rows and asked for the glide after trying the room below the list. Tests: - Unit scroll-container: 10 tests (3 new for tailSpace). - Unit view-tween: 4 new with a fake clock (glide lands on target, quick steps add up and stop at the end, a jump ends it, 0 ms jumps). - Gate on melektaus-atx: CHECK EXIT 0, 299 files, 4593 tests (4586 before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8679dfbe65 |
Group timeline rows by object and edit values in the row (#408)
The rows of the animation timeline stood in the order they were keyed. With several objects and properties the list was hard to read, and a value could only be changed by picking a key and using the inspector. The Play button also kept saying Pause after scrubbing had paused. - Each object gets a header row with its name, indented by depth. The groups follow the scene hierarchy, the root first. Rows whose object is missing go to the end. - Under an object the rows follow the order in which it lists its properties: X, Y, Z, then the order of the inspector. Only the view is sorted, the animation itself is unchanged and there is no undo step. After a move in the hierarchy the order is right by itself. - The row head shows the same field as the inspector with the value at the playhead. Without keys it shows the value of the object, for a relative row the offset. - A change in that field sets or changes the key at the playhead, selects it and is one undo step. A running playback pauses first. - The Play/Pause button follows every change of the playing state, so scrubbing that pauses shows Play again. Tests: - Unit animation + timeline-view: 100 passed, 3 new. - e2e 25 on melektaus-atx: ALL PASSED, 34 checks, 3 new (groups, order and values at frame 4, a typed 55 becomes a selected key on frame 4, Play then a click on the ruler shows Play and pauses). - Gate on melektaus-atx: exit 0, 297 files, 4561 tests. - tsc, eslint, Prettier clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
774207208c |
Key a video to its end at 1x and zoom the timeline further out (#407)
To play a clip at its real speed the user had to know its length, work out the last whole frame by hand and then scroll far to the right to set the end key. The timeline now does the sum, and it zooms out far enough to see a long clip in one piece. - The context menu of a Video time row gets "Video at 1x to its end from frame N". It keys the playhead with the row's value there and the last whole frame before the clip ends with the matching time. - The start key turns linear and keys in between are removed. The whole run is one undo step (KeyStraightRunCommand). - The clip is the one the sprite wears at the playhead, also when the texture is keyed in the same animation (rowVideoAt). - The entry is greyed out for relative rows, without a video, while the length is unknown, or when the playhead is already at the end. - The status line names the result, e.g. "frame 572 shows 57.2 s of 57.27 s". - The timeline zooms out to 0.02 px per frame instead of 4 px. The ruler labels follow a 1-2-5 step without an upper limit. Tests: - Unit video-clock + timeline-view: 27 tests green, 8 new - e2e 66 (atx, alone): ALL PASSED, 23 checks, 66l new - Gate (atx): CHECK EXIT 0, 297 files, 4553 tests - Counter-check: the command keeping the keys in between turns exactly the command test red Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ce933a4f32 |
Let a drag change only the picked clip in the Sound and Video tab (#403)
A drag with a clip picked could span a new clip in the strip or span the picked one anew over the wave. Both happened by accident while working on a clip. Now a drag only makes a new clip while none is picked. With one picked it moves its edges or the whole of it. - With no clip picked, a drag in the strip or over the wave (in the Video tab the track) spans a new clip, as before. - A click over the wave or in the track picks the clip at that point, as a click on its band does. Beside the clips it picks the sound or the video. The picked clip wins where clips lie over each other. - With a clip picked, its handles move its edges as before. A drag on its band in the strip moves the whole clip, its length kept and inside the file. Any other drag does nothing. - The rules live in waveView.ts (clipGrip, clipToPick, movedSpan) and both tabs use them. The cursor shows grab over the picked band. - The Video tab's track draws the clips not picked faint, as the wave does, so a click there has something to aim at. Tests: - Unit wave-view.test.ts: 16 tests (4 new) - Counter-check: a strip drag beside the picked clip spanning a new one again fails the picked-clip rule test - e2e 34 + 66 (atx): ALL PASSED, 162 checks, 1 new check in each - Gate (atx): 295 files, 4527 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2f376262d3 |
Let the Reverb's screen ring out and its reflections drift (#401)
The round screen of the Reverb stood still. It now moves, only for the look of it, and says nothing it did not say before. - The sound rings out again and again in real time. The dot in the middle flashes, and a thin front runs out over the rings as fast as the tail does. It fades as the tail falls, then 0.8 s of quiet. - A reflection lights up for a moment once the front has passed it. - The reflections turn slowly round the dot, each at its own pace and way (at most 0.16 rad/s). They sway 1.5 px in and out and twinkle. Their drift comes from a noise of its own, so they sit where they always did. - Rings, grid and dashed end stay one drawing. Only the dots, the front and the flash are drawn again each frame. - Nothing moves without a tail to see: no sound picked, or wet at 0. Tests: - reverb-view: 13 tests green (3 new) - e2e 34 on melektaus-atx: ALL PASSED, 144 checks - gate on melektaus-atx: 293 files, 4498 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
6383cc0d8e |
Show a Sample node as a device that draws its waveform (#400)
The Sample was the last node of an instrument that still showed rows. It now draws the sound or clip it plays, so the loop and what plays after the note can be seen instead of read off two numbers. - SamplePanel, SampleFace and sampleView: a compact gold plate of 480x280 with the sound picker beside the name, a waveform screen, the four ways of playing it as buttons two by two, the root as a knob that reads as a note, a lever for following the note and the loop's start above its end. - The waveform comes from the library's peaks of the played sound, a clip's own cut included, scaled by the sound's volume. It is loaded on demand as the Reverb's sound is. - Each part has its colour: played once, looped, played after the note, never heard. The loop is held as playback holds it: an end of 0 or past the sound is the end, a start not before it loops all of it. - Idle settings come from the node's own idleParams, so the device greys out what the rows did. - A new instrument places its Output at x 840, past the device. At 460 it lay under the Sample. - Hook __gm.sample.panel and a check in e2e 34 behind the first instrument check. Tests: - Unit sample-view: 11 tests, new. pattern and chip-presets green. - e2e 34 (atx): ALL PASSED, 144 checks (1 new). - Gate (atx): 293 files, 4495 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
a6c00f7b8b |
Show a Bitcrusher node as a device that draws its pixels (#399)
A Bitcrusher only said what it does in rows of numbers. How coarse its steps and holds are could not be seen. Now the node is a device of its own size. Its screen sends a sine through the node's own crush and draws what comes out in cells, as tall as a step and as wide as a hold, so the coarser the crush, the coarser the picture. - CrusherPanel draws a 680x280 plate in bubblegum pink. Bits and rate are large knobs, wet is small. Rate gives its low end more of the turn, 8 kHz sits just past the middle. - crushTrace runs a 440 Hz sine at 0 dBFS through Crusher at 48 kHz and blends it as the worklet does. Where each hold starts comes from Crusher too: a ramp through it at 16 bits takes a new value per hold. - cellOf shapes a hold. From 5 px a step or hold gets a grid line and a gap. Finer steps give a 2 px bar. A cell is never more than twice as tall as wide, so 1 bit gives bars at -1, 0 and 1. - The screen shows two periods, or 6 holds where the rate is low. The sine going in is a grey ghost. Below wet 1 the heard line lies over the cells. - Above the screen a word for how crushed it is, CLEAN to BROKEN. Under it the noise the bits leave and where the rate folds back. - 16 lamps show the bits kept, the highest on the left. A CRUSH lamp beside the name glows the more it crushes. - COLUMNS_OF gives the Bitcrusher 4 columns in the presets. - Hook __gm.bitcrusher.panel(id) for the sound FX and instrument graphs. Decided myself: - The screen is still. Nothing on it moves, so tick() says false. - How crushed counts whichever of bits or rate takes more, the rate in octaves, times wet. Found: at 8 kHz the first hold is 7 samples long, the rest 6. Six sixths add up to just under 1 in Crusher. The worklet does the same, so the screen shows what is heard. Every sfxr effect preset ends in a Bitcrusher and gets 3 columns wider. The Laser preset still fits whole on Fit. Tests: - Unit: crusher-view 13 tests (new). The screen matches Crusher with its wet on three settings. - Counter-test: without the wet in the blend the worklet test fails. - e2e 34: ALL PASSED, 143 checks (1 new). - Gate: CHECK EXIT 0, 292 files, 4484 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b145e1f288 |
Draw the Flanger's comb as a thin line with a fading trail (#398)
The comb was still drawn too heavy. Each frame showed the whole band
the comb had swept over the last 1/20 s, edged by two lines 1.5 wide,
and the faster the delay moved, the thicker that band got.
- flangerLine draws the comb where the light is as one line, 1 wide.
It runs through the ends of each column and the peaks and notches
in it, where they are. A comb finer than a column zigzags over it.
- Behind the line a faint trail shows what the comb swept over the
last 1/20 s, and over the last 1/40 s once more, so it fades with
age. A Flanger that stands still has none.
- The line fades out where its teeth move more than a quarter of a
tooth within the trail (clearAt), and is gone from half a tooth on.
There only the calm band of the trail is left, so nothing flickers.
- responseFrame gives the screen the line, how clear each point is,
the sharp columns for the notches and the trail. It replaces
responseColumnsOf, and RESPONSE_BLUR is now RESPONSE_TRAIL.
- The notches are counted on the sharp comb again, not on the band.
- The Phaser's response is drawn as the same thin line, without a
trail.
Tests:
Unit modulation-view (stein2): 26 tests, 2 new. The line holds the
comb's dB at each point and every notch. On a fast swing it fades
out over 8 kHz, stays clear under 1 kHz, and the trail is the same
band over 16 kHz in three frames. A still Flanger has no trail.
Counter-check: with clearAt always 1, the fade test fails.
e2e 34 (atx): ALL PASSED, 142 checks.
Gate (atx): CHECK EXIT 0, 291 files, 4471 tests.
Pictures of the default Flanger, a fast one (2 Hz, 5 ms) and one
sweeping to 20 ms, and of the Phaser: a thin line, the trail
behind it, a calm band high up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
f9a953f3d3 |
Keep the Flanger's comb calm and smooth where its teeth crowd (#398)
High up the Flanger's comb flickered and looked pixelated. Each column
of its screen took six readings, and its teeth pass there faster than
frames are drawn, up to 0.4 of a tooth a frame at 15 kHz by default.
Each frame caught other teeth, and each column was drawn as a sharp
rectangle.
- flangerColumns reckons each column exactly. The response only hangs
on the phase, and between two multiples of pi it runs one way. So a
column holds its lowest and highest at its ends or on the peaks and
notches inside it.
- Each frame is blurred over the delays of the last 1/20 s
(RESPONSE_BLUR), about what an eye blends. Where teeth pass faster
than that, they become a calm band from the deepest notch to the
highest peak. Further down, where they can be followed, they stay
sharp and move.
- The response is drawn as a band between two smooth lines through
the columns, no longer as one rectangle per column.
- responseColumnsOf picks the Phaser's readings or the Flanger's exact
columns. responseDb had no caller left and is gone.
Measured before choosing the blur, as the mean change of a column from
one frame to the next on the default Flanger (2-5 / 5-10 / 10-20 kHz):
readings as before 5.6 / 10.6 / 14.3 dB
exact, no blur 5.5 / 10.2 / 12.9 dB
exact, 1/60 s 5.0 / 7.6 / 4.0 dB
exact, 1/20 s 3.9 / 2.6 / 0.04 dB
Under 5 kHz most of what is left is the teeth really moving.
Tests:
Unit modulation-view (stein2): 24 tests, 2 new. The exact columns
hold what 4000 readings a column find there, and a fast swing
leaves the same band over 16 kHz in three frames in a row.
Counter-check: without the peaks and notches inside a column, both
new tests fail.
e2e 34 (atx): ALL PASSED, 142 checks.
Gate (atx): CHECK EXIT 0, 291 files, 4469 tests.
Pictures of the default Flanger and of one sweeping to 20 ms, near
and far, two of each 40 ms apart: smooth edges, the band the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
b6749cc43d |
Show Tremolo, Vibrato, Phaser and Flanger as devices that swing (#398)
The four effects that swing were still drawn as rows of numbers. Like
the Delay, the Saturation and the Limiter, each node now shows a device
of a fixed size. Its screen draws what it does over the first seconds
of a sound, and a light plays that in real time, over and over.
- One device for all four (ModulationPanel, ModulationFace and
modulationView), as KnobPanel is for Gain, Pitch, Mix and Pan. A short
description gives each its title, plate, buttons and knobs.
- Tremolo, 640x280 in cream: a wave its volume shapes, the volume as
its outline. It says DOWN TO 50%, EVERY 200 ms and DIPS -6 dB.
- Vibrato, 640x280 in plum: the pitch in semitones, cool below and warm
above. Under a wet below 1 the dry pitch stands beside it. It says
how late the voice is (LATE 0.93 ms).
- Phaser, 760x280 in mustard, and Flanger, 680x280 in steel blue: the
response from 20 Hz to 20 kHz, and under it a lane of the turn or the
delay over time. The response is drawn where the light is, so the
notches swirl and the comb jets. A comb finer than a column fills it.
- The light loops over 1, 2, 4 or 8 s, two LFO swings at least, for a
Flanger as long as its sweep takes to reach 20 ms. The face only
keeps the editor drawing while something swings and is heard.
- A Rate lamp beside each name goes from dark to lit with the swing.
- Nothing is listened to. The pictures use the worklets' own numbers:
lfoAt, vibratoSpeedUp (now exported from lfo.ts), the allpass chain
with its feedback, and the comb, reckoned at 48 kHz.
- Phaser and Flanger take 4 preset columns. Phased Pad was looked at
in a picture, nothing overlaps.
- Exported from nodes.ts: LFO_SHAPE, lfoShapeOf, TREMOLO_ROWS,
VIBRATO_ROWS, PHASER_STAGE_ROW and phaserStagesOf. FLANGER_MAX_MS is
exported from phaser.ts.
Decided here, as the request left the look open:
- A notch counts where the response falls below -3 dB, not some dB
under the loudest point. Feedback rings peaks far above 0 dB, and the
first rule counted 7 notches on a Phaser of 12 stages.
- Rate and Depth of Tremolo and Vibrato are big knobs, Wet is small.
Tests:
Unit modulation-view (stein2): 22 tests, new. Phaser and Flanger
give the dB of their worklets on a sine within 0.005 dB, the
Tremolo its samples, the Vibrato the pitch it reads at.
Counter-check: with the sign of the Phaser's feedback flipped in one
place, exactly the worklet test fails.
e2e 34 (atx): ALL PASSED, 142 checks, 4 new. Each device has its
size, its ports outside, a light that runs by itself, and a button
and a knob as one undo step each. Picking 8 stages on the Phaser
draws 4 notches.
Gate (atx): CHECK EXIT 0, 291 files, 4467 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
28909f0adf |
Show a Limiter node as a device that holds what it took away (#397)
A Limiter only said what it does in rows of numbers. How much it took away, and when, could not be seen at all, and a short sound is over before any needle could show it. Now the node is a device of its own size, and its screen catches the reduction from the moment a sound starts and holds it once the sound ends. - LimiterPanel draws a 720x280 plate of brushed brick. The threshold knob is large, ceiling and release are small beside it, auto-gain is a lever. A LIMIT lamp lights while it takes something away. - The screen hangs the reduction down from the top, 0 to 24 dB by the square root like the Compressor's meter. The level coming in runs along the bottom, orange above the limit. Time runs from the start of the sound over 1, 2, 4 or 8 s, and a longer sound rolls. - ReductionCapture in limiterView holds the capture after 0.4 s of silence, without the silence, until the next sound. It writes the most taken away and when, and NOW or LASTED on the right. - The Limiter is tapped now, like the Compressor. The face reads the loudest sample since the last frame on both sides. - SpectrumTaps.levels takes a lateBy. The side in front is read 5 ms earlier, so a peak going in is met by what it became. - limiterReduction leaves the auto-gain out and solves Wet's blend for the limited part. Less than 0.2 dB counts as none. - A capture is also held when the node leaves the picture. Changing a knob or the lever leaves it as it is. - Hook __gm.limiter.panel(id) for the sound FX and instrument graphs. Decided myself: - The capture stays held until the next sound, not for a fixed time. - The screen has no wet knob, as the Compressor's has none. - The Limiter's two taps cost two gains of 1 per voice. The analysers only exist while the node is in the picture. Measured on stein2: the seeded blip at -4.4 dBFS against -12 dB showed 7.56 dB. Three hits at 0, -8 and -2 dBFS showed 11.9 dB at the first. In e2e 34 on melektaus-atx, -10.44 dBFS in against -20 dB showed 9.56 dB, the difference to the hundredth. Tests: - Unit: limiter-view 13 tests (new), spectrum 6 (1 new), soundfx 67 with the Limiter's taps. - Counter-test: without Wet's share in the formula the blend test fails. - e2e 34: ALL PASSED, 138 checks (1 new). - Gate: CHECK EXIT 0, 290 files, 4445 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
80b25488ce |
Show a Saturation node as a device that glows with its drive (#396)
A Saturation in the graph was a stack of rows. The user wanted a device of its own style and size, like the Delay and the Reverb, that shows warmth and heat the harder the sound is driven. - The node is a copper plate of 720x280 with a button per type, the auto-gain lever, a big drive knob and small knobs for tone, output and wet. Each change is one undo step, wired numbers lock their knob. - The screen draws a sine at -12 dBFS, the level auto-gain is reckoned at, as a grey ghost. Over it is the wave that comes out: bent by the type, its DC taken out, auto-gain and output after it, blended by wet. Under it stand the auto-gain and the peak. - The heat is the drive over 48 dB, as far as wet lets it be heard. The wave goes from cold steel through ember red to white, a glow rises from below and flickers, and embers fly up from about 8 dB on. A lamp beside the name glows with it. - The node keeps the editor drawing only while there is heat. Without drive it draws nothing, unlike the Delay, which always animates. - The Reverb's private type button is now PickButton with its own colours, and the Saturation's type buttons use it as well. - No instrument preset uses a Saturation, so no preset needs more columns. Tests: - Unit saturation-view: 14 new tests. - Counter-check: without taking out the DC, exactly the DC test fails. - e2e 34 (atx): ALL PASSED, 137 checks, one new for type, lever and drive with undo. - Gate (atx): CHECK EXIT 0, 289 files, 4431 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
cb3183227b |
Keep drags at full speed in large projects (#394)
Dragging a band of a Filter or a Parametric EQ ran at about 30 fps in a large project. Each frame of a drag announces the changed project, and every listener of that event ran, also those whose work nobody sees while the drag goes on. - FramePreview marks its own frame, read through isDragFrame(). - The Scene view behind another tab skips such a frame and draws on coming back, as it already did while a game runs (#265). - preloadProjectFiles skips it as well. It walks every asset and checks every bake, which costs milliseconds in a large project. - The command that ends a drag emits for itself, and a drag that changes nothing ends with a plain preview, so both catch up then. - This holds for every drag through FramePreview, the knobs of the devices in the graph included. Measured on stein2 with a copy of a project of 162 assets, a band of a Parametric EQ dragged at 250 pointer moves a second for 3 s: - Listeners per frame: 17 ms before, 7.4 ms after. - Hidden Scene view: 814 ms before, 53 ms after. - preloadProjectFiles: 514 ms before, 0.4 ms after. - What is left is mostly the Hierarchy and Assets panels on screen (about 3 and 1.3 ms a frame). They stay as they are. Tests: - Unit frame-preview, play-throttle: 29 passed (1 new) - Counter-check: without the mark the new test fails - e2e 34 (melektaus-atx): ALL PASSED, 136 checks - Gate (melektaus-atx): CHECK EXIT 0, 288 files, 4417 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
d4a45c127b |
Show a Delay node as a device with its echoes over time (#393)
After the Chip, the Envelope, the Reverb and the one-knob devices, the Delay is drawn as a device of its own size too. Its screen shows what time, feedback, damping, wet and ping-pong make of a sound, and a light runs over the echoes at the beat they come at. - DelayPanel: a 680x280 plate in the Compressor's brushed aluminium, now exported as ALUMINIUM. A screen, levers for Sync and Ping-pong, knobs for Time, Feedback, Damping and Wet, and an Echo lamp. - The screen draws the sound as a white bar and each echo as a bar one step later. A bar is as tall as the echo is loud, evenly in dB to -60. It turns from warm white to rust as the damping takes the highs. With ping-pong the bars alternate above and below, the first left. - At most 24 bars are drawn, and the screen says how many more are heard. Under it stand the step and the end of the tail, above it where the highs of the first echo end. - The running light: DelayFace.tick returns true while the node is in the picture, so the editor keeps drawing, as for the Particles tab. The light goes round the span in real time, a bar glows as it is passed, and the lamp blinks with every echo but not with the sound. - On beats the screen counts at 120 BPM, as the tempo of the voice is not known in the graph. The time knob then names the note, dotted and triplet ones included. In seconds, 2 s and up reads "2 s max". - delayView holds the rules apart from the drawing: layout, tapers, echoTrain, echoCutoff for k rounds of the Butterworth low-pass, sweepAt and the texts. At wet 0 the rest is drawn faint. - delayEchoes in the core counts the echoes heard. The tail of a voice and the screen use it both. DELAY_ROWS, DELAY_SYNC and PING_PONG are exported. - Knob.reformat writes the number anew when its format changes, here when Sync switches between seconds and beats. - The test hook __gm.delay.panel returns knobs, levers, idle settings, the echoes drawn, the texts and where the light is. - Instrument presets give the Delay 4 columns. Echo Lead and Bell use one. In the picture of Echo Lead nothing overlaps. Found on the way: a node with a face draws no setting rows. The e2e check of a bypassed Delay (#354) read "Bypass: true" from them. It now checks the faded node and the stages heard, as before. Tests: - Unit delay-view: 17 tests, new. Counter-check: the first ping-pong echo on the right turns the side test red. - e2e 34 (melektaus-atx): 136 checks, 1 new. - Gate (melektaus-atx): 288 files, 4416 tests. - Pictures looked at: default, ping-pong, beats with damping, 83 echoes at 50 ms, wet 0, and the preset Echo Lead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
389d9555fa |
Show a Pan node as a one-knob device in cyan (#392)
After the Gain, the Pitch and the Mix, the Pan is the next node that is just one number. It now gets the same device in the graph instead of a row for its pan. - PAN_DEVICE: the Gain's 160x160 plate in cyan with one bipolar knob for Pan. The middle stands straight up, and the turn goes evenly to L and R, which are engraved at its ends like A and B on the Mix. - PAN_TAPER in hardwareView is 1, so left and right are heard alike. Device and unit test read the same constant. - PAN_ROW is exported from the core, so the knob has the node's range. - A linked Pan greys the knob, as for the Gain. - The test hook __gm.pan.panel returns the same shape as the Gain's. - No instrument preset uses a Pan, so no preset columns change. Tests: - Unit hardware-view: 17 tests, 2 new (the turn, and L and R inside the plate and clear of the screws). Counter-check: PAN_TAPER 2 turns the turn test red. - e2e 34 (melektaus-atx): 135 checks, 1 new. - Gate (melektaus-atx): 287 files, 4399 tests. - Pictures looked at: Gain, Pitch, Mix and Pan in a row, Pan at -0.35. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
fe4e0d9f1a |
Snap to the grid from the start in every graph tab (#391)
Laying out a graph by hand mostly means lining nodes up, so the magnet was switched on in nearly every session. Every graph tab now starts with it lit: Behavior, State machine, Shader, Particles, Sound FX, Instrument, the song's Routing and the Routing tab. - SnapToggle takes the state it starts in and lights its button to match. GRAPH_SNAPS_AT_FIRST sets it for the toggles and for GraphCanvas alike, so button and canvas cannot disagree. - Curve and Vector Art are no graphs and still start without it. - New hook __gm.graphSnapping(on?) reads every graph tab's magnet, or "mixed" where button and canvas differ, and switches them all. - The e2e harness reads the magnets after each navigate() and then switches them off, as it already sets the zoom transition to 0. Most checks drag a node to an exact spot. e2e 45 does the same itself, because the editor comes up there through the sign-in. - e2e 06 checks that all eight graph tabs started with the magnet on. The checks in 06, 26 and 65 that read it off now say they read what the harness left. Tests: - Unit snap-toggle: 4 tests, 1 new (lit at first, nothing asked of the target before a click). - e2e 06 (melektaus-atx): 150 checks, 1 new. e2e 34: 134 checks. - Gate (melektaus-atx, together with #390): 287 files, 4397 tests. - Pictures looked at: none, the state comes from the hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e70954115c |
Show Pitch and Mix nodes as one-knob devices in green and blue (#390)
The Gain has been a small device with one big knob since #384. The Pitch and the Mix are just as much a single number, so they now look and work the same way. The Mix also gets a lever for its curve, which until now was only a row in the inspector. - GainPanel and GainFace become KnobPanel and KnobFace. Each node is described by a KnobDevice: its plate, its row, how the knob turns, and an optional lever. The Gain looks as before. - Pitch: a green 160x160 plate with a bipolar knob for Semitones. 0 stands straight up and taper 2 puts an octave at 43 % of each half, so small steps near 0 are easy and the full +-64 is still there. - Mix: a blue 180x200 plate, the lever on top and the knob below. The knob has A and B engraved at the ends of its turn. The lever reads "crossfade" down and "both at center" up with its lamp lit. A flip is one undo step. - A wired Semitones or Mix greys the knob, as for the Gain. - The instrument presets give Pitch and Mix two columns. In the graph they are 281 and 261 units wide, more than one column of 240. - The test hooks __gm.pitch.panel and __gm.mix.panel return the same shape as __gm.gain.panel, plus the lever. Tests: - Unit hardware-view: 15 tests, 3 new (Pitch turn, lever above the knob and ends on the Mix plate). Counter-check: taper 1 turns the Pitch test red. - e2e 34 (melektaus-atx): 134 checks, 1 new. - Gate (melektaus-atx, together with #391): 287 files, 4397 tests. - Pictures looked at: Gain, Pitch and Mix in a row, Pitch at -24.5, Mix with both curves, the Bell preset without overlaps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e8021b1fd3 |
Show a Reverb node as a device with a round screen of its tail (#389)
The Compressor, the Gain, the Chip and the Envelope are devices in the graph. The Reverb was still a list of rows, and how long and how dark its tail rings was left to the numbers. - The node is a plate of 640x280 in pale lavender. Its inputs are on the left, its output on the right. - A round screen draws the tail as rings out from the sound. A ring's radius is its time, as the root of its seconds, on one scale up to 10.5 s for every Reverb. So a Room is a small disc and a Cave fills two thirds. - A ring is as bright as the tail is loud there, evenly in dB. Its colour is how much of the highs is left, from ice to violet, with the cut-off of the impulse response (reverbCutoff, now shared). - The pre-delay is a dark gap round the dot, a dashed ring marks -60 dB. Seeded dots stand for the reflections. Wet fades the tail against the dot. - A type of Sound draws the peaks of its sound from the library, each against its loudest. It says LOADING until they are there. - A button for each type, a list of the sounds and four chrome knobs: Decay, Damping, Pre-delay and Wet. - A knob the type does not take is greyed but turns. It goes over to Custom with the type's numbers, as one undo step. Picking a sound makes the type Sound, also one step. - NodeParamDrag can write several settings as one drag for that. - Test hook __gm.reverb.panel(nodeId), for the sound FX and the instrument graph. Tests: - Unit reverb-view: 10 new tests for the plate, the knob turn, the numbers per type, the scale, the tail and the texts. A screen that ignores damping turns the highs test red. - e2e 34 (atx): 1 new check (device between its ports, decay knob of a Hall goes to Custom as one undo step, Cave button, a sound from the list makes it Sound and draws that sound). - Probe pictures on stein2: Room, Hall, Cave, Plate, Custom with damping 1 and pre-delay 0.3 s, Sound. - Gate (atx): CHECK EXIT 0, 287 files, 4393 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
c75e72d4ee |
Show an Envelope node as a device with its ADSR curve (#388)
The Compressor, the Gain and the Chip are devices in the graph. The Envelope was still a list of rows, and its shape was left to the imagination of whoever read the numbers. - The node is a plate of 520x280 in pale sea green. Its inputs are on the left, out and value on the right. - A dark screen draws the curve over a note in amber. A letter sits above each stage, its time below it, and the sustain in percent above its line. - The time on the screen is squeezed. Each stage is as wide as the root of its seconds, at least 3 %, and the hold has a fixed fifth. So 5 ms of attack still shows beside a release of 3 s. - The falls use SETTLE, as the player does. With sustain 0 there is no hold and no release, and the screen says NOTE ENDS. - Attack, Decay, Sustain and Release are chrome knobs. The times use taper 4, so 5 ms sits an eighth of the way and 1 s not quite half. - From and To have no range a knob could turn through. They are NumberFields on the plate, typed in or scrubbed. - The decay at sustain 1 and From/To while nothing takes value are faded. While a link takes value, the bezel says what it moves between, as VALUE 400 TO 3500. The UI font has no arrow. - Instrument presets give an Envelope 3 columns of 240. - Test hook __gm.envelope.panel(nodeId). Tests: - Unit envelope-view: 6 new tests for the plate, the knob turn, the idle settings and the curve. A linear decay turns two of them red. - e2e 34 (atx): 1 new check on the Laser preset (device between its ports, curve ends with the decay, decay knob and scrubbed To field each one undo step). - Probe pictures on stein2: Synth Brass preset, pad, pluck, organ and an Envelope whose value moves a Chip. - Gate (atx): CHECK EXIT 0, 286 files, 4383 tests. The first run was red on the font test (#214) for an arrow in the bezel text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
ab37d3423a |
Let graphs zoom out to an eighth (#387)
With the Chip and the Filter drawn as devices, a preset such as the Laser is about 3060 units wide. In a window of 1280 px a fit then hit the floor of 0.25 and cut the Notes node off on the left. - ZOOM_LIMITS.min goes from 0.25 to 0.125. The graph zooms without steps, so one more step is the floor halved. 0.125 is also the smallest step of the scene view. - It holds for the wheel, for Fit and for the node board of plugins. - The grid still hides itself below 8 px, so it does not flicker. Tests: - Unit plugins and graph-framing: 55 tests green, the floor test now expects 0.125. - e2e 06 (atx): ALL PASSED, 149 checks. Only a comment that named 0.25 changed there. - Gate (atx): CHECK EXIT 0, 285 files, 4377 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
eb78f3cbf3 |
Show a Chip node as a device with its wave and its pitch (#386)
The Chip had eleven numbers in rows and nothing that showed what they do to a note. Its node is now a device of one size like the Compressor's, and two screens draw the wave and the pitch from the settings, worked right in the instrument graph. - ChipPanel is 840x280 in the warm grey of a handheld console. A button per wave, the "follow note" lever and a chrome knob per number, grouped Oscillator, Slide, Vibrato, Arpeggio and Repeat. - The wave screen draws two periods as loud as the Chip plays them, a square at its duty. toneValue and periodicSteps come out of chip.ts, so the screen and the worklet share one formula. - The pitch screen draws what slide, delta slide, vibrato, arpeggio and repeat make of the pitch over a note. It ends where the note falls below "min frequency" and starts from A4 while the Chip follows the note. - Idle settings are faded, wired ones greyed and locked, as the rows were. Every change is one undo step. - Knobs take a bipolar range: 0 straight up, the taper bends each half. Slide, delta slide, duty sweep and arpeggio use it. - The Compressor's auto-gain lever is now a Lever with a name of its own, which the Chip uses for "follow note". - Presets give a Chip five columns, as a Filter has four since #385. Found on the way: - With the Chip this wide the Laser preset is about 3060 units. In a 1280 px window Fit stops at the least zoom of 0.25 and cuts the Notes node off. The zoom limit is left as it is. Tests: - Unit: chip-view (9 new), knob (1 new), chip, chip-presets and hardware-view, 53 green - e2e 34 on melektaus-atx: ALL PASSED, 131 checks (1 new). The first run was red on the check itself: a Laser's "min frequency" can lie above A4, and the screen then rightly says the note ends at once. - Gate on melektaus-atx: CHECK EXIT 0, 285 files, 4377 tests - Counter-check: a square drawn with its duty mirrored turns the wave test red Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
17f47f6956 |
Drop the Gain's engraved name and centre its knob (#384)
The plate was taller than its one knob needs, and the engraved "GAIN" only repeated the node's title. Without it the plate is square, and the knob sits in its middle. - The plate is 160x160 instead of 160x220, without the "GAIN" engraving. - The knob is centred on what it shows: the "DB" caption and the dial. Since #382 its number hangs inside the dial's open bottom, so it adds no height. - CAPTION_AIR (4) takes out the empty space above the caption's capitals. Without it the knob sat a little too low. Measured on a picture at zoom 0.8: 19 px of air above "DB" and 19 px below the number. The user's screenshot showed about 14 above and 35 below. Tests: - Unit hardware-view: 12 tests green. The layout test now asks for the same air above and below. - e2e 34 (melektaus-atx): ALL PASSED, 126 checks. - Gate (melektaus-atx): CHECK EXIT 0, 283 files, 4359 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
01ee61868b |
Show a Gain node as an orange device with one knob (#384)
A Gain node now draws a small device in the graph, as the Compressor does since #380: the brushed plate in orange, 160x220 graph units, with one chrome knob between its inputs and its output. - GainPanel and GainFace join the node faces. The knob drags, resets on a double-click and keeps the wheel for the graph (#383). Each turn is one undo step, a linked Gain (dB) greys it. - The knob turns with a taper of 0.26, so 0 dB stands straight up and the range below -24 dB takes a fifth of the turn. - drawPlate, engrave and the brushed look move out of CompressorPanel so both devices share them. The Knob takes an arc colour, because the orange arc vanished on the orange plate. - No meter and no tap. Gain sits in every voice of an instrument, and a tap would add two audio nodes to each note for a display nobody asked for. Found on the way: - Knob.dialGlobal added half the dial unscaled. In a zoomed graph it pointed beside the dial. The Compressor's e2e only hit by luck. Tests: - Unit: 2 new for the Gain plate and its taper. - e2e 34 on atx: ALL PASSED, 126 checks (1 new). - Gate on atx: CHECK EXIT 0, 283 files, 4359 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
79a04e6764 |
Edit a Parametric EQ in its node, let the wheel only zoom graphs (#381)
The small curve below the EQ node's rows (#375) only showed the bands. Like the Compressor since #380, the node now shows the editor of its tab between its inputs and its output, and is worked right there. A double-click on the node still opens the same editor in a tab. Two wishes for the nodes with a panel came on top and share the same files, so they are in this commit as well: the numbers below the Compressor's knobs read as plain labels (#382), and the wheel over a node zooms the graph instead of turning a value (#383). #381, the EQ in its node: - EqPanel holds the curve, the band's numbers and the help line, taken out of EqEditor. The tab puts its one panel under a header, each EQ node has a panel of its own at 720 x 360 (EqFace). - A press on the curve neither picks nor moves the node. A double-click on the curve adds a band, one on the title or the edge opens the tab. - In the node a press on the Type or Slope button stays with the button, so the node is not picked or moved. - The spectrum runs in the node while it is in the picture. The tab lets go of the tap once, like the Compressor's, and Editor.tick lets every unseen tab and graph go before any seen one listens. - The count of bands moved from the tab's header into the band row, so the node shows it as well. - The node picture of #375 is gone with its only user: NodePicture, EQ_PICTURE and NodeView's drawing of it. #382, the numbers below the Compressor's knobs in its node: - A Knob with typed: false shows its number as a centred Label without a box, in the panel's ink. The ratio reads 4:1 (ratioText). - The label is as high as its text and sits up in the dial's open bottom, right under the ends of the scale. - Label takes a colour of its own. #383, the wheel in a graph: - GraphCanvas takes the wheel on the way down and stops it there. It zooms over a node too, and no knob or curve in a node sees it. - The tabs keep their wheel: a knob's step, a band's Q and the zoom of the EQ's dB. Found on the way: - A Menu opened below a button in the zoomed graph would have been placed by the button's own height. openBelow now takes the bottom edge as drawn. - The band row did not lay out its labels after a text change when its own size stayed. In the tab a later resize hid it, in the node "Band 1" and the count were missing. The row now lays out itself. - Handle and point positions for the checks were unscaled. They now go through toGlobal and hold at the graph's zoom. Tests: - Unit parametric-eq: 16 tests. The #375 tests now check eqCurvePoints and eqRangeOf directly. compressor-view: ratioText. - e2e 34 (atx): ALL PASSED, 125 checks (3 new: the EQ panel and its ports, editing in the node, the spectrum in the node with the tab open). The Compressor node's check reads its numbers as labels and 8:1. Both node checks now expect the wheel to zoom the graph and leave the ratio, the Q and the dB zoom alone. - The unseen-tab check now leaves for the Scene tab, since the node in the graph listens to the same tap. - Counter-check: the tab letting go of the tap every unseen frame stalls the node's spectrum with the tab open (1 frame against 22, probe on stein2). - Gate (atx): CHECK EXIT 0, 283 files, 4357 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
1f2b64fb4c |
Show a Compressor's panel in its node, its tab on a double-click (#380)
The panel of #379 opened in a window of its own and replaced the tab. The user wanted the device in the graph instead, and the tab of #378 back on a double-click. The switch Hardware look goes again. - A node can show a face instead of its rows (`NodeFace`). Its inputs sit on the left of it and its outputs on the right, in the middle of the height. Each side is as wide as its longest port name. - A Compressor node shows the panel at full size, 820x280 graph units. Its settings and numbers are not listed as rows, the panel has them. - The knobs and the lever work in the node. A drag, a notch of the wheel and a double-click are one undo step each. A press on them neither picks nor moves the node, and the wheel does not zoom the graph. The number below a knob is only shown, typing stays in the tab and the inspector. - The meter and the LEDs run while the node is in the picture of a sound FX or instrument graph that is seen. Out of the picture, folded or hidden, the node lets go of its taps. - The window of one size, `fixedSize`, `refit`, `keptSize` and the rule that kept a Compressor out of the tab row are gone again. - A knob's drag as one undo step lives in `nodeParamDrag.ts` now, for the tab and the node. The numbers of a node and what auto-gain says moved to `compressorView.ts`. Found and fixed on the way: - The tab and the node listen to the same taps. The tab let go of them on every frame it was not seen, so the node's meter stood still while the tab was open. Now the tab lets go once, and in a frame everything unseen lets go before anything seen listens. Tests: - Unit: compressor-view (2 new), documents (the test of `keptSize` is gone with it) - e2e 34 (stein2, melektaus-atx was off): ALL PASSED, 122 checks. The four checks of #379 are now four of the node: ports beside the panel, knobs and lever in the node, the needle when played (21.7 dB with the tab open), and the double-click opening the tab. - Gate (stein2): CHECK EXIT 0, 283 files, 4356 tests - Counter-checks: an old auto-gain number read as off turns exactly its unit test red. A tab that lets go on every frame leaves the node's needle at 0 dB and no LED lit (probe on stein2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9917253751 |
Open a Compressor as a device in a window of one size (#379)
The tab from #378 shows a Compressor as an editor. The user wanted it to look like a device as well, after a brushed aluminium plug-in, with a meter of the gain reduction instead of the pictures. The tab stays and is switched to per node. - Hardware look is a switch under Settings, on by default for new and existing Compressors. Only the editor reads it. - The panel is 820x280 px and opens straight in a window of that size. The window has no sizing corner and no Dock. A pane says so through `fixedSize`, and `keptSize` keeps the window's middle when it changes to that size. - A Compressor in this look never sits in the tab row. Opening one floats it, and switching the look on moves its tab into the window. - The panel has chrome knobs (threshold and makeup large), a meter with a needle that moves like a VU's, an LCD with the name and the reading, a lever with a lamp for auto-gain, and LEDs for the level going out. Everything is drawn in code. - The Knob takes a size and a chrome look, and leaves out its caption when it has no name, so the panel engraves its own. - `autoGainOn` is now `switchOn`, because the new switch reads the same way. - Where parts sit, the meter's scale, the needle's speed, the LEDs and the plate's grain are in `hardwareView.ts`, apart from the drawing. Decided myself: - The meter's scale runs by the square root, so the first dB is about as wide as the last nine. It is red from 10 dB on. - The LEDs light one every 3 dB from -39 to 0 dB and fall by 20 dB a second. - Switching the look changes the graph's fingerprint, like any other setting of a node. The sound FX is built again once. Findings, fixed on the way: - Pixi's `arc` goes on from the last point of the path. The meter's scale had a line from the corner of the face until each arc got a `moveTo` to its own start. - A text put into a Graphics makes Pixi warn that only Containers take children. The LCD is a Container now. - The first browser check read the needle at one moment and missed the short sound. The panel keeps the most its needle and LEDs showed. Tests: - Unit: hardware-view (10 new), documents (1 new) - e2e 34 (melektaus-atx): ALL PASSED, 122 checks (4 new). Played, the needle reached 20.9 dB and 5 LEDs lit. - Gate (melektaus-atx): CHECK EXIT 0, 283 files, 4355 tests (11 new) - Counter-checks: a window that keeps its corner instead of its middle, and a linear meter scale, each turn exactly their test red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
2d4909d248 |
Fit the Compressor's tab to any size, make auto-gain a switch (#378)
Two things came up on the new tab. Floated and sized down, it spilled its knobs out of its window, and sized up it left the window empty. Auto-gain lifted far too much: it brought 0 dB back to 0 dB, which at -40 dB, 4:1 and knee 6 is +28.1 dB, so a sound at -24 dB came out at -6 dB. And as a knob from 0 to 1 it blended between makeup and auto-gain, so at 1 the makeup knob did nothing. Layout: - The knobs stay on the bottom line, centred, with the help line above them. - The levels fill the height left above. The curve is a square at their right, centred up and down. It grows with the tab from 180 to 360 px and takes no more than 40 % of its width (picturesLayout). - A floated window cannot be sized below what its pane needs. A pane says so in its minWidth and minHeight, and the Compressor's is every knob and the smallest pictures. Other panes keep 320 x 200. - A pane put into an open window that is too small grows the window. Auto-gain, as decided in #378: - A Compressor's auto-gain gives back what it takes from a sound at -18 dB, so that one comes out as loud as it went in. At the numbers above that is +14.6 dB. - Auto-gain is a switch on the Compressor, the Limiter and the Saturation alike, a setting and no longer an input. The makeup or output is added on top of it instead of being blended with it. - A project from before reads a number as on above 0. Nodes never set keep their defaults: off on the Compressor, on on the other two. - The tab shows the switch at the right of the knobs, with the dB it gives below it. With it on, the curve has a line at -18 dB from the curve up to the diagonal. - Every level reading keeps the gain of its time. Before, the whole picture was read by the current makeup, so after a change the tinting of the last seconds was wrong or missing. Findings: - My first e2e run of the layout clicked Dock where it was before the window was sized up, so the window stayed open and the next check went red. The check reads the button again. Tests: - Gate (melektaus-atx): 282 files, 4344 tests (6 new) - e2e 34 (melektaus-atx): ALL PASSED, 118 checks (2 new) - Offline in Chrome: a sine at -18 dB through -24 dB and 4:1 came out at -17.92 dB with auto-gain on and -22.42 dB with it off. - Counter-checks: the curve set at the top instead of the middle, and every reading read at a gain of 0, each fail exactly their test. - Looked at the window in screenshots: at its least every knob and the switch show. At 1280 x 797 the levels fill the height, the curve is centred at the right and the knobs are at the bottom. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |