Live undo/redo: add/delete component and link, move, property edits

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>
This commit is contained in:
2026-10-06 20:03:26 +03:00
co-authored by Claude Opus 5.5
parent fd22eb314d
commit b98fe828da
9 changed files with 534 additions and 67 deletions
+9 -3
View File
@@ -1348,10 +1348,12 @@ def tidy_layout():
command.redo() # apply it; the command is the single writer of meta
if app_state.live_mode:
# No undo in live mode: each new position goes to the device, and a box the
# device refused goes back to where the device still has it.
# Each new position goes to the device, and a box the device refused goes back to
# where the device still has it. Only the boxes that moved are undoable.
failures = []
for element, old_val, _, _ in changes:
moved = []
for change in changes:
element, old_val, _, _ = change
new_val = element.find("prop[@name='meta']").get("val", "0")
if not live_hooks.live_set_prop_value(element, "meta", new_val):
failures.append(f"{element.get('name', '?')}: {live_hooks.last_error}")
@@ -1359,6 +1361,10 @@ def tidy_layout():
remove_prop(element, "meta")
else:
set_prop_value(element, "meta", old_val)
else:
moved.append(change)
if moved:
undo.push(undo.LiveMetaMoveCommand(moved, label="tidy layout"))
render_current_sheet()
if failures:
live_hooks.warn_failure(