Flatten SOX slot registry through full base-type chain

Types with intermediate parents (e.g. ontrolOx::PushButtonImage ->
Widget -> Service -> Component) only got Component + own slots, so
config/runtime streams were decoded against the wrong slot list and
x/y/imageFileName came out wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 09:01:59 +03:00
co-authored by Claude Opus 5.5
parent fd951f5b82
commit b0e769075f
2 changed files with 29 additions and 45 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
import configparser
import os
VERSION = "0.0.0.123"
VERSION = "0.0.0.124"
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
DEFAULT_GRID_SIZE = 10
DEFAULT_ADD_TIMESTAMP = True
+28 -44
View File
@@ -87,54 +87,39 @@ class DeviceSession:
def _load_slot_registry(self, parts):
"""Builds slot_registry from kit manifests for the device's schema."""
registry = {}
base_slots = []
own_slots = {} # "kit::Type" -> declared slots
bases = {} # "kit::Type" -> "kit::Base" or None
for kit_id, part in enumerate(parts):
kit_name = part.name
checksum = part.checksum
manifest = schema.find_manifest(kit_name, checksum)
if manifest is None:
continue
if kit_name == "sys":
base_slots = self._extract_component_base_slots(manifest)
self._register_kit_types(registry, kit_id, kit_name, manifest)
# Prepend inherited Component base slots (meta, etc.) to every type,
# then renumber IDs to match the wire slot index (positional in the
self._register_kit_types(registry, kit_id, kit_name, manifest,
own_slots, bases)
# Flatten each type's slots through its whole base chain (e.g.
# Component -> Service -> ontrolOx::Widget -> PushButtonImage), then
# renumber IDs to match the wire slot index (positional in the
# flattened inherited + own list — see sedona Type.java resolveSlots).
if base_slots:
for key in registry:
existing_names = {s["name"] for s in registry[key]}
for bs in base_slots:
if bs["name"] not in existing_names:
registry[key].insert(0, dict(bs))
for i, slot in enumerate(registry[key]):
slot["id"] = i
for key in registry:
qname = self.sox_client.type_name_map[key]
chain = []
seen = set()
t = qname
while t and t not in seen and t in own_slots:
seen.add(t)
chain.append(t)
t = bases.get(t)
flat = []
for t in reversed(chain):
flat.extend(dict(s) for s in own_slots[t])
for i, slot in enumerate(flat):
slot["id"] = i
registry[key] = flat
self.sox_client.slot_registry = registry
def _extract_component_base_slots(self, manifest):
"""Extracts base slots from sys::Component that all types inherit."""
for type_elem in manifest.iter("type"):
if type_elem.get("name") == "Component":
slots = []
for slot_elem in type_elem.findall("slot"):
slot_name = slot_elem.get("name", "")
slot_id = int(slot_elem.get("id", "-1"))
slot_type = slot_elem.get("type", "int")
flags_str = slot_elem.get("flags", "")
slots.append({
"name": slot_name,
"id": slot_id,
"type_name": slot_type,
"is_config": "c" in flags_str or "p" in flags_str,
"is_runtime": "r" in flags_str,
"is_action": "a" in flags_str,
"is_operator": "o" in flags_str,
"is_as_str": slot_type == "Str",
"is_prop": "a" not in flags_str,
})
return slots
return []
def _register_kit_types(self, registry, kit_id, kit_name, manifest):
def _register_kit_types(self, registry, kit_id, kit_name, manifest,
own_slots, bases):
root = manifest
for type_elem in root.iter("type"):
type_name = type_elem.get("name", "")
@@ -165,6 +150,8 @@ class DeviceSession:
})
registry[(kit_id, type_id_val)] = slots
full_name = f"{kit_name}::{type_name}"
own_slots[full_name] = slots
bases[full_name] = type_elem.get("base") or None
self.sox_client.type_name_map[(kit_id, type_id_val)] = full_name
# update type_name on any cached component of this type
for c in self.sox_client.cache:
@@ -368,16 +355,13 @@ class DeviceSession:
def _slot_name_by_id(self, kit_id, type_id_val, slot_id):
# SOX links use absolute slot IDs across the full inheritance chain.
# final_components_registry has correctly resolved absolute IDs;
# the per-type slot_registry only stores local (per-type) IDs
# which are wrong for types with intermediate parents.
type_name = self.sox_client.type_name_map.get((kit_id, type_id_val))
if type_name and type_name in app_state.final_components_registry:
for s in app_state.final_components_registry[type_name]:
if s["slot_id"] == slot_id:
return s["name"]
# Fallback to slot_registry (local IDs — wrong for inherited types,
# but keeps things working when the schema registry is not yet loaded)
# Fallback to slot_registry (flattened through the base chain, so
# its IDs are absolute too)
slots = self.sox_client.slot_registry.get((kit_id, type_id_val))
if slots:
for s in slots: