{
  "schema_version": 1,
  "type": "skill",
  "slug": "adom-schematics",
  "title": "Schematic Editor",
  "brief": "How the schematic file is structured and how to edit it. All edits are to the raw JSON; the app handles loading and resolving.",
  "version": "1.0.0",
  "tags": [],
  "license": "MIT",
  "source_path": "SKILL.md",
  "readme": "---\nname: schematic-crud\ndescription: Edit schematic JSON files in the Hydrogen project. Use when creating, parsing, or modifying schematic JSON: structure, MoleculeSymbol, Wire, NetLabel, symbolSource, pinOverrides, connectionData, or when the user asks how the schematic JSON format works.\n---\n\n# Schematic JSON\n\nHow the schematic file is structured and how to edit it. All edits are to the raw JSON; the app handles loading and resolving.\n\n## Root structure\n\n```json\n{\n  \"title\": \"Schematic\",\n  \"version\": \"2.0\",\n  \"pages\": [\n    { \"name\": \"Page 1\", \"components\": [ /* component objects */ ] }\n  ],\n  \"componentCount\": 123\n}\n```\n\n- **title** — Use `\"Schematic\"` for consistency with the app (optional; other titles load but this is the default).\n- **version** — `\"2.0\"` for the current format.\n- **Legacy:** Root may have `\"components\": [ ... ]` instead of `pages` (single page). When editing, prefer normalizing to `pages` with one entry.\n\nEvery component in `pages[i].components` must have:\n- **`__name__`** — type: `\"MoleculeSymbol\"` | `\"Wire\"` | `\"NetLabel\"` | `\"GND\"` | `\"PowerRail\"` | etc.\n- **`id`** — string (e.g. UUID). Must be unique. Wires and NetLabels reference other components by this.\n\n---\n\n## MoleculeSymbol\n\nTwo valid shapes. Use one or the other.\n\n### Minimal (symbol from library)\n\nUse when the symbol is identified by owner/molecule name; pin layout is not stored.\n\n```json\n{\n  \"__name__\": \"MoleculeSymbol\",\n  \"id\": \"uuid\",\n  \"enabled\": true,\n  \"locked\": false,\n  \"position\": [x, y],\n  \"name\": \"Display name\",\n  \"referenceDesignator\": \"U1\",\n  \"symbolSource\": {\n    \"owner\": \"string\",\n    \"moleculeName\": \"string\",\n    \"version\": \"v1\"\n  },\n  \"pinOverrides\": {\n    \"left:0\": \"net_1\",\n    \"right:3\": \"net_2\"\n  }\n}\n```\n\n- **symbolSource** — Identifies which symbol to load. Required in this format.\n- **pinOverrides** — Optional. Map **side:index** → net id or net name (e.g. `\"left:0\": \"net_gnd\"` or `\"right:3\": \"wire-uuid\"`). Values can be a **net name** (string like `\"net_gnd\"`, `\"VCC\"`) or a **wire id** (UUID of a Wire component). Use **side:index** when a symbol has duplicate pin names (e.g. many GNDs). Legacy files may use pin **name** as key; on load only the first pin per name is applied.\n- **referenceDesignator** — e.g. \"U1\", \"U2\". Stable; wires/labels can reference the symbol by this.\n- **symbolSource** — **owner**, **moleculeName**, and **version** must match exactly what exists in Curium; if the symbol file is missing, the symbol loads as a red box with no pins and pinOverrides are not applied.\n\n### Full (embedded pins)\n\nUse when the symbol was file-dropped or for legacy files. No `symbolSource`; full pin data is stored.\n\n```json\n{\n  \"__name__\": \"MoleculeSymbol\",\n  \"id\": \"uuid\",\n  \"enabled\": true,\n  \"locked\": false,\n  \"position\": [x, y],\n  \"name\": \"Display name\",\n  \"referenceDesignator\": \"U1\",\n  \"numPins\": 20,\n  \"leftPins\": [\n    { \"name\": \"GND\", \"uid\": \"pin_xxx\", \"index\": 0, \"side\": \"left\", \"description\": \"\", \"holeType\": \"medium\", \"netID\": \"net_1\" }\n  ],\n  \"topPins\": [],\n  \"rightPins\": [],\n  \"bottomPins\": [],\n  \"groups\": [ { \"name\": \"...\", \"side\": \"left\", \"pins\": [\"uid1\", \"uid2\"] } ],\n  \"jumpers\": [],\n  \"cornerPins\": [ { \"type\": 0, \"pinUid\": \"uid\" } ]\n}\n```\n\n- **leftPins / topPins / rightPins / bottomPins** — Arrays of pin objects. Each pin: **name**, **uid**, **index**, **side**, **description**, **holeType**, **netID** (optional).\n- **groups / jumpers** — **name**, **side**, **pins** (array of pin uids).\n- **cornerPins** — **type** (e.g. 0–3 for FL/FR/BL/BR), **pinUid**.\n\nWhen editing full format, keep **uid** values consistent: wires and net labels reference pins by **uid** in this file. When editing minimal format, **pinOverrides** keys are **side:index** (e.g. `left:0`); wires still reference pins by **pinName** in connectionData.\n\n---\n\n## Wire\n\nConnects two endpoints (components + pins) and stores a path. **Wires attach to pins** via **connectionData**: the app uses it to snap the wire to the pin, draw the connection, and set the pin’s net (filled dot). **You must add Wire components** for visible segments; pinOverrides and NetLabels alone do not create wires.\n\n**Attaching a wire to a pin:** Set **connectionData.startPin** and **connectionData.endPin** with **componentId** (or **referenceDesignator**) and **pinName**. **pinName must match the symbol’s pin name exactly** (e.g. \"VCC\", \"GND\", \"VOUT\", \"DOUT\", \"SDA\")—use names from the molecule’s symbol in Curium; wrong names will not attach. Use **pinIndex** (0-based among pins with that name) when the symbol has duplicate pin names (e.g. several \"GND\"). **uniqueId** can be omitted; the app resolves by pinName on load. **pinOverrides** on MoleculeSymbol can be omitted when every connection is made by a Wire; the app sets each pin’s net from the wire on load.\n\n```json\n{\n  \"__name__\": \"Wire\",\n  \"id\": \"uuid\",\n  \"enabled\": true,\n  \"locked\": false,\n  \"position\": [0, 0],\n  \"x1\": 100, \"y1\": 200, \"x2\": 300, \"y2\": 200,\n  \"color\": \"#ffffff\",\n  \"lineWidth\": 0.5,\n  \"segments\": [ { \"x\": 100, \"y\": 200 }, { \"x\": 200, \"y\": 200 }, { \"x\": 300, \"y\": 200 } ],\n  \"bendDirection\": \"horizontal-first\",\n  \"netName\": \"net_1\",\n  \"connectionData\": {\n    \"startPin\": {\n      \"componentId\": \"uuid-of-MoleculeSymbol\",\n      \"referenceDesignator\": \"U1\",\n      \"pinName\": \"MC16\",\n      \"pinIndex\": 0,\n      \"uniqueId\": \"pin_xxx\"\n    },\n    \"endPin\": {\n      \"componentId\": \"uuid-of-other-symbol\",\n      \"referenceDesignator\": \"U2\",\n      \"pinName\": \"HSDO\",\n      \"pinIndex\": 0,\n      \"uniqueId\": \"pin_yyy\"\n    }\n  }\n}\n```\n\n- **connectionData** — Required for the wire to attach to pins. **startPin** and **endPin** each have:\n  - **componentId** — `id` of the component (e.g. MoleculeSymbol). Must match an existing component in the same file.\n  - **referenceDesignator** — Optional but stable (e.g. \"U1\"). Use for readability and as fallback when componentId is missing.\n  - **pinName** — Must match the symbol pin name exactly (wrong names prevent attachment).\n  - **pinIndex** — Optional; use when the symbol has duplicate pin names (e.g. several GND).\n  - **uniqueId** — Optional; app resolves by pinName on load. When editing JSON, you can leave it or set it from the symbol’s pin list in full format.\n- **segments** — Array of `{ x, y }`. The drawn path. **x1,y1** and **x2,y2** usually match first and last segment (or endpoints).\n- **netName** — Net this wire belongs to (for netlist/highlight).\n\n**Pin names:** Wire **connectionData.pinName** must match the symbol pin list exactly (e.g. from Curium). Mismatched names prevent attachment; pins then stay unconnected (hollow). **pinOverrides** on MoleculeSymbol can be omitted when every connection is made by a Wire; the app sets pin nets from wires on load.\n\n**Editing wires:** Use **pinName** from the symbol actual pin list (e.g. Control Panel: VCC, GND, LED_DATA, SERVO_OUT, US_TRIG, US_ECHO, I2C_SDA, I2C_SCL). **uniqueId** can be wrong after a symbol is re-loaded from symbolSource; the app resolves by pinName. When adding a new wire, copy **componentId** from the target MoleculeSymbol and **pinName** from that symbol’s pin list.\n\n---\n\n## NetLabel\n\nLabels a net. Either attached to a pin or to a wire.\n\n```json\n{\n  \"__name__\": \"NetLabel\",\n  \"id\": \"uuid\",\n  \"enabled\": true,\n  \"locked\": false,\n  \"position\": [x, y],\n  \"netName\": \"net_1\",\n  \"labelId\": 1,\n  \"color\": \"#ff0000\",\n  \"showBorder\": true,\n  \"direction\": \"right\",\n  \"attachedWireId\": null,\n  \"wireAttachmentPoint\": 0.5,\n  \"attachedMoleculeId\": \"uuid-of-MoleculeSymbol\",\n  \"attachedPinId\": \"pin_xxx\",\n  \"attachedPinName\": \"MC16\",\n  \"stubWireEndpoint\": { \"x\": 120, \"y\": 200 },\n  \"pinOffset\": { \"x\": 20, \"y\": 0 }\n}\n```\n\n**Pin attachment** (label on a component pin):\n- **attachedMoleculeId** — Component **id**.\n- **attachedPinId** — Pin uid (in full-format symbols). Can become stale after load; app resolves by **attachedPinName**.\n- **attachedPinName** — Pin name. **Stable**; always set this when editing so the label still works after load.\n- **pinOffset** — `{ x, y }` offset from pin position to label position (keeps label in place when symbol moves).\n- **stubWireEndpoint** — Pin position (or same as pin); can be `{ x, y }`.\n- **attachedWireId** — null.\n\n**Wire attachment** (label on a wire):\n- **attachedWireId** — Wire **id**.\n- **wireAttachmentPoint** — 0–1, position along wire.\n- **stubWireEndpoint** — Point on wire.\n- **attachedMoleculeId** / **attachedPinId** / **attachedPinName** — null.\n\n**Editing net labels:** When attaching to a pin, set **attachedMoleculeId**, **attachedPinName**, and **pinOffset**; **attachedPinId** is optional (app can resolve by name). When renaming nets, update **netName** and any **pinOverrides** or wire **netName** that share that net.\n\n---\n\n## Editing rules\n\n1. **IDs** — Every component has a unique **id**. Wires use **connectionData.startPin/endPin.componentId**; net labels use **attachedMoleculeId** or **attachedWireId**. Do not reuse ids; do not remove a component and leave wires/labels pointing at its id without updating or removing them.\n2. **Stable identifiers** — Prefer **referenceDesignator** (e.g. \"U1\") and **pinName** when editing. **uniqueId** and **componentId** can change after load for symbols that use **symbolSource**; the app matches by refDes and pin name.\n3. **MoleculeSymbol** — Use **minimal** (symbolSource + pinOverrides) when the symbol is from the library; use **full** only when the symbol has no symbolSource (e.g. file-dropped). Do not mix: either symbolSource is present (and no leftPins/topPins/…) or it is absent (and full pin arrays are present).\n4. **Consistency** — After moving a symbol, **position** and **pinOffset** on attached net labels determine where the label sits; wire **segments** and **x1,y1,x2,y2** can be updated to match new endpoints, or leave for the app to recompute on load.\n5. **Rename net** — Update **netName** on every Wire and NetLabel on that net, and every **pinOverrides** entry whose value is that net id.\n6. **Delete** — Remove the component from **pages[i].components**. Remove or fix any Wire whose **connectionData** references that component, and any NetLabel whose **attachedMoleculeId** (or **attachedWireId**) references it.\n7. **Default Control Panel** — Leave the Control Panel in its default position and locked. The app adds it as a MoleculeSymbol (U1, adom/Control Panel/v1) at **position `[-70, 500]`** and **locked: true**. When editing or generating JSON, keep that symbol at `\"position\": [-70, 500]` and `\"locked\": true` so it matches the default and stays in place.\n\n**If pins look unconnected or everything looks “floating” after load:** (1) **pinOverrides** are applied only after the symbol loads from Curium—**symbolSource** (owner, moleculeName, version) must match exactly; wrong or missing molecules load as a red box with no pins. (2) **Wire** components are required for visible wire segments; without them you only have symbols and labels, so there are no lines between components. (3) Use root **title** `\"Schematic\"` and **version** `\"2.0\"` for consistency.\n\n---\n\n## Quick reference\n\n| Item | Required / stable for editing |\n|------|-------------------------------|\n| Any component | **__name__**, **id** |\n| MoleculeSymbol (minimal) | **symbolSource**, **position**, **referenceDesignator**, **pinOverrides** (side:index → net id) |\n| MoleculeSymbol (full) | **position**, **referenceDesignator**, **leftPins**/etc., pin **uid** and **name** |\n| Wire | **connectionData.startPin/endPin**: **componentId**, **pinName**; **segments**; **netName** |\n| NetLabel (on pin) | **attachedMoleculeId**, **attachedPinName**, **pinOffset**, **netName** |\n| NetLabel (on wire) | **attachedWireId**, **wireAttachmentPoint**, **netName** |\n",
  "author": {
    "id": "695820315b5f1e4db2fcf602",
    "name": "Kyle Bergstedt",
    "email": "kyle@adom.inc"
  },
  "visibility": {
    "public": true
  },
  "hero": null,
  "sample_prompts": [],
  "discovery_triggers": [],
  "discovery_pitch": null,
  "metadata": {},
  "created_at": "2026-05-28T05:30:28.891Z",
  "updated_at": "2026-05-28T05:30:28.891Z",
  "sub_skills": [],
  "parent_app": null
}