diff --git a/lib/components/base-components/NormalComponent/NormalComponent_doInitialPcbFootprintStringRender.ts b/lib/components/base-components/NormalComponent/NormalComponent_doInitialPcbFootprintStringRender.ts index 550a9245c..0f27761df 100644 --- a/lib/components/base-components/NormalComponent/NormalComponent_doInitialPcbFootprintStringRender.ts +++ b/lib/components/base-components/NormalComponent/NormalComponent_doInitialPcbFootprintStringRender.ts @@ -56,7 +56,6 @@ export function NormalComponent_doInitialPcbFootprintStringRender( result.footprintCircuitJson, ) component.addAll(fpComponents) - component._markDirty("InitializePortsFromChildren") } catch (err) { const db = component.root?.db if (db && component.source_component_id && component.pcb_component_id) { @@ -102,7 +101,6 @@ export function NormalComponent_doInitialPcbFootprintStringRender( soup as any, ) component.addAll(fpComponents) - component._markDirty("InitializePortsFromChildren") } catch (err) { const db = component.root?.db if (db && component.source_component_id && component.pcb_component_id) { @@ -174,13 +172,6 @@ export function NormalComponent_doInitialPcbFootprintStringRender( if (!Array.isArray(result) && result.cadModel) { component._asyncFootprintCadModel = result.cadModel } - // Ensure existing Ports re-run PcbPortRender now that pads exist - for (const child of component.children) { - if (child.componentName === "Port") { - child._markDirty?.("PcbPortRender") - } - } - component._markDirty("InitializePortsFromChildren") } catch (err) { const db = component.root?.db if (db && component.source_component_id && component.pcb_component_id) { diff --git a/lib/components/base-components/Renderable.ts b/lib/components/base-components/Renderable.ts index 63fd9fd17..8ab458396 100644 --- a/lib/components/base-components/Renderable.ts +++ b/lib/components/base-components/Renderable.ts @@ -233,7 +233,7 @@ export abstract class Renderable implements IRenderable { return this._asyncEffects.some((effect) => !effect.complete) } - private _hasIncompleteAsyncEffectsInSubtreeForPhase( + protected _hasIncompleteAsyncEffectsInSubtreeForPhase( phase: RenderPhase, ): boolean { // Check self @@ -249,6 +249,14 @@ export abstract class Renderable implements IRenderable { return false } + private _getRootRenderable(): Renderable { + let node: Renderable = this + while (node.parent && node.parent instanceof Renderable) { + node = node.parent + } + return node + } + getCurrentRenderPhase(): RenderPhase | null { return this._currentRenderPhase } @@ -307,10 +315,14 @@ export abstract class Renderable implements IRenderable { if (hasIncompleteEffects) return } - // Check declared async dependencies for this phase within subtree const deps = asyncPhaseDependencies[phase] || [] - for (const depPhase of deps) { - if (this._hasIncompleteAsyncEffectsInSubtreeForPhase(depPhase)) return + if (deps.length > 0) { + const root = this._getRootRenderable() + const checkNode = (root.children?.[0] as any) || root + for (const depPhase of deps) { + if (checkNode._hasIncompleteAsyncEffectsInSubtreeForPhase?.(depPhase)) + return + } } this._emitRenderLifecycleEvent(phase, "start") diff --git a/lib/components/primitive-components/Port/Port.ts b/lib/components/primitive-components/Port/Port.ts index f62f5f495..96a24d342 100644 --- a/lib/components/primitive-components/Port/Port.ts +++ b/lib/components/primitive-components/Port/Port.ts @@ -245,6 +245,9 @@ export class Port extends PrimitiveComponent { */ registerMatch(component: PrimitiveComponent) { this.matchedComponents.push(component) + if (this.renderPhaseStates.PcbPortRender.initialized && !this.pcb_port_id) { + this._markDirty("PcbPortRender") + } } getNameAndAliases() { const { _parsedProps: props } = this diff --git a/tests/repros/__snapshots__/repro-kicad-footprint-pcbpath-selector1-pcb.snap.svg b/tests/repros/__snapshots__/repro-kicad-footprint-pcbpath-selector1-pcb.snap.svg new file mode 100644 index 000000000..57225ba00 --- /dev/null +++ b/tests/repros/__snapshots__/repro-kicad-footprint-pcbpath-selector1-pcb.snap.svg @@ -0,0 +1 @@ +R1R1R2R2 \ No newline at end of file diff --git a/tests/repros/repro-kicad-footprint-pcbpath-selector1.test.tsx b/tests/repros/repro-kicad-footprint-pcbpath-selector1.test.tsx new file mode 100644 index 000000000..6487a091a --- /dev/null +++ b/tests/repros/repro-kicad-footprint-pcbpath-selector1.test.tsx @@ -0,0 +1,58 @@ +import { test, expect } from "bun:test" +import { getTestFixture } from "tests/fixtures/get-test-fixture" +import kicadModJson from "tests/fixtures/assets/R_0402_1005Metric.json" with { + type: "json", +} + +test("trace pcbPath selectors work with kicad footprints", async () => { + const { circuit } = getTestFixture() + + circuit.platform = { + footprintLibraryMap: { + kicad: async (footprintName: string) => { + return { + footprintCircuitJson: kicadModJson, + } + }, + }, + } + + circuit.add( + + + + + , + ) + + await circuit.renderUntilSettled() + + const selectorErrors = circuit.db.pcb_trace_error + .list() + .filter((e) => e.message?.includes("Could not resolve pcbPath selector")) + expect(selectorErrors.length).toBe(0) + + const pcbTrace = circuit.db.pcb_trace.list()[0] + expect(pcbTrace).toBeDefined() + expect(pcbTrace.route.length).toBeGreaterThanOrEqual(3) + + await expect(circuit).toMatchPcbSnapshot(import.meta.path) +})