diff --git a/src/ui/LayoutManager.ts b/src/ui/LayoutManager.ts index 2b5c90d..1978e1b 100644 --- a/src/ui/LayoutManager.ts +++ b/src/ui/LayoutManager.ts @@ -25,7 +25,7 @@ export class ArenaLayoutManager { case 'new-tab': return Promise.resolve(this.createNewTab()); case 'new-window': - return Promise.resolve(this.createNewWindow()); + return this.createNewWindow(); case 'reuse-active': default: return Promise.resolve(this.createReuseActive()); @@ -58,6 +58,28 @@ export class ArenaLayoutManager { return { doc, win }; } + private sleep(ms: number): Promise { + return new Promise((resolve) => window.setTimeout(resolve, ms)); + } + + /** + * Obsidian may return a popout leaf before it is actually attached to the + * popout window's DOM. If we split too early, the split can occur in the + * main window. + */ + private async waitForPopoutLeafAttachment( + popoutLeaf: WorkspaceLeaf, + timeoutMs = 5000, + pollMs = 50, + ): Promise { + const started = Date.now(); + while (Date.now() - started < timeoutMs) { + const { win } = this.resolveDocWinFromLeaf(popoutLeaf); + if (win !== window) return; + await this.sleep(pollMs); + } + } + // Mode: reuse-active // Use the user's active leaf as the arena's left; split it to create the right. private createReuseActive(): ArenaLayoutHandle { @@ -197,7 +219,7 @@ export class ArenaLayoutManager { // Mode: new-window // Open a pop-out window (if available), then split inside it. - private createNewWindow(): ArenaLayoutHandle { + private async createNewWindow(): Promise { const referenceLeaf = this.getUserLeaf(); const maybeAny = (this.app.workspace as { openPopoutLeaf?: unknown }).openPopoutLeaf; @@ -214,6 +236,8 @@ export class ArenaLayoutManager { return this.createRightSplit(); } + await this.waitForPopoutLeafAttachment(popLeft); + // Make sure subsequent splits happen in the pop-out attempt(() => this.app.workspace.setActiveLeaf(popLeft, { focus: true }));