UNPKG

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
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