name: symbol-creator description: Use when the user asks to "create a KiCad symbol", "create a Fusion 360 symbol", "make a schematic symbol", "create a .kicad_sym", "make an EAGLE .lbr", "design a component symbol", "build a symbol for [part name]", "make a symbol and send to KiCad", "send symbol to Fusion 360", or wants to visualize a KiCad symbol in the Gallia Viewer. Covers the full workflow from symbol creation to preview to delivery to local KiCad or Fusion 360 Electronics.
Schematic Symbol Creator
Create professional schematic symbols, preview them interactively in the Gallia Viewer, iterate with the user, and deliver to their local KiCad or Fusion 360 Electronics via Adom Desktop Conduit.
Supported output formats:
- KiCad (.kicad_sym) — native KiCad symbol library
- Fusion 360 Electronics (.lbr) — EAGLE XML library for Fusion's Electronics workspace
User Preferences
The skill stores user preferences in /home/adom/.claude/skills/adom/preferences.json. These control EDA tool, pin layout style, chip name display, distributor part number, and delivery target.
Step 0: Check Preferences (on first invocation)
At the start of every symbol creation session, read the preferences file:
import { readFileSync } from 'fs';
let prefs;
try {
prefs = JSON.parse(readFileSync('/home/adom/.claude/skills/adom/preferences.json', 'utf-8'));
} catch {
prefs = { edaTool: 'kicad', pinLayout: 'left-right', chipNameCentered: false, partNumber: 'none', deliveryTarget: 'ov' };
}
If the file contains only defaults (edaTool is "kicad" and deliveryTarget is "ov" — i.e. the user has never customized anything), open the preferences panel in Gallia Viewer and let the user know:
- Use the
gv_symbol_prefsMCP tool with mode "open" to switch the viewer to the preferences panel - Tell the user: "I've opened the Symbol Preferences panel in the Gallia Viewer — you can configure your EDA tool, pin layout style, and other preferences there. Feel free to tweak those settings whenever you like, or we can proceed with the defaults."
- Then proceed with symbol creation using whatever preferences are currently set — do NOT block on the user changing settings
Preference values and their effects:
| Preference | Values | Effect |
|---|---|---|
edaTool | kicad, fusion360, altium, cadence, tscircuit, easyeda, other | Controls primary output format and delivery target |
pinLayout | left-right, all-sides | left-right: pins on left and right only (default Adom style). all-sides: pins on left, top, right, and bottom |
chipNameCentered | true, false | If true, add a centered text element with the chip name in the middle of the symbol body (vertically centered) so it's always visible even when zoomed out |
partNumber | none, mouser, digikey, jlcpcb | If not "none", add a property field for the specified distributor's part number. Research and fill it in during Step 1 |
deliveryTarget | ov, desktop, both | ov: preview in Gallia Viewer first, only send to desktop when asked. desktop: send directly to desktop EDA after lint/validate pass. both: preview in Gallia Viewer AND send to desktop in parallel |
KiCad Service
All kicad-cli operations (SVG export, linting, STEP/GLB export, DRC) run on a shared remote KiCad CLI service on a dedicated container (john-service-kicad-cli-5948815f6104358b, port 8780). KiCad is NOT installed locally in user containers — it's ~600MB and only the service container has it.
The service is already installed and running. User containers access it via HTTP through kicad-api-client.js, which hardcodes the service URL. No env vars needed.
API client: /home/adom/gallia/viewer/kicad-api-client.js
Troubleshooting KiCad Service
If KiCad API calls fail (ECONNREFUSED, 404, timeout), the service is likely down — not missing. The API client includes a programmatic health check for structured diagnosis.
Programmatic Health Check
import { checkHealth } from '/home/adom/gallia/viewer/kicad-api-client.js';
const health = await checkHealth();
console.log('Service status:', health.status);
if (!health.ok) {
console.error('Error:', health.error);
console.error('Suggestion:', health.suggestion);
}
Decision Tree
status | Meaning | Action |
|---|---|---|
healthy | Service is running and responding | Failure is transient or in your code — retry or check your request |
node_down | Container reachable, Node process not running | Tell the user: "The KiCad CLI service process is down. You may need to SSH in and restart it: cd ~/gallia/services/kicad-cli && node server.js" |
container_unreachable | Container is stopped or network is down | Tell the user: "The KiCad CLI service container appears to be stopped. Please check your Adom workspace." |
unknown_error | Unexpected HTTP error or exception | Log the full health.error and check service logs on the container |
Service Down — No Local Fallback for Symbols
Unlike the footprint viewer, the symbol viewer does not currently have a local SVG fallback. Symbol rendering requires KiCad's SVG export because the symbol geometry (body rectangle, pin wires, pin names, group labels) is complex to reconstruct locally with correct positioning.
If the KiCad service is down and you need to create a symbol:
- The .kicad_sym file can still be created and validated structurally
- The linter (
kicad-lint.js) will fail because it needs the SVG export — skip the lint step - Create all other artifacts and tell the user: "The KiCad service is currently down so I can't generate the SVG preview or run the visual linter. The .kicad_sym file is ready — once the service is back, I'll generate the viewer and run the lint pass."
- If
deliveryTargetisdesktoporboth, the file can still be sent to KiCad directly (KiCad opens .kicad_sym files natively)
When to Request Service Enhancements
If an API call returns a JSON 404 like {"error": "Unknown route: /foo"}, that means the service is running but doesn't have the endpoint you're calling. This is different from the Coder proxy 404 (plain text 404 page not found = node process down).
A JSON 404 means the endpoint needs to be added to the service. When this happens:
- State the exact endpoint URL and HTTP method you tried
- Show the error response received
- Explain what you expected the endpoint to do
- Identify which skill(s) would benefit
- Tell the user: "The KiCad CLI service doesn't have an endpoint for [X] yet. This would need to be added to
gallia/services/kicad-cli/. Would you like me to design and implement that endpoint?"
The service source code is at /home/adom/gallia/services/kicad-cli/ with route handlers in routes/.
Manual Debug Steps (curl)
- Check health:
curl -s https://coder.john-service-kicad-cli-5948815f6104358b.containers.adom.inc/proxy/8780/health - If 404 (plain text): Container reachable, Node process down. SSH and restart.
- If connection refused/timeout: Container stopped. Check Adom workspace status.
- Override URL for local dev:
KICAD_API=http://127.0.0.1:8780
Symbol Categories
Not all symbols are ICs with rectangular bodies. The skill handles two categories:
Category A: ICs / Chips (rectangle body)
Multi-pin ICs like microcontrollers, op-amps, voltage regulators, battery management ICs. These get a rectangular body with pins on the sides, group labels, and the full Adom style treatment. This is the default path described in the rest of this skill guide.
Category B: Discrete Components (standard schematic shapes)
MOSFETs, BJTs, diodes, LEDs, resistors, capacitors, inductors, crystals, etc. These use standard schematic symbol shapes (transistor gates, diode triangles, resistor zigzags) that engineers expect to see. Do NOT draw these as rectangles.
For discrete components, always use KiCad's standard library symbols as the baseline:
import { symSearch, symGet } from '/home/adom/gallia/viewer/kicad-api-client.js';
// 1. Search for the part or its type in KiCad's standard libraries
const results = await symSearch('AO3400A'); // exact part number
// If no exact match, search by type:
// await symSearch('NMOS'); or await symSearch('Q_NMOS_GDS');
// 2. Get the best matching symbol as .kicad_sym content
const baselineSym = await symGet(results[0].library, results[0].symbol);
// Returns a complete .kicad_sym file with the correct graphical shape
Workflow for discrete components:
- Search the KiCad service for the standard symbol (
symSearch) - Download the baseline
.kicad_sym(symGet) — this has the correct graphical shape (gate arrows, diode triangles, etc.) - Customize the baseline:
- Update
(property "Value" ...)to the specific part number - Update
(property "Footprint" ...)to the correct package - Update
(property "Datasheet" ...)with the datasheet URL - Add
(property "Manufacturer" ...)and(property "Description" ...) - Update pin numbers if they differ from the generic symbol (check the datasheet!)
- Change
(generator "kicad_symbol_editor")to(generator "gallia")
- Update
- Create metadata — same
-metadata.jsonformat with pin descriptions - Preview — the viewer handles both rectangular and non-rectangular symbols via SVG export
Common standard symbol names in KiCad libraries:
| Component Type | Library | Symbol Name Pattern | Example |
|---|---|---|---|
| N-channel MOSFET | Transistor_FET | Q_NMOS_GDS, AO3400A | Gate/Drain/Source |
| P-channel MOSFET | Transistor_FET | Q_PMOS_GDS | Gate/Drain/Source |
| NPN transistor | Transistor_BJT | Q_NPN_BCE | Base/Collector/Emitter |
| PNP transistor | Transistor_BJT | Q_PNP_BCE | Base/Collector/Emitter |
| Diode | Device | D | Anode/Cathode |
| Zener diode | Device | D_Zener | Anode/Cathode |
| LED | Device | LED | Anode/Cathode |
| Resistor | Device | R | 2 passive pins |
| Capacitor | Device | C | 2 passive pins |
| Inductor | Device | L | 2 passive pins |
| Crystal | Device | Crystal | 2 passive pins |
If symSearch finds the exact part number (e.g., AO3400A exists in Transistor_FET), use that directly — it already has the correct pin mapping for that specific part.
If only a generic symbol exists (e.g., Q_NMOS_GDS), use it as the graphical baseline but verify pin numbers match the actual part's datasheet. Generic symbols use placeholder pin numbers that may not match the physical package.
Reference designator prefixes for discrete components:
| Type | Prefix |
|---|---|
| MOSFET / BJT / Transistor | Q |
| Diode / LED | D |
| Resistor | R |
| Capacitor | C |
| Inductor | L |
| Crystal / Oscillator | Y |
Workflow Overview
0. Check prefs → 1. Research → 2. Create symbol folder & files → 3. Lint → 4. Preview / Deliver → 5. Iterate
Delivery behavior depends on deliveryTarget preference:
ov(default): After every symbol change, preview in Gallia Viewer. Only send to desktop EDA when the user explicitly asks.desktop: After lint/validate pass, send directly to the user's desktop EDA (based onedaTool). Still preview in Gallia Viewer for iteration.both: After lint/validate pass, preview in Gallia Viewer AND send to desktop EDA in parallel.
IMPORTANT: After every symbol change, ALWAYS preview in Gallia Viewer regardless of delivery target. The delivery target controls whether the desktop EDA also gets an automatic copy.
Symbol Creator Service (Preferred Path)
A local HTTP API service at port 8781 handles all deterministic work: .kicad_sym generation, SVG theming, SymView (interactive viewer HTML) generation, lint, validate, and delivery. Claude's role is to research the part and call the service with structured data.
Service location: /home/adom/gallia/symbol-creator/
MCP tools: sym_create, sym_preview, sym_lint, sym_validate, sym_search, sym_deliver
HTTP API: POST /sym/create, /sym/preview, /sym/lint, /sym/validate, /sym/search, /sym/deliver
The service enforces all branding rules (dark theme, Adom purple group labels, gradient body fill, pin tooltips, hit zones) so symbols are consistent every time. See symbol-creator/README.md for full API docs.
SymView is the name for the branded interactive viewer HTML generated by the service.
Symbol Folder Structure
Every symbol gets a dedicated folder under the project schematics directory:
/home/adom/project/project-content/schematics/symbols/
PART_NAME/
PART_NAME.kicad_sym # The KiCad symbol file (native format)
PART_NAME-metadata.json # Pin descriptions + part info (feeds Gallia Viewer)
PART_NAME.svg # SVG export via KiCad service
PART_NAME-viewer.html # SymView — interactive symbol viewer HTML
README.md # Research summary, pin table, notes
NEVER use /tmp/ for symbol files. Always create and work in the symbol's project folder so the user can reference everything later.
Metadata JSON Format
The -metadata.json file persists all pin descriptions and part info so the Gallia Viewer can be regenerated without re-researching:
{
"symbolName": "PART_NAME",
"manufacturer": "Texas Instruments",
"package": "TQFP-48",
"description": "Short description of the part",
"datasheetUrl": "https://www.ti.com/lit/ds/symlink/part.pdf",
"pinCount": 48,
"pinDescriptions": {
"PIN_NAME": "Engineering-grade description of pin function...",
...
},
"groupDescriptions": {
"GROUP LABEL": "Description of this functional group and how its pins work together...",
...
}
}
Step 1: Research the Part
Before creating a symbol, gather accurate pin information:
- Search the web for the part's datasheet (pin table, package, pin functions)
- Identify every pin: number, name, electrical type, and functional group
- Note the package type (e.g., TQFP-48, VSSOP-8, SOT-223)
- Collect metadata: manufacturer, datasheet URL, description
- If
partNumberpreference is not "none": Search for the distributor part number on the specified distributor's website (Mouser, DigiKey, or JLCPCB). Add it as a property in the .kicad_sym file:- Mouser:
(property "Mouser" "XXX-XXXXXXX" (at 0 0 0) (effects (font (size 1.27 1.27)) hide)) - DigiKey:
(property "DigiKey" "XXX-XXXX-ND" (at 0 0 0) (effects (font (size 1.27 1.27)) hide)) - JLCPCB:
(property "JLCPCB" "CXXXXXX" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))
- Mouser:
Step 2: Create the Symbol Folder and Files
2a. Create the folder
mkdir -p /home/adom/project/project-content/schematics/symbols/PART_NAME
2b. Create the metadata JSON
Write the -metadata.json file FIRST with all pin descriptions, group descriptions, and part info. This is the single source of truth for the Gallia Viewer hover data — both pin tooltips and group label tooltips are driven from this file.
2c. Create the .kicad_sym file
File Format
The .kicad_sym format is an S-expression text file. KiCad's parser does NOT support comments (no ;; or #).
(kicad_symbol_lib
(version 20231120)
(generator "gallia")
(symbol "PART_NAME"
(pin_names (offset 1.016))
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "U" ...)
(property "Value" "PART_NAME" ...)
(property "Footprint" "Package:Footprint" ...)
(property "Datasheet" "https://..." ...)
(property "Description" "Short description" ...)
(property "Manufacturer" "Mfr Name" ...)
(symbol "PART_NAME_0_1"
(rectangle (start X1 Y1) (end X2 Y2)
(stroke (width 0.254) (type default))
(fill (type background))
)
)
(symbol "PART_NAME_1_1"
(pin TYPE STYLE (at X Y ANGLE) (length LEN) (name "NAME") (number "NUM"))
...
)
)
)
2d. Create the README.md
Required — do not skip. The README is the human-readable record of the symbol. Generate it with:
- Part name, manufacturer, package
- Datasheet link
- Pin table (number, name, type, group, description)
- Any research notes, application tips, or design considerations
- Link to the Gallia Viewer HTML using an absolute Hydrogen URL (see adom-api skill for URL format)
2e. Export SVG and generate SymView
After linting (Step 3), export the SVG via the KiCad service and generate the SymView HTML. Both go in the symbol folder.
Coordinate System
- Positive Y = UP (math coordinates, not screen)
- Pin
atposition is the connection tip (endpoint away from body) - Pin extends from tip TOWARD the body by
length - All coordinates on 2.54mm grid
Pin Angles
| Angle | Direction | Use for |
|---|---|---|
| 0 | Pin extends RIGHT from tip | Left-side pins |
| 180 | Pin extends LEFT from tip | Right-side pins |
| 90 | Pin extends UP from tip | Bottom-side pins |
| 270 | Pin extends DOWN from tip | Top-side pins |
Pin Electrical Types
Use accurate types — they affect ERC (Electrical Rules Check):
| Type | Use for |
|---|---|
power_in | VCC, VDD, GND, supply inputs |
power_out | Regulator outputs, voltage references |
input | Digital/analog inputs, clock pins |
output | Digital outputs, driver outputs |
bidirectional | I2C SDA, GPIO, data buses |
passive | Resistor connections, cell sense, crystal |
unconnected | NC (no connect) pins |
tri_state | Bus transceivers, tri-state outputs |
Adom Symbol Style
Group pins by function with 7.62mm (3 grid-step) gaps between groups.
Pin placement depends on the pinLayout preference:
pinLayout: "left-right" (default)
Place pins ONLY on the left and right sides:
- Left side: Inputs (analog sense, ADC, sensors, data in), ground (VSS/GND)
- Right side: Power supply (VCC, regulators), outputs, control, communication, protection
- Avoid bottom pins — bottom pins add rotated text that clutters the symbol
- Avoid top pins — rotated vertical text from top pins overlaps with side pin names at corners; prefer placing power pins on the right side instead
pinLayout: "all-sides"
Place pins on all four sides for a more compact symbol:
- Left side: Inputs (analog, ADC, sensors, data in)
- Right side: Outputs, control, communication
- Top side: Power supply (VCC, VDD, regulators)
- Bottom side: Ground (VSS, GND), thermal pad, NC pins
- Top/bottom pins use angles 270 (top) and 90 (bottom) — pin names render vertically
- Ensure body is wide enough that vertical pin names don't overlap with side pin names
Body Top Margin
Leave at least 1.5mm between the body top edge and the topmost group labels. The group label text (0.762mm font) needs clearance so it doesn't get clipped by the body edge. Formula: body_top = top_label_Y + 1.8 (minimum).
Body Bottom Margin
The body bottom edge should be 2.54mm below the lowest pin — just enough breathing room without wasting space. The Value property text is placed 2.54mm below the body bottom edge (outside the body). The Reference property is placed above the body top edge.
IMPORTANT: By default, the .kicad_sym file must NOT contain any centered text elements, separator lines, or description text inside the body rectangle. KiCad auto-renders the Value and Reference properties at their designated positions. Adding centered text causes overlap.
Exception — chipNameCentered: true: If the user has enabled this preference, add a centered text element with the chip/part name inside the body rectangle, vertically centered. This makes the part identifiable even when zoomed out. Use a text element in the _0_1 graphics sub-symbol:
(text "PART_NAME" (at 0 {body_center_y} 0)
(effects (font (size 1.27 1.27) (color 100 100 100 1)) (justify center))
)
Use a muted gray (color 100 100 100 1) so it doesn't compete with pin names or group labels.
Sizing Formula
body_top = top_label_Y + 1.8 (mm, margin above topmost group label)
body_bottom = lowest_pin_Y - 2.54 (mm, one grid step below lowest pin)
body_height = body_top - body_bottom (mm)
body_width = max(longest_left_name + longest_right_name + 8, 20) (mm)
pin_length = 2.54 (mm, standard)
inter_group = 7.62 (mm, 3 grid steps between groups)
intra_group = 2.54 (mm, standard pitch within a group)
Pin Group Labels
Use KiCad's native (text) elements inside the _0_1 graphics sub-symbol to label pin groups:
(text "GROUP NAME" (at X Y 0)
(effects (font (size 0.762 0.762) (color 132 0 0 1)) (justify left|right))
)
- Color: Dark red
(color 132 0 0 1)— used in the .kicad_sym file for KiCad compatibility. The Gallia Viewer overrides this with Adom purple#8C6BF7for the modern theme. - Font size: 0.762mm — small enough to not interfere with pin names
- Alignment: Left-aligned flush to the left body edge for left-side groups, right-aligned flush to the right body edge for right-side groups
- Label position: Place 2.0mm above the first pin in each group (gives clear visual separation)
- Left-side labels:
(at {body_left + 1.02} Y 0)with(justify left) - Right-side labels:
(at {body_right - 1.02} Y 0)with(justify right) - No divider lines — the red labels and pin spacing gaps are sufficient to communicate grouping
- Inter-group gap: Leave 7.62mm (3 grid steps) between the last pin of one group and the first pin of the next group. Within a group, pins are at the standard 2.54mm pitch. This yields a visible ~4mm gap between the preceding pin name text and the next group label — enough to read clearly in both the Gallia Viewer and native KiCad.
Final Checks
After placing all pins, do a final pass to verify:
- Side pin names from left and right must not collide in the middle of the body
- Pin numbers (outside the body) must not overlap with adjacent pin numbers
- Group labels have at least 1.5mm clearance from the body top edge
- If overlaps exist, widen the body or increase spacing between affected pins
Pin Descriptions
Write rich, engineering-grade descriptions for every pin (1-3 sentences each):
- Explain the pin's function and electrical characteristics
- Include recommended external components (capacitor values, resistor values)
- Note voltage ranges, current limits, or special connection requirements
- Use Unicode for units: Ω (U+03A9), µ (U+00B5), ± (U+00B1), — (U+2014)
Store descriptions in the -metadata.json file under pinDescriptions. Load them from there when generating the Gallia Viewer.
Group Descriptions
Write a description for every pin function group (2-4 sentences each):
- Explain the group's role in the overall IC architecture
- Summarize how the pins in the group work together
- Note key external component requirements or design considerations
- Mention relationships to other groups where relevant
Store descriptions in the -metadata.json file under groupDescriptions, keyed by the exact group label text (e.g., "CELL SENSING", "FET DRIVE"). The Gallia Viewer shows these as hover tooltips on the dark red group labels, styled with a warm red title and "Function Group" badge.
Step 3: Lint and Validate the Symbol
After creating or modifying a .kicad_sym file, run both the SVG-based linter and the artifact validator before previewing:
3a. Lint (visual correctness)
import { lintKicadSymbol } from '/home/adom/gallia/viewer/kicad-lint.js';
const result = await lintKicadSymbol('/path/to/symbol.kicad_sym', 'SYMBOL_NAME');
if (!result.passed) {
console.error('Lint errors:', result.errors);
// Fix the issues and re-lint before proceeding
}
if (result.warnings.length > 0) {
console.warn('Lint warnings:', result.warnings);
}
The linter renders the symbol via the remote KiCad service and checks:
- group-label-gap — labels must have >2.5mm visual gap from preceding pins
- pin-name-collision — left + right names must not overlap in the body center
- pin-number-overlap — adjacent pin numbers must not visually collide (warning)
- name-area-invasion — no pin/label text extending past the body bottom edge
- body-top-clipping — group labels need 1.5mm+ clearance from body top
Fix all errors before previewing. Re-run the linter after each fix to confirm.
3b. Validate (artifact completeness)
import { validateSymbolFolder } from '/home/adom/gallia/viewer/kicad-validate.js';
const result = await validateSymbolFolder('/path/to/PART_NAME/', 'PART_NAME');
if (!result.passed) {
console.error('Validation errors:', result.errors);
// Fix missing files or metadata before proceeding
}
if (result.warnings.length > 0) {
console.warn('Validation warnings:', result.warnings);
}
The validator checks:
- missing-kicad-sym —
.kicad_symfile exists - missing-metadata —
-metadata.jsonfile exists - missing-svg —
.svgexport exists - missing-viewer —
-viewer.htmlexists - missing-readme —
README.mdexists - pin-desc-missing — every pin name in the symbol has a
pinDescriptionsentry in metadata - group-desc-missing — every group label in the symbol has a
groupDescriptionsentry in metadata - pin-count-mismatch —
pinCountin metadata matches actual pin count in symbol - extra-pin-desc — stale pin description keys with no matching pin (warning)
- extra-group-desc — stale group description keys with no matching label (warning)
IMPORTANT: The validator will fail if any required artifact is missing. Create ALL files (metadata, .kicad_sym, README, SVG, SymView HTML) before the validate step passes. Run lint + validate together at every checkpoint.
Step 4: Export Artifacts, Preview, and Deliver
ALWAYS preview after every symbol change.
Delivery depends on the deliveryTarget preference:
ov(default): Preview in Gallia Viewer only. Desktop delivery happens later in Step 6 when user asks.desktop: Preview in Gallia Viewer, then also auto-deliver to the user's desktop EDA (based onedaToolpref).both: Preview in Gallia Viewer AND deliver to desktop EDA in parallel.
When edaTool is kicad, use Step 6a for desktop delivery. When edaTool is fusion360, use Step 6b. For other EDA tools (altium, cadence, tscircuit, easyeda, other), generate the .kicad_sym file as primary output and inform the user they'll need to import it into their EDA tool manually.
4a. Export SVG
import { symExportSvg, symExportSvgByName } from '/home/adom/gallia/viewer/kicad-api-client.js';
import { writeFileSync } from 'fs';
// For your custom .kicad_sym file:
const svg = await symExportSvg('/path/to/PART_NAME.kicad_sym', 'SYMBOL_NAME');
writeFileSync('/path/to/symbol-folder/PART_NAME.svg', svg);
// For standard library symbols (single call, no file needed):
const libSvg = await symExportSvgByName('Transistor_FET', 'AO3400A');
writeFileSync('/path/to/AO3400A.svg', libSvg);
4b. Generate SymView from metadata
CRITICAL: You MUST load the -metadata.json and pass pinDescriptions and groupDescriptions to generateSymbolViewer(). Without these options, the viewer renders the symbol correctly but pin hover tooltips will have no descriptions and group label hover tooltips will be empty. The viewer itself does not read the metadata file — it only uses what you pass in the options object.
import { generateSymbolViewer } from '/home/adom/gallia/viewer/kicad-viewer.js';
import { readFileSync, writeFileSync } from 'fs';
const metadata = JSON.parse(readFileSync('/path/to/PART_NAME-metadata.json', 'utf-8'));
const html = await generateSymbolViewer(
'/path/to/PART_NAME.kicad_sym',
metadata.symbolName,
{
pinDescriptions: metadata.pinDescriptions,
groupDescriptions: metadata.groupDescriptions
}
);
writeFileSync('/path/to/PART_NAME-viewer.html', html);
4c. Send to Gallia Viewer
import http from 'http';
import crypto from 'crypto';
const payload = JSON.stringify({
action: 'display',
content: {
contentType: 'html_interactive',
content: html,
title: 'SYMBOL_NAME',
id: crypto.randomUUID()
}
});
const req = http.request({
hostname: '127.0.0.1',
port: 8771,
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => console.log('Gallia Viewer:', res.statusCode, data));
});
req.write(payload);
req.end();
Viewer vs Native KiCad
The .kicad_sym file is 100% valid native KiCad format. The viewer uses a modern Adom-branded dark theme (NOT the old-school KiCad light style). See the brand.md skill guide for the full color palette and design system.
Viewer Style (Modern Adom Theme)
The viewer uses the Adom brand identity — NOT the default KiCad rendering style:
- Background:
#0d1117with subtle teal radial glow - Body rectangle: Dark gradient (
#1a2332→#141c27) with#30363dborder, rounded corners (rx: 4) - Pin wires (resting):
#4a5568stroke, 1.5px — visible but subtle - Pin wires (hover):
#00e6dcstroke, 3px — bright teal, unmissable - Pin dots:
#4a5568resting →#00e6dcon hover, radius 3 → 4.5 - Pin names:
#e6edf3monospace →#00e6dcon hover - Pin numbers:
#8b949emonospace atSCALE * 1.0— legible resting color - Group labels: Adom purple
#8C6BF7(NOT KiCad dark red) →#C5B3FFon hover - Reference (U):
#484f58muted above body - Value (part name):
#8b949esemibold below body - Tooltips: Frosted glass (
backdrop-filter: blur(16px)), teal accent border, 10px radius - Info bar:
#161b22background,#21262dborder, monospace values
Pin Hover Hit Areas
IC symbols (rectangle body with visible pin names): Each pin has two separate hit areas for reliable hover detection:
- Wire hit area — invisible rect covering the wire line + pin number region
- Pin name hit area — invisible rect sized to the pin name text bounding box (from KiCad's stroked-text
<g>elements)
Both hit areas trigger the same highlight (wire + dot + name + number all go teal) and show the same tooltip. This ensures hovering the pin name inside the body works just as well as hovering the wire outside.
Discrete symbols (MOSFETs, BJTs, diodes — hidden pin names): Hit zones are computed from injected text label positions using getBBox(). The viewer injects styled pin name/number labels at computed SVG positions (derived from the KiCad→SVG coordinate offset), then builds hover rects from those labels' bounding boxes. KiCad-rendered pin numbers are stripped first to avoid visual duplication.
The dispatch condition uses hidePinNames (from the .kicad_sym (pin_names (hide yes)) flag) to choose between IC-style and discrete-style hit zones.
Sticky Tooltips (Click to Pin)
Tooltips support a click-to-pin interaction so users can select and copy pin descriptions:
- Hover shows tooltip normally (
pointer-events: none,user-select: none) - Click a pin/group pins the tooltip in place — it gains
pointer-events: auto,user-select: text, and a brighter teal border (CSS classsticky) - Click the same pin again to unpin (toggle)
- Click anywhere else (except inside the tooltip) dismisses the sticky tooltip
- Click inside the tooltip does NOT dismiss — allows text selection and link clicking
- A small "click to pin - select text" hint appears at the bottom of non-sticky tooltips (hidden once sticky)
- While a tooltip is sticky, hover on other pins is suppressed — the pinned tooltip stays put
This is essential for copying engineering descriptions, clicking datasheet source links, etc.
Tooltip Positioning
Tooltips must never go off-screen. The positioning algorithm:
- Place tooltip at
cursor + 14px(right and below) - If overflows right edge → flip to left of cursor
- If overflows bottom edge → flip above cursor
- Clamp to viewport edges with 14px padding on all sides
Pin Data Sourcing (Trust & Transparency)
AI-generated symbols need provenance to build user trust. Every symbol viewer MUST include:
Info bar "Pin Source" field — A clickable link to the datasheet used for pin data, displayed in the bottom info bar next to the Datasheet link. Format:
"Manufacturer PartName Datasheet"linking to the datasheet URL.Tooltip source attribution — Every pin hover tooltip includes a small "Source:" footer at the bottom linking to the same datasheet. Styled with purple label and blue link, separated by a subtle border.
Wiki Edit Icon (Community Feedback Loop)
Every symbol viewer MUST include a "Suggest edit" button in the top-right corner that links to the Adom Wiki component page in edit mode. This enables community corrections when the AI gets pin data wrong.
Implementation:
- Position:
absolute, top-right of the viewer div - Style: Semi-transparent frosted glass background, Adom purple accent border, pencil SVG icon
- Opacity:
0.7resting →1.0on hover (unobtrusive but discoverable) - Link:
https://coder.john-service-wiki-a054078430ce38e4.containers.adom.inc/proxy/8785/wiki/components/{PART_NAME_LOWERCASE}/edit?field=content - Target:
_blank(opens wiki in new tab)
<a class="edit-btn" href="https://coder.john-service-wiki-a054078430ce38e4.containers.adom.inc/proxy/8785/wiki/components/PART_NAME_LOWER/edit?field=content" target="_blank" title="Suggest corrections to this symbol on the Adom Wiki">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M11.5 1.5l3 3L5 14H2v-3L11.5 1.5z"/>
<path d="M9.5 3.5l3 3"/>
</svg>
Suggest edit
</a>
CSS:
.edit-btn {
position: absolute; top: 10px; right: 10px; z-index: 100;
display: flex; align-items: center; gap: 6px;
padding: 5px 10px;
background: rgba(22,27,34,0.75);
backdrop-filter: blur(8px);
border: 1px solid rgba(140,107,247,0.2);
border-radius: 6px;
color: #8b949e; font-size: 11px;
cursor: pointer; text-decoration: none;
transition: all 0.2s; opacity: 0.7;
}
.edit-btn:hover {
opacity: 1;
background: rgba(140,107,247,0.12);
border-color: rgba(140,107,247,0.4);
color: #C5B3FF;
}
.edit-btn svg { width: 12px; height: 12px; }
Viewer Features Summary
The viewer provides:
- Modern Adom-branded dark theme with teal/purple accents
- Pin highlighting: wire + dot + name + number all highlight together in teal on hover
- Rich tooltips with pin type badge, description, and datasheet source attribution
- Group label hover with purple highlight, "Function Group" badge, and group description
- Info bar with Mfr, Pkg, Pins, Datasheet, Pin Source
- "Suggest edit" button linking to Adom Wiki for community corrections
- Zoom (scroll) and pan (drag) interactivity
- Frosted glass tooltips that stay within viewport bounds
- ViewBox-aware zoom-to-fit — reads the
.debug-bboxrect from the SVG DOM and usessvgEl.viewBox.baseValto correctly convert user-space coordinates to pixels, ensuring symbols are centered regardless of viewBox origin offset - Discrete component support — injected pin labels, stripped duplicate pin numbers, coordinate-based hit zones
Step 5: Iterate with the User
Show the symbol in Gallia Viewer and ask for feedback on:
- Pin grouping and arrangement
- Missing or incorrect pins
- Description quality
- Overall aesthetics
Regenerate and redisplay after each round of changes. Update all artifacts (SVG, SymView HTML, metadata) in the symbol folder after each change. After each round, re-run both lint and validate to ensure nothing fell out of sync.
Step 6a: Deliver to Local KiCad (only when user asks)
Do NOT send to KiCad automatically. Only send when the user explicitly asks to install/send the symbol to their local KiCad.
Pre-delivery lint + validate check
Before sending to KiCad, run both the linter and validator one final time to ensure no regressions:
import { lintKicadSymbol } from '/home/adom/gallia/viewer/kicad-lint.js';
import { validateSymbolFolder } from '/home/adom/gallia/viewer/kicad-validate.js';
const lintResult = await lintKicadSymbol(symPath, symbolName);
const validateResult = await validateSymbolFolder(folderPath, symbolName);
if (!lintResult.passed || !validateResult.passed) {
// Do not send — fix errors first
}
Close existing Symbol Editor, then install and open
Always close the Symbol Editor before installing — if a previous symbol is open, openAfterInstall won't switch to the new one. The close command is harmless if no editor is open.
import { readFile } from 'fs/promises';
const CONDUIT = 'http://127.0.0.1:8766';
const send = (msg, timeout = 15000) => fetch(CONDUIT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'send_and_wait', message: msg, timeoutMs: timeout })
}).then(r => r.json());
// 1. Close any open Symbol Editor (ignore errors — may not be open)
await send({
type: 'command',
payload: { app: 'kicad', command: 'close_symbol_editor', args: {} }
}).catch(() => {});
// 2. Brief pause for the editor to fully close
await new Promise(r => setTimeout(r, 2000));
// 3. Install and open the new symbol
const data = await readFile('/path/to/symbol.kicad_sym');
const result = await send({
type: 'command',
payload: {
app: 'kicad',
command: 'install_symbol',
args: {
fileName: 'symbol.kicad_sym',
fileContent: data.toString('base64'), // MUST be 'fileContent', not 'fileData'
fileSize: data.length,
libraryName: 'Adom_Custom',
description: 'Short description',
openAfterInstall: true,
symbolName: 'SYMBOL_NAME'
}
}
}, 60000);
Key: The base64 field MUST be named fileContent (not fileData).
Step 6b: Deliver to Fusion 360 Electronics (only when user asks)
Do NOT send to Fusion 360 automatically. Only send when the user explicitly asks.
This converts the KiCad .kicad_sym into an EAGLE .lbr, sends it to the desktop, and opens it in Fusion 360's Electronics Library editor.
Convert KiCad .kicad_sym to EAGLE .lbr
The conversion is a direct coordinate mapping — KiCad and EAGLE both use mm with Y-up. Build the EAGLE XML from the KiCad symbol data:
Pin type mapping
| KiCad type | EAGLE direction | Notes |
|---|---|---|
power_in | pwr | VCC, VDD, GND |
power_out | out | Regulator outputs |
input | in | Digital/analog inputs |
output | out | Digital outputs |
bidirectional | io | I2C SDA, GPIO |
passive | pas | Resistors, crystals |
unconnected | nc | NC pins |
tri_state | hiz | Bus transceivers |
Pin rotation mapping
| KiCad angle | EAGLE rotation | Side |
|---|---|---|
| 0 (extends right) | (none) | Left-side pins |
| 180 (extends left) | rot="R180" | Right-side pins |
| 90 (extends up) | rot="R90" | Bottom-side pins |
| 270 (extends down) | rot="R270" | Top-side pins |
Pin length mapping
KiCad pin length 2.54mm = EAGLE length="short". Other EAGLE lengths: point (0), middle (5.08mm), long (7.62mm).
NC pins
EAGLE requires unique pin names. For multiple NC pins, use the @ suffix convention: NC@1, NC@2, etc. The @ suffix is hidden in the schematic — all display as "NC".
EAGLE .lbr structure
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE eagle SYSTEM "eagle.dtd">
<eagle version="9.6.2">
<drawing>
<settings>...</settings>
<grid distance="0.1" unitdist="inch" unit="inch" .../>
<layers>
<!-- Must include all standard layers (1-98). Copy from any reference .lbr. -->
</layers>
<library>
<description>PART_NAME — short description</description>
<packages></packages>
<symbols>
<symbol name="PART_NAME">
<!-- Box outline: 4 wires on layer 94 (Symbols) -->
<wire x1="..." y1="..." x2="..." y2="..." width="0.254" layer="94"/>
<!-- ... 3 more wires for the rectangle -->
<!-- >NAME and >VALUE labels -->
<text x="0" y="..." size="1.27" layer="95" align="bottom-center">>NAME</text>
<text x="0" y="..." size="1.27" layer="96" align="top-center">>VALUE</text>
<!-- Group labels on layer 94 -->
<text x="..." y="..." size="0.762" layer="94">GROUP NAME</text>
<text x="..." y="..." size="0.762" layer="94" align="bottom-right">GROUP NAME</text>
<!-- Pins -->
<pin name="VDD" x="12.54" y="17.78" length="short" direction="pwr" rot="R180"/>
<pin name="GND" x="-12.54" y="17.78" length="short" direction="pwr"/>
<!-- ... more pins ... -->
</symbol>
</symbols>
<devicesets>
<deviceset name="PART_NAME" prefix="U">
<description>Part description</description>
<gates>
<gate name="G$1" symbol="PART_NAME" x="0" y="0"/>
</gates>
<devices>
<device name="">
<connects/>
<technologies><technology name=""/></technologies>
</device>
</devices>
</deviceset>
</devicesets>
</library>
</drawing>
</eagle>
Send to desktop and open in Fusion 360
Save the .lbr to the symbol folder, then use MCP tools:
1. Write /path/to/PART_NAME/PART_NAME.lbr
2. send_files (filePaths: ["/path/to/PART_NAME.lbr"], targetApp: "fusion360", destinationFolder: "fusion")
3. fusion_open_lbr (filePath from destinationPaths[0], symbolName: "PART_NAME", verify: true)
The fusion_open_lbr command:
- Opens the .lbr in Fusion's Electronics Library editor via
Document.newDesignFromLocal - Navigates to the specified symbol with
EDIT PART_NAME.sym - Optionally verifies the library loaded correctly by exporting a script
Important: destinationFolder must be a relative path (e.g. "fusion") — absolute paths are rejected by the desktop app.
Reference: Sample Symbols
Working examples in the container:
| Part | File | Pins | Package |
|---|---|---|---|
| TLV1117-33 | /home/adom/gallia/server/samples/ti-tlv1117-33.kicad_sym | 4 | SOT-223 |
| TXB0102 | /home/adom/gallia/server/samples/ti-txb0102.kicad_sym | 8 | VSSOP-8 |
| TXB0104 | /home/adom/gallia/server/samples/ti-txb0104.kicad_sym | 12 | TSSOP-8 |
| BQ76952 | /home/adom/project/project-content/schematics/symbols/BQ76952/BQ76952.kicad_sym | 48 | TQFP-48 |
| TLV1117-50 | /home/adom/project/project-content/schematics/symbols/TLV1117-50/TLV1117-50.kicad_sym | 4 | SOT-223 |
| ICM-42688-P | /home/adom/project/project-content/schematics/symbols/ICM-42688-P/ICM-42688-P.kicad_sym | 14 | LGA-14 |
| BMI423 | /home/adom/project/project-content/schematics/symbols/BMI423/BMI423.kicad_sym | 14 | LGA-14 |
| NE555 | /home/adom/project/project-content/schematics/symbols/NE555/NE555.kicad_sym | 8 | PDIP-8 / SOIC-8 |
| AO3400A | /home/adom/project/project-content/schematics/symbols/AO3400A/AO3400A.kicad_sym | 3 | SOT-23 |
| BSS138 | /home/adom/project/project-content/schematics/symbols/BSS138/BSS138.kicad_sym | 3 | SOT-23 |
| IRLML6401 | /home/adom/project/project-content/schematics/symbols/IRLML6401/IRLML6401.kicad_sym | 3 | SOT-23 |
| IRLML2060 | /home/adom/project/project-content/schematics/symbols/IRLML2060/IRLML2060.kicad_sym | 3 | SOT-23 |
Troubleshooting: Conduit Connection Issues
When sending symbols to KiCad via Adom Desktop Conduit, connection issues can cause timeouts. The server now auto-detects and prunes stale connections via WebSocket protocol-level ping/pong (~60s max to detect a dead connection).
Check who's connected
Use the conduit_status MCP tool to see all connected clients, their IPs, hostnames, and connection freshness (lastPong timestamp). Multiple clients from the same hostname may indicate a stale connection that hasn't been pruned yet.
Kick all connections
Use the conduit_kick_all MCP tool to force-disconnect all clients. Active Adom Desktop Conduit apps auto-reconnect within seconds. This is safe and idempotent — use it whenever you suspect stale connections are causing timeouts.
Symptoms of stale connections
send_filesorkicad_install_symboltimes out after 30-60sconduit_statusshows multiple clients from the same hostname- One client has a very old
lastPongtimestamp
Common Pitfalls
No comments in .kicad_sym — KiCad's S-expression parser rejects
;;and#commentsCoordinate grid — All pin positions must be on the 2.54mm grid for proper connectivity
Pin number accuracy — Pin numbers must match the physical package exactly; verify against the datasheet
No centered text in .kicad_sym — NEVER add text elements (name, description, separator lines) inside the body rectangle. KiCad auto-renders the Value and Reference properties at their designated positions. Adding centered text causes overlap.
Body top clipping — Group labels at the top edge need 1.5mm+ clearance or they get visually clipped
Description overflow — The viewer auto-truncates, but keep descriptions concise (<60 chars) so nothing is lost
Unused VC/sense pins — For configurable-series parts (like BQ76952), document how unused inputs should be connected
Pin name/number format — CRITICAL for hover tooltips — The Gallia Viewer's pin parser requires the compact inline format for
(name ...)and(number ...). Adding nested(effects ...)blocks breaks the regex and silently disables ALL pin hover tooltips (group label tooltips still work, making this easy to miss). Always write pins like this:(pin power_in line (at -17.54 5.08 0) (length 2.54) (name "GND") (number "10"))NOT like this (nested effects — parser fails, no pin tooltips):
(pin power_in line (at -17.54 5.08 0) (length 2.54) (name "GND" (effects (font (size 1.27 1.27)))) (number "10" (effects (font (size 1.27 1.27)))) )KiCad renders pin text at the default size regardless — explicit font effects on
name/numberare unnecessary and break the viewer.
Related Skills
- footprint-creator — After creating a schematic symbol, use the
footprint-creatorskill to create the matching PCB footprint (.kicad_mod). The footprint maps pad numbers to the symbol's pin numbers. - A future 3d-model-creator skill will handle STEP/WRL 3D models that attach to footprints.