Documentation of the component metadata auto-generation system and the override mechanism that preserves custom property configurations across regenerations.
---
## Table of Contents
1. [Overview](#overview)
2. [How It Works](#how-it-works)
3. [The Problem: Lost Customizations](#the-problem-lost-customizations)
6. [How Overrides Are Applied](#how-overrides-are-applied)
7. [Current Overrides](#current-overrides)
8. [Adding New Overrides](#adding-new-overrides)
9. [Generator Internals](#generator-internals)
10. [File Reference](#file-reference)
---
## Overview
Velxio uses 48+ electronic components from the [wokwi-elements](https://github.com/wokwi/wokwi-elements) library. Each component needs metadata (properties, categories, pin counts, controls) for the UI to render property dialogs, component picker, and simulation logic.
Instead of maintaining this metadata manually, a **generator script** scans the wokwi-elements TypeScript source files, extracts `@property` decorators, `@customElement` tag names, and `pinInfo` getters, then produces `frontend/public/components-metadata.json`.
├── Reads .stories.ts for display name → name: "LED"
├── Maps id to category via CATEGORY_MAP → category: "output"
│
├── Applies overrides from component-overrides.json ← NEW
│
▼
frontend/public/components-metadata.json
```
**Running the generator:**
```bash
cd scripts
npx ts-node generate-component-metadata.ts
```
Or if `ts-node` is not available:
```bash
npx tsx scripts/generate-component-metadata.ts
```
---
## The Problem: Lost Customizations
The generator reads property types from the wokwi-elements source and infers a UI control:
| TypeScript Type | Inferred Control |
|-----------------|-----------------|
| `boolean` | `boolean` |
| `number` | `range` |
| `string` | `text` |
This is a reasonable default, but some components need **richer controls** that can't be inferred from the TypeScript source:
- **LED `color`**: The wokwi-elements source declares `@property() color = 'red'` (a plain string). But in the UI, we want a **dropdown select** with predefined colors (`red`, `green`, `blue`, etc.), not a free-text input.
- **SSD1306 `protocol`**: The wokwi-elements source has no `protocol` property at all — it's a Velxio-specific concept for choosing between I2C and SPI communication modes. We need to **inject an entirely new property** that doesn't exist in wokwi-elements.
Previously, these customizations were made by hand-editing `components-metadata.json`. But every time the generator ran (e.g., after updating third-party), it would **overwrite the entire file** and the customizations would be lost.
A separate JSON file at `scripts/component-overrides.json` stores all custom property overrides. The generator reads this file **after** scanning wokwi-elements and applies the overrides before writing the output.
```
Auto-generated from wokwi-elements + component-overrides.json
(properties, defaults, pin counts) (custom controls, new properties)
│ │
└──────────────┬─────────────────────────┘
▼
components-metadata.json
(final merged output)
```
Since `component-overrides.json` is a separate file that the generator **reads but never writes**, customizations are preserved across any number of regenerations.
---
## Override File Format
```json
{
"$comment": "Custom property overrides applied AFTER auto-generation...",
"<component-id>": {
"properties": {
"<property-name>": {
// Fields to patch on an existing property, or full definition for a new one
}
},
"defaultValues": {
"<property-name>": "<default-value>"
}
}
}
```
### Patching an Existing Property
To change the control type of an existing property (one that the generator already extracts from wokwi-elements):
This finds the existing `color` property on the LED component and patches it with `control: "select"` and `options: [...]`. All other fields (`name`, `type`, `defaultValue`) are kept from the auto-generated version.
### Adding a New Property
To inject a property that doesn't exist in the wokwi-elements source:
```json
{
"ssd1306": {
"properties": {
"protocol": {
"name": "protocol",
"type": "string",
"defaultValue": "i2c",
"control": "select",
"description": "Communication protocol",
"options": ["i2c", "spi"]
}
},
"defaultValues": {
"protocol": "i2c"
}
}
}
```
Since no existing property named `protocol` exists on the SSD1306, the generator **appends** it to the properties array. The `defaultValues` merge ensures the default is set.
---
## How Overrides Are Applied
The `applyOverrides()` method in the generator works as follows:
```
For each component in the generated metadata:
1. Check if component-overrides.json has an entry for this component ID
2. If yes, for each property override:
a. Find existing property by name in the component's properties array
b. If found → Object.assign(existing, patch) (merge/overwrite fields)
c. If not found → push(patch) to properties array (add new property)
3. Merge any defaultValues from the override into the component's defaultValues
```
The key behavior:
| Scenario | Action |
|----------|--------|
| Override property exists in wokwi-elements | Patch: only overridden fields change, rest preserved |
| Override property does NOT exist in wokwi-elements | Add: full property definition appended |
**UI result:** When clicking on an LED in the simulator, the property dialog shows a dropdown instead of a text input.
### SSD1306 Protocol Selector
**Component:** `ssd1306`
**Property:** `protocol` (new, not in wokwi-elements)
**Control:** `select` with `["i2c", "spi"]`
**Default:** `"i2c"`
The real SSD1306 OLED display supports both I2C and SPI communication. In Velxio, the simulation logic reads this property to decide which bus to attach:
- **I2C mode**: Registers as an `I2CDevice` on the simulator's I2C bus. Responds to address `0x3C`. Uses control byte (`0x00` = command, `0x40` = data).
- **SPI mode**: Hooks into the SPI `onByte` callback. Uses the DC (Data/Command) pin to distinguish commands from data.
The simulation code in `PartSimulationRegistry` reads the protocol at attachment time:
const protocol = (comp?.properties?.protocol as string) ?? 'i2c';
if (protocol === 'spi') return attachSSD1306SPI(element, simulator, getPin);
// I2C default...
},
});
```
**UI result:** The property dialog shows an I2C/SPI dropdown. A colored badge (blue = I2C, orange = SPI) appears next to the component label on the canvas.
---
## Adding New Overrides
### Step 1: Edit component-overrides.json
Add a new entry keyed by the component's `id` (the part after `wokwi-` in the tag name):