@tscircuit/core 0.0.984 → 0.0.986

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +124 -109
  2. package/dist/index.js +77 -18
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -16,114 +16,6 @@ import { CopperPourPipelineSolver } from '@tscircuit/copper-pour-solver';
16
16
  import { ConnectivityMap } from 'circuit-json-to-connectivity-map';
17
17
  import { GraphicsObject } from 'graphics-debug';
18
18
 
19
- declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "InflateSubcircuitCircuitJson", "SourceNameDuplicateComponentRemoval", "PcbFootprintStringRender", "InitializePortsFromChildren", "CreateNetsFromProps", "AssignFallbackProps", "CreateTracesFromProps", "CreateTracesFromNetLabels", "CreateTraceHintsFromProps", "SourceGroupRender", "AssignNameToUnnamedComponents", "SourceRender", "SourceParentAttachment", "PortMatching", "OptimizeSelectorCache", "SourceTraceRender", "SourceAddConnectivityMapKey", "SourceDesignRuleChecks", "SimulationRender", "SchematicComponentRender", "SchematicPortRender", "SchematicPrimitiveRender", "SchematicComponentSizeCalculation", "SchematicLayout", "SchematicTraceRender", "SchematicReplaceNetLabelsWithSymbols", "PanelBoardLayout", "PcbComponentRender", "PcbPrimitiveRender", "PcbFootprintLayout", "PcbPortRender", "PcbPortAttachment", "PcbComponentSizeCalculation", "PcbComponentAnchorAlignment", "PcbLayout", "PcbBoardAutoSize", "PanelLayout", "PcbTraceHintRender", "PcbManualTraceRender", "PcbTraceRender", "PcbRouteNetIslands", "PcbCopperPourRender", "PcbDesignRuleChecks", "SilkscreenOverlapAdjustment", "CadModelRender", "PartsEngineRender", "SimulationSpiceEngineRender"];
20
- type RenderPhase = (typeof orderedRenderPhases)[number];
21
- type RenderPhaseFn<K extends RenderPhase = RenderPhase> = `doInitial${K}` | `update${K}` | `remove${K}`;
22
- type RenderPhaseStates = Record<RenderPhase, {
23
- initialized: boolean;
24
- dirty: boolean;
25
- }>;
26
- type AsyncEffect = {
27
- effectName: string;
28
- promise: Promise<void>;
29
- phase: RenderPhase;
30
- complete: boolean;
31
- };
32
- type RenderPhaseFunctions = {
33
- [T in RenderPhaseFn]?: () => void;
34
- };
35
- type IRenderable = RenderPhaseFunctions & {
36
- renderPhaseStates: RenderPhaseStates;
37
- runRenderPhase(phase: RenderPhase): void;
38
- runRenderPhaseForChildren(phase: RenderPhase): void;
39
- shouldBeRemoved: boolean;
40
- children: IRenderable[];
41
- runRenderCycle(): void;
42
- };
43
- declare abstract class Renderable implements IRenderable {
44
- renderPhaseStates: RenderPhaseStates;
45
- shouldBeRemoved: boolean;
46
- children: IRenderable[];
47
- /** PCB-only SMTPads, PlatedHoles, Holes, Silkscreen elements etc. */
48
- isPcbPrimitive: boolean;
49
- /** Schematic-only, lines, boxes, indicators etc. */
50
- isSchematicPrimitive: boolean;
51
- _renderId: string;
52
- _currentRenderPhase: RenderPhase | null;
53
- private _asyncEffects;
54
- parent: Renderable | null;
55
- constructor(props: any);
56
- _markDirty(phase: RenderPhase): void;
57
- _queueAsyncEffect(effectName: string, effect: () => Promise<void>): void;
58
- protected _emitRenderLifecycleEvent(phase: RenderPhase, startOrEnd: "start" | "end"): void;
59
- getString(): string;
60
- _hasIncompleteAsyncEffects(): boolean;
61
- _hasIncompleteAsyncEffectsInSubtreeForPhase(phase: RenderPhase): boolean;
62
- getCurrentRenderPhase(): RenderPhase | null;
63
- getRenderGraph(): Record<string, any>;
64
- getTopLevelRenderable(): Renderable;
65
- runRenderCycle(): void;
66
- /**
67
- * This runs all the render methods for a given phase, calling one of:
68
- * - doInitial*
69
- * - update*
70
- * -remove*
71
- * ...depending on the current state of the component.
72
- */
73
- runRenderPhase(phase: RenderPhase): void;
74
- runRenderPhaseForChildren(phase: RenderPhase): void;
75
- renderError(message: string | Omit<PcbTraceError, "pcb_error_id"> | Omit<PcbPlacementError, "pcb_error_id"> | Omit<PcbManualEditConflictWarning, "pcb_error_id"> | Omit<PcbViaClearanceError, "pcb_error_id">): void;
76
- }
77
-
78
- /**
79
- * This is how we render in React. This can be a confusing part of the codebase,
80
- * but here are some helpful reference implementations:
81
- *
82
- * https://github.com/diegomura/react-pdf/blob/fabecc56727dfb6d590a3fa1e11f50250ecbbea1/packages/reconciler/src/reconciler-31.js
83
- * https://github.com/pmndrs/react-three-fiber/blob/ec4f00bb61cc4f6e28b3a12b1dca9daa5594f10e/packages/fiber/src/core/renderer.ts
84
- *
85
- *
86
- */
87
-
88
- type ReactSubtree = {
89
- element: ReactElement;
90
- component: NormalComponent;
91
- };
92
-
93
- type SchematicBoxPortPositionWithMetadata = {
94
- trueIndex: number;
95
- pinNumber: number;
96
- side: "left" | "right" | "top" | "bottom";
97
- distanceFromOrthogonalEdge: number;
98
- x: number;
99
- y: number;
100
- };
101
- interface SchematicBoxDimensions {
102
- pinCount: number;
103
- getPortPositionByPinNumber(pinNumber: number): SchematicBoxPortPositionWithMetadata | null;
104
- getSize(): {
105
- width: number;
106
- height: number;
107
- };
108
- getSizeIncludingPins(): {
109
- width: number;
110
- height: number;
111
- };
112
- }
113
- interface SchematicBoxComponentDimensions {
114
- schWidth: number;
115
- schHeight: number;
116
- }
117
-
118
- interface BoardI {
119
- componentName: string;
120
- boardThickness: number;
121
- _connectedSchematicPortPairs: Set<string>;
122
- allLayers: ReadonlyArray<LayerRef>;
123
- _getBoardCalcVariables(): Record<string, number>;
124
- pcb_board_id?: string | null;
125
- }
126
-
127
19
  declare const SOLVERS: {
128
20
  PackSolver2: typeof PackSolver2;
129
21
  AutoroutingPipelineSolver: typeof AutoroutingPipelineSolver;
@@ -270,6 +162,124 @@ interface DebugLogOutputEvent {
270
162
  content: any;
271
163
  }
272
164
 
165
+ interface IRootCircuit {
166
+ emit(event: RootCircuitEventName, ...args: any[]): void;
167
+ on(event: RootCircuitEventName, listener: (...args: any[]) => void): void;
168
+ _hasIncompleteAsyncEffectsForPhase(phase: RenderPhase): boolean;
169
+ }
170
+
171
+ declare const orderedRenderPhases: readonly ["ReactSubtreesRender", "InflateSubcircuitCircuitJson", "SourceNameDuplicateComponentRemoval", "PcbFootprintStringRender", "InitializePortsFromChildren", "CreateNetsFromProps", "AssignFallbackProps", "CreateTracesFromProps", "CreateTracesFromNetLabels", "CreateTraceHintsFromProps", "SourceGroupRender", "AssignNameToUnnamedComponents", "SourceRender", "SourceParentAttachment", "PortMatching", "OptimizeSelectorCache", "SourceTraceRender", "SourceAddConnectivityMapKey", "SourceDesignRuleChecks", "SimulationRender", "SchematicComponentRender", "SchematicPortRender", "SchematicPrimitiveRender", "SchematicComponentSizeCalculation", "SchematicLayout", "SchematicTraceRender", "SchematicReplaceNetLabelsWithSymbols", "PanelBoardLayout", "PcbComponentRender", "PcbPrimitiveRender", "PcbFootprintLayout", "PcbPortRender", "PcbPortAttachment", "PcbComponentSizeCalculation", "PcbComponentAnchorAlignment", "PcbLayout", "PcbBoardAutoSize", "PanelLayout", "PcbTraceHintRender", "PcbManualTraceRender", "PcbTraceRender", "PcbRouteNetIslands", "PcbCopperPourRender", "PcbDesignRuleChecks", "SilkscreenOverlapAdjustment", "CadModelRender", "PartsEngineRender", "SimulationSpiceEngineRender"];
172
+ type RenderPhase = (typeof orderedRenderPhases)[number];
173
+ declare const renderPhaseIndexMap: Map<"ReactSubtreesRender" | "InflateSubcircuitCircuitJson" | "SourceNameDuplicateComponentRemoval" | "PcbFootprintStringRender" | "InitializePortsFromChildren" | "CreateNetsFromProps" | "AssignFallbackProps" | "CreateTracesFromProps" | "CreateTracesFromNetLabels" | "CreateTraceHintsFromProps" | "SourceGroupRender" | "AssignNameToUnnamedComponents" | "SourceRender" | "SourceParentAttachment" | "PortMatching" | "OptimizeSelectorCache" | "SourceTraceRender" | "SourceAddConnectivityMapKey" | "SourceDesignRuleChecks" | "SimulationRender" | "SchematicComponentRender" | "SchematicPortRender" | "SchematicPrimitiveRender" | "SchematicComponentSizeCalculation" | "SchematicLayout" | "SchematicTraceRender" | "SchematicReplaceNetLabelsWithSymbols" | "PanelBoardLayout" | "PcbComponentRender" | "PcbPrimitiveRender" | "PcbFootprintLayout" | "PcbPortRender" | "PcbPortAttachment" | "PcbComponentSizeCalculation" | "PcbComponentAnchorAlignment" | "PcbLayout" | "PcbBoardAutoSize" | "PanelLayout" | "PcbTraceHintRender" | "PcbManualTraceRender" | "PcbTraceRender" | "PcbRouteNetIslands" | "PcbCopperPourRender" | "PcbDesignRuleChecks" | "SilkscreenOverlapAdjustment" | "CadModelRender" | "PartsEngineRender" | "SimulationSpiceEngineRender", number>;
174
+ type RenderPhaseFn<K extends RenderPhase = RenderPhase> = `doInitial${K}` | `update${K}` | `remove${K}`;
175
+ type RenderPhaseStates = Record<RenderPhase, {
176
+ initialized: boolean;
177
+ dirty: boolean;
178
+ }>;
179
+ type AsyncEffect = {
180
+ asyncEffectId: string;
181
+ effectName: string;
182
+ promise: Promise<void>;
183
+ phase: RenderPhase;
184
+ complete: boolean;
185
+ };
186
+ type RenderPhaseFunctions = {
187
+ [T in RenderPhaseFn]?: () => void;
188
+ };
189
+ type IRenderable = RenderPhaseFunctions & {
190
+ renderPhaseStates: RenderPhaseStates;
191
+ runRenderPhase(phase: RenderPhase): void;
192
+ runRenderPhaseForChildren(phase: RenderPhase): void;
193
+ shouldBeRemoved: boolean;
194
+ children: IRenderable[];
195
+ runRenderCycle(): void;
196
+ };
197
+ declare abstract class Renderable implements IRenderable {
198
+ renderPhaseStates: RenderPhaseStates;
199
+ shouldBeRemoved: boolean;
200
+ children: IRenderable[];
201
+ /** PCB-only SMTPads, PlatedHoles, Holes, Silkscreen elements etc. */
202
+ isPcbPrimitive: boolean;
203
+ /** Schematic-only, lines, boxes, indicators etc. */
204
+ isSchematicPrimitive: boolean;
205
+ _renderId: string;
206
+ _currentRenderPhase: RenderPhase | null;
207
+ private _asyncEffects;
208
+ parent: Renderable | null;
209
+ constructor(props: any);
210
+ _markDirty(phase: RenderPhase): void;
211
+ _queueAsyncEffect(effectName: string, effect: () => Promise<void>): void;
212
+ protected _emitRenderLifecycleEvent(phase: RenderPhase, startOrEnd: "start" | "end"): void;
213
+ getString(): string;
214
+ _hasIncompleteAsyncEffects(): boolean;
215
+ _hasIncompleteAsyncEffectsInSubtreeForPhase(phase: RenderPhase): boolean;
216
+ _hasIncompleteAsyncEffectsForPhase(phase: RenderPhase): boolean;
217
+ getCurrentRenderPhase(): RenderPhase | null;
218
+ getRenderGraph(): Record<string, any>;
219
+ getTopLevelRenderable(): Renderable;
220
+ runRenderCycle(): void;
221
+ /**
222
+ * This runs all the render methods for a given phase, calling one of:
223
+ * - doInitial*
224
+ * - update*
225
+ * -remove*
226
+ * ...depending on the current state of the component.
227
+ */
228
+ runRenderPhase(phase: RenderPhase): void;
229
+ runRenderPhaseForChildren(phase: RenderPhase): void;
230
+ protected _getRootCircuit(): IRootCircuit | null;
231
+ renderError(message: string | Omit<PcbTraceError, "pcb_error_id"> | Omit<PcbPlacementError, "pcb_error_id"> | Omit<PcbManualEditConflictWarning, "pcb_error_id"> | Omit<PcbViaClearanceError, "pcb_error_id">): void;
232
+ }
233
+
234
+ /**
235
+ * This is how we render in React. This can be a confusing part of the codebase,
236
+ * but here are some helpful reference implementations:
237
+ *
238
+ * https://github.com/diegomura/react-pdf/blob/fabecc56727dfb6d590a3fa1e11f50250ecbbea1/packages/reconciler/src/reconciler-31.js
239
+ * https://github.com/pmndrs/react-three-fiber/blob/ec4f00bb61cc4f6e28b3a12b1dca9daa5594f10e/packages/fiber/src/core/renderer.ts
240
+ *
241
+ *
242
+ */
243
+
244
+ type ReactSubtree = {
245
+ element: ReactElement;
246
+ component: NormalComponent;
247
+ };
248
+
249
+ type SchematicBoxPortPositionWithMetadata = {
250
+ trueIndex: number;
251
+ pinNumber: number;
252
+ side: "left" | "right" | "top" | "bottom";
253
+ distanceFromOrthogonalEdge: number;
254
+ x: number;
255
+ y: number;
256
+ };
257
+ interface SchematicBoxDimensions {
258
+ pinCount: number;
259
+ getPortPositionByPinNumber(pinNumber: number): SchematicBoxPortPositionWithMetadata | null;
260
+ getSize(): {
261
+ width: number;
262
+ height: number;
263
+ };
264
+ getSizeIncludingPins(): {
265
+ width: number;
266
+ height: number;
267
+ };
268
+ }
269
+ interface SchematicBoxComponentDimensions {
270
+ schWidth: number;
271
+ schHeight: number;
272
+ }
273
+
274
+ interface BoardI {
275
+ componentName: string;
276
+ boardThickness: number;
277
+ _connectedSchematicPortPairs: Set<string>;
278
+ allLayers: ReadonlyArray<LayerRef>;
279
+ _getBoardCalcVariables(): Record<string, number>;
280
+ pcb_board_id?: string | null;
281
+ }
282
+
273
283
  declare function resolveStaticFileImport(path: string, platform?: PlatformConfig): Promise<string>;
274
284
 
275
285
  declare class RootCircuit {
@@ -298,6 +308,8 @@ declare class RootCircuit {
298
308
  */
299
309
  projectUrl?: string;
300
310
  _hasRenderedAtleastOnce: boolean;
311
+ private _asyncEffectIdsByPhase;
312
+ private _asyncEffectPhaseById;
301
313
  constructor({ platform, projectUrl, }?: {
302
314
  platform?: PlatformConfig;
303
315
  projectUrl?: string;
@@ -312,6 +324,7 @@ declare class RootCircuit {
312
324
  render(): void;
313
325
  renderUntilSettled(): Promise<void>;
314
326
  private _hasIncompleteAsyncEffects;
327
+ _hasIncompleteAsyncEffectsForPhase(phase: RenderPhase): boolean;
315
328
  getCircuitJson(): AnyCircuitElement[];
316
329
  toJson(): AnyCircuitElement[];
317
330
  getSvg(options: {
@@ -335,6 +348,8 @@ declare class RootCircuit {
335
348
  removeListener(event: RootCircuitEventName, listener: (...args: any[]) => void): void;
336
349
  enableDebug(debug: string | null | false): void;
337
350
  getClientOrigin(): string;
351
+ private _registerAsyncEffectStart;
352
+ private _registerAsyncEffectEnd;
338
353
  }
339
354
  /**
340
355
  * @deprecated
@@ -82669,4 +82684,4 @@ declare module "react/jsx-runtime" {
82669
82684
  }
82670
82685
  }
82671
82686
 
82672
- export { AnalogSimulation, type AsyncEffect, type AutorouterCompleteEvent, type AutorouterErrorEvent, type AutorouterEvent, type AutorouterProgressEvent, type AutoroutingEndEvent, type AutoroutingErrorEvent, type AutoroutingProgressEvent, type AutoroutingStartEvent, Battery, Board, Breakout, BreakoutPoint, CadAssembly, CadModel, Capacitor, Chip, Circuit, type ComponentWithPins, Constraint, CopperPour, CopperText, Crystal, CurrentSource, Cutout, type DebugLogOutputEvent, Diode, FabricationNoteDimension, FabricationNotePath, FabricationNoteRect, FabricationNoteText, Fiducial, Footprint, Fuse, type GenericConnectionsAndSelectorsSel, type GenericLocalAutorouter, Group, Hole, type IRenderable, Inductor, Interconnect, Jumper, Keepout, Led, type LocalCacheEngine, Mosfet, Net, NetLabel, NormalComponent, type Obstacle, OpAmp, type PackingEndEvent, type PackingErrorEvent, type PackingStartEvent, Panel, PcbNoteDimension, PcbNoteLine, PcbNotePath, PcbNoteRect, PcbNoteText, PcbTrace, PinHeader, type PinLabelSpec, Pinout, PlatedHole, Port, Potentiometer, PowerSource, PrimitiveComponent, Project, PushButton, type RenderPhase, type RenderPhaseFn, type RenderPhaseFunctions, type RenderPhaseStates, Renderable, Resistor, Resonator, RootCircuit, type RootCircuitEventName, SOLVERS, SchematicArc, SchematicBox, SchematicCell, SchematicCircle, SchematicLine, SchematicPath, SchematicRect, SchematicRow, SchematicTable, SchematicText, type Sel, SilkscreenCircle, SilkscreenLine, SilkscreenPath, SilkscreenRect, SilkscreenText, type SimpleRouteConnection, type SimpleRouteJson, type SimplifiedPcbTrace, SmtPad, SolderJumper, type SolverName, type SolverStartedEvent, Subcircuit, Switch, SymbolComponent as Symbol, TestPoint, Trace, TraceHint, Transistor, Via, VoltageProbe, VoltageSource, applyEditEvents, applyEditEventsToManualEditsFile, applyPcbEditEventsToManualEditsFile, applySchematicEditEventsToManualEditsFile, createUseComponent, getPhaseTimingsFromRenderEvents, getSimpleRouteJsonFromCircuitJson, normalizePinLabels, orderedRenderPhases, resolveStaticFileImport, sel, useCapacitor, useChip, useDiode, useLed, useRenderedCircuit, useResistor };
82687
+ export { AnalogSimulation, type AsyncEffect, type AutorouterCompleteEvent, type AutorouterErrorEvent, type AutorouterEvent, type AutorouterProgressEvent, type AutoroutingEndEvent, type AutoroutingErrorEvent, type AutoroutingProgressEvent, type AutoroutingStartEvent, Battery, Board, Breakout, BreakoutPoint, CadAssembly, CadModel, Capacitor, Chip, Circuit, type ComponentWithPins, Constraint, CopperPour, CopperText, Crystal, CurrentSource, Cutout, type DebugLogOutputEvent, Diode, FabricationNoteDimension, FabricationNotePath, FabricationNoteRect, FabricationNoteText, Fiducial, Footprint, Fuse, type GenericConnectionsAndSelectorsSel, type GenericLocalAutorouter, Group, Hole, type IRenderable, Inductor, Interconnect, Jumper, Keepout, Led, type LocalCacheEngine, Mosfet, Net, NetLabel, NormalComponent, type Obstacle, OpAmp, type PackingEndEvent, type PackingErrorEvent, type PackingStartEvent, Panel, PcbNoteDimension, PcbNoteLine, PcbNotePath, PcbNoteRect, PcbNoteText, PcbTrace, PinHeader, type PinLabelSpec, Pinout, PlatedHole, Port, Potentiometer, PowerSource, PrimitiveComponent, Project, PushButton, type RenderPhase, type RenderPhaseFn, type RenderPhaseFunctions, type RenderPhaseStates, Renderable, Resistor, Resonator, RootCircuit, type RootCircuitEventName, SOLVERS, SchematicArc, SchematicBox, SchematicCell, SchematicCircle, SchematicLine, SchematicPath, SchematicRect, SchematicRow, SchematicTable, SchematicText, type Sel, SilkscreenCircle, SilkscreenLine, SilkscreenPath, SilkscreenRect, SilkscreenText, type SimpleRouteConnection, type SimpleRouteJson, type SimplifiedPcbTrace, SmtPad, SolderJumper, type SolverName, type SolverStartedEvent, Subcircuit, Switch, SymbolComponent as Symbol, TestPoint, Trace, TraceHint, Transistor, Via, VoltageProbe, VoltageSource, applyEditEvents, applyEditEventsToManualEditsFile, applyPcbEditEventsToManualEditsFile, applySchematicEditEventsToManualEditsFile, createUseComponent, getPhaseTimingsFromRenderEvents, getSimpleRouteJsonFromCircuitJson, normalizePinLabels, orderedRenderPhases, renderPhaseIndexMap, resolveStaticFileImport, sel, useCapacitor, useChip, useDiode, useLed, useRenderedCircuit, useResistor };
package/dist/index.js CHANGED
@@ -161,6 +161,9 @@ var orderedRenderPhases = [
161
161
  "PartsEngineRender",
162
162
  "SimulationSpiceEngineRender"
163
163
  ];
164
+ var renderPhaseIndexMap = new Map(
165
+ orderedRenderPhases.map((phase, index) => [phase, index])
166
+ );
164
167
  var asyncPhaseDependencies = {
165
168
  PcbFootprintLayout: ["PcbFootprintStringRender"],
166
169
  PcbComponentSizeCalculation: ["PcbFootprintStringRender"],
@@ -182,6 +185,7 @@ var asyncPhaseDependencies = {
182
185
  PcbComponentAnchorAlignment: ["PcbFootprintStringRender"]
183
186
  };
184
187
  var globalRenderCounter = 0;
188
+ var globalAsyncEffectCounter = 0;
185
189
  var Renderable = class _Renderable {
186
190
  renderPhaseStates;
187
191
  shouldBeRemoved = false;
@@ -207,7 +211,7 @@ var Renderable = class _Renderable {
207
211
  }
208
212
  _markDirty(phase) {
209
213
  this.renderPhaseStates[phase].dirty = true;
210
- const phaseIndex = orderedRenderPhases.indexOf(phase);
214
+ const phaseIndex = renderPhaseIndexMap.get(phase);
211
215
  for (let i = phaseIndex + 1; i < orderedRenderPhases.length; i++) {
212
216
  this.renderPhaseStates[orderedRenderPhases[i]].dirty = true;
213
217
  }
@@ -216,7 +220,9 @@ var Renderable = class _Renderable {
216
220
  }
217
221
  }
218
222
  _queueAsyncEffect(effectName, effect) {
223
+ const asyncEffectId = `${this._renderId}:${globalAsyncEffectCounter++}`;
219
224
  const asyncEffect = {
225
+ asyncEffectId,
220
226
  promise: effect(),
221
227
  // TODO don't start effects until end of render cycle
222
228
  phase: this._currentRenderPhase,
@@ -224,9 +230,10 @@ var Renderable = class _Renderable {
224
230
  complete: false
225
231
  };
226
232
  this._asyncEffects.push(asyncEffect);
227
- if ("root" in this && this.root) {
228
- ;
229
- this.root.emit("asyncEffect:start", {
233
+ const root = this._getRootCircuit();
234
+ if (root) {
235
+ root.emit("asyncEffect:start", {
236
+ asyncEffectId,
230
237
  effectName,
231
238
  componentDisplayName: this.getString(),
232
239
  phase: asyncEffect.phase
@@ -234,9 +241,10 @@ var Renderable = class _Renderable {
234
241
  }
235
242
  asyncEffect.promise.then(() => {
236
243
  asyncEffect.complete = true;
237
- if ("root" in this && this.root) {
238
- ;
239
- this.root.emit("asyncEffect:end", {
244
+ const root2 = this._getRootCircuit();
245
+ if (root2) {
246
+ root2.emit("asyncEffect:end", {
247
+ asyncEffectId,
240
248
  effectName,
241
249
  componentDisplayName: this.getString(),
242
250
  phase: asyncEffect.phase
@@ -248,9 +256,10 @@ var Renderable = class _Renderable {
248
256
  ${error.stack}`
249
257
  );
250
258
  asyncEffect.complete = true;
251
- if ("root" in this && this.root) {
252
- ;
253
- this.root.emit("asyncEffect:end", {
259
+ const root2 = this._getRootCircuit();
260
+ if (root2) {
261
+ root2.emit("asyncEffect:end", {
262
+ asyncEffectId,
254
263
  effectName,
255
264
  componentDisplayName: this.getString(),
256
265
  phase: asyncEffect.phase,
@@ -267,10 +276,10 @@ ${error.stack}`
267
276
  componentDisplayName: this.getString(),
268
277
  type: granular_event_type
269
278
  };
270
- if ("root" in this && this.root) {
271
- ;
272
- this.root.emit(granular_event_type, eventPayload);
273
- this.root.emit("renderable:renderLifecycle:anyEvent", {
279
+ const root = this._getRootCircuit();
280
+ if (root) {
281
+ root.emit(granular_event_type, eventPayload);
282
+ root.emit("renderable:renderLifecycle:anyEvent", {
274
283
  ...eventPayload,
275
284
  type: granular_event_type
276
285
  });
@@ -296,6 +305,13 @@ ${error.stack}`
296
305
  }
297
306
  return false;
298
307
  }
308
+ _hasIncompleteAsyncEffectsForPhase(phase) {
309
+ const root = this._getRootCircuit();
310
+ if (root?._hasIncompleteAsyncEffectsForPhase) {
311
+ return root._hasIncompleteAsyncEffectsForPhase(phase);
312
+ }
313
+ return this._hasIncompleteAsyncEffectsInSubtreeForPhase(phase);
314
+ }
299
315
  getCurrentRenderPhase() {
300
316
  return this._currentRenderPhase;
301
317
  }
@@ -344,7 +360,7 @@ ${error.stack}`
344
360
  this._emitRenderLifecycleEvent(phase, "end");
345
361
  return;
346
362
  }
347
- const prevPhaseIndex = orderedRenderPhases.indexOf(phase) - 1;
363
+ const prevPhaseIndex = renderPhaseIndexMap.get(phase) - 1;
348
364
  if (prevPhaseIndex >= 0) {
349
365
  const prevPhase = orderedRenderPhases[prevPhaseIndex];
350
366
  const hasIncompleteEffects = this._asyncEffects.filter((e) => e.phase === prevPhase).some((e) => !e.complete);
@@ -352,9 +368,8 @@ ${error.stack}`
352
368
  }
353
369
  const deps = asyncPhaseDependencies[phase] || [];
354
370
  if (deps.length > 0) {
355
- const root = this.getTopLevelRenderable();
356
371
  for (const depPhase of deps) {
357
- if (root._hasIncompleteAsyncEffectsInSubtreeForPhase(depPhase)) {
372
+ if (this._hasIncompleteAsyncEffectsForPhase(depPhase)) {
358
373
  return;
359
374
  }
360
375
  }
@@ -380,6 +395,12 @@ ${error.stack}`
380
395
  child.runRenderPhase(phase);
381
396
  }
382
397
  }
398
+ _getRootCircuit() {
399
+ if ("root" in this) {
400
+ return this.root ?? null;
401
+ }
402
+ return null;
403
+ }
383
404
  renderError(message) {
384
405
  if (typeof message === "string") {
385
406
  throw new Error(message);
@@ -20999,7 +21020,7 @@ import { identity as identity5 } from "transformation-matrix";
20999
21020
  var package_default = {
21000
21021
  name: "@tscircuit/core",
21001
21022
  type: "module",
21002
- version: "0.0.983",
21023
+ version: "0.0.985",
21003
21024
  types: "dist/index.d.ts",
21004
21025
  main: "dist/index.js",
21005
21026
  module: "dist/index.js",
@@ -21149,6 +21170,8 @@ var RootCircuit = class {
21149
21170
  */
21150
21171
  projectUrl;
21151
21172
  _hasRenderedAtleastOnce = false;
21173
+ _asyncEffectIdsByPhase = /* @__PURE__ */ new Map();
21174
+ _asyncEffectPhaseById = /* @__PURE__ */ new Map();
21152
21175
  constructor({
21153
21176
  platform,
21154
21177
  projectUrl
@@ -21241,8 +21264,12 @@ var RootCircuit = class {
21241
21264
  this.emit("renderComplete");
21242
21265
  }
21243
21266
  _hasIncompleteAsyncEffects() {
21267
+ if (this._asyncEffectPhaseById.size > 0) return true;
21244
21268
  return this.children.some((child) => child._hasIncompleteAsyncEffects());
21245
21269
  }
21270
+ _hasIncompleteAsyncEffectsForPhase(phase) {
21271
+ return (this._asyncEffectIdsByPhase.get(phase)?.size ?? 0) > 0;
21272
+ }
21246
21273
  getCircuitJson() {
21247
21274
  if (!this._hasRenderedAtleastOnce) this.render();
21248
21275
  return this.db.toArray();
@@ -21292,6 +21319,11 @@ var RootCircuit = class {
21292
21319
  }
21293
21320
  _eventListeners = {};
21294
21321
  emit(event, ...args) {
21322
+ if (event === "asyncEffect:start") {
21323
+ this._registerAsyncEffectStart(args[0]);
21324
+ } else if (event === "asyncEffect:end") {
21325
+ this._registerAsyncEffectEnd(args[0]);
21326
+ }
21295
21327
  if (!this._eventListeners[event]) return;
21296
21328
  for (const listener of this._eventListeners[event]) {
21297
21329
  listener(...args);
@@ -21325,6 +21357,32 @@ var RootCircuit = class {
21325
21357
  }
21326
21358
  return "";
21327
21359
  }
21360
+ _registerAsyncEffectStart(payload) {
21361
+ if (!payload?.asyncEffectId || !payload.phase) return;
21362
+ const { asyncEffectId, phase } = payload;
21363
+ const existingPhase = this._asyncEffectPhaseById.get(asyncEffectId);
21364
+ if (existingPhase && existingPhase !== phase) {
21365
+ this._asyncEffectIdsByPhase.get(existingPhase)?.delete(asyncEffectId);
21366
+ }
21367
+ if (!this._asyncEffectIdsByPhase.has(phase)) {
21368
+ this._asyncEffectIdsByPhase.set(phase, /* @__PURE__ */ new Set());
21369
+ }
21370
+ this._asyncEffectIdsByPhase.get(phase).add(asyncEffectId);
21371
+ this._asyncEffectPhaseById.set(asyncEffectId, phase);
21372
+ }
21373
+ _registerAsyncEffectEnd(payload) {
21374
+ if (!payload?.asyncEffectId) return;
21375
+ const { asyncEffectId } = payload;
21376
+ const phase = this._asyncEffectPhaseById.get(asyncEffectId) ?? payload.phase;
21377
+ if (phase) {
21378
+ const phaseSet = this._asyncEffectIdsByPhase.get(phase);
21379
+ phaseSet?.delete(asyncEffectId);
21380
+ if (phaseSet && phaseSet.size === 0) {
21381
+ this._asyncEffectIdsByPhase.delete(phase);
21382
+ }
21383
+ }
21384
+ this._asyncEffectPhaseById.delete(asyncEffectId);
21385
+ }
21328
21386
  };
21329
21387
  var Project = RootCircuit;
21330
21388
  var Circuit = RootCircuit;
@@ -21630,6 +21688,7 @@ export {
21630
21688
  getSimpleRouteJsonFromCircuitJson,
21631
21689
  normalizePinLabels,
21632
21690
  orderedRenderPhases,
21691
+ renderPhaseIndexMap,
21633
21692
  resolveStaticFileImport,
21634
21693
  sel,
21635
21694
  useCapacitor,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.984",
4
+ "version": "0.0.986",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",