Implement live device add/delete component and app actions

Fix add component from palette in live mode: resolve <app> element
as comp_id 0, fix type_id lookup to use type_name_map, set type_name
on newly added SoxComponent, sync device values into DOM after add,
write meta position back to device, and subscribe new component to COV.

Fix delete component in live mode: clean up subscriptions for deleted
component and all its descendants.

Add tree root context menu with device app actions (save, hibernate,
quit, restart, reboot) in live mode. Device save clears dirty state.

Clean up connection lost handling to do a full disconnect.
Fix slot registry to copy base slots and renumber IDs correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 16:30:42 +03:00
co-authored by Claude Opus 4.6
parent 9a483a0b0e
commit 2530bbd92c
7 changed files with 102 additions and 19 deletions
+14 -9
View File
@@ -66,6 +66,7 @@ def live_add_component(parent_element, kit_name, type_name, comp_name, config_va
try:
new_comp = ds.sox_client.add(parent_id, kit_id, type_id_val, comp_name, sox_config)
ds.after_add(new_comp.id)
return new_comp.id
except Exception as e:
app_state.set_status(f"Add failed: {e}", warn=True)
@@ -84,8 +85,16 @@ def live_delete_component(element):
if comp_id is None:
return False
descendant_ids = []
for desc in element.iter():
if desc.tag == "comp" and desc is not element:
did = _comp_id(desc)
if did is not None:
descendant_ids.append(did)
try:
ds.sox_client.delete(comp_id)
ds.after_delete(comp_id, descendant_ids)
return True
except Exception as e:
app_state.set_status(f"Delete failed: {e}", warn=True)
@@ -189,6 +198,8 @@ def live_invoke(element, slot_name, arg=None):
# ── Helpers ─────────────────────────────────────────────────────
def _comp_id(element):
if element.tag == "app":
return 0
cid = element.get("id")
if cid is None:
return None
@@ -222,15 +233,9 @@ def _kit_id(ds, kit_name):
def _type_id(ds, kit_id, type_name):
if kit_id is None:
return None
for (kid, tid), slots in ds.sox_client.slot_registry.items():
if kid == kit_id:
comp = ds.sox_client.cache_get(0)
if comp and comp.type_name.endswith(f"::{type_name}"):
return tid
# fallback: search all cached components for a matching type_name
for c in ds.sox_client.cache:
if c is not None and c.kit_id == kit_id and c.type_name.endswith(f"::{type_name}"):
return c.type_id
for (kid, tid), full_name in ds.sox_client.type_name_map.items():
if kid == kit_id and full_name.endswith(f"::{type_name}"):
return tid
return None