Skip to content

fix(frontend): draw cables for links without a pad name - #1035

Merged
srperens merged 1 commit into
mainfrom
fix/frontend-padless-links
Oct 8, 2026
Merged

srperens merged 1 commit into
mainfrom
fix/frontend-padless-links

Conversation

@srperens

@srperens srperens commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

Written by Claude (Claude Code).

Problem

A flow link may name an element or block without a pad ("vsrc" or "vsrc::"). The API accepts it, and the backend links it with GStreamer picking the pad, so the flow runs. But the flow editor drew no cable for such a link, and the graph looked unconnected. Found while building flows through the API for #1034.

Change

  • Root cause. draw_link and is_point_near_link (frontend/src/graph/interaction.rs) read each link end with parse_pad_ref(..).unwrap_or_default(). That returns None for "vsrc", so the end resolved to an empty node and no cable was drawn.
  • "vsrc::" parsed as a pad literally named ":". On an element without pad info (e.g. decodebin), that bogus pad also showed up among the pads taken from links.
  • New parse_link_endpoint (graph/mod.rs). It returns no pad for "id", "id::" and "id:". Explicit pads still parse through parse_pad_ref, unchanged.
  • New GraphEditor::resolve_link_endpoint. A padless from end goes to the node's first output pad, and a padless to end to its first input pad. The pads come from what the editor already renders: element pads, or a block's external pads. With no known pad, the cable is drawn to the node edge rather than not at all.
  • Padless ends no longer add pads in get_actual_input_pads / get_actual_output_pads (graph/data.rs).

This is a rendering change only: no backend, API or stored-link change.

Evidence

Claim Test (frontend/src/graph/interaction.rs) Reverted
Padless ends resolve to the first pad in the link's direction, id:: included, for elements and blocks, with a node-edge fallback padless_link_ends_resolve_to_the_first_pad_in_the_link_direction fails
Explicit-pad links resolve as before explicit_pad_link_ends_are_unchanged passes (guards the unchanged path)
A padless end adds no ":" pad to an element without pad info padless_link_ends_add_no_pad_to_an_element_without_pad_info fails

Checked by hand in the web GUI (macOS) by the maintainer: a flow created through the API with padless links now shows all its cables.

Tests

Ran on macOS, on the head rebased onto main:

  • cargo test --workspace --lib graph::: the 14 frontend graph tests passed, including the 3 new ones.
  • cargo clippy --workspace --all-targets -- -D warnings is clean. The pre-commit hook also ran wasm32 clippy.

Not run: the native GUI.

For the reviewer

  • "First pad" for an element with request pads is the first pad in the rendered list, which may be the empty placeholder (e.g. sink_0). It is drawn correctly, but it is not necessarily the pad GStreamer picks at runtime.
  • "id:" (an empty pad name) is now treated as padless. Before, it was drawn to the node centre.
  • Split point. The editor still splits explicit pad refs at the first colon, while the backend's ElementPadRef::from_string splits at the last one. This is left alone so that explicit-pad behaviour stays identical.

🤖 Generated with Claude Code

A link end may name only an element or block ("id" or "id::"), and the
backend links it with GStreamer picking the pad. The flow editor parsed
link ends with parse_pad_ref, which returns None for "id", so the end
resolved to an empty node id and no cable was drawn. "id::" parsed as a
pad literally named ":", and pads-from-links rendered that bogus pad on
elements without pad info.

Resolve a padless end to the node's first pad in the link's direction
(output pad for from, input pad for to), taken from the pads the editor
already renders (element pad templates or block external pads). With no
known pad the end falls back to the node edge, so a cable is always
drawn. Padless ends no longer add pads when pads come from links.
Explicit-pad ends parse as before. Stored links are not rewritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@srperens
srperens marked this pull request as ready for review October 7, 2026 16:28

@srperens srperens left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Approve — the editor now reads link ends the way the backend does for "id" and "id::", both drawing paths and the pad-list builders go through one parser, and the three new tests ran in CI and pass.

Posted as a comment because GitHub refuses an approval from the account that opened the PR. The verdict is Approve. A human needs to merge it.

Claims

Claim Verdict Evidence
Padless ends used to resolve to an empty node and draw nothing CONFIRMED on main, parse_pad_ref returns None without a colon and the call was .unwrap_or_default(). Both call sites are replaced: frontend/src/graph/interaction.rs:16 — let (from_id, from_pad) = self.resolve_link_endpoint(&link.from, false); and the hit-test at frontend/src/graph/interaction.rs:202 — let (from_id, from_pad) = self.resolve_link_endpoint(&link.from, false);
"id::" no longer becomes a pad named ":" CONFIRMED frontend/src/graph/mod.rs:286 — if let Some(id) = pad_ref.strip_suffix("::") {, the same element-only form the backend parses at types/src/element.rs:103 — if let Some(stripped) = spec.strip_suffix("::") {
Explicit pads unchanged CONFIRMED everything else still goes through parse_pad_ref's first-colon split; only an empty pad name changes: frontend/src/graph/mod.rs:291 — Some((id, _)) => (id, None),
New tests guard the change CONFIRMED Check (Linux) job 112838200945, cargo test --package strom-frontend: all three graph::interaction::tests::* cases print ok. Two of the three fail on revert: resolve_link_endpoint disappears, and get_actual_input_pads("dec") gets back the ":" pad

Diagnosis — This is the right layer. The backend already accepts these links, so the editor is the only thing that was wrong. Coverage is BOUNDED to drawing and hit-testing. Two other frontend paths still assume id:pad, and this PR does not make either of them worse:

  • Re-routing a cable from its input pad. frontend/src/graph/data.rs:440 — let existing = self.links.iter().position(|link| link.to == pad_ref); A padless to end now draws on the first sink pad, but dragging from that pad does not detach it. It starts a second link instead.
  • Copying a flow. frontend/src/app/import_export.rs:314 — if let Some((old_id, pad_name)) = link.from.split_once(':') { A bare "vsrc" end is never remapped to the new id, so the copied flow links to an id that does not exist. "vsrc::" is remapped correctly. This bug was there before the PR, and is worth a follow-up issue.

Radius — LOCAL: three files under frontend/src/graph/. No backend, API or stored-link change. Check & Build (WASM) is green, so both targets compile.

Overlaps — none. #897 is the only other open PR touching frontend/src/graph/data.rs. It removes PadToRender::template_name, and this diff reads only .name, so the two do not conflict.

Design note — ElementPadRef::from_string in strom-types already parses this grammar, but it splits at the last colon. The author kept the editor's first-colon split on purpose so that explicit-pad behaviour stays identical. That is a fair reason not to reuse it here.

Tests & CI — Check (Linux), Build (Linux x86_64/ARM64), Check & Build (WASM), API Contract Check and Static JS Tests are green at 22e7b73. macOS and Windows were skipped, but nothing here is platform-specific.

Confidence: HIGH

@srperens
srperens merged commit ebd10bd into main Oct 8, 2026
9 checks passed
@srperens
srperens deleted the fix/frontend-padless-links branch October 8, 2026 08:09
@srperens srperens mentioned this pull request Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant