simple-circuit-engine 0.0.9 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -57,6 +57,7 @@ export declare abstract class AbstractCircuitController extends EventEmitter<Con
57
57
  get componentObject3Ds(): Map<UUID, THREE.Object3D>;
58
58
  get enodeObject3Ds(): Map<UUID, THREE.Object3D>;
59
59
  get wireObject3Ds(): Map<UUID, Line2>;
60
+ get visualContext(): VisualContext;
60
61
  protected get grid(): THREE.GridHelper | null;
61
62
  protected set grid(grid: THREE.GridHelper);
62
63
  /**
@@ -139,7 +140,7 @@ export declare abstract class AbstractCircuitController extends EventEmitter<Con
139
140
  /**
140
141
  * Get the current cursor position on the ground plane (y=0) in world coordinates
141
142
  * The position is clamped within the circuit grid boundaries but not snapped to grid
142
- * @param bound - Whether to constrain position within grid boundaries
143
+ * @param bound - Whether to constrain position within grid boundaries, default false
143
144
  */
144
145
  cursorGroundPlanePosition(bound?: boolean): THREE.Vector3;
145
146
  /**
@@ -201,138 +202,6 @@ export declare abstract class AbstractCircuitController extends EventEmitter<Con
201
202
  protected onResize(_width: number, _height: number): void;
202
203
  }
203
204
 
204
- /**
205
- * Tool for adding new components to the circuit
206
- * Provides ghost preview, grid snapping, and placement validation
207
- */
208
- export declare class AddComponentTool implements IEditingTool {
209
- readonly type: ToolType;
210
- private _controller;
211
- private _componentType;
212
- private _ghostPreview;
213
- private _previewPosition;
214
- private _previewRotation;
215
- private _hasOverlap;
216
- private _gridPositionMoveHandler;
217
- private _pointerDownHandler;
218
- private _wheelHandler;
219
- private _keyDownHandler;
220
- constructor(controller: CircuitController);
221
- /**
222
- * Called when tool becomes active (T010)
223
- * Attaches event listeners for hover and click interactions
224
- */
225
- onActivate(): void;
226
- /**
227
- * Called when tool is deactivated (T011)
228
- * Removes event listeners and cleans up preview objects
229
- */
230
- onDeactivate(): void;
231
- /**
232
- * Set the component type to place (T012)
233
- * Creates a new ghost preview for the selected component type
234
- *
235
- * @param type - Component type to place
236
- */
237
- setComponentType(type: ComponentType | null): void;
238
- /**
239
- * Cycle through available component types (for ctrl+scroll)
240
- * @param forward
241
- * @private
242
- */
243
- private cycleComponentTypes;
244
- /**
245
- * Create ghost preview for the current component type (T013)
246
- * Uses FactoryRegistry to create visual representation
247
- * @private
248
- */
249
- private _createGhostPreview;
250
- /**
251
- * Apply ghost effect to preview object (T014)
252
- * Makes the preview semi-transparent to indicate it's not yet placed
253
- *
254
- * @param object - Object3D to apply ghost effect to
255
- * @private
256
- */
257
- private _applyGhostEffect;
258
- /**
259
- * Dispose ghost preview and cleanup resources
260
- * @private
261
- */
262
- private _disposeGhostPreview;
263
- /**
264
- * Get preview objects to render in the scene (T015)
265
- * Returns the ghost preview if available
266
- *
267
- * @returns Array containing ghost preview object
268
- */
269
- getPreviewObjects(): THREE.Object3D[];
270
- /**
271
- * Check if preview overlaps with existing components (T021)
272
- * Uses THREE.Box3 bounding box collision detection
273
- *
274
- * @returns true if overlap detected, false otherwise
275
- * @private
276
- */
277
- private _checkOverlap;
278
- /**
279
- * Apply invalid placement visual effect (T022)
280
- * Sets red emissive color to indicate invalid placement
281
- *
282
- * @param object - Object3D to apply effect to
283
- * @private
284
- */
285
- private _applyInvalidEffect;
286
- /**
287
- * Remove invalid placement visual effect (T023)
288
- * Restores normal emissive values
289
- *
290
- * @param object - Object3D to restore
291
- * @private
292
- */
293
- private _removeInvalidEffect;
294
- /**
295
- * Handle gridPosition move events (T016, T024)
296
- * Updates preview position with grid snapping and checks for overlap
297
- *
298
- * @param worldPosition - Current cursor position in world coordinates (already grid-snapped)
299
- */
300
- handleGridPositionMove(worldPosition: THREE.Vector3): void;
301
- /**
302
- * Handle pointer down events for component placement or selection (T017, T019, T026, T027, T042)
303
- * - If clicking on existing component: select it
304
- * - If clicking empty space: place component at preview position
305
- *
306
- * @param worldPosition - Click position in world coordinates
307
- */
308
- handlePointerDown(worldPosition: THREE.Vector3): void;
309
- /**
310
- * Handle scroll events for rotation (User Story 3 - Phase 5)
311
- * Rotates preview by 90 degrees per scroll
312
- *
313
- * @param delta - Scroll delta (positive = scroll down, negative = scroll up)
314
- * @param ctrlKey
315
- */
316
- handleScroll(delta: number, ctrlKey: boolean): void;
317
- /**
318
- * Handle keyboard events for component deletion (T035-T039)
319
- * Deletes selected component when Delete or Backspace key is pressed
320
- *
321
- * @param event - Keyboard event
322
- */
323
- handleKeyDown(event: KeyboardEvent): void;
324
- /**
325
- * Get current cursor type based on tool state (T018, T043)
326
- * Returns cursor based on hover state:
327
- * - 'pointer' when hovering existing component
328
- * - 'not-allowed' when hovering over occupied space for placement
329
- * - 'crosshair' otherwise
330
- *
331
- * @returns Current cursor style
332
- */
333
- getCursorType(): CursorType;
334
- }
335
-
336
205
  /**
337
206
  * Visual factory for Battery components
338
207
  *
@@ -343,7 +212,8 @@ export declare class AddComponentTool implements IEditingTool {
343
212
  * - Component hitbox for raycasting
344
213
  */
345
214
  export declare class BatteryVisualFactory extends ComponentVisualFactoryBase {
346
- createVisual(component: Component): THREE.Object3D;
215
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
216
+ private createPinsVisual;
347
217
  }
348
218
 
349
219
  /**
@@ -369,21 +239,21 @@ declare class BehaviorRegistry {
369
239
  * @throws TypeError if behavior is null/undefined or componentType is empty
370
240
  * @returns The registry instance for chaining
371
241
  */
372
- register(behavior: ComponentBehavior): BehaviorRegistry;
242
+ register(behavior: IComponentBehavior): BehaviorRegistry;
373
243
  /**
374
244
  * Register multiple behaviors at once.
375
245
  * Convenience method for bulk registration.
376
246
  *
377
247
  * @param behaviors - Array of behaviors to register
378
248
  */
379
- registerAll(behaviors: ComponentBehavior[]): void;
249
+ registerAll(behaviors: IComponentBehavior[]): void;
380
250
  /**
381
251
  * Get the behavior for a component type.
382
252
  *
383
253
  * @param componentType - Type identifier (e.g., "battery", "led")
384
254
  * @returns The registered behavior, or undefined if not found
385
255
  */
386
- get(componentType: string): ComponentBehavior | undefined;
256
+ get(componentType: string): IComponentBehavior | undefined;
387
257
  /**
388
258
  * Check if a behavior is registered for a component type.
389
259
  *
@@ -417,26 +287,10 @@ declare class BehaviorRegistry {
417
287
  }
418
288
 
419
289
  /**
420
- * Result returned by component behavior evaluation.
421
- * Describes state changes and future events to schedule.
422
- *
423
- * @public
290
+ * Sentinel value representing the "Branching Point" pseudo-component entry.
291
+ * When selected, BuildTool creates a branching point instead of a component.
424
292
  */
425
- declare interface BehaviorResult {
426
- /**
427
- * Updated component state
428
- */
429
- readonly componentState: ComponentState;
430
- /**
431
- * boolean indicating
432
- * if the component state has changed (or events scheduled ?)
433
- */
434
- hasChanged: boolean;
435
- /**
436
- * Events to schedule for future ticks (e.g., delayed transitions).
437
- */
438
- readonly scheduledEvents: ReadonlyArray<ScheduledEvent>;
439
- }
293
+ export declare const BRANCHING_POINT_SENTINEL: "__branching_point__";
440
294
 
441
295
  /**
442
296
  * Factory for creating branching point visuals.
@@ -507,11 +361,16 @@ export declare class BuildTool implements IEditingTool {
507
361
  private _controller;
508
362
  private mode;
509
363
  private lastCancelledOp;
364
+ private lastOperationCompletedTs;
510
365
  private wireCreationState;
511
366
  private wireDragState;
512
367
  private componentDragState;
513
368
  private bpDragState;
514
369
  private clipboard;
370
+ private pickerWidget;
371
+ private ghostPreview;
372
+ private hasOverlap;
373
+ private pickerSelection;
515
374
  /**
516
375
  * Construct a new BuildTool instance
517
376
  * @param controller - The circuit scene controllerType instance
@@ -715,6 +574,52 @@ export declare class BuildTool implements IEditingTool {
715
574
  * @param event - Mouse event for screen position
716
575
  */
717
576
  private openConfigPanel;
577
+ /**
578
+ * Enter add_component mode: open the picker widget and listen for cursor movement
579
+ * @param event - Mouse event (used for widget positioning)
580
+ */
581
+ private enterAddComponentMode;
582
+ /**
583
+ * Exit add_component mode: close widget, dispose ghost, return to idle
584
+ */
585
+ private exitAddComponentMode;
586
+ /**
587
+ * Called when user selects or deselects an item in the picker widget
588
+ */
589
+ private onPickerSelectionChange;
590
+ /**
591
+ * Place the currently selected item (component or branching point) at cursor position
592
+ */
593
+ private placeSelectedItem;
594
+ /**
595
+ * Update ghost preview position and overlap check during add_component mode
596
+ */
597
+ private updateAddComponentPreview;
598
+ /**
599
+ * Create ghost preview for the selected item
600
+ * @param selection - Component type or branching point sentinel
601
+ */
602
+ private createGhostPreview;
603
+ /**
604
+ * Dispose ghost preview and cleanup resources
605
+ */
606
+ private disposeGhostPreview;
607
+ /**
608
+ * Apply semi-transparent ghost effect to preview object
609
+ */
610
+ private applyGhostEffect;
611
+ /**
612
+ * Check if ghost preview overlaps with existing components
613
+ */
614
+ private checkGhostOverlap;
615
+ /**
616
+ * Apply red emissive to indicate invalid placement
617
+ */
618
+ private applyInvalidEffect;
619
+ /**
620
+ * Remove red emissive invalid placement effect
621
+ */
622
+ private removeInvalidEffect;
718
623
  }
719
624
 
720
625
  /**
@@ -861,7 +766,7 @@ declare class Circuit {
861
766
  /**
862
767
  * Create a new empty circuit.
863
768
  */
864
- constructor(name?: string);
769
+ constructor(options: CircuitOptions);
865
770
  get name(): string;
866
771
  set name(value: string);
867
772
  /**
@@ -892,6 +797,13 @@ declare class Circuit {
892
797
  * ```
893
798
  */
894
799
  addComponent(type: ComponentType, position: Position, rotation: Rotation, config?: Map<string, string> | undefined): Component;
800
+ /**
801
+ * Resolve and update the transitionSpan config for a component
802
+ * if automatic resolution returns undefined nothing is done on the component
803
+ *
804
+ * @param component - Component to resolve transitionSpan for
805
+ */
806
+ resolveTransitionSpan(component: Component): void;
895
807
  /**
896
808
  * Remove a component from the circuit.
897
809
  *
@@ -1174,6 +1086,7 @@ declare class Circuit {
1174
1086
  * iterate through all components, enodes and wires positions to get the size that allows to enclose all elements.
1175
1087
  * @param margin - optional margin to add to the size
1176
1088
  * @returns size that allows to enclose all elements plus margin
1089
+ * @todo if calls to this method at each build operation ends causes slowness see to optimize by restricting checked elements
1177
1090
  */
1178
1091
  getEnclosingSize(margin?: number): number;
1179
1092
  /**
@@ -1187,12 +1100,7 @@ declare class Circuit {
1187
1100
  * localStorage.setItem('my-circuit', JSON.stringify(json));
1188
1101
  * ```
1189
1102
  */
1190
- toJSON(): {
1191
- metadata: object;
1192
- components: object[];
1193
- enodes: object[];
1194
- wires: object[];
1195
- };
1103
+ toJSON(): ICircuit;
1196
1104
  /**
1197
1105
  * Deserialize circuit from JSON.
1198
1106
  *
@@ -1207,12 +1115,7 @@ declare class Circuit {
1207
1115
  * const circuit = Circuit.fromJSON(json);
1208
1116
  * ```
1209
1117
  */
1210
- static fromJSON(json: {
1211
- metadata: ICircuitMetadata;
1212
- components: object[];
1213
- enodes: object[];
1214
- wires?: object[];
1215
- }): Circuit;
1118
+ static fromJSON(json: ICircuit): Circuit;
1216
1119
  }
1217
1120
 
1218
1121
  /**
@@ -1297,15 +1200,6 @@ export declare class CircuitController extends AbstractCircuitController {
1297
1200
  */
1298
1201
  toggleTool(toolType: ToolType): void;
1299
1202
  deactivateTool(toolType: ToolType): void;
