feat(views): draw boundary ports as schematic symbols, not boxes - #23
Open
endrix wants to merge 6 commits into
Open
feat(views): draw boundary ports as schematic symbols, not boxes#23endrix wants to merge 6 commits into
endrix wants to merge 6 commits into
Conversation
A network's inputs and outputs drew as rounded pills, 120x50, with the name centred and the type under it. The wire met the pill at its vertical centre — which is the GAP between the two lines of text, so the wire ran through the middle of the label. They are now schematic port symbols: an arrow glyph sitting on the wire's own axis, with the text running outward from it, away from the wire. An input is a bare arrow; an output is an arrow stopping against a bar, the way a terminal is drawn. The name sits ON the axis, so the wire enters on the name line; the type sits on a second line below, clear of the wire entirely. Names are right-aligned on inputs and left-aligned on outputs. Both text columns therefore run away from the glyph column into the margin, and the glyphs stay in a straight line. Node width stays fixed across every boundary node for exactly that reason: their inner edges line up whatever ELK does with alignment inside a layer, and text overflowing into the margin is harmless. Height drops from 50 to a 22px row pitch, a little under half. The geometry lives in shared, alongside the port anchor and for the same reason. The two halves are computed in different processes — the server sizes the node and places the port the routers anchor to, the client draws the arrow — and nothing connects them at runtime. Disagree by a pixel and the wire ends near the arrow rather than on it, which no error reports. Two things the spec calls for that needed a decision rather than a number. The pill was what showed selection and hover, so that moves to the hit target: a 20x22 rectangle centred on the glyph, far larger than the 8px arrow so it can be grabbed, and only one row tall so that hiding the type line at low zoom cannot move the drag target. And the port element stays in the model as the edge anchor but is no longer drawn, since the glyph would otherwise be doubled. The type line is no longer tinted by direction. It is supporting text on its own line, and colouring it made it compete with the name.
…symbol up Three faults from the first cut, one of them mine to have caught before it shipped. The name rendered ON TOP of its own glyph. The text was drawn inside a group translated by `label.position` — inherited from the old code, where it was equally meaningless. These labels carry no layout feature precisely so that nothing but the view positions them, which means their position is simply whatever it was last left at; translating by it moved the text off the coordinates the view had just computed. The text is now drawn directly, with no wrapping transform. The type took the name's colour. It carries both `boundary-label` and `boundary-type-label`, so `.boundary-input .boundary-label` matched it too — at the SAME specificity, two classes each, leaving document order to decide. The direction rules came later and won, and the type rendered green on inputs and blue on outputs. Qualifying with both classes outranks them outright, and the rule now also sits after them so a reorder cannot recreate the tie. That one is worth a test rather than a fix alone, because a colour assertion would not have caught it: the declaration was always present and always correct, and what was wrong was whether it applied. So the test asserts the two things that decide that — specificity, and that it is not leaning on order. Writing it turned up a second-order version of the same trap: the first draft scanned the raw file and matched the selector inside the comment EXPLAINING the rule. It strips comments now, so only what the browser sees is scanned. Finally the whole symbol was too small. The drawing it came from is set at a finer weight than the diagram renders at, so a 9.5px name and an 8px arrow read as fine print beside a 3px wire. Everything is scaled by about a quarter, in proportion: the arrow still spans most of the name's line, the type still sits a little under two thirds of the name, and the row grows 22 to 28.
…look like a node's Two changes with one shape: a boundary port is now drawn as ONE object by the node's own view, the way entity nodes already draw themselves. The name was a separate label element rendered by the label view. That is what made it float: an element positioned by nothing still carries whatever position it was last left with, and two pieces of code — the label view placing text and the node view placing the arrow — had no way to agree on where the symbol was. Removing the transform fixed the symptom; drawing the name in the node view, beside the glyph it belongs to, removes the possibility. Glyph, name and type are now built together from the node's own args. The type reuses the NODES' footer class rather than styling of its own, so a port's type reads exactly like an entity's — one definition of what a type looks like instead of two that can drift. That also retires a specificity fight rather than winning it. The type label carried both `boundary-label` and `boundary-type-label`, so the rule tinting a NAME green matched it too at identical specificity; order decided, and the type came out in the name's colour. Nothing tints it now because no direction selector can reach it, which the test states as the rule: what tints by direction may reach the name and the glyph, and nothing else. The label elements stay in the model — a rename addresses `<node>_label_name`, and the server owns them — but render nothing. One consequence worth naming: double-clicking the name ON THE CANVAS to rename went with the rendered label. Renaming from the property panel is unaffected. If canvas rename is wanted back it should be a double-click on the port symbol itself, which is a listener rather than a label.
The name kept rendering centred on its own glyph, through two attempted fixes.
Neither was the cause.
A presentation attribute loses to any stylesheet rule, and upstream
GLSP/Sprotty styles set `text-anchor: middle`. So `attrs: {'text-anchor':
'end'}` was simply ignored: the text centred on the x it was given, which put
the arrow about halfway along the name every time. Removing the label's stale
transform and then moving the text into the node view were both real
improvements, and both left this untouched, which is why the picture barely
changed.
Setting it as an inline style wins.
This was already known here. The port labels forty lines further down carry the
fix and a comment saying exactly why — "Presentation attributes can be
overridden by CSS... Force via inline style." I wrote new text-drawing code in
the same file without reading it, then explained the resulting overlap twice
with theories that fitted the symptom.
So the test asserts the STYLE specifically rather than that anchoring exists at
all: an assertion that merely found `text-anchor` in the source would have
passed for the whole life of the bug.
The name was drawn by the port's own view, but the port was not grabbable by it: the hit target was a 24px box centred on the glyph, so clicking or dragging a port by its name did nothing, and selecting one outlined the arrow while the name sat outside the outline. Drawn as part of the symbol, behaving as a separate thing. The target is now the node's whole row. Its width comes from the node rather than a constant, which is the part that matters — any fixed number is a box around the arrow again, just a wider one. The small box had a real reason behind it, which the row keeps: a 10px arrow is hard to hit, and the target must not depend on the type line, since that can be hidden at low zoom. The height is still the fixed row pitch and still measured from nothing, so hiding the type moves nothing. Text that overruns the node's width is not covered — the box is 120 wide and the text runs outward into the margin past that. It affects only unusually long names, and widening the node to fit each one would break the alignment the column depends on.
… size the target to the text Two faults, both from treating the port symbol as decoration laid beside the diagram rather than as part of it. An edge ending at a boundary port drew its own arrowhead on top of the port's. They do not merely duplicate each other: the edge's tip is deliberately nudged four pixels PAST its endpoint so it meets the stroke cap, and that endpoint is the glyph's own edge — so the arrowhead came to rest inside the glyph. The drawing shows a plain line running into the symbol, which is what a schematic does, because the symbol is already the arrow. Suppressed in `renderArrow` itself so every edge view that inherits it is covered. The grab target was the node's box, a fixed 120 wide. That width exists so the glyph columns line up and says nothing about how wide any one port reads, so a short name left dead clickable space beside it while a long one overflowed the box and stopped being clickable at the very point it became visible. The target now follows the text, out from the glyph. That needs a text width, which the model cannot measure, so it is estimated. Acceptable only because nothing about the drawing depends on it — glyph, text and anchor are all placed from fixed geometry, and being a few pixels out makes the target slightly generous or slightly tight while moving nothing. Marked as never to be used for layout. The arrowhead test is worth noting. Written first as a search of the source for the guard, it passed with the guard disabled by `false &&` — it was checking that the words were there, not that they did anything. It now calls `renderArrow` and asserts what comes back, with an ordinary node as the control, and fails under that same mutation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements panels 4a and 4b. Boundary ports were rounded pills, 120×50, name centred with the type under it — and the wire met the pill at its vertical centre, which is the gap between the two lines, so it ran through the middle of the label.
They are now schematic port symbols.
#a8a8a2Names right-aligned on inputs, left-aligned on outputs, so both text columns run away from the glyph column into the margin and the glyphs stay in a straight line.
Why node width stays fixed
The glyphs line up only if the nodes' inner edges do, and ELK's alignment inside a layer is not something to rely on for that. Keeping one width across every boundary node makes their inner edges align regardless, and text that outgrows the box overflows toward the sheet edge — empty space. Sizing each node to its own text would have been the obvious move and would have broken the column.
Geometry lives in shared
Alongside the port anchor, for the same reason. The two halves are computed in different processes — the server sizes the node and places the port the routers anchor to, the client draws the arrow — and nothing reconciles them at runtime. Disagree by a pixel and the wire ends near the arrow instead of on it, which no error reports and no existing test would catch.
That is what the new suite pins: the anchor the routers compute must land exactly on the arrow's tip (
anchor.x === node.widthfor an input,0for an output) and on the axis. Mutation-checked by putting the anchor back on the node centre — the axis test fails, which is precisely the old bug.Two decisions the spec left open
One deviation worth flagging: the type is no longer tinted by direction (it was green/blue with the name). The spec gives it
#a8a8a2on both sides, and colouring it made it compete with the name — but if you wanted the tint kept, say so.Not done:
wire 1.6px on the axis. Edge stroke width is global, not per-port, so changing it would restyle every edge in the diagram. Happy to do it as its own change if that is what the row means.Verification
diagram-server 226 tests, diagram-client 93, sidecar 184, typecheck 5/5, bundles build.
The
extension-coresnapshot and neutrality Gate 1 are still red here — both fixed by #20, not in this branch's ancestry, neither file touched.