diff --git a/app_state.py b/app_state.py index 1c51f57..9211312 100644 --- a/app_state.py +++ b/app_state.py @@ -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.083" +VERSION = "0.0.0.084" PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties") DEFAULT_GRID_SIZE = 10 DEFAULT_ADD_TIMESTAMP = True diff --git a/device_session.py b/device_session.py index 618d84f..79172ce 100644 --- a/device_session.py +++ b/device_session.py @@ -68,15 +68,49 @@ class DeviceSession: def _load_slot_registry(self, parts): """Builds slot_registry from kit manifests for the device's schema.""" registry = {} + base_slots = [] 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 + 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, bs) 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.iter("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): root = manifest for type_elem in root.iter("type"): @@ -239,14 +273,22 @@ class DeviceSession: return comp_elem def build_links_elements(self, sax_root): - """Adds elements under the app element from cached link data.""" - app_elem = sax_root.find("app") - if app_elem is None: - return + """Adds elements under a container, matching .sax structure.""" + links_elem = sax_root.find("links") + if links_elem is None: + links_elem = ET.SubElement(sax_root, "links") + + seen = set() for c in self.sox_client.cache: if c is None: continue for lnk in c.links: + link_key = (lnk.from_comp_id, lnk.from_slot_id, + lnk.to_comp_id, lnk.to_slot_id) + if link_key in seen: + continue + seen.add(link_key) + from_comp = self.sox_client.cache_get(lnk.from_comp_id) to_comp = self.sox_client.cache_get(lnk.to_comp_id) if from_comp is None or to_comp is None: @@ -261,7 +303,7 @@ class DeviceSession: to_path = self._comp_path(lnk.to_comp_id) if from_path and to_path and from_slot_name and to_slot_name: - ET.SubElement(app_elem, "link", + ET.SubElement(links_elem, "link", **{"from": f"{from_path}.{from_slot_name}", "to": f"{to_path}.{to_slot_name}"})