1300
- /**
1301
- * Get the list of available component types for the AddComponent tool
1302
- */
1303
- getAvailableComponentTypes(): ComponentType[];
1304
- /**
1305
- * Set the component type for the AddComponent tool
1306
- * @param componentType
1307
- */
1308
- setAddComponentType(componentType: ComponentType | null): void;
1309
1203
  /**
1310
1204
  * Get the currently active tool (FR-028)
1311
1205
  *
@@ -1380,12 +1274,12 @@ export declare class CircuitController extends AbstractCircuitController {
1380
1274
  *
1381
1275
  * @param type - Component type to add
1382
1276
  * @param worldPosition - Position in 3D world coordinates (x, z)
1383
- * @param rotation - 3D world rotation
1277
+ * @param rotation - 3D world rotation, if left null the default componentType rotation is applied
1384
1278
  * @param config - Optional configuration map for the component
1385
1279
  * @param pinSources - Optional array of source types for the component pins
1386
1280
  * @returns The created Component
1387
1281
  */
1388
- addComponent(type: ComponentType, worldPosition: THREE.Vector3, rotation: Euler, config?: Map<string, string> | undefined, pinSources?: Array<ENodeSourceType | undefined | null> | undefined): Component;
1282
+ addComponent(type: ComponentType, worldPosition: THREE.Vector3, rotation: Euler | null, config?: Map<string, string> | undefined, pinSources?: Array<ENodeSourceType | undefined | null> | undefined): Component;
1389
1283
  /**
1390
1284
  * edit component config and update visuals if necessary
1391
1285
  *
@@ -1690,39 +1584,42 @@ export declare interface CircuitEngineEventMap extends ControllerEventMap {
1690
1584
  modeChanged: ModeChangedEvent;
1691
1585
  }
1692
1586
 
1693
- /**
1694
- * Circuit metadata placeholder
1695
- */
1696
- declare class CircuitMetadata {
1697
- name: string;
1587
+ declare class CircuitMetadata implements ICircuitMetadata {
1588
+ version: string;
1589
+ options: CircuitOptions;
1698
1590
  size: number;
1699
1591
  divisions: number;
1700
1592
  cameraOptions: CameraOptions;
1701
1593
  /**
1702
1594
  * Create a new CircuitMetadata holding general information about the Circuit.
1703
1595
  *
1704
- * @param name - Name of the circuit
1596
+ * @param version - Circuit version
1597
+ * @param options - Circuit options
1705
1598
  * @param size - Size of the circuit grid
1706
1599
  * @param divisions - Divisions in the circuit grid
1707
1600
  * @param cameraOptions - Camera Options at startup
1708
1601
  * @throws {TypeError} If size or divisions are not integers
1709
1602
  */
1710
- constructor(name: string, size: number, divisions: number, cameraOptions: CameraOptions);
1711
- toJSON(): {
1712
- name: string;
1713
- size: number;
1714
- divisions: number;
1715
- cameraOptions: ICameraOptions;
1716
- };
1717
- static fromJSON(json: {
1718
- name: string;
1719
- size: number;
1720
- divisions: number;
1721
- cameraOptions: ICameraOptions;
1722
- }): CircuitMetadata;
1603
+ constructor(version: string, options: CircuitOptions, size: number, divisions: number, cameraOptions: CameraOptions);
1604
+ toJSON(): ICircuitMetadata;
1605
+ static fromJSON(json: ICircuitMetadata): CircuitMetadata;
1723
1606
  toString(): string;
1724
1607
  }
1725
1608
 
1609
+ declare class CircuitOptions implements ICircuitOptions {
1610
+ name: string;
1611
+ defaultLogicFamily: LogicFamily;
1612
+ /**
1613
+ * Create new circuit options.
1614
+ *
1615
+ * @param name - Circuit name (default: Untitled Circuit)
1616
+ * @param defaultLogicFamily - Circuit default logic family (default: CMOS1)
1617
+ */
1618
+ constructor(name?: string, defaultLogicFamily?: LogicFamily);
1619
+ toJSON(): ICircuitOptions;
1620
+ static fromJSON(json: ICircuitOptions): CircuitOptions;
1621
+ }
1622
+
1726
1623
  /**
1727
1624
  * Simulation Circuit Runner Controller Implementation
1728
1625
  *
@@ -2138,10 +2035,14 @@ declare class Component {
2138
2035
  * Configuration parameters for this component instance.
2139
2036
  *
2140
2037
  * This map holds key-value pairs representing configurable settings
2141
- * The available configuration keys depend on the component type see ComponentTypeMetadata for details.
2038
+ * The available configuration keys depend on the component type see IComponentTypeMetadata for details.
2142
2039
  *
2143
2040
  */
2144
2041
  config: Map<string, string>;
2042
+ /**
2043
+ * allow to flag a component as non editable (feature to implement)
2044
+ */
2045
+ editable: boolean;
2145
2046
  /**
2146
2047
  * Create a new component.
2147
2048
  *
@@ -2154,6 +2055,7 @@ declare class Component {
2154
2055
  * @param rotation - Orientation angle (integer degrees)
2155
2056
  * @param pins - Array of pin ENode UUIDs
2156
2057
  *
2058
+ * @param editable
2157
2059
  * @example
2158
2060
  * ```typescript
2159
2061
  * // Usually created via Circuit:
@@ -2172,7 +2074,7 @@ declare class Component {
2172
2074
  * );
2173
2075
  * ```
2174
2076
  */
2175
- constructor(type: ComponentType, position: Position, rotation: Rotation, pins: ReadonlyArray<UUID>);
2077
+ constructor(type: ComponentType, position: Position, rotation: Rotation, pins: ReadonlyArray<UUID>, editable?: boolean);
2176
2078
  getPinLabel(pinId: UUID): string | undefined;
2177
2079
  setAllParameters(config: Map<string, string>): void;
2178
2080
  setParameter(key: string, value: string): void;
@@ -2218,117 +2120,23 @@ declare class Component {
2218
2120
  * // }
2219
2121
  * ```
2220
2122
  */
2221
- toJSON(): {
2222
- id: UUID;
2223
- type: ComponentType;
2224
- position: {
2225
- x: number;
2226
- y: number;
2227
- };
2228
- rotation: number;
2229
- pins: UUID[];
2230
- config: {
2231
- [key: string]: string;
2232
- };
2233
- };
2123
+ toJSON(): IComponent;
2234
2124
  /**
2235
2125
  * Deserialize component from JSON.
2236
2126
  *
2237
2127
  * @param json - Component data
2238
2128
  * @returns Component instance
2239
2129
  *
2240
- * @example
2241
- * ```typescript
2242
- * const json = {
2243
- * id: "550e8400-...",
2244
- * type: "battery",
2245
- * position: { x: 10, y: 20 },
2246
- * rotation: 90,
2247
- * pins: ['1b4f6f3c-ce ....', '2c5e7g4d-df ...'],
2248
- * config: { "voltage": "5V" }
2249
- * };
2250
- *
2251
- * const component = Component.fromJSON(json);
2252
- * console.log(component.position.x); // 10
2253
- * ```
2254
2130
  */
2255
- static fromJSON(json: {
2256
- id: UUID;
2257
- type: ComponentType;
2258
- position: {
2259
- x: number;
2260
- y: number;
2261
- };
2262
- rotation: number;
2263
- pins: UUID[];
2264
- config: {
2265
- [key: string]: string;
2266
- };
2267
- }): Component;
2131
+ static fromJSON(json: IComponent): Component;
2268
2132
  }
2269
2133
 
2270
2134
  /**
2271
- * Component behavior interface for registry-based extensibility.
2272
- * Each component type (Battery, LED, Switch, etc.) implements this interface.
2273
- *
2274
- * Behaviors are stateless - all state is stored in ComponentState and SimulationState.
2275
- * The behavior's job is to compute new states based on current inputs and component state.
2276
- *
2277
- * @public
2135
+ * Describes a named group of component types
2278
2136
  */
2279
- declare interface ComponentBehavior {
2280
- /**
2281
- * Component type this behavior handles (e.g., "battery", "led", "switch").
2282
- * Used as the key in BehaviorRegistry.
2283
- */
2284
- readonly componentType: ComponentType;
2285
- /**
2286
- * Create initial state for a component instance.
2287
- * Called when simulation is initialized.
2288
- * Initial state may use component satic configuration (e.g., initial switch position).
2289
- *
2290
- * @param component - The component to initialize
2291
- * @returns Initial ComponentState for this component
2292
- */
2293
- createInitialState(component: Component): ComponentState;
2294
- /**
2295
- * Determine if conductivity is allowed between two pins of the component.
2296
- * Called during simulation when evaluating electrical connectivity.
2297
- * @param component
2298
- * @param state current component state
2299
- * @param conductivityType
2300
- * @param pinId
2301
- * @param otherPinId
2302
- */
2303
- allowConductivity(component: Component, state: ComponentState, conductivityType: ENodeSourceType, pinId: string, otherPinId: string): boolean;
2304
- /**
2305
- * Define component state change in response to its pins state change (after propagateConductivity)
2306
- *
2307
- * @param component - The component being evaluated
2308
- * @param state - component state prior to this evaluation
2309
- * @param nodeStates - Current electrical states of all ENodes in the simulation
2310
- * @param targetTick - target tick
2311
- * @returns Result containing updated state and scheduled events
2312
- */
2313
- onPinsChange(component: Component, state: ComponentState, nodeStates: ReadonlyMap<UUID, NodeElectricalState>, targetTick: number): BehaviorResult;
2314
- /**
2315
- * Define component state change in response to a User command being received
2316
- *
2317
- * @param component - The component being evaluated
2318
- * @param state - component state prior to this evaluation
2319
- * @param command - UserCommand to process
2320
- * @returns Result containing updated state and scheduled events
2321
- */
2322
- onUserCommand(component: Component, state: ComponentState, command: UserCommand): BehaviorResult;
2323
- /**
2324
- * Define component state change in response to a ScheduledEvent firing at ready
2325
- *
2326
- * @param component - The component being evaluated
2327
- * @param state - component state prior to this evaluation
2328
- * @param event - firing ScheduledEvent to process
2329
- * @returns Result containing updated state and scheduled events
2330
- */
2331
- onEventFiring(component: Component, state: ComponentState, event: ScheduledEvent): BehaviorResult;
2137
+ export declare interface ComponentGroup {
2138
+ readonly id: string;
2139
+ readonly label: string;
2332
2140
  }
2333
2141
 
2334
2142
  /**
@@ -2340,6 +2148,79 @@ export declare interface ComponentHitboxUserData {
2340
2148
  componentType: ComponentType;
2341
2149
  }
2342
2150
 
2151
+ /**
2152
+ * Persisted widget state (survives open/close cycles within a session).
2153
+ */
2154
+ export declare interface ComponentPickerState {
2155
+ selectedGroupId: string;
2156
+ selectedItem: PickerSelection | null;
2157
+ widgetWidth: number;
2158
+ widgetHeight: number;
2159
+ }
2160
+
2161
+ /**
2162
+ * DOM overlay widget for selecting components from grouped registry.
2163
+ *
2164
+ * Features:
2165
+ * - Group dropdown for filtering component types
2166
+ * - Scrollable item list with selection highlight
2167
+ * - Draggable header bar
2168
+ * - Resizable via CSS resize
2169
+ * - State persistence across open/close (group, selection, size)
2170
+ */
2171
+ export declare class ComponentPickerWidget {
2172
+ private readonly registry;
2173
+ private container;
2174
+ private groupDropdown;
2175
+ private itemList;
2176
+ private state;
2177
+ private readonly onSelectionChange;
2178
+ private readonly onClose;
2179
+ private escapeHandler;
2180
+ private dragOffset;
2181
+ private dragMoveHandler;
2182
+ private dragEndHandler;
2183
+ /**
2184
+ * @param registry - Grouped factory registry providing groups and component types
2185
+ * @param onSelectionChange - Called when user selects or deselects an item
2186
+ * @param onClose - Called when user closes the widget (close button or Escape)
2187
+ */
2188
+ constructor(registry: IGroupedFactoryRegistry, onSelectionChange: (selection: PickerSelection | null) => void, onClose: () => void);
2189
+ /** Whether the widget is currently open and visible */
2190
+ get isOpen(): boolean;
2191
+ /** The currently selected item (persists across open/close) */
2192
+ get currentSelection(): PickerSelection | null;
2193
+ /**
2194
+ * Open the widget at the given screen position.
2195
+ * If already open, repositions to the new location.
2196
+ *
2197
+ * @param screenPosition - Screen coordinates (typically from mouse event)
2198
+ */
2199
+ open(screenPosition: {
2200
+ x: number;
2201
+ y: number;
2202
+ }): void;
2203
+ /**
2204
+ * Close the widget and remove DOM.
2205
+ * Preserves state (group, selection, size) for next open.
2206
+ */
2207
+ close(): void;
2208
+ /**
2209
+ * Full cleanup including state reset.
2210
+ */
2211
+ dispose(): void;
2212
+ private createDOM;
2213
+ private renderItemList;
2214
+ private createItemElement;
2215
+ private selectItem;
2216
+ private positionContainer;
2217
+ private startDrag;
2218
+ private onDragMove;
2219
+ private endDrag;
2220
+ private registerEventListeners;
2221
+ private removeEventListeners;
2222
+ }
2223
+
2343
2224
  /**
2344
2225
  * Base class for component simulation state.
2345
2226
  * Extended by specific component types (BatteryState, LEDState, etc.)
@@ -2374,29 +2255,31 @@ declare abstract class ComponentState {
2374
2255
  }
2375
2256
 
2376
2257
  /**
2377
- * Enumeration of available component types.
2258
+ * Enumeration of ALL available component types.
2378
2259
  *
2379
2260
  * Each component type represents a specific electrical element that can be
2380
2261
  * placed in a circuit (battery, LED, transistor, etc.).
2381
- *
2382
- * @example
2383
- * ```typescript
2384
- * const type = ComponentType.Battery;
2385
- * const metadata = COMPONENT_TYPE_METADATA[type];
2386
- * console.log(metadata.name); // "Battery"
2387
- * console.log(metadata.pins); // Map([["cathode", ENodeSourceType.Voltage], ["anode", ENodeSourceType.Current]])
2388
- * ```
2389
2262
  */
2390
2263
  declare enum ComponentType {
2264
+ Cube = "cube",// no pins component for testing purposes mainly
2265
+ Label = "label",// decorative text label with no pins
2391
2266
  Battery = "battery",
2392
2267
  Switch = "switch",
2268
+ DoubleThrowSwitch = "doubleThrowSwitch",
2393
2269
  Lightbulb = "lightbulb",
2394
2270
  Relay = "relay",
2395
- Transistor = "transistor",
2396
2271
  SmallLED = "smallLED",
2397
2272
  RectangleLED = "rectangleLED",
2398
- Cube = "cube",// no pins component for testing purposes mainly
2399
- Label = "label"
2273
+ Inverter = "inverter",
2274
+ NandGate = "nandGate",
2275
+ Nand4Gate = "nand4Gate",
2276
+ Nand8Gate = "nand8Gate",
2277
+ NorGate = "norGate",
2278
+ Nor4Gate = "nor4Gate",
2279
+ Nor8Gate = "nor8Gate",
2280
+ XorGate = "xorGate",
2281
+ Xor4Gate = "xor4Gate",
2282
+ Xor8Gate = "xor8Gate"
2400
2283
  }
2401
2284
 
2402
2285
  /**
@@ -2420,7 +2303,7 @@ declare enum ComponentType {
2420
2303
  * @example
2421
2304
  * ```typescript
2422
2305
  * export class BatteryVisualFactory extends ComponentVisualFactoryBase {
2423
- * createVisual(component: Component): THREE.Object3D {
2306
+ * createVisual(component: Component, context?: VisualContext): THREE.Object3D {
2424
2307
  * const group = new THREE.Group();
2425
2308
  * // ... create battery-specific visual
2426
2309
  * return group;
@@ -2438,11 +2321,12 @@ declare abstract class ComponentVisualFactoryBase implements IComponentVisualFac
2438
2321
  protected static readonly DEFAULT_SELECTION_COLOR = 16746496;
2439
2322
  /** Default selection emissive intensity (higher than hover) */
2440
2323
  protected static readonly DEFAULT_SELECTION_INTENSITY = 0.8;
2324
+ defaultRotation(): number;
2441
2325
  /**
2442
2326
  * Create the Three.js visual representation for a component
2443
2327
  * Must be implemented by subclasses
2444
2328
  */
2445
- abstract createVisual(component: Component): THREE.Object3D;
2329
+ abstract createVisual(component: Component, context: VisualContext): THREE.Object3D;
2446
2330
  /**
2447
2331
  * By default no visual configuration-based updates is needed
2448
2332
  */
@@ -2479,6 +2363,10 @@ declare abstract class ComponentVisualFactoryBase implements IComponentVisualFac
2479
2363
  * @param object3D - The component's root Three.js object
2480
2364
  */
2481
2365
  removeSelection(object3D: THREE.Object3D): void;
2366
+ /**
2367
+ * @private
2368
+ */
2369
+ private pointPinGroupToward;
2482
2370
  /**
2483
2371
  * Create a pin group with hitbox and visual sphere
2484
2372
  *
@@ -2487,19 +2375,36 @@ declare abstract class ComponentVisualFactoryBase implements IComponentVisualFac
2487
2375
  * - Hemisphere visual (blue sphere)
2488
2376
  * - Hover callback in userData
2489
2377
  *
2490
- * @param componentId - UUID of the parent component
2491
- * @param pinId - UUID of this pin/enode
2492
- * @param label - Human-readable label (e.g., 'input', 'output', 'cathode')
2493
- * @param sourceType - Optional source type (voltage/current) : if provided this pin will be locked to that type
2378
+ * @param node - ENode to create as visual pin
2379
+ * @param pointsTo - rotate pin to make it point the wanted direction
2380
+ * @param visualRotation - if set rotate the visual of the pin to adjust display without affecting hitbox
2494
2381
  * @returns THREE.Group configured as pin group
2495
2382
  */
2496
- protected createPinGroup(componentId: string, pinId: string, label: string, sourceType?: ENodeSourceType | null): THREE.Group;
2383
+ protected createPinGroup(node: ENode, pointsTo?: Direction2D, visualRotation?: THREE.Euler | null): THREE.Group;
2384
+ /**
2385
+ * Utility to create semi spheres visually closing pins
2386
+ * @param pinGroup
2387
+ * @param material
2388
+ * @protected
2389
+ */
2390
+ protected createPinCounterpart(pinGroup: THREE.Group, material: THREE.MeshStandardMaterial): THREE.Mesh | null;
2497
2391
  /**
2498
2392
  * Find pin group by label within a component Object3D
2499
2393
  * @param object3D
2500
2394
  * @param label
2501
2395
  */
2502
2396
  findPinGroup(object3D: THREE.Object3D, label: string): THREE.Group | null;
2397
+ /**
2398
+ * fin pin inner visual from within its pin group
2399
+ * @param pinGroup
2400
+ */
2401
+ findPinVisualFromGroup(pinGroup: THREE.Group): THREE.Mesh | null;
2402
+ /**
2403
+ * Find pin group visual by label within a component Object3D
2404
+ * @param object3D
2405
+ * @param label
2406
+ */
2407
+ findPinVisual(object3D: THREE.Object3D, label: string): THREE.Mesh | null;
2503
2408
  /**
2504
2409
  * Create component hitbox mesh
2505
2410
  *
@@ -2517,20 +2422,20 @@ declare abstract class ComponentVisualFactoryBase implements IComponentVisualFac
2517
2422
  protected pinColorForSourceType(sourceType: ENodeSourceType | null): number;
2518
2423
  protected pinColorForElectricalState(state: 'current' | 'voltage' | 'vc' | 'idle'): number;
2519
2424
  /**
2520
- * Updates the visual color of a component pin based on its source type.
2425
+ * Updates the visual color of a component pin based on its sourceType type.
2521
2426
  *
2522
- * This method changes the pin's material color to reflect the source type:
2523
- * - null/undefined: bronze (default pin color)
2427
+ * This method changes the pin's material color to reflect the sourceType type:
2428
+ * - null/undefined: copper (default pin color)
2524
2429
  * - Voltage: red
2525
2430
  * - Current: blue
2526
2431
  *
2527
2432
  * @param pinGroup - The THREE.Group containing the pin visual (created by createPinGroup)
2528
- * @param sourceType - The new source type (null for no source)
2433
+ * @param sourceType - The new sourceType (null for no sourceType)
2529
2434
  *
2530
2435
  * @remarks
2531
2436
  * - Searches for the child mesh with userData.type === 'enode'
2532
2437
  * - Updates both color and emissive properties for visual consistency
2533
- * - If sourceType is null/undefined, restores default bronze pin color
2438
+ * - If sourceType is null/undefined, restores default copper pin color
2534
2439
  * - Color scheme matches BranchingPointVisualFactory for consistency
2535
2440
  */
2536
2441
  updatePinSourceType(pinGroup: THREE.Object3D, sourceType: ENodeSourceType | null): void;
@@ -2554,7 +2459,7 @@ declare abstract class ComponentVisualFactoryBase implements IComponentVisualFac
2554
2459
  *
2555
2460
  * Override in subclasses that have configurable options.
2556
2461
  */
2557
- getConfigFormDefinition(): ConfigFormDefinition | null;
2462
+ getConfigFormDefinition(_config?: Map<string, string>): ConfigFormDefinition | null;
2558
2463
  /**
2559
2464
  * Map core config to form data (default: identity mapping)
2560
2465
  *
@@ -2592,6 +2497,8 @@ export declare interface ConfigFieldDefinition {
2592
2497
  max?: number;
2593
2498
  /** Step increment for number type */
2594
2499
  step?: number;
2500
+ /** Whether this field is read-only in the form */
2501
+ disabled?: boolean;
2595
2502
  }
2596
2503
 
2597
2504
  /**
@@ -2610,7 +2517,7 @@ export declare type ControllerCallback<T = any> = (payload: T) => void;
2610
2517
  /**
2611
2518
  * Supported scene controllerType event types (includes tool system events)
2612
2519
  */
2613
- export declare type ControllerEvent = 'ready' | 'error' | 'circuitLoaded' | 'circuitCleared' | 'gridPositionMove' | 'hover' | 'unhover' | 'select' | 'deselect' | 'toolActivated' | 'toolDeactivated' | 'toolOperationStarted' | 'toolOperationCompleted' | 'toolOperationCancelled' | 'toolValidationError' | 'addComponentTypeChanged' | 'cursorChangeRequested' | 'circuitElementAction' | 'circuitMetadataEdition' | 'selectionChange' | 'simulationPlayed' | 'simulationPaused' | 'simulationStepped' | 'simulationTick' | 'simulationUserCommand' | 'simulationStopped' | 'simulationSpeedChanged';
2520
+ export declare type ControllerEvent = 'ready' | 'error' | 'circuitLoaded' | 'circuitCleared' | 'gridPositionMove' | 'hover' | 'unhover' | 'select' | 'deselect' | 'toolActivated' | 'toolDeactivated' | 'toolOperationStarted' | 'toolOperationCompleted' | 'toolOperationCancelled' | 'toolValidationError' | 'cursorChangeRequested' | 'circuitElementAction' | 'circuitMetadataEdition' | 'selectionChange' | 'simulationPlayed' | 'simulationPaused' | 'simulationStepped' | 'simulationTick' | 'simulationUserCommand' | 'simulationStopped' | 'simulationSpeedChanged';
2614
2521
 
2615
2522
  /**
2616
2523
  * Event payload map for type-safe event emission
@@ -2688,9 +2595,6 @@ export declare interface ControllerEventMap {
2688
2595
  mode: unknown;
2689
2596
  errorMessage: string;
2690
2597
  };
2691
- addComponentTypeChanged: {
2692
- componentType: ComponentType | null;
2693
- };
2694
2598
  cursorChangeRequested: {
2695
2599
  cursorType: CursorType;
2696
2600
  };
@@ -2719,7 +2623,7 @@ export declare interface ControllerEventMap {
2719
2623
  tick: number;
2720
2624
  dirty: unknown;
2721
2625
  };
2722
- simulationUserCommand: UserCommand;
2626
+ simulationUserCommand: IUserCommand;
2723
2627
  simulationStopped: {
2724
2628
  tick: number;
2725
2629
  };
@@ -2809,30 +2713,103 @@ export declare class DefaultVisualFactory extends ComponentVisualFactoryBase {
2809
2713
  }
2810
2714
 
2811
2715
  /**
2812
- * Operating mode of the CircuitEngine
2813
- * - 'edit': Static circuit editing mode (tools, selection, manipulation)
2814
- * - 'simulation': Live simulation mode (playback, animation, user commands)
2716
+ * convenience to control standard rotations of meshes on X-Z plan
2815
2717
  */
2816
- export declare type EngineMode = 'edit' | 'simulation';
2718
+ declare type Direction2D = 'right' | 'bottom' | 'left' | 'top';
2817
2719
 
2818
2720
  /**
2819
- * Configuration options for CircuitEngine initialization
2721
+ * Visual factory for Double Switch components
2722
+ *
2723
+ * Creates:
2724
+ * - 2 * Input pole (sphere)
2725
+ * - Output pole (box)
2726
+ * - Contactor (cylinder, rotatable for animation)
2727
+ * - Input pin group
2728
+ * - Output pin group
2729
+ * - Component hitbox for raycasting
2730
+ *
2731
+ * Animation:
2732
+ * - Rotates contactor based on open/closed state
2820
2733
  */
2821
- export declare interface EngineOptions {
2822
- /**
2823
- * Initial operating mode
2824
- * @default 'edit'
2825
- */
2826
- initialMode?: EngineMode;
2827
- /**
2828
- * Controller options
2734
+ export declare class DoubleThrowSwitchVisualFactory extends ComponentVisualFactoryBase {
2735
+ /** Rotation for switch to input 1 */
2736
+ private readonly INPUT1_ROTATION;
2737
+ /** Rotation for switch toggling */
2738
+ private readonly INTERMEDIATE_ROTATION;
2739
+ /** Rotation for switch to input 2 */
2740
+ private readonly INPUT2_ROTATION;
2741
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
2742
+ private createPinsVisual;
2743
+ /**
2744
+ * Get config form definition for Double Switch
2745
+ *
2746
+ * @returns Form definition with initialState boolean field
2829
2747
  */
2830
- controllerOptions?: ControllerOptions;
2748
+ getConfigFormDefinition(): ConfigFormDefinition | null;
2749
+ /**
2750
+ * Map core config to form data (T024)
2751
+ * Converts "input1"/"input2" strings to boolean
2752
+ *
2753
+ * @param config - Core component config
2754
+ * @returns Form data with boolean initialState
2755
+ */
2756
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
2757
+ /**
2758
+ * Map form data to core config (T024)
2759
+ * Converts boolean to "input1"/"input2" strings
2760
+ *
2761
+ * @param formData - Form data with boolean initialState
2762
+ * @returns Core config with string initialState
2763
+ */
2764
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
2765
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
2766
+ /**
2767
+ * Update switch animation based on simulation state
2768
+ *
2769
+ * @param object3D - The Object3D created by createVisual()
2770
+ * @param state - The Switch's current simulation state, or null in edition mode
2771
+ *
2772
+ * @remarks
2773
+ * Rotates the contactor group to visually represent input1/2 state
2774
+ */
2775
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
2776
+ /**
2777
+ * Find the contactor group within the component group
2778
+ *
2779
+ * @param object3D - The Object3D group created by createVisual()
2780
+ * @returns The contactor's parent group if found, null otherwise
2781
+ *
2782
+ * @remarks
2783
+ * Searches for a mesh with userData.part === 'contactor' and returns its parent
2784
+ */
2785
+ private findContactorGroup;
2786
+ }
2787
+
2788
+ /**
2789
+ * Operating mode of the CircuitEngine
2790
+ * - 'edit': Static circuit editing mode (tools, selection, manipulation)
2791
+ * - 'simulation': Live simulation mode (playback, animation, user commands)
2792
+ */
2793
+ export declare type EngineMode = 'edit' | 'simulation';
2794
+
2795
+ /**
2796
+ * Configuration options for CircuitEngine initialization
2797
+ */
2798
+ export declare interface EngineOptions {
2799
+ /**
2800
+ * Initial operating mode
2801
+ * @default 'edit'
2802
+ */
2803
+ initialMode?: EngineMode;
2804
+ /**
2805
+ * Controller options
2806
+ */
2807
+ controllerOptions?: ControllerOptions;
2831
2808
  /**
2832
2809
  * Options passed to CircuitRunner when created
2833
2810
  * @default { enableHistory: false }
2834
2811
  */
2835
- runnerOptions?: RunnerOptions;
2812
+ runnerOptions?: IRunnerOptions;
2836
2813
  }
2837
2814
 
2838
2815
  /**
@@ -2911,6 +2888,11 @@ declare class ENode {
2911
2888
  * Is the ENode a source of voltage or current?
2912
2889
  */
2913
2890
  source: ENodeSourceType | undefined;
2891
+ /**
2892
+ * Pin role classification: free, mainVcc, vcc, mainGnd, gnd, logicInput, logicOutput.
2893
+ * @readonly
2894
+ */
2895
+ readonly subtype: string;
2914
2896
  /**
2915
2897
  * Create a new electrical node.
2916
2898
  *
@@ -2923,6 +2905,7 @@ declare class ENode {
2923
2905
  * @param position - Grid position (branching points only)
2924
2906
  * @param source - Source type (Voltage/Current) or undefined
2925
2907
  *
2908
+ * @param subtype
2926
2909
  * @example
2927
2910
  * ```typescript
2928
2911
  * // Pin node (internal to Circuit)
@@ -2944,7 +2927,7 @@ declare class ENode {
2944
2927
  * );
2945
2928
  * ```
2946
2929
  */
2947
- constructor(type: ENodeType, component: UUID | undefined, pinLabel: string | undefined, position: Position | undefined, source?: ENodeSourceType | undefined);
2930
+ constructor(type: ENodeType, component: UUID | undefined, pinLabel: string | undefined, position: Position | undefined, source?: ENodeSourceType | undefined, subtype?: string);
2948
2931
  /**
2949
2932
  * Get the position of this electrical node.
2950
2933
  *
@@ -3011,17 +2994,7 @@ declare class ENode {
3011
2994
  * // }
3012
2995
  * ```
3013
2996
  */
3014
- toJSON(): {
3015
- id: UUID;
3016
- type: ENodeType;
3017
- component?: UUID | null;
3018
- pinLabel?: string | null;
3019
- position?: {
3020
- x: number;
3021
- y: number;
3022
- } | null;
3023
- source?: ENodeSourceType | null;
3024
- };
2997
+ toJSON(): IENode;
3025
2998
  /**
3026
2999
  * Deserialize ENode from JSON.
3027
3000
  *
@@ -3040,17 +3013,7 @@ declare class ENode {
3040
3013
  * const enode = ENode.fromJSON(json);
3041
3014
  * ```
3042
3015
  */
3043
- static fromJSON(json: {
3044
- id: UUID;
3045
- type: ENodeType;
3046
- component?: UUID;
3047
- pinLabel?: string;
3048
- position?: {
3049
- x: number;
3050
- y: number;
3051
- };
3052
- source?: ENodeSourceType;
3053
- }): ENode;
3016
+ static fromJSON(json: IENode): ENode;
3054
3017
  }
3055
3018
 
3056
3019
  /**
@@ -3063,48 +3026,23 @@ export declare interface EnodeHitboxUserData {
3063
3026
  label: string | null;
3064
3027
  }
3065
3028
 
3066
- /**
3067
- * ENode Source Type Enumeration
3068
- *
3069
- * Defines the two types of electrical pinSources in the circuit model.
3070
- *
3071
- * @module core/types/ENodeSourceType
3072
- */
3073
3029
  /**
3074
3030
  * Type of electrical pinSources in the circuit.
3075
3031
  *
3076
3032
  * ENodes have a sourceType which can be undefined or one of the following:
3077
- *
3078
3033
  * - **Voltage**: ENode that provides a voltage source to the circuit. All Enodes of this type are considered at the same positive potential.
3079
- *
3080
3034
  * - **Current**: Ground/neutral ENodes that provides a current source to the circuit. All Enodes of this type are considered as points at
3081
- * the same 0V potential and are points from where electrons enters the circuit.
3082
- *
3083
- * playback modules use these properties to determine where to draw voltage/current from.
3084
- *
3035
+ * the same 0V-GROUND potential and are points from where electrons enters the circuit.
3085
3036
  */
3086
3037
  declare enum ENodeSourceType {
3087
- /**
3088
- * Voltage pinSources
3089
- */
3090
3038
  Voltage = "Voltage",
3091
- /**
3092
- * Current pinSources
3093
- */
3094
3039
  Current = "Current"
3095
3040
  }
3096
3041
 
3097
- /**
3098
- * ENode Type Enumeration
3099
- *
3100
- * Defines the two types of electrical connection points in the circuit model.
3101
- *
3102
- * @module core/types/ENodeType
3103
- */
3104
3042
  /**
3105
3043
  * Type of electrical node (ENode) in the circuit.
3106
3044
  *
3107
- * ENodes represent atomic electrical connection points and come in two variants (immutable after node creation):
3045
+ * ENodes represent atomic electrical connection points and come in two variants (immutable after node creation)
3108
3046
  *
3109
3047
  * - **Pin**: Connection point belonging to a Component. Position is derived
3110
3048
  * from the parent component's position, rotation, and pin index. Automatically
@@ -3113,19 +3051,6 @@ declare enum ENodeSourceType {
3113
3051
  * - **BranchingPoint**: Junction point where wires split. Has an independent
3114
3052
  * position on the grid. Automatically created when wires are split, deleted
3115
3053
  * when no wires remain connected (orphaned).
3116
- *
3117
- * @example
3118
- * ```typescript
3119
- * // Pin node (belongs to component)
3120
- * if (node.type === ENodeType.Pin) {
3121
- * console.log('Component pin with label', node.pinLabel);
3122
- * }
3123
- *
3124
- * // Branching point (wire junction)
3125
- * if (node.type === ENodeType.BranchingPoint) {
3126
- * console.log('Branch at position', node.position);
3127
- * }
3128
- * ```
3129
3054
  */
3130
3055
  declare enum ENodeType {
3131
3056
  /**
@@ -3321,6 +3246,114 @@ export declare class FactoryRegistry implements IFactoryRegistry {
3321
3246
  getRegisteredTypes(): ComponentType[];
3322
3247
  }
3323
3248
 
3249
+ /**
3250
+ * Factory registry with named group support.
3251
+ *
3252
+ * Implements both IFactoryRegistry (backward compat) and IGroupedFactoryRegistry.
3253
+ * Components may only be registered inside a group context via addGroup().
3254
+ *
3255
+ * Group insertion order is preserved (Map maintains insertion order).
3256
+ * Duplicate group ids merge their components; the label from the first
3257
+ * registration is preserved.
3258
+ *
3259
+ * @example
3260
+ * ```typescript
3261
+ * const registry = new GroupedFactoryRegistry(new DefaultVisualFactory())
3262
+ * .addGroup('basic', 'Basic Components', group => group
3263
+ * .add(ComponentType.Battery, new BatteryVisualFactory())
3264
+ * .add(ComponentType.Switch, new SwitchVisualFactory())
3265
+ * )
3266
+ * .addGroup('outputs', 'Output Components', group => group
3267
+ * .add(ComponentType.Lightbulb, new LightbulbVisualFactory())
3268
+ * );
3269
+ *
3270
+ * registry.getGroups(); // [{ id: 'basic', ... }, { id: 'outputs', ... }]
3271
+ * registry.getGroupOf(ComponentType.Battery); // { id: 'basic', label: 'Basic Components' }
3272
+ * registry.getRegisteredTypes('basic'); // [ComponentType.Battery, ComponentType.Switch]
3273
+ * registry.get(ComponentType.Battery); // BatteryVisualFactory instance
3274
+ * ```
3275
+ */
3276
+ export declare class GroupedFactoryRegistry implements IFactoryRegistry, IGroupedFactoryRegistry {
3277
+ private readonly _factories;
3278
+ private readonly _fallbackFactory;
3279
+ private readonly _groups;
3280
+ private readonly _typeToGroupId;
3281
+ /**
3282
+ * Create a new grouped factory registry.
3283
+ *
3284
+ * @param fallbackFactory - Factory to use for unregistered component types
3285
+ * @throws {TypeError} If fallbackFactory is null or undefined
3286
+ */
3287
+ constructor(fallbackFactory: IComponentVisualFactory);
3288
+ /**
3289
+ * Add a named group and register component factories within it.
3290
+ *
3291
+ * If a group with the same id already exists, the new components are merged
3292
+ * into it and the original label is preserved.
3293
+ *
3294
+ * @param id - Unique group identifier
3295
+ * @param label - Human-readable group display name
3296
+ * @param builderCallback - Callback receiving a builder for adding components
3297
+ * @throws {TypeError} If id or label is empty/whitespace
3298
+ * @returns this (for chaining)
3299
+ */
3300
+ addGroup(id: string, label: string, builderCallback: (group: IComponentGroupBuilder) => void): this;
3301
+ /**
3302
+ * Retrieve the factory for a component type.
3303
+ *
3304
+ * @returns Factory, or the fallback factory if type not registered.
3305
+ * Never returns null or undefined.
3306
+ */
3307
+ get(type: ComponentType): IComponentVisualFactory;
3308
+ /**
3309
+ * Check if a factory is registered for a component type.
3310
+ *
3311
+ * @returns true if explicitly registered, false if would use fallback
3312
+ */
3313
+ has(type: ComponentType): boolean;
3314
+ /**
3315
+ * Get the fallback factory used for unregistered types.
3316
+ */
3317
+ getFallbackFactory(): IComponentVisualFactory;
3318
+ /**
3319
+ * Get all defined groups in insertion order.
3320
+ *
3321
+ * @returns New array of ComponentGroup objects (safe to mutate)
3322
+ */
3323
+ getGroups(): ComponentGroup[];
3324
+ /**
3325
+ * Get the group a component type belongs to.
3326
+ *
3327
+ * @param type - Component type to look up
3328
+ * @returns ComponentGroup, or undefined if type is not registered
3329
+ */
3330
+ getGroupOf(type: ComponentType): ComponentGroup | undefined;
3331
+ /**
3332
+ * Get registered component types, optionally filtered by group.
3333
+ *
3334
+ * @param groupId - If provided, returns only types in that group
3335
+ * @returns New array of ComponentType values (safe to mutate).
3336
+ * Returns empty array if groupId is unknown.
3337
+ */
3338
+ getRegisteredTypes(groupId?: string): ComponentType[];
3339
+ /**
3340
+ * Unregister a factory for a component type.
3341
+ *
3342
+ * The type is removed from its group's list and from the reverse lookup.
3343
+ * The group record itself is preserved even if it becomes empty.
3344
+ *
3345
+ * @param type - Component type to remove
3346
+ * @returns true if factory was registered and removed, false otherwise
3347
+ */
3348
+ unregister(type: ComponentType): boolean;
3349
+ /**
3350
+ * Not supported. Use addGroup() to register components within a named group.
3351
+ *
3352
+ * @throws {Error} Always throws
3353
+ */
3354
+ register(_type: ComponentType, _factory: IComponentVisualFactory): IFactoryRegistry;
3355
+ }
3356
+
3324
3357
  /**
3325
3358
  * Three.js layer assignments for hitbox meshes and rendering
3326
3359
  *
@@ -3533,6 +3566,36 @@ export declare class HoverManager {
3533
3566
  private _isSameHover;
3534
3567
  }
3535
3568
 
3569
+ /**
3570
+ * Result returned by component behavior evaluation.
3571
+ * Describes state changes and future events to schedule.
3572
+ *
3573
+ * @public
3574
+ */
3575
+ declare interface IBehaviorResult {
3576
+ /**
3577
+ * Updated component state
3578
+ */
3579
+ readonly componentState: ComponentState;
3580
+ /**
3581
+ * boolean indicating
3582
+ * if the component state has changed (or events scheduled ?)
3583
+ */
3584
+ hasChanged: boolean;
3585
+ /**
3586
+ * boolean indicating that all pending events on this component should be cancelled
3587
+ * (example change of input during the rising/falling state of a gate)
3588
+ */
3589
+ shouldCancelPending: boolean;
3590
+ /**
3591
+ * Events to schedule for future ticks (e.g., delayed transitions).
3592
+ */
3593
+ readonly scheduledEvents: ReadonlyArray<IScheduledEvent>;
3594
+ }
3595
+
3596
+ /**
3597
+ * Circuit camera options (used to position camera at startup)
3598
+ */
3536
3599
  declare type ICameraOptions = {
3537
3600
  position: IPosition3D;
3538
3601
  lookAtPosition: IPosition3D;
@@ -3541,13 +3604,125 @@ declare type ICameraOptions = {
3541
3604
  far: number;
3542
3605
  };
3543
3606
 
3607
+ /** circuit type */
3608
+ declare type ICircuit = {
3609
+ metadata: ICircuitMetadata;
3610
+ components: Iterable<IComponent>;
3611
+ enodes: Iterable<IENode>;
3612
+ wires: Iterable<IWire>;
3613
+ };
3614
+
3615
+ /** circuit metadata type — combines user writable options and managed metadata */
3544
3616
  declare type ICircuitMetadata = {
3545
- name: string;
3617
+ version: string;
3618
+ options: ICircuitOptions;
3619
+ cameraOptions: ICameraOptions;
3546
3620
  size: number;
3547
3621
  divisions: number;
3548
- cameraOptions: ICameraOptions;
3549
3622
  };
3550
3623
 
3624
+ /**
3625
+ * User editable options for a Circuit
3626
+ */
3627
+ declare type ICircuitOptions = {
3628
+ name: string;
3629
+ defaultLogicFamily: LogicFamily;
3630
+ };
3631
+
3632
+ /** Interface defining a Component **/
3633
+ declare interface IComponent {
3634
+ id: UUID;
3635
+ type: ComponentType;
3636
+ position: IPosition;
3637
+ rotation: number;
3638
+ pins: UUID[];
3639
+ config: {
3640
+ [key: string]: string;
3641
+ };
3642
+ editable: boolean;
3643
+ }
3644
+
3645
+ /**
3646
+ * Component behavior interface for registry-based extensibility.
3647
+ * Each component type (Battery, LED, Switch, etc.) implements this interface.
3648
+ *
3649
+ * Behaviors are stateless - all state is stored in ComponentState and SimulationState.
3650
+ * The behavior's job is to compute new states based on current inputs and component state.
3651
+ *
3652
+ * @public
3653
+ */
3654
+ declare interface IComponentBehavior {
3655
+ /**
3656
+ * Component type this behavior handles (e.g., "battery", "led", "switch").
3657
+ * Used as the key in BehaviorRegistry.
3658
+ */
3659
+ componentType: ComponentType;
3660
+ /**
3661
+ * Create initial state for a component instance.
3662
+ * Called when simulation is initialized.
3663
+ * Initial state may use component satic configuration (e.g., initial switch position).
3664
+ *
3665
+ * @param component - The component to initialize
3666
+ * @returns Initial ComponentState for this component
3667
+ */
3668
+ createInitialState(component: Component): ComponentState;
3669
+ /**
3670
+ * Determine if conductivity is allowed between two pins of the component.
3671
+ * Called during simulation when evaluating electrical connectivity.
3672
+ * @param component
3673
+ * @param state current component state
3674
+ * @param conductivityType
3675
+ * @param pinId
3676
+ * @param otherPinId
3677
+ */
3678
+ allowConductivity(component: Component, state: ComponentState, conductivityType: ENodeSourceType, pinId: string, otherPinId: string): boolean;
3679
+ /**
3680
+ * Define component state change in response to its pins state change (after propagateConductivity)
3681
+ *
3682
+ * @param component - The component being evaluated
3683
+ * @param state - component state prior to this evaluation
3684
+ * @param nodeStates - Current electrical states of all ENodes in the simulation
3685
+ * @param targetTick - target tick
3686
+ * @returns Result containing updated state and scheduled events
3687
+ */
3688
+ onPinsChange(component: Component, state: ComponentState, nodeStates: ReadonlyMap<UUID, INodeElectricalState>, targetTick: number): IBehaviorResult;
3689
+ /**
3690
+ * Define component state change in response to a User command being received
3691
+ *
3692
+ * @param component - The component being evaluated
3693
+ * @param state - component state prior to this evaluation
3694
+ * @param command - IUserCommand to process
3695
+ * @returns Result containing updated state and scheduled events
3696
+ */
3697
+ onUserCommand(component: Component, state: ComponentState, command: IUserCommand): IBehaviorResult;
3698
+ /**
3699
+ * Define component state change in response to a IScheduledEvent firing at ready
3700
+ *
3701
+ * @param component - The component being evaluated
3702
+ * @param state - component state prior to this evaluation
3703
+ * @param event - firing IScheduledEvent to process
3704
+ * @returns Result containing updated state and scheduled events
3705
+ */
3706
+ onEventFiring(component: Component, state: ComponentState, event: IScheduledEvent): IBehaviorResult;
3707
+ }
3708
+
3709
+ /**
3710
+ * Builder interface for adding components to a group.
3711
+ * Used as the callback parameter type in addGroup().
3712
+ *
3713
+ * @example
3714
+ * ```typescript
3715
+ * registry.addGroup('basic', 'Basic Components', (group: IComponentGroupBuilder) => {
3716
+ * group
3717
+ * .add(ComponentType.Battery, new BatteryVisualFactory())
3718
+ * .add(ComponentType.Switch, new SwitchVisualFactory());
3719
+ * });
3720
+ * ```
3721
+ */
3722
+ export declare interface IComponentGroupBuilder {
3723
+ add(type: ComponentType, factory: IComponentVisualFactory): IComponentGroupBuilder;
3724
+ }
3725
+
3551
3726
  /**
3552
3727
  * Interface for component visual factories
3553
3728
  *
@@ -3560,7 +3735,7 @@ declare type ICircuitMetadata = {
3560
3735
  * @example
3561
3736
  * ```typescript
3562
3737
  * class MyComponentFactory extends ComponentVisualFactoryBase {
3563
- * createVisual(component: Component): THREE.Object3D {
3738
+ * createVisual(component: Component, context?: VisualContext): THREE.Object3D {
3564
3739
  * const group = new THREE.Group();
3565
3740
  * // ... create visual elements
3566
3741
  * group.userData.componentId = component.id;
@@ -3571,10 +3746,15 @@ declare type ICircuitMetadata = {
3571
3746
  * ```
3572
3747
  */
3573
3748
  export declare interface IComponentVisualFactory {
3749
+ /**
3750
+ * @returns nominal rotation along the Y axis when component added (angle in radian)
3751
+ */
3752
+ defaultRotation(): number;
3574
3753
  /**
3575
3754
  * Create the Three.js visual representation for a component
3576
3755
  *
3577
3756
  * @param component - The circuit component to visualize
3757
+ * @param context - Optional visual context providing access to ENode data
3578
3758
  * @returns THREE.Object3D (typically a Group) containing the visual
3579
3759
  *
3580
3760
  * @remarks
@@ -3585,7 +3765,7 @@ export declare interface IComponentVisualFactory {
3585
3765
  * - Create pin groups with enodes on HitboxLayers.ENODE layer
3586
3766
  * - Return objects positioned at origin (scene controllerType handles placement)
3587
3767
  */
3588
- createVisual(component: Component): THREE.Object3D;
3768
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
3589
3769
  /**
3590
3770
  * Update visual based on component configuration
3591
3771
  *
@@ -3673,13 +3853,16 @@ export declare interface IComponentVisualFactory {
3673
3853
  /**
3674
3854
  * Get the config form definition for this component type
3675
3855
  *
3856
+ * @param config - Optional current component config to compute field states (e.g., disabled)
3676
3857
  * @returns Form definition with field specifications, or null if no config
3677
3858
  *
3678
3859
  * @remarks
3679
3860
  * Defines the UI controls for editing component configuration.
3680
3861
  * Return null for components with no configurable options.
3862
+ * When config is provided, implementations may use it to set field.disabled
3863
+ * based on interdependencies (e.g., transitionSpan disabled when defaultLogicFamily != Sandbox).
3681
3864
  */
3682
- getConfigFormDefinition(): ConfigFormDefinition | null;
3865
+ getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;
3683
3866
  /**
3684
3867
  * Map core component config (string values) to form data (typed values)
3685
3868
  *
@@ -3764,6 +3947,17 @@ export declare interface IEditingTool {
3764
3947
  getPreviewObjects(): THREE.Object3D[];
3765
3948
  }
3766
3949
 
3950
+ /** Interface defining an ElectricalNode **/
3951
+ declare interface IENode {
3952
+ id: UUID;
3953
+ type: ENodeType;
3954
+ component?: UUID | null;
3955
+ pinLabel?: string | null;
3956
+ position?: IPosition | null;
3957
+ source?: ENodeSourceType | null;
3958
+ subtype: string;
3959
+ }
3960
+
3767
3961
  /**
3768
3962
  * Registry interface for managing component visual factories
3769
3963
  *
@@ -3785,15 +3979,6 @@ export declare interface IEditingTool {
3785
3979
  * ```
3786
3980
  */
3787
3981
  export declare interface IFactoryRegistry {
3788
- /**
3789
- * Register a visual factory for a specific component type
3790
- *
3791
- * @param type - Component type identifier
3792
- * @param factory - Factory (class instance or function) to create visuals for this type
3793
- * @throws {TypeError} If factory is null or undefined
3794
- * @returns This IFactoryRegistry instance (for chaining)
3795
- */
3796
- register(type: ComponentType, factory: IComponentVisualFactory): IFactoryRegistry;
3797
3982
  /**
3798
3983
  * Retrieve the factory for a component type
3799
3984
  *
@@ -3835,19 +4020,241 @@ export declare interface IFactoryRegistry {
3835
4020
  }
3836
4021
 
3837
4022
  /**
3838
- * 3D Position Type
3839
- *
3840
- * Represents a 3D position. Used for:
3841
- * - Camera placement
4023
+ * Extended registry interface with grouping support.
3842
4024
  *
3843
- * @module core/types/Position3D
4025
+ * Adds group management on top of the basic factory retrieval,
4026
+ * allowing client UIs to build organized component palettes.
3844
4027
  */
3845
- declare type IPosition3D = {
3846
- x: number;
4028
+ export declare interface IGroupedFactoryRegistry {
4029
+ addGroup(id: string, label: string, builderCallback: (group: IComponentGroupBuilder) => void): IGroupedFactoryRegistry;
4030
+ get(type: ComponentType): IComponentVisualFactory;
4031
+ has(type: ComponentType): boolean;
4032
+ getFallbackFactory(): IComponentVisualFactory;
4033
+ getGroups(): ComponentGroup[];
4034
+ getGroupOf(type: ComponentType): ComponentGroup | undefined;
4035
+ getRegisteredTypes(groupId?: string): ComponentType[];
4036
+ unregister(type: ComponentType): boolean;
4037
+ }
4038
+
4039
+ /**
4040
+ * Binary electrical state for wires and enodes (connection points)
4041
+ * @module core/simulation/states
4042
+ * @public
4043
+ */
4044
+ declare interface INodeElectricalState {
4045
+ /**
4046
+ * True if voltage is present at this node (potential > 0V).
4047
+ * False if node is at ground potential or floating.
4048
+ */
4049
+ hasVoltage: boolean;
4050
+ /**
4051
+ * True if current is actively flowing through this node.
4052
+ * False if no current flow (open circuit or equilibrium).
4053
+ */
4054
+ hasCurrent: boolean;
4055
+ /**
4056
+ * True only if the node is locked from state changes at circuit build time (ex: battery pins or other fixed-voltage/current pinSources).
4057
+ * Important: Those nodes should never have their electrical state modified by the simulation controller!
4058
+ * Always false for wires
4059
+ */
4060
+ locked: boolean;
4061
+ }
4062
+
4063
+ /**
4064
+ * Visual factory for Inverter/Buffer components
4065
+ *
4066
+ * Creates:
4067
+ * - Inverter triangle or Buffer trapezoid extrude geom mesh
4068
+ * - Vcc, input and output pin group
4069
+ * - Component hitbox for raycasting
4070
+ *
4071
+ * Animation:
4072
+ * - Emissive glow when component is high (based on simulation state)
4073
+ */
4074
+ export declare class InverterVisualFactory extends ComponentVisualFactoryBase {
4075
+ /** Inverter high color (white glow) */
4076
+ private static readonly HIGH_COLOR;
4077
+ /** Inverter high emissive intensity */
4078
+ private static readonly HIGH_INTENSITY;
4079
+ /** Shared low Inverter envelope geometry */
4080
+ private readonly inverterLowGeometry;
4081
+ /** Shared transient Inverter envelope geometry */
4082
+ private readonly inverterTransientGeometry;
4083
+ /** Shared high Inverter envelope geometry */
4084
+ private readonly inverterHighGeometry;
4085
+ /** Shared low Buffer envelope geometry */
4086
+ private readonly bufferLowGeometry;
4087
+ /** Shared transient Buffer envelope geometry */
4088
+ private readonly bufferTransientGeometry;
4089
+ /** Shared high Buffer envelope geometry */
4090
+ private readonly bufferHighGeometry;
4091
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4092
+ private createPinsVisual;
4093
+ private replaceEnvelope;
4094
+ /**
4095
+ * Get config form definition for Inverter
4096
+ *
4097
+ * @param config - Optional current config to determine disabled state of transitionSpan
4098
+ * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number
4099
+ */
4100
+ getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;
4101
+ /**
4102
+ * Map core config to form data
4103
+ * Converts "positive"/"negative" strings to boolean
4104
+ *
4105
+ * @param config - Core component config
4106
+ * @returns Form data with boolean activationLogic
4107
+ */
4108
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
4109
+ /**
4110
+ * Map form data to core config
4111
+ * Converts boolean to "positive"/"negative" strings
4112
+ *
4113
+ * @param formData - Form data with boolean activationLogic
4114
+ * @returns Core config with string activationLogic
4115
+ */
4116
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
4117
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4118
+ /**
4119
+ * Update Inverter animation based on simulation state
4120
+ *
4121
+ * @param object3D - The Object3D created by createVisual()
4122
+ * @param state - The Inverter's current simulation state
4123
+ */
4124
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
4125
+ /**
4126
+ * Find the envelope mesh within the component group
4127
+ *
4128
+ * @param object3D - The Object3D group created by createVisual()
4129
+ * @returns The envelope mesh if found, null otherwise
4130
+ *
4131
+ * @remarks
4132
+ * Searches for a mesh with userData.part === 'envelope'
4133
+ */
4134
+ private findEnvelopeMesh;
4135
+ /**
4136
+ * Find the negative marker mesh within the component group
4137
+ *
4138
+ * @param object3D - The Object3D group created by createVisual()
4139
+ * @returns The negative marker mesh if found, null otherwise
4140
+ *
4141
+ * @remarks
4142
+ * Searches for a mesh with userData.part === 'negativeMarker'
4143
+ */
4144
+ protected findNegativeMarkerMesh(object3D: THREE.Object3D): (THREE.Mesh & {
4145
+ material: THREE.MeshStandardMaterial;
4146
+ }) | null;
4147
+ }
4148
+
4149
+ /**
4150
+ * 2D Position Type
4151
+ * Represents a 2D position on the circuit grid
4152
+ */
4153
+ declare type IPosition = {
4154
+ x: number;
4155
+ y: number;
4156
+ };
4157
+
4158
+ /**
4159
+ * 3D Position Type
4160
+ * Represents a 3D position. Used for Camera placement
4161
+ */
4162
+ declare type IPosition3D = {
4163
+ x: number;
3847
4164
  y: number;
3848
4165
  z: number;
3849
4166
  };
3850
4167
 
4168
+ /**
4169
+ * Configuration options for CircuitRunner
4170
+ */
4171
+ declare interface IRunnerOptions {
4172
+ /**
4173
+ * Enable historical state tracking.
4174
+ * When true, past simulation states are preserved up to historyLimit.
4175
+ * When false (default), only current state is retained for better performance.
4176
+ * @default false
4177
+ */
4178
+ enableHistory?: boolean;
4179
+ /**
4180
+ * Maximum number of historical states to retain when enableHistory is true.
4181
+ * Uses circular buffer—oldest states are overwritten when limit is reached.
4182
+ * Must be a positive integer.
4183
+ * @default 1000
4184
+ */
4185
+ historyLimit?: number;
4186
+ }
4187
+
4188
+ /**
4189
+ * Scheduled event for delayed component transitions.
4190
+ * Events are ordered by readyAtTick in a min-heap priority queue.
4191
+ * Events with same readyAtTick are processed in FIFO order (by scheduledAtTick).
4192
+ *
4193
+ * @public
4194
+ */
4195
+ declare interface IScheduledEvent {
4196
+ /**
4197
+ * UUID of target component.
4198
+ */
4199
+ readonly targetId: UUID;
4200
+ /**
4201
+ * Tick when this event was scheduled (for FIFO ordering).
4202
+ * @readonly
4203
+ */
4204
+ readonly scheduledAtTick: number;
4205
+ /**
4206
+ * Tick when this event should be processed.
4207
+ * @readonly
4208
+ */
4209
+ readonly readyAtTick: number;
4210
+ /**
4211
+ * Indicates the type of this event, eg 'ClosingEnd', 'OpeningEnd', etc.
4212
+ */
4213
+ readonly type: string;
4214
+ /**
4215
+ * extra parameters associated with this event.
4216
+ */
4217
+ readonly parameters?: Map<string, string> | undefined;
4218
+ }
4219
+
4220
+ /**
4221
+ * User command to be executed during simulation.
4222
+ * Commands can be queued for future ticks or executed immediately.
4223
+ *
4224
+ * @public
4225
+ */
4226
+ declare interface IUserCommand {
4227
+ /**
4228
+ * Type of command.
4229
+ */
4230
+ readonly type: 'toggle_switch';
4231
+ /**
4232
+ * UUID of target component.
4233
+ */
4234
+ readonly targetId: UUID;
4235
+ /**
4236
+ * tick when this command was scheduled.
4237
+ */
4238
+ scheduledAtTick: number;
4239
+ /**
4240
+ * Extra parameters associated with this command.
4241
+ *
4242
+ * For `toggle_switch` commands:
4243
+ * - `tickCount`: Number of ticks for the switch transition. Computed at toggle time
4244
+ * using the formula: `ceil(transitionUserSpan × simulationSpeed / 1000)` with minimum of 1.
4245
+ * If not provided, behavior uses default transition timing.
4246
+ */
4247
+ readonly parameters?: Map<string, string> | null;
4248
+ }
4249
+
4250
+ /** Interface defining a Wire (link between 2 ENodes supporting intermediate position to tune its path) **/
4251
+ declare interface IWire {
4252
+ id: UUID;
4253
+ node1: UUID;
4254
+ node2: UUID;
4255
+ intermediatePositions: IPosition[];
4256
+ }
4257
+
3851
4258
  /**
3852
4259
  * Visual factory for Label components
3853
4260
  *
@@ -3886,13 +4293,7 @@ export declare class LabelVisualFactory extends ComponentVisualFactoryBase {
3886
4293
  private static readonly BASE_FONT_SIZE;
3887
4294
  /** Padding around text in pixels */
3888
4295
  private static readonly PADDING;
3889
- /**
3890
- * Create the Three.js visual representation for a Label component
3891
- *
3892
- * @param component - The Label component to visualize
3893
- * @returns THREE.Group containing hitbox and text mesh
3894
- */
3895
- createVisual(component: Component): THREE.Object3D;
4296
+ createVisual(component: Component, _context: VisualContext): THREE.Object3D;
3896
4297
  /**
3897
4298
  * Create a canvas with rendered text
3898
4299
  *
@@ -4015,7 +4416,8 @@ export declare class LightbulbVisualFactory extends ComponentVisualFactoryBase {
4015
4416
  private static readonly BULB_LIT_COLOR;
4016
4417
  /** Lightbulb lit emissive intensity */
4017
4418
  private static readonly BULB_LIT_INTENSITY;
4018
- createVisual(component: Component): THREE.Object3D;
4419
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4420
+ private createPinsVisual;
4019
4421
  /**
4020
4422
  * Get config form definition for Lightbulb
4021
4423
  *
@@ -4060,6 +4462,15 @@ export { LineGeometry }
4060
4462
 
4061
4463
  export { LineMaterial }
4062
4464
 
4465
+ /**
4466
+ * Available logic families
4467
+ *
4468
+ * - `CMOS1`: CMOS technology, base unit 1 inverter = 1 tick
4469
+ * - `TTL1`: TTL technology, base unit 1 NAND2 = 1 tick
4470
+ * - `Sandbox`: User-defined delays, no technology constraints
4471
+ */
4472
+ declare type LogicFamily = 'CMOS1' | 'TTL1' | 'Sandbox';
4473
+
4063
4474
  /**
4064
4475
  * Configuration options for Controllers and Engine
4065
4476
  **/
@@ -4289,37 +4700,291 @@ export declare class MultiSelectTool implements IEditingTool {
4289
4700
  * 2. Selected components (cascades to connected wires)
4290
4701
  * 3. Selected branching points
4291
4702
  */
4292
- deleteSelection(): boolean;
4293
- }
4294
-
4295
- /**
4296
- * Operating modes for the MultiSelectTool
4297
- */
4298
- export declare type MultiSelectToolMode = 'idle' | 'selecting' | 'dragging';
4299
-
4300
- /**
4301
- * Binary electrical state for wires and enodes (connection points)
4302
- * @module core/simulation/states
4303
- */
4304
- declare interface NodeElectricalState {
4703
+ deleteSelection(): boolean;
4704
+ }
4705
+
4706
+ /**
4707
+ * Operating modes for the MultiSelectTool
4708
+ */
4709
+ export declare type MultiSelectToolMode = 'idle' | 'selecting' | 'dragging';
4710
+
4711
+ /**
4712
+ * Visual factory for NAND gates components
4713
+ *
4714
+ * Creates:
4715
+ * - Gate mesh
4716
+ * - vcc, gnd, inputs and output pin groups
4717
+ * - Component hitbox for raycasting
4718
+ *
4719
+ * Animation:
4720
+ * - Emissive glow when Gate is high (based on simulation state)
4721
+ */
4722
+ export declare class Nand4GateVisualFactory extends NandGateVisualFactory {
4723
+ /** Shared open envelope geometry */
4724
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4725
+ /** Shared transient envelope geometry */
4726
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4727
+ /** Shared transient envelope geometry */
4728
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4729
+ /** Shared geometry for negative marker **/
4730
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4731
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4732
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4733
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4734
+ }
4735
+
4736
+ /**
4737
+ * Visual factory for NAND gates components
4738
+ *
4739
+ * Creates:
4740
+ * - Gate mesh
4741
+ * - vcc, gnd, inputs and output pin groups
4742
+ * - Component hitbox for raycasting
4743
+ *
4744
+ * Animation:
4745
+ * - Emissive glow when Gate is high (based on simulation state)
4746
+ */
4747
+ export declare class Nand8GateVisualFactory extends NandGateVisualFactory {
4748
+ /** Shared open envelope geometry */
4749
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4750
+ /** Shared transient envelope geometry */
4751
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4752
+ /** Shared transient envelope geometry */
4753
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4754
+ /** Shared geometry for negative marker **/
4755
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4756
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4757
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4758
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4759
+ }
4760
+
4761
+ /**
4762
+ * Visual factory for NAND gates components
4763
+ *
4764
+ * Creates:
4765
+ * - Gate mesh
4766
+ * - vcc, gnd, inputs and output pin groups
4767
+ * - Component hitbox for raycasting
4768
+ *
4769
+ * Animation:
4770
+ * - Emissive glow when Gate is high (based on simulation state)
4771
+ */
4772
+ export declare class NandGateVisualFactory extends ComponentVisualFactoryBase {
4773
+ /** Gate high color */
4774
+ protected static readonly HIGH_COLOR = 16777215;
4775
+ /** Gate high emissive intensity */
4776
+ protected static readonly HIGH_INTENSITY = 0.3;
4777
+ /** Shared open envelope geometry */
4778
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4779
+ /** Shared transient envelope geometry */
4780
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4781
+ /** Shared transient envelope geometry */
4782
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4783
+ /** Shared material for negative marker **/
4784
+ protected static readonly negativeMarkerMaterial: THREE.MeshStandardMaterial;
4785
+ /** Shared geometry for negative marker **/
4786
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4787
+ defaultRotation(): number;
4788
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4789
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4790
+ /**
4791
+ * Get config form definition
4792
+ *
4793
+ * @param config - Optional current config to determine disabled state of transitionSpan
4794
+ * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number
4795
+ */
4796
+ getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;
4797
+ /**
4798
+ * Map core config to form data
4799
+ * Converts "positive"/"negative" strings to boolean
4800
+ *
4801
+ * @param config - Core component config
4802
+ * @returns Form data with boolean activationLogic
4803
+ */
4804
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
4805
+ /**
4806
+ * Map form data to core config
4807
+ * Converts boolean to "positive"/"negative" strings
4808
+ *
4809
+ * @param formData - Form data with boolean activationLogic
4810
+ * @returns Core config with string activationLogic
4811
+ */
4812
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
4813
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4814
+ /**
4815
+ * Update animation based on simulation state
4816
+ *
4817
+ * @param object3D - The Object3D created by createVisual()
4818
+ * @param state - The component current simulation state
4819
+ */
4820
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
4821
+ /**
4822
+ * Find the envelope mesh within the component group
4823
+ *
4824
+ * @param object3D - The Object3D group created by createVisual()
4825
+ * @returns The envelope mesh if found, null otherwise
4826
+ *
4827
+ * @remarks
4828
+ * Searches for a mesh with userData.part === 'envelope'
4829
+ */
4830
+ protected findEnvelopeMesh(object3D: THREE.Object3D): (THREE.Mesh & {
4831
+ material: THREE.MeshStandardMaterial;
4832
+ }) | null;
4833
+ /**
4834
+ * Find the negative marker mesh within the component group
4835
+ *
4836
+ * @param object3D - The Object3D group created by createVisual()
4837
+ * @returns The negative marker mesh if found, null otherwise
4838
+ *
4839
+ * @remarks
4840
+ * Searches for a mesh with userData.part === 'negativeMarker'
4841
+ */
4842
+ protected findNegativeMarkerMesh(object3D: THREE.Object3D): (THREE.Mesh & {
4843
+ material: THREE.MeshStandardMaterial;
4844
+ }) | null;
4845
+ }
4846
+
4847
+ /**
4848
+ * Visual factory for NOR gates components
4849
+ *
4850
+ * Creates:
4851
+ * - Gate mesh
4852
+ * - vcc, gnd, inputs and output pin groups
4853
+ * - Component hitbox for raycasting
4854
+ *
4855
+ * Animation:
4856
+ * - Emissive glow when Gate is high (based on simulation state)
4857
+ */
4858
+ export declare class Nor4GateVisualFactory extends NorGateVisualFactory {
4859
+ /** Shared open envelope geometry */
4860
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4861
+ /** Shared transient envelope geometry */
4862
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4863
+ /** Shared transient envelope geometry */
4864
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4865
+ /** Shared geometry for negative marker **/
4866
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4867
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4868
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4869
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4870
+ }
4871
+
4872
+ /**
4873
+ * Visual factory for NOR gates components
4874
+ *
4875
+ * Creates:
4876
+ * - Gate mesh
4877
+ * - vcc, gnd, inputs and output pin groups
4878
+ * - Component hitbox for raycasting
4879
+ *
4880
+ * Animation:
4881
+ * - Emissive glow when Gate is high (based on simulation state)
4882
+ */
4883
+ export declare class Nor8GateVisualFactory extends NorGateVisualFactory {
4884
+ /** Shared open envelope geometry */
4885
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4886
+ /** Shared transient envelope geometry */
4887
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4888
+ /** Shared transient envelope geometry */
4889
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4890
+ /** Shared geometry for negative marker **/
4891
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4892
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4893
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4894
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4895
+ }
4896
+
4897
+ /**
4898
+ * Visual factory for NOR gates components
4899
+ *
4900
+ * Creates:
4901
+ * - Gate mesh
4902
+ * - vcc, gnd, inputs and output pin groups
4903
+ * - Component hitbox for raycasting
4904
+ *
4905
+ * Animation:
4906
+ * - Emissive glow when Gate is high (based on simulation state)
4907
+ */
4908
+ export declare class NorGateVisualFactory extends ComponentVisualFactoryBase {
4909
+ /** Gate high color */
4910
+ protected static readonly HIGH_COLOR = 16777215;
4911
+ /** Gate high emissive intensity */
4912
+ protected static readonly HIGH_INTENSITY = 0.3;
4913
+ /** Shared open envelope geometry */
4914
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
4915
+ /** Shared transient envelope geometry */
4916
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
4917
+ /** Shared transient envelope geometry */
4918
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
4919
+ /** Shared material for negative marker **/
4920
+ protected static readonly negativeMarkerMaterial: THREE.MeshStandardMaterial;
4921
+ /** Shared geometry for negative marker **/
4922
+ protected static readonly negativeMarkerGeometry: THREE.CylinderGeometry;
4923
+ defaultRotation(): number;
4924
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
4925
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
4926
+ /**
4927
+ * Get config form definition
4928
+ *
4929
+ * @param config - Optional current config to determine disabled state of transitionSpan
4930
+ * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number
4931
+ */
4932
+ getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;
4933
+ /**
4934
+ * Map core config to form data
4935
+ * Converts "positive"/"negative" strings to boolean
4936
+ *
4937
+ * @param config - Core component config
4938
+ * @returns Form data with boolean activationLogic
4939
+ */
4940
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
4941
+ /**
4942
+ * Map form data to core config
4943
+ * Converts boolean to "positive"/"negative" strings
4944
+ *
4945
+ * @param formData - Form data with boolean activationLogic
4946
+ * @returns Core config with string activationLogic
4947
+ */
4948
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
4949
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
4305
4950
  /**
4306
- * True if voltage is present at this node (potential > 0V).
4307
- * False if node is at ground potential or floating.
4951
+ * Update animation based on simulation state
4952
+ *
4953
+ * @param object3D - The Object3D created by createVisual()
4954
+ * @param state - The component current simulation state
4308
4955
  */
4309
- hasVoltage: boolean;
4956
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
4310
4957
  /**
4311
- * True if current is actively flowing through this node.
4312
- * False if no current flow (open circuit or equilibrium).
4958
+ * Find the envelope mesh within the component group
4959
+ *
4960
+ * @param object3D - The Object3D group created by createVisual()
4961
+ * @returns The envelope mesh if found, null otherwise
4962
+ *
4963
+ * @remarks
4964
+ * Searches for a mesh with userData.part === 'envelope'
4313
4965
  */
4314
- hasCurrent: boolean;
4966
+ protected findEnvelopeMesh(object3D: THREE.Object3D): (THREE.Mesh & {
4967
+ material: THREE.MeshStandardMaterial;
4968
+ }) | null;
4315
4969
  /**
4316
- * True only if the node is locked from state changes at circuit build time (ex: battery pins or other fixed-voltage/current pinSources).
4317
- * Important: Those nodes should never have their electrical state modified by the simulation controller!
4318
- * Always false for wires
4970
+ * Find the negative marker mesh within the component group
4971
+ *
4972
+ * @param object3D - The Object3D group created by createVisual()
4973
+ * @returns The negative marker mesh if found, null otherwise
4974
+ *
4975
+ * @remarks
4976
+ * Searches for a mesh with userData.part === 'negativeMarker'
4319
4977
  */
4320
- locked: boolean;
4978
+ protected findNegativeMarkerMesh(object3D: THREE.Object3D): (THREE.Mesh & {
4979
+ material: THREE.MeshStandardMaterial;
4980
+ }) | null;
4321
4981
  }
4322
4982
 
4983
+ /**
4984
+ * Union of actual component types and the branching point sentinel.
4985
+ */
4986
+ export declare type PickerSelection = ComponentType | typeof BRANCHING_POINT_SENTINEL;
4987
+
4323
4988
  /**
4324
4989
  * Position Type for 2D Discrete Grid
4325
4990
  *
@@ -4328,7 +4993,7 @@ declare interface NodeElectricalState {
4328
4993
  * - Branching point ENode positions
4329
4994
  * - Wire intermediate waypoints
4330
4995
  *
4331
- * @module core/types/Position
4996
+ * @module core/utils
4332
4997
  */
4333
4998
  /**
4334
4999
  * Position on a 2D discrete grid with integer coordinates.
@@ -4510,7 +5175,8 @@ export declare class RectangleLEDVisualFactory extends ComponentVisualFactoryBas
4510
5175
  private static readonly LED_LIT_COLOR;
4511
5176
  /** LED lit emissive intensity */
4512
5177
  private static readonly LED_LIT_INTENSITY;
4513
- createVisual(component: Component): THREE.Object3D;
5178
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
5179
+ private createPinsVisual;
4514
5180
  /**
4515
5181
  * Get config form definition for SmallLED (T027)
4516
5182
  *
@@ -4568,37 +5234,58 @@ export declare class RectangleLEDVisualFactory extends ComponentVisualFactoryBas
4568
5234
  }
4569
5235
 
4570
5236
  /**
4571
- * Register all basic component visual factories in the given registry
4572
- * Basic components are : Battery, Lightbulb, RectangleLED, Relay, SmallLED, Switch, Transistor
5237
+ * Register all basic components visual factories in the basic group
5238
+ * Basic components are : Battery, Label, Switches, Lightbulb, RectangleLED, Relay, SmallLED
5239
+ * @public
5240
+ * @param registry - A grouped factory registry to populate
5241
+ * @returns The input registry for chaining
5242
+ */
5243
+ export declare function registerBasicComponentsFactories(registry: IGroupedFactoryRegistry): IGroupedFactoryRegistry;
5244
+
5245
+ /**
5246
+ * Register all logic gates components visual factories in the gates group
5247
+ * gates are : Inverter, NAND (2,4,8 inputs), NOR (2,4,8 inputs) and XOR (2,4,8 inputs)
5248
+ * AND/OR are gotten by changing the activationLogic of NAND/NOR
4573
5249
  * @public
4574
- * @param registry
4575
- * @return the input factory registry for chaining
5250
+ * @param registry - A grouped factory registry to populate
5251
+ * @returns The input registry for chaining
4576
5252
  */
4577
- export declare function registerBasicComponentsFactories(registry: IFactoryRegistry): IFactoryRegistry;
5253
+ export declare function registerGatesComponentsFactories(registry: IGroupedFactoryRegistry): IGroupedFactoryRegistry;
4578
5254
 
4579
5255
  /**
4580
5256
  * Visual factory for Relay components
4581
5257
  *
4582
- * Creates:
4583
- * - Component hitbox for raycasting
4584
- * - Cylinder representing the coil with two poles
4585
- * - Output commanded switch with contactor
4586
- * - Contactor (cylinder, rotatable for animation)
4587
5258
  * Animation:
4588
5259
  * - Rotates contactor based on open/closed state
4589
5260
  */
4590
5261
  export declare class RelayVisualFactory extends ComponentVisualFactoryBase {
5262
+ /** Coil ring geometry */
5263
+ private readonly COIL_GEOM;
5264
+ /** Coil bar geometry */
5265
+ private readonly COIL_BAR_GEOM;
5266
+ private readonly COIL_BAR_Z_OPEN;
5267
+ private readonly COIL_BAR_Z_INTERMEDIATE;
5268
+ private readonly COIL_BAR_Z_CLOSED;
5269
+ private readonly COIL_BAR_Z_INV_INTERMEDIATE;
5270
+ private readonly COIL_BAR_Z_INV_OPEN;
5271
+ /** Power In bar geometry */
5272
+ private readonly PWIN_BAR_GEOM;
5273
+ /** normal contactor geom */
5274
+ private CONTACTOR_GEOM;
4591
5275
  /** Rotation for open relay (contactor misaligned) */
4592
- private static readonly OPEN_ROTATION;
5276
+ private readonly OPEN_ROTATION;
4593
5277
  /** Rotation for opening/closing relay */
4594
- private static readonly INTERMEDIATE_ROTATION;
5278
+ private readonly INTERMEDIATE_ROTATION;
4595
5279
  /** Rotation for closed relay (contactor aligned) */
4596
- private static readonly CLOSED_ROTATION;
5280
+ private readonly CLOSED_ROTATION;
4597
5281
  /** Rotation for opening/closing negative activation logic relay */
4598
- private static readonly INVERTED_INTERMEDIATE_ROTATION;
5282
+ private readonly INVERTED_INTERMEDIATE_ROTATION;
4599
5283
  /** Rotation for open negative activation logic relay (contactor misaligned toward the coil) */
4600
- private static readonly INVERTED_OPEN_ROTATION;
4601
- createVisual(component: Component): THREE.Object3D;
5284
+ private readonly INVERTED_OPEN_ROTATION;
5285
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
5286
+ private createPinsVisual;
5287
+ private createContactorGroup;
5288
+ private createCoilGroup;
4602
5289
  /**
4603
5290
  * Get config form definition for Relay (T025)
4604
5291
  *
@@ -4643,7 +5330,7 @@ export declare class RelayVisualFactory extends ComponentVisualFactoryBase {
4643
5330
  */
4644
5331
  private findContactorGroup;
4645
5332
  /**
4646
- * Find the coil within the component group
5333
+ * Find the coil bar within the component group
4647
5334
  *
4648
5335
  * @param object3D - The Object3D group created by createVisual()
4649
5336
  * @returns The contactor's parent group if found, null otherwise
@@ -4651,16 +5338,14 @@ export declare class RelayVisualFactory extends ComponentVisualFactoryBase {
4651
5338
  * @remarks
4652
5339
  * Searches for a mesh with userData.part === 'coil' and returns its parent
4653
5340
  */
4654
- private findCoil;
5341
+ private findCoilBar;
4655
5342
  }
4656
5343
 
4657
5344
  /**
4658
5345
  * Rotation Type for Component Orientation
4659
- *
4660
5346
  * Represents orientation angle for components on the 2D grid.
4661
5347
  * Uses integer degrees for discrete rotation values.
4662
- *
4663
- * @module core/types/Rotation
5348
+ * @module core/utils
4664
5349
  */
4665
5350
  /**
4666
5351
  * Rotation angle for component orientation.
@@ -4739,59 +5424,6 @@ declare class Rotation {
4739
5424
  toString(): string;
4740
5425
  }
4741
5426
 
4742
- /**
4743
- * Configuration options for CircuitRunner
4744
- * @module core/simulation/types
4745
- */
4746
- declare interface RunnerOptions {
4747
- /**
4748
- * Enable historical state tracking.
4749
- * When true, past simulation states are preserved up to historyLimit.
4750
- * When false (default), only current state is retained for better performance.
4751
- * @default false
4752
- */
4753
- enableHistory?: boolean;
4754
- /**
4755
- * Maximum number of historical states to retain when enableHistory is true.
4756
- * Uses circular buffer—oldest states are overwritten when limit is reached.
4757
- * Must be a positive integer.
4758
- * @default 1000
4759
- */
4760
- historyLimit?: number;
4761
- }
4762
-
4763
- /**
4764
- * Scheduled event for delayed component transitions.
4765
- * Events are ordered by readyAtTick in a min-heap priority queue.
4766
- * Events with same readyAtTick are processed in FIFO order (by scheduledAtTick).
4767
- *
4768
- * @public
4769
- */
4770
- declare interface ScheduledEvent {
4771
- /**
4772
- * UUID of target component.
4773
- */
4774
- readonly targetId: UUID;
4775
- /**
4776
- * Tick when this event was scheduled (for FIFO ordering).
4777
- * @readonly
4778
- */
4779
- readonly scheduledAtTick: number;
4780
- /**
4781
- * Tick when this event should be processed.
4782
- * @readonly
4783
- */
4784
- readonly readyAtTick: number;
4785
- /**
4786
- * Indicates the type of this event, eg 'ClosingEnd', 'OpeningEnd', etc.
4787
- */
4788
- readonly type: string;
4789
- /**
4790
- * extra parameters associated with this event.
4791
- */
4792
- readonly parameters?: Map<string, string> | undefined;
4793
- }
4794
-
4795
5427
  /**
4796
5428
  * Callback invoked when selection changes
4797
5429
  *
@@ -4999,7 +5631,8 @@ export declare class SmallLEDVisualFactory extends ComponentVisualFactoryBase {
4999
5631
  private static readonly LED_LIT_COLOR;
5000
5632
  /** LED lit emissive intensity */
5001
5633
  private static readonly LED_LIT_INTENSITY;
5002
- createVisual(component: Component): THREE.Object3D;
5634
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
5635
+ private createPinsVisual;
5003
5636
  /**
5004
5637
  * Get config form definition for SmallLED (T027)
5005
5638
  *
@@ -5072,12 +5705,13 @@ export declare class SmallLEDVisualFactory extends ComponentVisualFactoryBase {
5072
5705
  */
5073
5706
  export declare class SwitchVisualFactory extends ComponentVisualFactoryBase {
5074
5707
  /** Rotation for closed switch (contactor aligned) */
5075
- private static readonly CLOSED_ROTATION;
5708
+ private readonly CLOSED_ROTATION;
5076
5709
  /** Rotation for opening/closing switch */
5077
- private static readonly INTERMEDIATE_ROTATION;
5710
+ private readonly INTERMEDIATE_ROTATION;
5078
5711
  /** Rotation for open switch (contactor misaligned) */
5079
- private static readonly OPEN_ROTATION;
5080
- createVisual(component: Component): THREE.Object3D;
5712
+ private readonly OPEN_ROTATION;
5713
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
5714
+ private createPinsVisual;
5081
5715
  /**
5082
5716
  * Get config form definition for Switch (T024)
5083
5717
  *
@@ -5131,128 +5765,27 @@ export declare class SwitchVisualFactory extends ComponentVisualFactoryBase {
5131
5765
  *
5132
5766
  * Note: 'build' replaces the previous tools: 'position', 'wire', 'delete', 'branchingPoint'
5133
5767
  */
5134
- export declare type ToolType = 'build' | 'addComponent' | 'multiSelect';
5768
+ export declare type ToolType = 'build' | 'multiSelect';
5135
5769
 
5136
5770
  /**
5137
- * Visual factory for Transistor components
5138
- *
5139
- * Creates:
5140
- * - Transistor Ring mesh
5141
- * - Collector, Base and Emitter pin group
5142
- * - Component hitbox for raycasting
5143
- *
5144
- * Animation:
5145
- * - Emissive glow when Transistor is lit (based on simulation state)
5771
+ * core utilities types definitions
5772
+ * @module core/utils
5146
5773
  */
5147
- export declare class TransistorVisualFactory extends ComponentVisualFactoryBase {
5148
- /** Transistor lit color (yellow glow) */
5149
- private static readonly TRANSISTOR_CLOSED_COLOR;
5150
- /** Transistor lit emissive intensity */
5151
- private static readonly TRANSISTOR_CLOSED_INTENSITY;
5152
- /** Shared open Transistor envelope geometry */
5153
- private readonly openGeometry;
5154
- /** Shared transient Transistor envelope geometry */
5155
- private readonly transientGeometry;
5156
- /** Shared transient Transistor envelope geometry */
5157
- private readonly closedGeometry;
5158
- createVisual(component: Component): THREE.Object3D;
5159
- /**
5160
- * Get config form definition for Transistor (T026)
5161
- *
5162
- * @returns Form definition with activationLogic boolean field
5163
- */
5164
- getConfigFormDefinition(): ConfigFormDefinition | null;
5165
- /**
5166
- * Map core config to form data (T026)
5167
- * Converts "positive"/"negative" strings to boolean
5168
- *
5169
- * @param config - Core component config
5170
- * @returns Form data with boolean activationLogic
5171
- */
5172
- mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
5173
- /**
5174
- * Map form data to core config (T026)
5175
- * Converts boolean to "positive"/"negative" strings
5176
- *
5177
- * @param formData - Form data with boolean activationLogic
5178
- * @returns Core config with string activationLogic
5179
- */
5180
- mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
5181
- updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
5182
- /**
5183
- * Update Transistor animation based on simulation state
5184
- *
5185
- * @param object3D - The Object3D created by createVisual()
5186
- * @param state - The Transistor's current simulation state
5187
- */
5188
- updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
5189
- /**
5190
- * Find the envelope mesh within the component group
5191
- *
5192
- * @param object3D - The Object3D group created by createVisual()
5193
- * @returns The envelope mesh if found, null otherwise
5194
- *
5195
- * @remarks
5196
- * Searches for a mesh with userData.part === 'envelope'
5197
- */
5198
- private findEnvelopeMesh;
5199
- }
5200
-
5201
5774
  /**
5202
- * User command to be executed during simulation.
5203
- * Commands can be queued for future ticks or executed immediately.
5204
- *
5205
- * @public
5775
+ * Universally Unique Identifier (RFC 4122 UUID v4).
5206
5776
  */
5207
- declare interface UserCommand {
5208
- /**
5209
- * Type of command.
5210
- */
5211
- readonly type: 'toggle_switch';
5212
- /**
5213
- * UUID of target component.
5214
- */
5215
- readonly targetId: UUID;
5216
- /**
5217
- * tick when this command was scheduled.
5218
- */
5219
- scheduledAtTick: number;
5220
- /**
5221
- * Extra parameters associated with this command.
5222
- *
5223
- * For `toggle_switch` commands:
5224
- * - `tickCount`: Number of ticks for the switch transition. Computed at toggle time
5225
- * using the formula: `ceil(transitionUserSpan × simulationSpeed / 1000)` with minimum of 1.
5226
- * If not provided, behavior uses default transition timing.
5227
- */
5228
- readonly parameters?: Map<string, string> | null;
5229
- }
5777
+ declare type UUID = string;
5230
5778
 
5231
5779
  /**
5232
- * UUID Type and Generation Utilities
5780
+ * Lightweight context passed to visual factories so they can access
5781
+ * ENode data (subtype, source, wires…) when creating pin visuals.
5233
5782
  *
5234
- * Provides RFC 4122 UUID v4 identifiers for all entities in the circuit model.
5235
- * Uses native crypto.randomUUID() when available (modern browsers, Node 19+),
5236
- * with fallback for older environments.
5237
- *
5238
- * @module core/types/Identifier
5239
- */
5240
- /**
5241
- * Universally Unique Identifier (RFC 4122 UUID v4).
5242
- *
5243
- * Used as the primary identifier for all circuit entities:
5244
- * - Circuit
5245
- * - Component
5246
- * - ENode (electrical nodes)
5247
- * - Wire
5248
- *
5249
- * @example
5250
- * ```typescript
5251
- * const id: UUID = generateUUID();
5252
- * console.log(id); // "550e8400-e29b-41d4-a716-446655440000"
5253
- * ```
5783
+ * Circuit satisfies this interface structurally controllers can pass
5784
+ * `this._circuit` directly without a wrapper.
5254
5785
  */
5255
- declare type UUID = string;
5786
+ export declare interface VisualContext {
5787
+ getENode(id: UUID): ENode | undefined;
5788
+ }
5256
5789
 
5257
5790
  /**
5258
5791
  * Electrical connection between two ENodes.
@@ -5361,15 +5894,7 @@ declare class Wire {
5361
5894
  * // }
5362
5895
  * ```
5363
5896
  */
5364
- toJSON(): {
5365
- id: UUID;
5366
- node1: UUID;
5367
- node2: UUID;
5368
- intermediatePositions: {
5369
- x: number;
5370
- y: number;
5371
- }[];
5372
- };
5897
+ toJSON(): IWire;
5373
5898
  /**
5374
5899
  * Deserialize wire from JSON.
5375
5900
  *
@@ -5388,15 +5913,7 @@ declare class Wire {
5388
5913
  * const wire = Wire.fromJSON(json);
5389
5914
  * ```
5390
5915
  */
5391
- static fromJSON(json: {
5392
- id: UUID;
5393
- node1: UUID;
5394
- node2: UUID;
5395
- intermediatePositions: {
5396
- x: number;
5397
- y: number;
5398
- }[];
5399
- }): Wire;
5916
+ static fromJSON(json: IWire): Wire;
5400
5917
  }
5401
5918
 
5402
5919
  /**
@@ -5644,4 +6161,146 @@ export declare class WireVisualManager {
5644
6161
  private clientToContainerCoords;
5645
6162
  }
5646
6163
 
6164
+ /**
6165
+ * Visual factory for XOR gates components
6166
+ *
6167
+ * Creates:
6168
+ * - Gate mesh
6169
+ * - vcc, gnd, inputs and output pin groups
6170
+ * - Component hitbox for raycasting
6171
+ *
6172
+ * Animation:
6173
+ * - Emissive glow when Gate is high (based on simulation state)
6174
+ */
6175
+ export declare class Xor4GateVisualFactory extends XorGateVisualFactory {
6176
+ /** XOR tail geometry */
6177
+ protected readonly tailGeometry: THREE.ExtrudeGeometry;
6178
+ /** Shared open envelope geometry */
6179
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
6180
+ /** Shared transient envelope geometry */
6181
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
6182
+ /** Shared transient envelope geometry */
6183
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
6184
+ /** Shared geometry for negative marker **/
6185
+ protected readonly negativeMarkerGeometry: THREE.CylinderGeometry;
6186
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
6187
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
6188
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
6189
+ }
6190
+
6191
+ /**
6192
+ * Visual factory for XOR gates components
6193
+ *
6194
+ * Creates:
6195
+ * - Gate mesh
6196
+ * - vcc, gnd, inputs and output pin groups
6197
+ * - Component hitbox for raycasting
6198
+ *
6199
+ * Animation:
6200
+ * - Emissive glow when Gate is high (based on simulation state)
6201
+ */
6202
+ export declare class Xor8GateVisualFactory extends XorGateVisualFactory {
6203
+ /** XOR tail geometry */
6204
+ protected readonly tailGeometry: THREE.ExtrudeGeometry;
6205
+ /** Shared open envelope geometry */
6206
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
6207
+ /** Shared transient envelope geometry */
6208
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
6209
+ /** Shared transient envelope geometry */
6210
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
6211
+ /** Shared geometry for negative marker **/
6212
+ protected readonly negativeMarkerGeometry: THREE.CylinderGeometry;
6213
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
6214
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
6215
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
6216
+ }
6217
+
6218
+ /**
6219
+ * Visual factory for XOR gates components
6220
+ *
6221
+ * Creates:
6222
+ * - Gate mesh
6223
+ * - vcc, gnd, inputs and output pin groups
6224
+ * - Component hitbox for raycasting
6225
+ *
6226
+ * Animation:
6227
+ * - Emissive glow when Gate is high (based on simulation state)
6228
+ */
6229
+ export declare class XorGateVisualFactory extends ComponentVisualFactoryBase {
6230
+ /** Gate high color */
6231
+ protected static readonly HIGH_COLOR = 16777215;
6232
+ /** Gate high emissive intensity */
6233
+ protected static readonly HIGH_INTENSITY = 0.3;
6234
+ /** XOR tail geometry */
6235
+ protected readonly tailGeometry: THREE.ExtrudeGeometry;
6236
+ /** Shared open envelope geometry */
6237
+ protected readonly lowGeometry: THREE.ExtrudeGeometry;
6238
+ /** Shared transient envelope geometry */
6239
+ protected readonly transientGeometry: THREE.ExtrudeGeometry;
6240
+ /** Shared transient envelope geometry */
6241
+ protected readonly highGeometry: THREE.ExtrudeGeometry;
6242
+ /** Shared material for negative marker **/
6243
+ protected readonly negativeMarkerMaterial: THREE.MeshStandardMaterial;
6244
+ /** Shared geometry for negative marker **/
6245
+ protected readonly negativeMarkerGeometry: THREE.CylinderGeometry;
6246
+ defaultRotation(): number;
6247
+ createVisual(component: Component, context: VisualContext): THREE.Object3D;
6248
+ protected createPinsVisual(component: Component, context: VisualContext, group: THREE.Group): void;
6249
+ /**
6250
+ * Get config form definition for XOR/XNOR Gate
6251
+ *
6252
+ * @param config - Optional current config to determine disabled state of transitionSpan
6253
+ * @returns Form definition with defaultLogicFamily dropdown, activationLogic boolean, and transitionSpan number
6254
+ */
6255
+ getConfigFormDefinition(config?: Map<string, string>): ConfigFormDefinition | null;
6256
+ /**
6257
+ * Map core config to form data
6258
+ * Converts "positive"/"negative" strings to boolean
6259
+ *
6260
+ * @param config - Core component config
6261
+ * @returns Form data with boolean activationLogic
6262
+ */
6263
+ mapCoreConfigToForm(config: Map<string, string>): Map<string, any>;
6264
+ /**
6265
+ * Map form data to core config
6266
+ * Converts boolean to "positive"/"negative" strings
6267
+ *
6268
+ * @param formData - Form data with boolean activationLogic
6269
+ * @returns Core config with string activationLogic
6270
+ */
6271
+ mapFormToCoreConfig(formData: Map<string, any>): Map<string, string>;
6272
+ updateFromConfiguration(object3D: THREE.Object3D, config: Map<string, string>): void;
6273
+ /**
6274
+ * Update animation based on simulation state
6275
+ *
6276
+ * @param object3D - The Object3D created by createVisual()
6277
+ * @param state - The component current simulation state
6278
+ */
6279
+ updateAnimation(object3D: THREE.Object3D, state: ComponentState | null): void;
6280
+ /**
6281
+ * Find the envelope mesh within the component group
6282
+ *
6283
+ * @param object3D - The Object3D group created by createVisual()
6284
+ * @returns The envelope mesh if found, null otherwise
6285
+ *
6286
+ * @remarks
6287
+ * Searches for a mesh with userData.part === 'envelope'
6288
+ */
6289
+ protected findEnvelopeMesh(object3D: THREE.Object3D): (THREE.Mesh & {
6290
+ material: THREE.MeshStandardMaterial;
6291
+ }) | null;
6292
+ /**
6293
+ * Find the negative marker mesh within the component group
6294
+ *
6295
+ * @param object3D - The Object3D group created by createVisual()
6296
+ * @returns The negative marker mesh if found, null otherwise
6297
+ *
6298
+ * @remarks
6299
+ * Searches for a mesh with userData.part === 'negativeMarker'
6300
+ */
6301
+ protected findNegativeMarkerMesh(object3D: THREE.Object3D): (THREE.Mesh & {
6302
+ material: THREE.MeshStandardMaterial;
6303
+ }) | null;
6304
+ }
6305
+
5647
6306
  export { }