Right-click a component on the wiresheet or in the tree and Properties takes
over the editor pane, listing every slot the manifest declares rather than the
handful a box paints. Config slots that are not @readonly are white and
editable; everything else is grey.
Rows are built from the slot's facets, read against SedonaDoc/niagara.html:
@trueText/@falseText, @range, @precision, @unit, and @min/@max, which bound
what Save accepts. Three facets it had wrong:
- null is a value, not an absence. control::WriteFloat pairs 'in' with a
setNull action and the sample app stores val="null" on four of them, so
null now shows, parses back, and reads as @nullText where declared.
- @min/@max on a Buf count characters. TcpDevice.ipAddr is a Buf(17) with
@max 16; a longer string is refused rather than overrunning the device.
- an int's implicit range is noise. [-2147483648 - 2147483647] said no more
than "a whole number", so only byte and short print a range they did not
declare.
meta gets a row of its own: the '>>' opens its user groups as checkboxes and
its position as x and y, since typing 822411269 into a field sets neither.
Save writes only the rows that moved, as one undoable SlotValueCommand, and
refuses the lot if any row fails its range check. An edited field paints the
view in the canvas's own dirty tint, and every way of leaving asks before
discarding.
The file name on the tree's root row goes red while anything is unwritten,
counting both a dirty document and unsaved fields. Two things this needed:
the nav tree runs on its own style, because a themed Treeview's selected
foreground beats a row tag's; and the tag is added to or removed from the row
rather than recoloured, because reconfiguring a tag does not repaint rows that
are already drawn.
Also fixes a latent hazard the panel exposed: Del and Backspace are bound
application-wide, so they now stand down while a text field has focus rather
than deleting the component behind it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Right-clicking a component in the navigation tree offered Delete Component
alone, while the same component on the wiresheet also offered Link Mark and
Link From / Link To. Which list a component was picked from should not change
what can be done with it, so both popups are now built by
fill_component_popup.
Its entries close over the component instead of reading a selection back,
which is what lets each popup act on the row or box that was actually clicked
-- the canvas commands went through app_state.selected_canvas_tag, which a
tree row never sets. do_link_mark, do_link_from_mark, do_link_to_mark and
delete_tree_selected_component fall out with it, and Link Mark now says in the
status bar what it marked, since nothing else on screen shows that.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Right-clicking a wire or a knob now offers Go to Link Wire Sheet "<name>",
naming the sheet the far end is on -- the end a grey knob stands in for. A link
with both ends on the open sheet has nowhere to go, so the entry is left off
rather than greyed out.
find_component_by_path turns a link's from/to reference back into the component
it points at, the inverse of get_component_path, and the sheet is that
component's parent; a parent of <app> is the file's root node, which the popup
names after the file.
Arriving selects the same link and scrolls it to the middle of the view. It is
found by its element rather than by the tag it was picked on, since a repaint
hands out fresh wire tags, and it waits for that repaint from an after_idle
callback: ttk queues its selection event rather than dispatching it, so the
sheet is not painted when the tree node is selected -- and painting a sheet
clears the selected link, which was wiping the selection this feature had just
made. The jump is an ordinary sheet opening, so Back returns to where the link
was picked.
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>
Cmd+W closes the window on macOS, so the Command half of the pair is left
alone; this is the one shortcut that is not bound on both modifiers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Joins the Command/Control shortcut pairs, so the chord works the same on
either platform, and the View menu entry now shows it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tree / Palette hides the left column so the wiresheet has the whole window.
ttk has no hide flag for a pane, so the column is dropped from the paned
window and re-inserted at the front, which is why the configured width is
re-applied afterwards.
Back and Forward walk the sheets opened this session, with two arrow buttons
in a new toolbar on the same commands. History entries hold the element whose
children are painted rather than a tree item id: rebuilding the tree hands out
fresh ids, while the elements themselves survive. Recording runs from the tree
selection handler, the one route every sheet opening takes, and is suppressed
while a walk drives that selection so it cannot rewrite its own trail. Opening
a different file retires the history; a delete or an undo drops only the
entries whose element has left the document, keeping the position on screen.
open_file builds the tree inline instead of going through
refresh_treeview_from_dom, so it never raised the tree-filled callback and the
history had no way to see a new file. It and reset_to_startup_state now fire
it, which also gets the tree column fitted on load rather than on the first
resize.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A kit with no manifest under the Sedona home folder called sys.exit(1),
tearing down the whole app over one unopenable file.
schema now raises MissingManifestError instead, collecting every
unresolved kit first so one dialog names them all rather than the user
fixing them one reopen at a time. describe_missing_manifests builds the
body, pointing at the configured Sedona home and at Preferences.
open_file catches it, reports it, and calls the new
reset_to_startup_state to return to the just-launched state: no file,
empty tree, empty sheet, cleared registries and undo stack. The generic
except now resets too, since a half-parsed file previously left
current_file_path and a stale root behind.
Placing a palette component hits the same loader, where a full reset
would be too destructive: it rolls back the <kit> entry it just added,
rebuilds the previous registry, and abandons only that placement.
schema no longer imports sys or tkinter, so it reports nothing itself
and stays testable without a display. base_window_title moves to
app_state so the reset can restore the title without duplicating it.
README: document the new behaviour, drop the fixed gap, and correct the
stale "no undo, no keyboard shortcuts for save" claim.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>