Skip to content

Label a tool's input port even when it has only one - #2012

Merged
giswqs merged 3 commits into
mainfrom
fix/model-builder-single-input-label
Aug 20, 2026
Merged

Label a tool's input port even when it has only one#2012
giswqs merged 3 commits into
mainfrom
fix/model-builder-single-input-label

Conversation

@giswqs

@giswqs giswqs commented Aug 20, 2026

Copy link
Copy Markdown
Member

Follow-up to #1983.

Multi-input tools got a labelled row per port in that PR, but a single-input tool kept a bare dot. A bare dot says a connection goes here without saying what belongs on it, so a user facing Buffer had no more idea what to wire in than they had facing Raster Streams To Vector before its ports were named.

What changed

Every input port on a tool node is now labelled.

The synthetic input and output nodes stay bare: their single port carries the node's own kind as its name, so labelling it would print "Output" under a card already headed OUTPUT. Output ports are still labelled only when a tool has several, since one result port needs no telling apart.

Two sizing corrections came with it:

  • CARD_HEADER_HEIGHT was 36, but the rendered header (a 10px uppercase line over a 12px title inside the card's 8px padding) occupies 40. At 36 the first port row overlapped the tool name. That was invisible on a multi-input card because its rows start lower, and only surfaced once one-row cards existed.
  • A labelled card is now always a little taller than a bare one. At the compact 64px a single row's text sat hard against the card's bottom edge.

Verification

Driven in a real browser against the production build:

Node Height Labels
Input / Output 64px none (unchanged)
Buffer 70px Input layer
Clip 84px Input layer, Overlay (clip) layer
Raster Streams To Vector 84px d8_pntr, streams_raster

Each label is centred on its dot to the pixel, clears the title box, and sits inside the card. Wiring is unaffected: dragging onto the second row still connects streams_raster and keyboard-activating the first still connects d8_pntr, both edges terminating within 1px of their own port row.

6512 frontend tests pass; pre-commit clean.

Summary by CodeRabbit

  • Bug Fixes
    • Improved model graph node layout and spacing for clearer visualization.
    • Input ports on tool nodes are now labeled consistently, including single-input tools.
    • Preserved appropriate labeling for synthetic nodes and multi-output tools.
    • Improved edge alignment and canvas sizing for model graph cards.
    • Enhanced card sizing and port positioning for more consistent graph rendering.
    • Improved spacing and alignment across model graph cards for easier interpretation.

Multi-input tools got a labelled row per port in #1983, but a single-input tool
kept a bare dot. A bare dot says a connection goes here without saying what
belongs on it, so a user facing Buffer had no more idea what to wire in than
they had facing Raster Streams To Vector before its ports were named.

Every input port on a tool node is now labelled. The synthetic `input` and
`output` nodes stay bare: their single port carries the node's own kind as its
name, so labelling it would print "Output" under a card already headed OUTPUT.
Output ports are still labelled only when a tool has several, since one result
port needs no telling apart.

Two sizing corrections that go with it:

- CARD_HEADER_HEIGHT was 36, but the rendered header (a 10px uppercase line
  over a 12px title inside the card's 8px padding) occupies 40. At 36 the first
  port row overlapped the tool name; that was invisible on a multi-input card
  because its rows start lower, and only showed up once one-row cards existed.
- A labelled card is now always a little taller than a bare one. At the compact
  64px a single row's text sat hard against the card's bottom edge.

Verified in a browser: Buffer renders as a 70px card reading "Input layer",
Clip and Raster Streams To Vector as 84px cards reading their two inputs, and
the Input/Output nodes stay bare at 64px. Each label is centred on its dot to
the pixel, clears the title, and sits inside the card. Wiring is unaffected:
edges still terminate within 1px of their own port row.
Copilot AI lite review requested due to automatic review settings August 20, 2026 04:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 95edf9ca-a2a2-41d2-b8e8-3ddbdcfc047e

📥 Commits

Reviewing files that changed from the base of the PR and between ac50ba5 and b4110f9.

📒 Files selected for processing (1)
  • apps/geolibre-desktop/src/components/processing/model-builder/ModelBuilderPanel.tsx
💤 Files with no reviewable changes (1)
  • apps/geolibre-desktop/src/components/processing/model-builder/ModelBuilderPanel.tsx

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The model builder now distinguishes tool and synthetic nodes during card layout. Tool input ports receive labels, while synthetic ports remain unlabeled. Canvas extents, edge anchors, card sizing, and port rendering use the updated geometry.

Changes

Model builder layout

Layer / File(s) Summary
Node-kind-aware card geometry
apps/geolibre-desktop/src/components/processing/model-builder/ModelBuilderPanel.tsx
cardLayout accepts ModelGraphNodeKind. Tool nodes label all input ports. Synthetic nodes remain unlabeled. Header spacing, minimum card height, and the shared port band were updated.
Geometry integration
apps/geolibre-desktop/src/components/processing/model-builder/ModelBuilderPanel.tsx
Canvas extents, edge anchors, node-card sizing, and port rendering use node-kind-aware geometry and the shared port band.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to b4110

This change labels tool input ports and adjusts card sizing so labels remain readable without affecting wiring; no actionable merge-blocking risk remains after normal checks and review.

Possibly related PRs

  • opengeos/GeoLibre#1983: This PR modifies the same model-builder canvas and extends its node and port geometry.

Poem

A rabbit checked each port in line,
Tool labels now look neat and fine!
Synthetic ports stay clean and bare,
Edges and cards align with care.
Hop, hop—the model map is clear!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: labeling a tool's input port when the tool has only one input.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/model-builder-single-input-label

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🔍 Cloudflare PR preview

Item Value
Site https://93c3bf0d.geolibre-preview.pages.dev
Demo app https://93c3bf0d.geolibre-preview.pages.dev/demo/
Commit ac50ba5

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • Making labelIn true for any tool with ≥1 input (not just >1) changes card height/port geometry for the common 1-input/1-output shape. The labelled input dot is positioned by row (CARD_HEADER_HEIGHT + PORT_ROW_HEIGHT/2 ≈ y=49 on a 70px card) while the still-unlabelled single output dot stays centered by the old spacing formula (y=35), a 14px vertical mismatch on the same card. Previously both sides were unlabelled and centered at the same y, so this is a new (if minor) visual regression that spreads a pre-existing asymmetry (already present for multi-input/single-output cards like Clip) to nearly every simple tool node. Confidence: medium — reasoned from the arithmetic in cardLayout/portPosition, not visually confirmed. (ModelBuilderPanel.tsx:192)

Security

  • None found.

Performance

  • None found; the change only adjusts layout constants and a boolean condition, no new computation in hot paths.

Quality

  • Logic is otherwise consistent: labelOut condition is unchanged, the synthetic input/output nodes are correctly excluded via the kind === "tool" check, and all three call sites (cardLayout, resize bounds, GraphEdges, GraphNodeCard) were updated together to pass node.kind, so edges and cards stay in sync. No obvious naming or maintainability issues in the diff.

CLAUDE.md

  • No violations. The touched component has no existing test coverage, so this change doesn't interact with the frontend coverage ratchet described in CLAUDE.md.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🔍 GitHub Pages PR preview

Item Value
Site https://opengeos.org/pages-preview/GeoLibre/pr-2012/
Demo app https://opengeos.org/pages-preview/GeoLibre/pr-2012/demo/
Commit ac50ba5

Note

GitHub Pages built this preview successfully, but its serving edge returned HTTP 403 when checked. The links may still be propagating.

- Keep a card's input and output dots on the same vertical band. Labelling
  every tool input moved the input side to row-based positions while an
  unlabelled single output stayed centred on the whole card, so a 1-in/1-out
  tool — the most common shape there is — had its two dots 14px apart and drew
  a visibly bent edge through a straight two-node pipeline. Both sides now
  share one port band: a labelled side fills it row by row, an unlabelled side
  spreads its dots down the same band instead of down the whole card.

  This goes slightly further than the suggestion of pinning the unlabelled side
  to the labelled side's first row. Centring on the band degenerates to exactly
  that for one row, and for a multi-input tool it puts the lone output midway
  between the inputs rather than level with the topmost one, which is how the
  edges want to meet it.

Measured in a browser, dot offsets from the card top:
  before  Buffer in=50 out=36 | Clip in=50,68 out=43
  after   Buffer in=50 out=50 | Clip in=50,68 out=59
The Input/Output data nodes are untouched at out=33. Edges still terminate
within 1px of their own port row, and the labels still clear the title and sit
inside the card.
@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs: None found. Traced cardLayout/portPosition/portsOf through all call sites (card render, edge anchoring, canvas extent calc) — the shared PortBand correctly aligns unlabelled sides with labelled ones, the height formula matches the PR's own verification table (70/84/84px), synthetic input/output nodes correctly stay unlabelled via the kind === "tool" guard, and no division-by-zero or stale-default-parameter risk since all three portPosition callers pass layout.band explicitly. Confidence: high.

Security: None applicable — pure client-side layout/rendering logic, no user input parsing or injection surface.

Performance: None — the added kind parameter and object literal for band are negligible; GraphNodeCard remains memoized as before.

Quality: Left one minor inline nit — a stray, now-fully-redundant one-line doc comment (pre-existing from #1983) sits above CARD_HEADER_HEIGHT instead of above portPosition; this PR adds a proper, complete doc block directly above portPosition itself, making the stray line dead weight worth deleting while this area is already being touched. Confidence: low/cosmetic.

CLAUDE.md: No violations — the file isn't one of the mirrored-constant modules requiring special sync checks, and no new user-facing strings were added without t() (all labels route through the existing portLabel/i18n path).

No automated test covers the new cardLayout sizing/labelling logic (verification was manual, per the PR description), but since cardLayout isn't exported and the PR includes a concrete before/after measurement table, this is a reasonable tradeoff rather than a gap worth blocking on.

- Delete the orphaned "Where a port's connector dot sits" line above
  CARD_HEADER_HEIGHT. It documented portPosition, but #1983 inserted the card
  geometry constants between the two and left the comment stranded on the
  wrong declaration. portPosition now carries its own doc block, so the stray
  line is redundant as well as misplaced.
@giswqs
giswqs merged commit e7fd8e8 into main Aug 20, 2026
19 of 20 checks passed
@giswqs
giswqs deleted the fix/model-builder-single-input-label branch August 20, 2026 04:20
Comment on lines 197 to 200
const height =
labelIn || labelOut
? Math.max(NODE_HEIGHT, CARD_HEADER_HEIGHT + rows * PORT_ROW_HEIGHT + 6)
? Math.max(NODE_HEIGHT + 6, CARD_HEADER_HEIGHT + rows * PORT_ROW_HEIGHT + 8)
: NODE_HEIGHT;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor, low confidence: with labelIn now true for any single-input tool (previously only multi-input tools grew past NODE_HEIGHT), the great majority of tool cards will now render at 70px instead of 64px. findFreePosition in apps/geolibre-desktop/src/lib/model-graph-edit.ts still assumes every node is NODE_HEIGHT (64) tall when spacing newly-placed nodes (NODE_HEIGHT + NODE_GAP = 80px steps), so the intended 16px gap between a labelled single-input card and the node placed below it shrinks to ~10px. That's not an actual overlap for the single-input case (70 < 80), but it does erode the margin that comment block is relying on, and for a tool with 3+ inputs (height 102px vs the 80px step) it can still visibly overlap the next placed card — a pre-existing issue this change makes noticeably more common by widening which nodes it applies to. Worth a follow-up to make findFreePosition/auto-layout height-aware, even if out of scope for this PR.

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • The card-geometry rewrite (cardLayout, portPosition, PortBand) is internally consistent: all three call sites were updated to pass node.kind, the height/band math matches the PR's own verification table (Buffer 70px, Clip/Raster Streams To Vector 84px, Input/Output unchanged at 64px), and the synthetic input/output nodes are correctly excluded from labelIn so they don't get a redundant "Input"/"Output" label. No functional bugs found in the changed lines. (High confidence)
  • Low-confidence note posted inline: making labelIn apply to every single-input tool (not just multi-input ones) means most tool cards now render taller (70px vs the previous 64px baseline), but findFreePosition in apps/geolibre-desktop/src/lib/model-graph-edit.ts (not touched by this diff) still spaces newly-placed nodes assuming a flat 64px height. This shrinks the intended 16px gap to ~10px for the common single-input case (no actual overlap) and can still visibly overlap for 3+ input tools — a pre-existing issue this PR makes more common by widening which nodes it applies to. Likely fine to leave as a follow-up rather than blocking this PR.

Security

  • None found; this is a pure client-side layout/geometry change with no new I/O, string interpolation, or external input handling.

Performance

Quality

  • No new tests were added, but the changed logic (cardLayout/portPosition) is internal, non-exported, DOM-geometry code that's inherently hard to unit test without rendering; the PR body documents manual browser verification instead, which is a reasonable tradeoff here. (Low confidence this needs changing)
  • The unused default parameter band: PortBand = { top: 0, height: NODE_HEIGHT } on portPosition (line 230) is dead code since every call site now passes band explicitly — harmless, not worth a change.

CLAUDE.md

  • No violations found. No new user-facing strings were introduced (labels reuse the existing portLabel/i18n plumbing from feat(processing): ArcGIS-style Model Builder canvas #1983), so no locale file updates were needed, and no other repo conventions (CSP allowlists, mirrored constants, Whitebox catalog, etc.) are implicated by this change.

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.

2 participants