From 0828d10ab55a46c010dfbeaf8a51bdffb744f4fd Mon Sep 17 00:00:00 2001 From: Mike Thicke Date: Sat, 30 May 2026 23:14:29 -0400 Subject: [PATCH] Defer CM6 mount to onMount so the host is attached when EditorView reads it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After externalising CM6, Obsidian's flavour of EditorView crashed in its constructor with "Cannot read properties of null (reading 'CSSStyleSheet')" — it dereferences `parent.ownerDocument.defaultView` during style mounting, and Solid's `ref={...}` callback fires before the element is appended to the document, so defaultView was null. Move the mountComposer call from a ref callback into onMount and capture the host element through a separate ref assignment. The host is in the document by the time onMount runs, so EditorView's style mounting finds a real window. Also fixes the "failed to open" notice that came from the failed view construction. Co-Authored-By: Claude Opus 4.7 --- src/components/UserInput.tsx | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/UserInput.tsx b/src/components/UserInput.tsx index 36ed535..fcf6a76 100644 --- a/src/components/UserInput.tsx +++ b/src/components/UserInput.tsx @@ -4,6 +4,7 @@ import { createSignal, createEffect, onCleanup, + onMount, useContext, Show, } from "solid-js"; @@ -55,9 +56,17 @@ export const UserInput: Component = ({ const [composer, setComposer] = createSignal(null); const [isModalOpen, setIsModalOpen] = createSignal(false); - const mountInto = (parent: HTMLDivElement) => { + let composerHost: HTMLDivElement | undefined; + + // The mount runs in onMount (not in ref={...}) so the host element is + // actually attached to the document by the time CM6 reads + // `parent.ownerDocument.defaultView` — Obsidian's flavour of EditorView + // dereferences `defaultView.CSSStyleSheet`, and Solid's refs fire before + // mount, so a synchronous EditorView construction crashes there. + onMount(() => { + if (!composerHost) return; const handle = mountComposer({ - parent, + parent: composerHost, placeholder: hasModels() ? "Type your message..." : "No models available", @@ -87,10 +96,8 @@ export const UserInput: Component = ({ handle.destroy(); }); - // Defer focus until after the mount finishes so CM6's selection is - // ready to receive it. - queueMicrotask(() => handle.focus()); - }; + handle.focus(); + }); /** * Detects `[[` and `#` typing and opens the Obsidian suggestion modal, @@ -213,7 +220,12 @@ export const UserInput: Component = ({ return (
-
+
{ + composerHost = el; + }} + />