Ctrl+A hands the sheet the keyboard, and repainting the same sheet keeps
whatever of the selection is still on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tidy Layout
- Box heights are settled in left-to-right and right-to-left sweeps so each
box lines its slot up with the other end of its wires; gaps above the
minimum are free, and columns are re-sorted to untangle crossings.
- A wire that skips columns takes a placeholder in each column it crosses,
so boxes are ordered and spaced around it.
- A box nothing feeds is held back far enough left that wires from further
left always enter higher slots of the same target.
Wire routing
- Only the short stubs at a wire's two slots may pass close to a box; the
rest of every run keeps its clearance, so no wire grazes a box edge. A link
with no clear route is shown as knobs.
- Lanes that cross no other wire are tried first; climbing wires route top
source first, falling ones bottom first, and a deferred wire defers the
rest of its gap so lanes keep that order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The top/left margin no box may enter is grey and ungridded, so the white
sheet starts where boxes can actually be placed.
- Saving/unsaved state no longer resets the canvas to white: the sheet goes
white/pink and the area around it grey/greyish pink.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Tidy Layout lays linked boxes out left to right by signal flow, in as few
columns as the longest chain allows; feedback loops are cut where they run
backwards, and sources sit just before the boxes they feed.
- Boxes with no link on the open sheet go to the bottom of the first column,
alphabetically.
- A box that wires skipping its column would cross is pushed down below them.
- The wiresheet is fixed at meta's 255-step reach; outside it is light grey.
Dragging keeps a box's top-left corner on the sheet.
- Sox write_i4 wraps to signed, so meta with x >= 128 reaches the device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+click toggles a link in the selection; Del or the link popup deletes
every selected link after one confirmation, as a single undoable step.
On a device, links the device refuses stay and are named in one warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wires now round their bends, hop over the wires they cross, and show a dot
where wires from one output split. Wires sharing a pin may share a trunk.
Routes keep one grid square from a box's top and bottom and two from its
sides, never turn closer than two squares beside a box, and keep half a
square from other wires. The runs pinned to every slot are claimed before
any wire is routed, so an early wire no longer lays its run along a row a
later slot has to leave by. A wire that cannot fit the grid spacing falls
back to the old tighter one rather than turning into knobs.
The styling geometry lives in wire_style.py; draw_links is split into
collecting, routing and painting around a WireJob dataclass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl+click picks out several boxes on the sheet, and Ctrl/Shift+click several
rows in the tree. Copies are named on from their original (Add1 -> Add2), sit
one step right and down, and keep the links that ran between the copied
components. One undo step; on a device the originals are read back in full
and the copies made there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Undo and redo now work against a connected device; each step is sent
there through Sox. Deleting a component first reads its whole subtree
back (config, unloaded children, links, place among siblings) so undo
can recreate it; recreated components take new device ids, which the
DOM elements pick up so later steps still reach them. Binary Buf
config (a user's cred) is read with read_prop, since the cache decodes
every Buf as a Str. Moves, tidy layout, and Properties / page editor /
user edits record only what the device accepted.
A step that does not fully reach the device warns and clears the
history. History is also cleared on every view change in live mode,
as opening a sheet rebuilds the DOM from the device.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The final vertical run into an input slot now stands at least
1.5 * GRID_SIZE in front of the box, so the arrowhead sits on a straight
run instead of on the bend. Column gaps in tidy layout widen to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hovering a slot row highlights the half under the pointer and shows a
direction cursor. Pressing on a slot drags a rubber band; dropping on
another slot links them, dropping on a title bar opens the Link dialog
with the dragged slot preselected, and a no-entry cursor marks a slot
that would refuse the link. A right click that never moves still opens
the context menu.
The palette now leaves out types whose facets carry @palette=false, and
kits with nothing placeable left.
Also points the default device host at 192.168.1.251.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In live mode the device is now written first and the document only takes
what the device accepted. Every live_hooks failure records a reason, and
warn_failure() shows it in a dialog and the status bar instead of failing
silently.
- Move, properties, OX page editor: rejected values are rolled back and listed
- Add/remove link: no longer edits the DOM alone when an end can't be resolved
- Add/delete component: report why the device refused
- Tidy Layout: sends each new position to the device, reverts refusals,
marks the document dirty, and pushes no undo step in live mode
- User Manager: add (one Sox add carrying cred/perm/prov), edit and delete
now go to the device
- Binary Buf slots (e.g. User.cred) are sent as Buf bytes, not as Str text
- Bump version to 0.0.0.136
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_slot_name_by_id() now resolves SOX link slot IDs against the
inheritance-flattened final_components_registry instead of the
per-type slot_registry whose local IDs were wrong for types with
intermediate parents (e.g. RegFloat→Register→Component). This
produced incorrect link endpoints, inflated linked-slot visibility,
and different box sizes between file and live modes.
Also: tighten routing constants for denser wiring, add wiresheet
PNG print/export (Ctrl+P), sort link routing jobs by gap distance,
remove debug route logging, and fix live tree-expand re-selection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Right-clicking a component now offers Link View alongside Properties, from
the wiresheet and from the navigation tree alike. It takes over the editor
pane the same way and answers one question -- what is wired to this
component, and where does it go.
The chosen component sits in the middle wearing the stroke a selected box
wears on the sheet, with what links into it on the left and what it links
out to on the right, so every arrow points the way the signal travels.
Pale yellow ground and no grid: nothing here can be positioned, because
the layout is derived from the links every time it is drawn and no part of
it is stored in the file. Read-only throughout -- no drag, no add, no
delete -- so a component's wiring can be read without the risk of
rearranging it.
Every line is grey, since this is not a wiresheet and a wire-blue line
would invite the reader to treat it as one. A link on the same sheet is
solid; one that crosses is dashed, keeps the grey knob that sheet paints
on the slot, and has the far component drawn in with its sheet named
beneath it. A reference the document cannot resolve gets a placeholder
naming the path, because a link the view silently dropped would be the one
thing a reader could not find out from here.
Clicking a wire or a component follows the link: it opens the sheet of the
end that is not the component the view is about, with the link selected in
red. A link that stays on one sheet therefore goes back to that sheet
where both ends are drawn, and one that crosses opens the other sheet --
the end the view could only show as a knob.
Supporting changes:
- draw_component_box is factored out of render_components, so the sheet
and this view paint a component with the same code and a box cannot read
differently between them. draw_knob takes its canvas for the same
reason.
- The editor pane's hand-over is now shared: _take_editor_pane,
_give_editor_pane_back and close_editor_panel replace the
Properties-only versions, so "you have not saved that" is still asked
once, in one place, whichever panel has the pane.
- Fixes the jump to a link's other sheet, which arrived with the link not
selected. An after_idle callback is not late enough -- ttk dispatches
its own selection event later than one of those, so the repaint wiped
the selection. The link is left in _pending_link_selection for
on_tree_select to honour once it has painted.
- Each gap is sized to hold a lane per wire crossing it, and lanes are
handed out nearest-target-first, so a fan of a dozen links nests instead
of crowding a dozen verticals into a 30px band.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fits the whole painted sheet in the visible canvas and scrolls it into view,
from the View menu, Ctrl/Cmd+9, or the four-corners button after the zoom
percentage. The ladder gains 25% and 33% at the bottom, so a wide sheet has
somewhere to fit into.
Fit is the one scale that is not on the ladder -- a sheet fits at whatever
scale it fits at -- so zoom_step now takes the first level above or below the
scale in force rather than the neighbour of the nearest rung.
The sheet is measured from item coordinates rather than canvas.bbox(), which
is inflated by the outline stroke: a stroke is the same two pixels at every
zoom, so a bbox divided back to 1:1 reports a slightly smaller sheet the
further in it is zoomed, and fitting an already fitted sheet crept from 288%
to 291% instead of standing still. Anything painted left of or above the
canvas origin is measured from the origin, since no amount of scrolling brings
it into view.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A ladder of scales from 50% to 300%, stepped by Zoom In / Zoom Out / Zoom 100%,
by the two buttons beside the navigation arrows, and by Ctrl and the wheel,
which holds the point under the pointer still. A menu or keyboard zoom holds
the middle of the view instead, so it closes in on what is already being
looked at.
Painting and routing stay at 1:1, because that is where every metric is tuned:
box header and row heights, wire stubs, lane gaps, standoffs, knob radii. Zoom
scales the finished picture, so a zoomed sheet keeps the proportions of a 1:1
one and the router reaches the same decisions at every scale. Text comes along
because the three box fonts are resized once the boxes have been measured with
them.
redraw_links replaces the direct draw_links calls a drag or a drop makes: the
router reads box positions off the canvas, so the boxes go back to 1:1 for the
routing pass and the result is scaled again. Canvas coordinates carry the zoom,
which is why the drag, release and drop handlers now snap against
meta_scale * zoom -- a box dragged the same distance across the screen lands on
the same meta step at 50%, 100% and 200%.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pushed into the wire-coloured half, the centre filled almost all of it: that
half is only KNOB_RADIUS wide and the centre is KNOB_CORE_RADIUS, so at the
real 8px knob size the wire colour was reduced to a sliver and the marker read
as plain grey. Centred, both halves keep enough colour to tell apart.
Found on a slot carrying two links on its own sheet plus one leaving it, which
is the only arrangement that produces this knob and did not occur in testSax
until now.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Layout rules for tidy and for dragging:
- Boxes snap to whole meta steps live while dragged, not only on release.
- The left-most column and top row clear the canvas edge by at least 30px,
and neighbours by at least 30px, each rounded up to the next step.
- Box widths round up to the grid so a right edge lands on a grid line.
- Column gaps widen only where wires need lanes, so most stay at exactly 30px.
- A sheet too wide for meta's 0..255 range is laid out at 2, then 3, then 4
grid squares per step. The multiplier is derived from the sheet's contents,
so painting and tidying agree on it without recording anything in the file.
The grid size in Preferences is untouched.
Wire routing:
- No wire may pass under a component. A link with no clear route is shown as
knobs on both slots instead of a line hidden behind a box.
- Vertical runs hug a box within 15px, never closer than 10px, and two runs
sharing a gap stand 10px apart.
- Horizontal runs of different links stay 5px apart. Runs from one output pin
may share, since a fan-out is one signal.
- Straight routes claim their rows first, as their segments are pinned to
their slots and cannot move later.
Knobs:
- Wire colour with a small white centre when the far end is on this sheet,
grey when it is on another, half and half when a slot carries both.
- Selectable and deletable like a wire, and raised above the boxes so a click
reaches them.
- Selecting a same-sheet pair draws a red dashed line between the two knobs.
Also:
- get_box_bounds returns the rectangle actually drawn rather than canvas.bbox,
which was inflated by the outline stroke and by selection.
- Preferences offers to round the box width to a multiple of the grid.
- The tidy coordinate-limit warning said to lower the grid size; raising it is
what helps, since the reach is 255 * grid size.
- Tidy Layout moves from its own menu into Edit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Implement full link workflow between component slots on the wiresheet:
- Link Mark / Link From / Link To context menu items for creating links
- Link dialog with slot type validation and source/target swap button
- Selectable link wires (click to highlight in red) with right-click delete
- Undo/redo support for both add and delete link operations
- Component deletion already cascades to remove related links
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>