fix: Resolve replaceChild and activeElement errors in FolkJS components

- Fix activeElement undefined error by guarding against missing shadowRoot
- Fix replaceChild "parameter 2 is not of type Node" error in all 15 child
  components by using :scope > div selector to find container div directly
  instead of incorrectly searching inside slot.parentElement

The bug was caused by looking for a nested div that doesn't exist - the slot's
parent IS the container div that needs to be replaced.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-01-03 13:38:06 +01:00
parent 8eef5b58b7
commit 42b29ff9d7
16 changed files with 66 additions and 102 deletions

View File

@ -269,13 +269,10 @@ export class FolkCalendar extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
// Get element references

View File

@ -238,13 +238,10 @@ export class FolkChat extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
// Get element references

View File

@ -258,13 +258,10 @@ export class FolkEmbed extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
const content = wrapper.querySelector(".content") as HTMLElement;

View File

@ -229,13 +229,11 @@ export class FolkGoogleItem extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper.querySelector(".card")!, existingDiv);
}
// Replace the container div (slot's parent) with our card
const containerDiv = root.querySelector(":scope > div");
const cardEl = wrapper.querySelector(".card");
if (containerDiv && cardEl) {
containerDiv.replaceWith(cardEl);
}
// Toggle visibility on badge click

View File

@ -264,13 +264,10 @@ export class FolkImageGen extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#promptInput = wrapper.querySelector(".prompt-input");

View File

@ -308,13 +308,10 @@ export class FolkMap extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#mapEl = wrapper.querySelector(".map");

View File

@ -173,10 +173,10 @@ export class FolkMarkdown extends FolkShape {
</div>
`;
// Move existing slot content into our wrapper
const slot = root.querySelector("slot");
if (slot) {
slot.parentElement?.replaceChild(wrapper, slot.parentElement.querySelector("div")!);
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
// Get references to elements

View File

@ -366,13 +366,10 @@ export class FolkObsNote extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#editor = wrapper.querySelector(".editor");

View File

@ -195,13 +195,11 @@ export class FolkPiano extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper.querySelector(".piano-container")!, existingDiv);
}
// Replace the container div (slot's parent) with our piano container
const containerDiv = root.querySelector(":scope > div");
const pianoContainer = wrapper.querySelector(".piano-container");
if (containerDiv && pianoContainer) {
containerDiv.replaceWith(pianoContainer);
}
// Get references

View File

@ -293,13 +293,10 @@ export class FolkPrompt extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#messagesEl = wrapper.querySelector(".messages");

View File

@ -356,7 +356,9 @@ export class FolkShape extends FolkElement {
return;
}
const focusedElement = (this.renderRoot as ShadowRoot).activeElement as HTMLElement | null;
const shadowRoot = this.renderRoot as ShadowRoot | undefined;
if (!shadowRoot) return;
const focusedElement = shadowRoot.activeElement as HTMLElement | null;
const target = event.composedPath()[0] as HTMLElement;
let handle: Handle | null = null;
if (target) {

View File

@ -88,12 +88,11 @@ export class FolkSlide extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
slot.parentElement.replaceChild(
wrapper.querySelector(".slide-container")!,
slot.parentElement.querySelector("div")!
);
// Replace the container div (slot's parent) with our slide container
const containerDiv = root.querySelector(":scope > div");
const slideContainer = wrapper.querySelector(".slide-container");
if (containerDiv && slideContainer) {
containerDiv.replaceWith(slideContainer);
}
// Update label from attribute

View File

@ -349,13 +349,10 @@ export class FolkTranscription extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#recordBtn = wrapper.querySelector(".record-btn");

View File

@ -325,13 +325,10 @@ export class FolkVideoChat extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
const content = wrapper.querySelector(".content") as HTMLElement;

View File

@ -359,13 +359,10 @@ export class FolkVideoGen extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#promptInput = wrapper.querySelector(".prompt-input");

View File

@ -340,13 +340,10 @@ export class FolkWorkflowBlock extends FolkShape {
</div>
`;
const slot = root.querySelector("slot");
if (slot?.parentElement) {
const parent = slot.parentElement;
const existingDiv = parent.querySelector("div");
if (existingDiv) {
parent.replaceChild(wrapper, existingDiv);
}
// Replace the container div (slot's parent) with our wrapper
const containerDiv = root.querySelector(":scope > div");
if (containerDiv) {
containerDiv.replaceWith(wrapper);
}
this.#contentEl = wrapper.querySelector(".content");