circuit-bricks
Version:
A modular, Lego-style SVG circuit component system for React (ALPHA - Not for production use)
1,717 lines (1,710 loc) • 54.3 kB
JavaScript
import { getAllComponents, getComponentSchema, getComponentsByCategory, registerComponent, validateCircuitState } from "./registry-D4rkLXEi.mjs";
//#region rolldown:runtime
var __defProp = Object.defineProperty;
var __export = (target, all) => {
for (var name in all) __defProp(target, name, {
get: all[name],
enumerable: true
});
};
//#endregion
//#region src/utils/circuitValidation.ts
/**
* Validate a circuit for common issues
*
* @param circuit - The circuit state to validate
* @returns Array of validation issues
*/
function validateCircuit(circuit) {
const validationResult = validateCircuitState(circuit);
if (!validationResult.success) return [{
type: "error",
message: `Invalid circuit structure: ${validationResult.error}`
}];
const issues = [];
issues.push(...validateWireConnections(circuit));
issues.push(...validateFloatingPorts(circuit));
issues.push(...validateShortCircuits(circuit));
return issues;
}
/**
* Validate wire connections to ensure they reference valid components
*/
function validateWireConnections(circuit) {
const issues = [];
const componentIds = new Set(circuit.components.map((c) => c.id));
for (const wire of circuit.wires) {
if (!componentIds.has(wire.from.componentId)) issues.push({
type: "error",
message: `Wire connected to non-existent component ID: ${wire.from.componentId}`,
wireId: wire.id
});
if (!componentIds.has(wire.to.componentId)) issues.push({
type: "error",
message: `Wire connected to non-existent component ID: ${wire.to.componentId}`,
wireId: wire.id
});
const sourceComponent = circuit.components.find((c) => c.id === wire.from.componentId);
const destComponent = circuit.components.find((c) => c.id === wire.to.componentId);
if (sourceComponent) {
const schema = getComponentSchema(sourceComponent.type);
if (schema && !schema.ports.some((p) => p.id === wire.from.portId)) issues.push({
type: "error",
message: `Wire connected to non-existent port: ${wire.from.portId} on component ${sourceComponent.type}`,
wireId: wire.id
});
}
if (destComponent) {
const schema = getComponentSchema(destComponent.type);
if (schema && !schema.ports.some((p) => p.id === wire.to.portId)) issues.push({
type: "error",
message: `Wire connected to non-existent port: ${wire.to.portId} on component ${destComponent.type}`,
wireId: wire.id
});
}
}
return issues;
}
/**
* Check for floating input/output ports that should be connected
*/
function validateFloatingPorts(circuit) {
const issues = [];
const connectedPorts = new Map();
for (const wire of circuit.wires) {
if (!connectedPorts.has(wire.from.componentId)) connectedPorts.set(wire.from.componentId, new Set());
if (!connectedPorts.has(wire.to.componentId)) connectedPorts.set(wire.to.componentId, new Set());
connectedPorts.get(wire.from.componentId).add(wire.from.portId);
connectedPorts.get(wire.to.componentId).add(wire.to.portId);
}
for (const component of circuit.components) {
const schema = getComponentSchema(component.type);
if (!schema) continue;
const componentConnectedPorts = connectedPorts.get(component.id) || new Set();
for (const port of schema.ports) {
if (port.type === "input" && !componentConnectedPorts.has(port.id)) issues.push({
type: "warning",
message: `Floating input port: ${port.id} on ${schema.name} (${component.id})`,
componentId: component.id
});
if (port.type === "output" && !componentConnectedPorts.has(port.id)) issues.push({
type: "warning",
message: `Floating output port: ${port.id} on ${schema.name} (${component.id})`,
componentId: component.id
});
}
}
return issues;
}
/**
* Check for short circuits (multiple outputs connected together)
*/
function validateShortCircuits(circuit) {
const issues = [];
const connectionGraph = new Map();
for (const wire of circuit.wires) {
const fromKey = `${wire.from.componentId}-${wire.from.portId}`;
const toKey = `${wire.to.componentId}-${wire.to.portId}`;
if (!connectionGraph.has(fromKey)) connectionGraph.set(fromKey, new Set());
if (!connectionGraph.has(toKey)) connectionGraph.set(toKey, new Set());
connectionGraph.get(fromKey).add(toKey);
connectionGraph.get(toKey).add(fromKey);
}
const portTypes = new Map();
for (const component of circuit.components) {
const schema = getComponentSchema(component.type);
if (!schema) continue;
for (const port of schema.ports) portTypes.set(`${component.id}-${port.id}`, port.type);
}
const visited = new Set();
const connectedComponents = [];
for (const [port, connections] of connectionGraph.entries()) {
if (visited.has(port)) continue;
const connectedPorts = new Set();
const queue = [port];
visited.add(port);
while (queue.length > 0) {
const currentPort = queue.shift();
connectedPorts.add(currentPort);
for (const connectedPort of connectionGraph.get(currentPort) || []) if (!visited.has(connectedPort)) {
queue.push(connectedPort);
visited.add(connectedPort);
}
}
const outputPorts = [...connectedPorts].filter((p) => portTypes.get(p) === "output");
if (outputPorts.length > 1) issues.push({
type: "error",
message: `Short circuit detected: ${outputPorts.length} output ports connected together`
});
}
return issues;
}
//#endregion
//#region src/llm/componentDiscovery.ts
/**
* Convert a ComponentSchema to simplified ComponentInfo for LLM consumption
*/
function schemaToComponentInfo(schema) {
return {
id: schema.id,
name: schema.name,
category: schema.category,
description: schema.description,
ports: schema.ports.map((port) => ({
id: port.id,
type: port.type,
label: port.label
})),
properties: schema.properties.map((prop) => ({
key: prop.key,
label: prop.label,
type: prop.type,
default: prop.default,
unit: prop.unit,
options: prop.options
}))
};
}
/**
* List all available components in a format optimized for LLM consumption
*
* @returns Array of simplified component information
*/
function listAvailableComponents() {
const allComponents = getAllComponents();
return allComponents.map(schemaToComponentInfo);
}
/**
* Get components filtered by category
*
* @param category - The category to filter by
* @returns Array of components in the specified category
*/
function listComponentsByCategory(category) {
const components = getComponentsByCategory(category);
return components.map(schemaToComponentInfo);
}
/**
* Get detailed information about a specific component
*
* @param componentId - The ID of the component to get details for
* @returns Detailed component information or null if not found
*/
function getComponentDetails(componentId) {
const schema = getComponentSchema(componentId);
if (!schema) return null;
return schemaToComponentInfo(schema);
}
/**
* Get all available component categories
*
* @returns Array of unique category names
*/
function getAllCategories() {
const allComponents = getAllComponents();
const categories = [...new Set(allComponents.map((c) => c.category))];
return categories.sort();
}
/**
* Search components by name, description, or category
*
* @param query - Search query string
* @returns Array of search results with relevance scores
*/
function searchComponents(query) {
const allComponents = getAllComponents();
const searchTerm = query.toLowerCase();
const results = [];
for (const schema of allComponents) {
const matchedFields = [];
let relevanceScore = 0;
if (schema.name.toLowerCase().includes(searchTerm)) {
matchedFields.push("name");
relevanceScore += 10;
}
if (schema.id.toLowerCase().includes(searchTerm)) {
matchedFields.push("id");
relevanceScore += 8;
}
if (schema.category.toLowerCase().includes(searchTerm)) {
matchedFields.push("category");
relevanceScore += 5;
}
if (schema.description.toLowerCase().includes(searchTerm)) {
matchedFields.push("description");
relevanceScore += 3;
}
for (const prop of schema.properties) if (prop.label.toLowerCase().includes(searchTerm) || prop.key.toLowerCase().includes(searchTerm)) {
matchedFields.push("properties");
relevanceScore += 1;
break;
}
if (relevanceScore > 0) results.push({
component: schemaToComponentInfo(schema),
relevanceScore,
matchedFields
});
}
return results.sort((a, b) => b.relevanceScore - a.relevanceScore);
}
/**
* Get registry metadata optimized for LLM consumption
*
* @returns Comprehensive registry metadata
*/
function getRegistryMetadata() {
const allComponents = getAllComponents();
const categories = getAllCategories();
const componentsByCategory = {};
for (const category of categories) componentsByCategory[category] = listComponentsByCategory(category);
return {
totalComponents: allComponents.length,
categories,
componentsByCategory,
lastUpdated: new Date().toISOString()
};
}
/**
* Get a human-readable summary of the component registry
*
* @returns String summary of all available components
*/
function getRegistrySummary() {
const metadata = getRegistryMetadata();
let summary = `# Circuit-Bricks Component Registry\n\n`;
summary += `Total Components: ${metadata.totalComponents}\n`;
summary += `Categories: ${metadata.categories.length}\n\n`;
for (const category of metadata.categories) {
const components = metadata.componentsByCategory[category];
summary += `## ${category.charAt(0).toUpperCase() + category.slice(1)} (${components.length} components)\n\n`;
for (const component of components) {
summary += `### ${component.name} (${component.id})\n`;
summary += `${component.description}\n\n`;
summary += `**Ports:** ${component.ports.map((p) => `${p.id} (${p.type})`).join(", ")}\n`;
if (component.properties.length > 0) summary += `**Properties:** ${component.properties.map((p) => `${p.label} (${p.type})`).join(", ")}\n`;
summary += "\n";
}
}
summary += `\n*Last updated: ${metadata.lastUpdated}*`;
return summary;
}
/**
* Get a detailed summary of a specific component for LLM consumption
*
* @param componentId - The ID of the component to summarize
* @returns Detailed string summary or null if component not found
*/
function getComponentDetailedSummary(componentId) {
const component = getComponentDetails(componentId);
if (!component) return null;
let summary = `# ${component.name} (${component.id})\n\n`;
summary += `**Category:** ${component.category}\n`;
summary += `**Description:** ${component.description}\n\n`;
summary += `## Ports\n`;
for (const port of component.ports) {
summary += `- **${port.id}** (${port.type})`;
if (port.label) summary += `: ${port.label}`;
summary += "\n";
}
if (component.properties.length > 0) {
summary += `\n## Properties\n`;
for (const prop of component.properties) {
summary += `- **${prop.label}** (${prop.key})\n`;
summary += ` - Type: ${prop.type}\n`;
summary += ` - Default: ${prop.default}\n`;
if (prop.unit) summary += ` - Unit: ${prop.unit}\n`;
if (prop.options) summary += ` - Options: ${prop.options.map((o) => `${o.value} (${o.label})`).join(", ")}\n`;
summary += "\n";
}
}
return summary;
}
//#endregion
//#region src/llm/circuitGeneration.ts
/**
* Generate a human-readable description of a circuit
*
* @param circuit - The circuit state to describe
* @returns Detailed circuit description
*/
function describeCircuit(circuit) {
const components = circuit.components.map((comp) => {
const schema = getComponentSchema(comp.type);
return {
id: comp.id,
type: comp.type,
name: schema?.name || comp.type,
position: comp.position,
properties: comp.props
};
});
const connections = circuit.wires.map((wire) => {
const fromComp = circuit.components.find((c) => c.id === wire.from.componentId);
const toComp = circuit.components.find((c) => c.id === wire.to.componentId);
const fromSchema = fromComp ? getComponentSchema(fromComp.type) : null;
const toSchema = toComp ? getComponentSchema(toComp.type) : null;
return {
from: {
component: fromSchema?.name || wire.from.componentId,
port: wire.from.portId
},
to: {
component: toSchema?.name || wire.to.componentId,
port: wire.to.portId
},
description: `${fromSchema?.name || "Component"} ${wire.from.portId} → ${toSchema?.name || "Component"} ${wire.to.portId}`
};
});
const categories = [...new Set(components.map((c) => {
const schema = getComponentSchema(c.type);
return schema?.category || "unknown";
}))];
const powerSources = components.filter((c) => {
const schema = getComponentSchema(c.type);
return schema?.category === "power" || schema?.id === "battery" || schema?.id === "voltage-source";
}).map((c) => c.name);
const hasGround = components.some((c) => {
const schema = getComponentSchema(c.type);
return schema?.id === "ground";
});
let summary = `This circuit contains ${components.length} components and ${connections.length} connections. `;
if (powerSources.length > 0) summary += `Power is provided by ${powerSources.join(", ")}. `;
if (hasGround) summary += `The circuit includes a ground connection. `;
summary += `Components span ${categories.length} categories: ${categories.join(", ")}.`;
return {
summary,
components,
connections,
analysis: {
totalComponents: components.length,
totalConnections: connections.length,
categories,
powerSources,
hasGround
}
};
}
/**
* Suggest valid connections between components in a circuit
*
* @param components - Array of component instances
* @returns Array of connection suggestions
*/
function suggestConnections(components) {
const suggestions = [];
const componentData = components.map((comp) => ({
instance: comp,
schema: getComponentSchema(comp.type)
})).filter((data) => data.schema !== void 0);
const powerSources = componentData.filter((data) => data.schema.category === "power" || data.schema.id === "battery" || data.schema.id === "voltage-source");
const grounds = componentData.filter((data) => data.schema.id === "ground");
for (const powerSource of powerSources) {
const positivePort = powerSource.schema.ports.find((p) => p.id === "positive" || p.type === "output");
if (positivePort) for (const comp of componentData) {
if (comp.instance.id === powerSource.instance.id) continue;
const inputPorts = comp.schema.ports.filter((p) => p.type === "input");
for (const inputPort of inputPorts) suggestions.push({
from: {
componentId: powerSource.instance.id,
portId: positivePort.id
},
to: {
componentId: comp.instance.id,
portId: inputPort.id
},
reason: `Connect power source to ${comp.schema.name} input`,
confidence: .8
});
}
}
if (grounds.length > 0) {
const ground = grounds[0];
const groundPort = ground.schema.ports.find((p) => p.id === "terminal");
if (groundPort) {
for (const powerSource of powerSources) {
const negativePort = powerSource.schema.ports.find((p) => p.id === "negative");
if (negativePort) suggestions.push({
from: {
componentId: powerSource.instance.id,
portId: negativePort.id
},
to: {
componentId: ground.instance.id,
portId: groundPort.id
},
reason: "Connect power source negative to ground",
confidence: .9
});
}
for (const comp of componentData) {
if (comp.instance.id === ground.instance.id) continue;
const outputPorts = comp.schema.ports.filter((p) => p.type === "output" || p.id === "cathode" || p.id === "emitter");
for (const outputPort of outputPorts) suggestions.push({
from: {
componentId: comp.instance.id,
portId: outputPort.id
},
to: {
componentId: ground.instance.id,
portId: groundPort.id
},
reason: `Connect ${comp.schema.name} output to ground`,
confidence: .7
});
}
}
}
return suggestions.sort((a, b) => b.confidence - a.confidence);
}
/**
* Generate a basic circuit template from a description
*
* @param description - Text description of the desired circuit
* @returns Basic circuit template
*/
function generateCircuitTemplate(description) {
const lowerDesc = description.toLowerCase();
let templateId = "basic";
let templateName = "Basic Circuit";
let components = [];
let wires = [];
if (lowerDesc.includes("led") || lowerDesc.includes("light")) {
templateId = "led-circuit";
templateName = "LED Circuit";
components = [
{
id: "battery1",
type: "battery",
position: {
x: 100,
y: 150
},
props: { voltage: 9 }
},
{
id: "resistor1",
type: "resistor",
position: {
x: 250,
y: 150
},
props: {
resistance: 330,
tolerance: 5
}
},
{
id: "led1",
type: "led",
position: {
x: 400,
y: 150
},
props: {
color: "#ff0000",
forwardVoltage: 1.8
}
},
{
id: "ground1",
type: "ground",
position: {
x: 250,
y: 250
},
props: {}
}
];
wires = [
{
id: "wire1",
from: {
componentId: "battery1",
portId: "positive"
},
to: {
componentId: "resistor1",
portId: "left"
}
},
{
id: "wire2",
from: {
componentId: "resistor1",
portId: "right"
},
to: {
componentId: "led1",
portId: "anode"
}
},
{
id: "wire3",
from: {
componentId: "led1",
portId: "cathode"
},
to: {
componentId: "ground1",
portId: "terminal"
}
},
{
id: "wire4",
from: {
componentId: "battery1",
portId: "negative"
},
to: {
componentId: "ground1",
portId: "terminal"
}
}
];
} else if (lowerDesc.includes("voltage divider") || lowerDesc.includes("divider")) {
templateId = "voltage-divider";
templateName = "Voltage Divider";
components = [
{
id: "battery1",
type: "battery",
position: {
x: 100,
y: 100
},
props: { voltage: 9 }
},
{
id: "resistor1",
type: "resistor",
position: {
x: 250,
y: 100
},
props: {
resistance: 1e3,
tolerance: 5
}
},
{
id: "resistor2",
type: "resistor",
position: {
x: 250,
y: 200
},
props: {
resistance: 1e3,
tolerance: 5
}
},
{
id: "ground1",
type: "ground",
position: {
x: 250,
y: 300
},
props: {}
}
];
wires = [
{
id: "wire1",
from: {
componentId: "battery1",
portId: "positive"
},
to: {
componentId: "resistor1",
portId: "left"
}
},
{
id: "wire2",
from: {
componentId: "resistor1",
portId: "right"
},
to: {
componentId: "resistor2",
portId: "left"
}
},
{
id: "wire3",
from: {
componentId: "resistor2",
portId: "right"
},
to: {
componentId: "ground1",
portId: "terminal"
}
},
{
id: "wire4",
from: {
componentId: "battery1",
portId: "negative"
},
to: {
componentId: "ground1",
portId: "terminal"
}
}
];
}
return {
id: templateId,
name: templateName,
description: `Generated template for: ${description}`,
category: "generated",
components,
wires
};
}
/**
* Analyze circuit requirements from user description
*
* This function identifies what components are needed for a circuit based on
* the user's natural language description.
*
* @param description - Natural language description of the desired circuit
* @returns Analysis result with required components and availability
*/
function analyzeCircuitRequirements(description) {
const lowerDesc = description.toLowerCase();
let circuitType = "unknown";
let circuitDescription = "";
let requiredComponents = [];
if (lowerDesc.includes("lcr") || lowerDesc.includes("inductor") && lowerDesc.includes("capacitor") && lowerDesc.includes("resistor")) {
circuitType = "LCR Circuit";
circuitDescription = "A circuit containing an inductor (L), capacitor (C), and resistor (R) - typically used for resonance and filtering applications";
requiredComponents = [
{
id: "inductor",
name: "Inductor",
category: "passive",
description: "A passive component that stores energy in a magnetic field",
isAvailable: false,
reason: "Component not found in registry",
suggestedProperties: [{
key: "inductance",
label: "Inductance",
type: "number",
default: 10,
unit: "mH"
}, {
key: "tolerance",
label: "Tolerance",
type: "number",
default: 5,
unit: "%"
}],
suggestedPorts: [{
id: "left",
type: "inout"
}, {
id: "right",
type: "inout"
}]
},
{
id: "capacitor",
name: "Capacitor",
category: "passive",
description: "A passive component that stores electrical energy in an electric field",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "resistor",
name: "Resistor",
category: "passive",
description: "A passive component that implements electrical resistance",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "battery",
name: "Battery",
category: "power",
description: "DC voltage source for powering the circuit",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "ground",
name: "Ground",
category: "reference",
description: "Reference point for the circuit",
isAvailable: true,
reason: "Available in component registry"
}
];
} else if (lowerDesc.includes("rc") || lowerDesc.includes("resistor") && lowerDesc.includes("capacitor")) {
circuitType = "RC Circuit";
circuitDescription = "A circuit containing a resistor (R) and capacitor (C) - used for timing and filtering applications";
requiredComponents = [
{
id: "resistor",
name: "Resistor",
category: "passive",
description: "A passive component that implements electrical resistance",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "capacitor",
name: "Capacitor",
category: "passive",
description: "A passive component that stores electrical energy in an electric field",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "battery",
name: "Battery",
category: "power",
description: "DC voltage source for powering the circuit",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "ground",
name: "Ground",
category: "reference",
description: "Reference point for the circuit",
isAvailable: true,
reason: "Available in component registry"
}
];
} else if (lowerDesc.includes("led") || lowerDesc.includes("light")) {
circuitType = "LED Circuit";
circuitDescription = "A circuit to drive an LED with proper current limiting";
requiredComponents = [
{
id: "led",
name: "LED",
category: "output",
description: "Light-emitting diode",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "resistor",
name: "Current Limiting Resistor",
category: "passive",
description: "Resistor to limit current through the LED",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "battery",
name: "Battery",
category: "power",
description: "DC voltage source for powering the circuit",
isAvailable: true,
reason: "Available in component registry"
},
{
id: "ground",
name: "Ground",
category: "reference",
description: "Reference point for the circuit",
isAvailable: true,
reason: "Available in component registry"
}
];
} else {
circuitType = "Custom Circuit";
circuitDescription = `Custom circuit based on description: ${description}`;
const componentKeywords = [
{
keyword: "resistor",
id: "resistor",
name: "Resistor",
category: "passive"
},
{
keyword: "capacitor",
id: "capacitor",
name: "Capacitor",
category: "passive"
},
{
keyword: "inductor",
id: "inductor",
name: "Inductor",
category: "passive"
},
{
keyword: "led",
id: "led",
name: "LED",
category: "output"
},
{
keyword: "diode",
id: "diode",
name: "Diode",
category: "semiconductor"
},
{
keyword: "transistor",
id: "transistor-npn",
name: "Transistor",
category: "semiconductor"
},
{
keyword: "battery",
id: "battery",
name: "Battery",
category: "power"
},
{
keyword: "switch",
id: "switch",
name: "Switch",
category: "control"
}
];
for (const comp of componentKeywords) if (lowerDesc.includes(comp.keyword)) {
const schema = getComponentSchema(comp.id);
requiredComponents.push({
id: comp.id,
name: comp.name,
category: comp.category,
description: schema?.description || `${comp.name} component`,
isAvailable: !!schema,
reason: schema ? "Available in component registry" : "Component not found in registry"
});
}
if (requiredComponents.length > 0) {
const hasPower = requiredComponents.some((c) => c.category === "power");
const hasGround = requiredComponents.some((c) => c.id === "ground");
if (!hasPower) requiredComponents.push({
id: "battery",
name: "Battery",
category: "power",
description: "DC voltage source for powering the circuit",
isAvailable: true,
reason: "Available in component registry"
});
if (!hasGround) requiredComponents.push({
id: "ground",
name: "Ground",
category: "reference",
description: "Reference point for the circuit",
isAvailable: true,
reason: "Available in component registry"
});
}
}
for (const component of requiredComponents) {
const schema = getComponentSchema(component.id);
component.isAvailable = !!schema;
if (schema) component.reason = "Available in component registry";
else component.reason = "Component not found in registry - will be auto-generated";
}
const availableComponents = requiredComponents.filter((c) => c.isAvailable);
const missingComponents = requiredComponents.filter((c) => !c.isAvailable);
const suggestedLayout = generateCircuitLayout(requiredComponents);
return {
circuitType,
description: circuitDescription,
requiredComponents,
missingComponents,
availableComponents,
suggestedLayout
};
}
/**
* Generate a suggested layout for circuit components
*
* @param components - Array of component requirements
* @returns Suggested layout with positions
*/
function generateCircuitLayout(components) {
const componentPositions = {};
const gridSpacing = 150;
const startX = 100;
const startY = 100;
let currentX = startX;
let currentY = startY;
let maxX = startX;
let maxY = startY;
const powerComponents = components.filter((c) => c.category === "power");
for (const comp of powerComponents) {
componentPositions[comp.id] = {
x: currentX,
y: currentY
};
currentY += gridSpacing;
maxY = Math.max(maxY, currentY);
}
currentX += gridSpacing;
currentY = startY;
const passiveComponents = components.filter((c) => c.category === "passive");
for (const comp of passiveComponents) {
componentPositions[comp.id] = {
x: currentX,
y: currentY
};
currentY += gridSpacing;
maxY = Math.max(maxY, currentY);
if (currentY > startY + gridSpacing * 2) {
currentX += gridSpacing;
currentY = startY;
}
}
currentX += gridSpacing;
currentY = startY;
const outputComponents = components.filter((c) => c.category === "output" || c.category === "semiconductor");
for (const comp of outputComponents) {
componentPositions[comp.id] = {
x: currentX,
y: currentY
};
currentY += gridSpacing;
maxY = Math.max(maxY, currentY);
}
const groundComponent = components.find((c) => c.id === "ground");
if (groundComponent) {
componentPositions[groundComponent.id] = {
x: startX + gridSpacing,
y: maxY + gridSpacing
};
maxY += gridSpacing * 2;
}
maxX = Math.max(maxX, currentX + 100);
return {
width: maxX + 100,
height: maxY + 100,
componentPositions
};
}
/**
* Auto-generate missing component schemas
*
* This function creates component schemas for missing components based on
* electrical engineering knowledge and existing component patterns.
*
* @param missingComponents - Array of missing component requirements
* @returns Array of generated component schemas
*/
function generateMissingComponents(missingComponents) {
const generatedSchemas = [];
for (const missing of missingComponents) {
let schema;
switch (missing.id) {
case "inductor":
schema = {
id: "inductor",
name: "Inductor",
category: "passive",
description: "A passive component that stores energy in a magnetic field",
defaultWidth: 80,
defaultHeight: 30,
ports: [{
id: "left",
x: 0,
y: 15,
type: "inout"
}, {
id: "right",
x: 80,
y: 15,
type: "inout"
}],
properties: [{
key: "inductance",
label: "Inductance",
type: "number",
unit: "mH",
default: 10,
min: 0
}, {
key: "tolerance",
label: "Tolerance",
type: "number",
unit: "%",
default: 5,
min: 0,
max: 20
}],
svgPath: "M10,15 h10 C25,5 25,25 35,15 C45,5 45,25 55,15 C65,5 65,25 70,15 h10"
};
break;
case "transformer":
schema = {
id: "transformer",
name: "Transformer",
category: "passive",
description: "A device that transfers electrical energy between circuits through electromagnetic induction",
defaultWidth: 100,
defaultHeight: 80,
ports: [
{
id: "primary_left",
x: 0,
y: 20,
type: "input",
label: "P1"
},
{
id: "primary_right",
x: 0,
y: 60,
type: "input",
label: "P2"
},
{
id: "secondary_left",
x: 100,
y: 20,
type: "output",
label: "S1"
},
{
id: "secondary_right",
x: 100,
y: 60,
type: "output",
label: "S2"
}
],
properties: [{
key: "turns_ratio",
label: "Turns Ratio",
type: "number",
default: 1,
min: .1
}, {
key: "primary_inductance",
label: "Primary Inductance",
type: "number",
unit: "mH",
default: 100,
min: 0
}],
svgPath: "M20,20 C30,10 30,30 40,20 C50,10 50,30 60,20 M60,20 C70,10 70,30 80,20 C90,10 90,30 100,20 M20,60 C30,50 30,70 40,60 C50,50 50,70 60,60 M60,60 C70,50 70,70 80,60 C90,50 90,70 100,60"
};
break;
case "op-amp":
schema = {
id: "op-amp",
name: "Operational Amplifier",
category: "integrated",
description: "A high-gain voltage amplifier with differential inputs",
defaultWidth: 80,
defaultHeight: 60,
ports: [
{
id: "in_positive",
x: 0,
y: 20,
type: "input",
label: "+"
},
{
id: "in_negative",
x: 0,
y: 40,
type: "input",
label: "-"
},
{
id: "output",
x: 80,
y: 30,
type: "output",
label: "Out"
},
{
id: "vcc",
x: 40,
y: 0,
type: "input",
label: "V+"
},
{
id: "vee",
x: 40,
y: 60,
type: "input",
label: "V-"
}
],
properties: [{
key: "gain",
label: "Open Loop Gain",
type: "number",
default: 1e5,
min: 1
}, {
key: "supply_voltage",
label: "Supply Voltage",
type: "number",
unit: "V",
default: 15,
min: 3
}],
svgPath: "M10,10 L10,50 L70,30 Z M40,0 L40,10 M40,50 L40,60"
};
break;
default:
schema = generateGenericComponent(missing);
break;
}
generatedSchemas.push(schema);
}
return generatedSchemas;
}
/**
* Generate a generic component schema based on component requirements
*/
function generateGenericComponent(requirement) {
const defaultPorts = requirement.suggestedPorts || [{
id: "left",
type: "inout"
}, {
id: "right",
type: "inout"
}];
const defaultProperties = requirement.suggestedProperties || [{
key: "value",
label: "Value",
type: "number",
default: 1
}];
return {
id: requirement.id,
name: requirement.name,
category: requirement.category,
description: requirement.description,
defaultWidth: 60,
defaultHeight: 30,
ports: defaultPorts.map((port, index) => ({
id: port.id,
x: index === 0 ? 0 : 60,
y: 15,
type: port.type,
label: port.label
})),
properties: defaultProperties.map((prop) => ({
key: prop.key,
label: prop.label,
type: prop.type,
default: prop.default,
unit: prop.unit
})),
svgPath: "M10,15 h40 M0,15 h10 M50,15 h10"
};
}
/**
* Generate a complete circuit with auto-generated missing components
*
* This is the main function that orchestrates the entire circuit generation process:
* 1. Analyzes requirements from description
* 2. Identifies missing components
* 3. Auto-generates missing component schemas
* 4. Registers them in the registry
* 5. Creates the complete circuit with proper connections
* 6. Returns the circuit ready for rendering
*
* @param description - Natural language description of the desired circuit
* @returns Complete circuit template with all components and connections
*/
function generateCompleteCircuit(description) {
const analysis = analyzeCircuitRequirements(description);
if (analysis.missingComponents.length > 0) {
const generatedSchemas = generateMissingComponents(analysis.missingComponents);
for (const schema of generatedSchemas) registerComponent(schema);
}
const components = [];
const layout = analysis.suggestedLayout;
for (const requirement of analysis.requiredComponents) {
const position = layout?.componentPositions[requirement.id] || {
x: 100,
y: 100
};
const schema = getComponentSchema(requirement.id);
if (schema) {
const props = {};
for (const prop of schema.properties) props[prop.key] = prop.default;
components.push({
id: requirement.id + "1",
type: requirement.id,
position,
props
});
}
}
const wires = generateIntelligentConnections(components, analysis.circuitType);
return {
id: analysis.circuitType.toLowerCase().replace(/\s+/g, "-"),
name: analysis.circuitType,
description: analysis.description,
category: "auto-generated",
components,
wires
};
}
/**
* Generate intelligent wire connections based on circuit type and electrical principles
*/
function generateIntelligentConnections(components, circuitType) {
const wires = [];
let wireCounter = 1;
const powerSource = components.find((c) => c.type === "battery" || c.type === "voltage-source");
const ground = components.find((c) => c.type === "ground");
const passiveComponents = components.filter((c) => c.type === "resistor" || c.type === "capacitor" || c.type === "inductor");
const activeComponents = components.filter((c) => c.type === "led" || c.type === "diode" || c.type.includes("transistor"));
if (!powerSource || !ground) return wires;
switch (circuitType) {
case "LCR Circuit":
if (passiveComponents.length >= 3) {
const inductor = passiveComponents.find((c) => c.type === "inductor");
const capacitor$1 = passiveComponents.find((c) => c.type === "capacitor");
const resistor$1 = passiveComponents.find((c) => c.type === "resistor");
if (inductor && capacitor$1 && resistor$1) {
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "positive"
},
to: {
componentId: inductor.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: inductor.id,
portId: "right"
},
to: {
componentId: capacitor$1.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: capacitor$1.id,
portId: "right"
},
to: {
componentId: resistor$1.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: resistor$1.id,
portId: "right"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "negative"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
}
}
break;
case "RC Circuit":
const resistor = passiveComponents.find((c) => c.type === "resistor");
const capacitor = passiveComponents.find((c) => c.type === "capacitor");
if (resistor && capacitor) {
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "positive"
},
to: {
componentId: resistor.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: resistor.id,
portId: "right"
},
to: {
componentId: capacitor.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: capacitor.id,
portId: "right"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "negative"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
}
break;
case "LED Circuit":
const currentLimitingResistor = passiveComponents.find((c) => c.type === "resistor");
const led = activeComponents.find((c) => c.type === "led");
if (currentLimitingResistor && led) {
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "positive"
},
to: {
componentId: currentLimitingResistor.id,
portId: "left"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: currentLimitingResistor.id,
portId: "right"
},
to: {
componentId: led.id,
portId: "anode"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: led.id,
portId: "cathode"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "negative"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
}
break;
default:
if (passiveComponents.length > 0) {
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "positive"
},
to: {
componentId: passiveComponents[0].id,
portId: "left"
}
});
for (let i = 0; i < passiveComponents.length - 1; i++) wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: passiveComponents[i].id,
portId: "right"
},
to: {
componentId: passiveComponents[i + 1].id,
portId: "left"
}
});
const lastComponent = passiveComponents[passiveComponents.length - 1];
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: lastComponent.id,
portId: "right"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
wires.push({
id: `wire${wireCounter++}`,
from: {
componentId: powerSource.id,
portId: "negative"
},
to: {
componentId: ground.id,
portId: "terminal"
}
});
}
break;
}
return wires;
}
//#endregion
//#region src/llm/validation.ts
/**
* Validate a circuit design with LLM-friendly output
*
* @param circuit - The circuit state to validate
* @returns Validation result with clear messages
*/
function validateCircuitDesign(circuit) {
const errors = [];
const warnings = [];
const suggestions = [];
for (const component of circuit.components) {
const schema = getComponentSchema(component.type);
if (!schema) {
errors.push(`Component type "${component.type}" (ID: ${component.id}) is not registered in the component registry.`);
continue;
}
for (const prop of schema.properties) {
const value = component.props[prop.key];
if (value === void 0 && prop.default === void 0) warnings.push(`Component "${component.id}" is missing required property "${prop.label}" (${prop.key}).`);
if (value !== void 0) switch (prop.type) {
case "number":
if (typeof value !== "number") errors.push(`Property "${prop.label}" of component "${component.id}" must be a number, got ${typeof value}.`);
else {
if (prop.min !== void 0 && value < prop.min) errors.push(`Property "${prop.label}" of component "${component.id}" must be at least ${prop.min}, got ${value}.`);
if (prop.max !== void 0 && value > prop.max) errors.push(`Property "${prop.label}" of component "${component.id}" must be at most ${prop.max}, got ${value}.`);
}
break;
case "boolean":
if (typeof value !== "boolean") errors.push(`Property "${prop.label}" of component "${component.id}" must be a boolean, got ${typeof value}.`);
break;
case "select":
if (prop.options && !prop.options.some((opt) => opt.value === value)) {
const validOptions = prop.options.map((opt) => opt.value).join(", ");
errors.push(`Property "${prop.label}" of component "${component.id}" must be one of: ${validOptions}, got ${value}.`);
}
break;
}
}
}
for (const wire of circuit.wires) {
const fromComponent = circuit.components.find((c) => c.id === wire.from.componentId);
const toComponent = circuit.components.find((c) => c.id === wire.to.componentId);
if (!fromComponent) {
errors.push(`Wire "${wire.id}" references non-existent source component "${wire.from.componentId}".`);
continue;
}
if (!toComponent) {
errors.push(`Wire "${wire.id}" references non-existent destination component "${wire.to.componentId}".`);
continue;
}
const fromSchema = getComponentSchema(fromComponent.type);
const toSchema = getComponentSchema(toComponent.type);
if (!fromSchema || !toSchema) continue;
const fromPort = fromSchema.ports.find((p) => p.id === wire.from.portId);
const toPort = toSchema.ports.find((p) => p.id === wire.to.portId);
if (!fromPort) errors.push(`Wire "${wire.id}" references non-existent port "${wire.from.portId}" on component "${fromComponent.id}" (${fromSchema.name}).`);
if (!toPort) errors.push(`Wire "${wire.id}" references non-existent port "${wire.to.portId}" on component "${toComponent.id}" (${toSchema.name}).`);
if (fromPort && toPort) {
if (fromPort.type === "input" && toPort.type === "input") warnings.push(`Wire "${wire.id}" connects two input ports, which may not be electrically valid.`);
if (fromPort.type === "output" && toPort.type === "output") warnings.push(`Wire "${wire.id}" connects two output ports, which may cause conflicts.`);
}
}
const powerSources = circuit.components.filter((comp) => {
const schema = getComponentSchema(comp.type);
return schema?.category === "power" || schema?.id === "battery" || schema?.id === "voltage-source";
});
const grounds = circuit.components.filter((comp) => {
const schema = getComponentSchema(comp.type);
return schema?.id === "ground";
});
if (powerSources.length === 0) warnings.push("Circuit has no power sources. Consider adding a battery or voltage source.");
if (grounds.length === 0) warnings.push("Circuit has no ground connection. This may cause issues in real circuits.");
if (powerSources.length > 1) warnings.push(`Circuit has ${powerSources.length} power sources. Ensure they are properly isolated or connected.`);
const connectedComponents = new Set();
for (const wire of circuit.wires) {
connectedComponents.add(wire.from.componentId);
connectedComponents.add(wire.to.componentId);
}
for (const component of circuit.components) if (!connectedComponents.has(component.id)) warnings.push(`Component "${component.id}" is not connected to any other components.`);
if (powerSources.length > 0 && grounds.length === 0) suggestions.push("Add a ground component to complete the circuit.");
if (circuit.components.length > 0 && circuit.wires.length === 0) suggestions.push("Add wire connections between components to create a functional circuit.");
const hasResistor = circuit.components.some((comp) => {
const schema = getComponentSchema(comp.type);
return schema?.id === "resistor";
});
const hasLED = circuit.components.some((comp) => {
const schema = getComponentSchema(comp.type);
return schema?.id === "led";
});
if (hasLED && !hasResistor && powerSources.length > 0) suggestions.push("Consider adding a current-limiting resistor in series with the LED to prevent damage.");
try {
const existingValidation = validateCircuit(circuit);
for (const issue of existingValidation) if (issue.type === "error") errors.push(issue.message);
else if (issue.type === "warning") warnings.push(issue.message);
} catch (error) {
errors.push(`Circuit validation failed: ${error instanceof Error ? error.message : "Unknown error"}`);
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions
};
}
/**
* Validate a single component instance
*
* @param component - The component instance to validate
* @returns Validation result
*/
function validateComponentInstance(component) {
const errors = [];
const warnings = [];
const suggestions = [];
const schema = getComponentSchema(component.type);
if (!schema) {
errors.push(`Component type "${component.type}" is not registered.`);
return {
isValid: false,
errors,
warnings,
suggestions
};
}
for (const prop of schema.properties) {
const value = component.props[prop.key];
if (value === void 0 && prop.default === void 0) warnings.push(`Missing property "${prop.label}" (${prop.key}).`);
}
if (typeof component.position.x !== "number" || typeof component.position.y !== "number") errors.push("Component position must have numeric x and y coordinates.");
if (!component.id || typeof component.id !== "string") errors.push("Component must have a valid string ID.");
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions
};
}
/**
* Validate a wire connection
*
* @param wire - The wire to validate
* @param components - Array of components in the circuit
* @returns Validation result
*/
function validateWireConnection(wire, components) {
const errors = [];
const warnings = [];
const suggestions = [];
const fromComponent = components.find((c) => c.id === wire.from.componentId);
const toComponent = components.find((c) => c.id === wire.to.componentId);
if (!fromComponent) errors.push(`Source component "${wire.from.componentId}" not found.`);
if (!toComponent) errors.push(`Destination component "${wire.to.componentId}" not found.`);
if (fromComponent && toComponent) {
const fromSchema = getComponentSchema(fromComponent.type);
const toSchema = getComponentSchema(toComponent.type);
if (fromSchema && toSchema) {
const fromPort = fromSchema.ports.find((p) => p.id === wire.from.portId);
const toPort = toSchema.ports.find((p) => p.id === wire.to.portId);
if (!fromPort) errors.push(`Port "${wire.from.portId}" not found on ${fromSchema.name}.`);
if (!toPort) errors.push(`Port "${wire.to.portId}" not found on ${toSchema.name}.`);
if (fromPort && toPort) {
if (fromPort.type === "input" && toPort.type === "input") warnings.push("Connecting two input ports may not be electrically valid.");
}
}
}
return {
isValid: errors.length === 0,
errors,
warnings,
suggestions
};
}
//#endregion
//#region src/llm/index.ts
var llm_exports = {};
__export(llm_exports, {
analyzeCircuitRequirements: () => analyzeCircuitRequirements,
describeCircuit: () => describeCircuit,
generateCircuitTemplate: () => generateCircuitTemplate,
generateCompleteCircuit: () => generateCompleteCircuit,
generateMissingComponents: () => generateMissingComponents,
getAPIHelp: () => getAPIHelp,
getAPIStatus: () => getAPIStatus,
getAllCategories: () => getAllCategories,
getComponentDetailedSummary: () => getComponentDetailedSummary,
getComponentDetails: () => getComponentDetails,
getQuickStart: () => getQuickStart,
getRegistryMetadata: () => getRegistryMetadata,
getRegistrySummary: () => getRegistrySummary,
listAvailableComponents: () => listAvailableComponents,
listComponentsByCategory: () => listComponentsByCategory,
searchComponents: () => searchComponents,
suggestConnections: () => suggestConnections,
validateCircuitDesign: () => validateCircuitDesign,
validateComponentInstance: () => validateComponentInstance,
validateWireConnection: () => validateWireConnection
});
/**
* Quick start function for LLMs to get an overview of available components
*
* @returns A comprehensive overview of the component registry
*/
function getQuickStart() {
const allComponents = listAvailableComponents();
const categories = getAllCategories();
const popularComponentIds = [
"resistor",
"capacitor",
"battery",
"led",
"ground",
"switch",
"diode",
"transistor-npn"
];
const popularComponents = popularComponentIds.map((id) => allComponents.find((comp) => comp.id === id)).filter((comp) => comp !== void 0).map((comp) => ({
id: comp.id,
name: comp.name,
category: comp.category,
description: comp.description
}));
const exampleUsage = `
// Example: Create a simple LED circuit
import { generateCircuitTemplate, validateCircuitDesign } from 'circuit-bricks/llm';
// Generate a basic LED circuit template
const circuit = generateCircuitTemplate('LED circuit with battery and