An edge to a vanished port draws as a ghost row now, not as nothing (#60)

root committed 2026-09-14 11:50:43 +02:00
1 parent 0af2886351
commit 6e524c262e
3 files changed
+18 -6

No files matched your search

+10 -2
@@ -118,8 +118,16 @@ shown on the node and editable in the inspector.
bottom edge shrinks it again (keys `a`, `b`, `c`, … up to 26). `Run behavior` takes its ports
from the behavior it names, and a behavior's `Start`/`End` node declares them. Pointing a call
at a different behavior **takes the edges of the ports that go away** and gives them back on
undo: an edge on a port that no longer exists draws as nothing but survives every save, and
would come back to life once the port returns.
undo.
- **An edge can outlive its port**, because a derived list loses a key without anything touching
the edges: a state deleted in its machine, the `machine` edge cut, a `Start` node or a whole
asset removed. Only a setting of the node itself is cleaned up (above) — the other four reach
across graphs, where no single command can see them. So the canvas puts the key back as a
**ghost port**: a hollow red pin, the key as its label, the edge drawn dashed in the warning
colour and the node marked *"1 edge on a port that is gone"*. It is a handle, not a port —
`resolveDef` never has it, so nothing runs through it and nothing new can be connected to it;
right-clicking it offers the usual link menu, and *Disconnect* is how it goes away. Leaving it
alone is just as valid: put the state back and the edge works again.
- **Structs**: several named values travelling as one, so a bundle needs one edge instead of one
per part. `Pack` declares its fields as inputs, `Expand` reads them back out as outputs, a
behavior port may be declared `struct` — which is how a bundle crosses a call — and a
+2 -2
@@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs
# code is lost (tail's is reported), so a red run reads as green
npm run build # writes dist/ and regenerates index.html
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
node tools/seed-e2e.mjs && node tools/e2e.mjs # 837 editor checks, screenshots in /tmp/gm-e2e-*.png
node tools/seed-e2e.mjs && node tools/e2e.mjs # 844 editor checks, screenshots in /tmp/gm-e2e-*.png
GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step
./e2e.sh start [GM_STEPS] # the same run detached, see below
./e2e.sh status # live progress, or the last run's result
@@ -165,4 +165,4 @@ widths to find click points. The **render device is not in this repo** — `/dev
and a 1 GB `/dev/shm` come from `container.options` in the runner's own `config.yaml`, which
applies to every job on that runner. Without them chromium falls back to llvmpipe, which gets
WebGL 1.0 only. Measured in the job container: `ANGLE (Intel, Vulkan 1.3.230, HD Graphics 630)`,
WebGL 2.0, and the same 837 checks as here.
WebGL 2.0, and the same 844 checks as here.
+6 -2
@@ -14,14 +14,18 @@ unconnected (or takes a `State machine` node, the literal every kind has):
- **`State branch`** grows **one exec output per state**, named after the state, and continues
along the one the machine is in. Before `Set state` has run the machine is in **no state**, and
the flow ends there — there is no start state. A state the machine no longer has (deleted, or of
another machine) ends it too.
another machine) ends it too — the run ignores a ghost port, whatever the canvas draws.
- **`Set state`** is the mirror image: **one exec input per state**, one exec output. The input the
flow *arrives through* names the new state — so `OnStart → Set state (Open)` and `On key down →
Set state (Closed)` are two edges into one node. The interpreter hands every node the exec input
a pulse entered through (`run(…, entry)`); this is the first node to read it.
The ports are keyed by the **state's node id** and labelled by its name: renaming a state keeps
every edge, deleting one leaves its edges dangling like any port that is gone. Which machine the
every edge. Deleting one is the awkward case, because the port lives in a *behavior* while the
state lives in the *machine* — so nothing cleans up there, and the edge stays behind. It is not
lost, though: the behavior's node shows the key as a **ghost port** with the edge dashed in the
warning colour, and *Disconnect* on its right-click menu removes it. Put the state back and the
edge works again. Which machine the
ports belong to is read off the graph, not off a running value — the inline pick, or a
`State machine` literal wired in; a variable or an `Item at` feeding the input yields no state
ports, exactly as `Run behavior` knows its behavior only as a setting. Picking another machine