Skip to content

Commit 4c38d9c

Browse files
committed
perf(startup): optimize Shiki and Monaco deferred loading
- Remove eager preloading of Shiki highlighter and Monaco editor from index.tsx startup path. Both now load purely on demand: - Shiki loads when a code block first appears in AI chat or file preview - Monaco loads when the user first opens/focuses an editor tab - Remove preloadHighlighter() from shikiHighlighter.ts, add getPlainHighlightHtml() as a synchronous fallback for rendering code before Shiki loads (plain <pre> block with escaped HTML) - Convert static MonacoManager import in FileEditHandlers to a lazy dynamic import, preventing monacoManager from being pulled into the CortexDesktopLayout bundle at parse time - Add documentation clarifying the editor pool (EDITOR_POOL_SIZE = 4) is created on demand — no editors are pre-allocated in monacoManager.ts
1 parent 4298dde commit 4c38d9c

File tree

4 files changed

+22
-47
lines changed

4 files changed

+22
-47
lines changed

src/components/cortex/handlers/FileEditHandlers.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ import { useNavigate } from "@solidjs/router";
77
import { useEditor } from "@/context/editor/EditorProvider";
88
import { useSDK } from "@/context/SDKContext";
99
import { fsWriteFile } from "@/utils/tauri-api";
10-
import { MonacoManager } from "@/utils/monacoManager";
1110
import { createLogger } from "@/utils/logger";
1211
import { getWindowLabel } from "@/utils/windowStorage";
1312

1413
const logger = createLogger("FileEditHandlers");
1514

15+
let _monacoMgr: typeof import("@/utils/monacoManager") | null = null;
16+
1617
function getActiveMonacoEditor() {
17-
const monaco = MonacoManager.getInstance().getMonacoOrNull();
18+
if (!_monacoMgr) {
19+
import("@/utils/monacoManager").then(m => { _monacoMgr = m; });
20+
return null;
21+
}
22+
const monaco = _monacoMgr.MonacoManager.getInstance().getMonacoOrNull();
1823
if (!monaco) return null;
1924
const editors = monaco.editor.getEditors();
2025
for (const ed of editors) {

src/index.tsx

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { render } from "solid-js/web";
22
import { Router, Route } from "@solidjs/router";
33
import { lazy, Suspense } from "solid-js";
44
import { initializeWindowStorage } from "@/utils/windowStorage";
5-
import { preloadHighlighter } from "@/utils/shikiHighlighter";
5+
66
// CRITICAL: Use AppShell instead of App for instant first paint
77
// AppShell is minimal (~1KB) and lazy-loads AppCore (with 68 providers) after render
88
import AppShell from "./AppShell";
@@ -43,39 +43,10 @@ logStartup("Window storage init done");
4343
// These resources are not needed for initial render but improve UX when accessed.
4444
// Loading during idle time prevents blocking the main thread during startup.
4545

46-
function deferredPreload() {
47-
// Preload Shiki highlighter for code blocks in chat/markdown
48-
preloadHighlighter();
49-
50-
// Preload Monaco editor for code editing
51-
// This warms up the Monaco instance so it's ready when user opens a file
52-
import("@/utils/monacoManager").then(({ MonacoManager }) => {
53-
MonacoManager.getInstance().ensureLoaded().catch(() => {
54-
// Silent fail - Monaco will load on demand if preload fails
55-
});
56-
});
57-
}
58-
59-
/**
60-
* Second-pass preload: Removed - was causing Vite to add modulepreload
61-
* for heavy chunks, blocking startup.
62-
*
63-
* These chunks will load naturally when AppCore imports them after first paint.
64-
* The lazy() wrapper in AppShell ensures they don't block initial render.
65-
*/
66-
// function deferredPreloadPhase2() { /* removed */ }
67-
68-
if ('requestIdleCallback' in window) {
69-
// Phase 1: Critical preloads (Monaco, Shiki) - needed soon after startup
70-
(window as any).requestIdleCallback(() => {
71-
deferredPreload();
72-
}, { timeout: 3000 });
73-
74-
// Phase 2 removed - heavy context preloads were causing Vite modulepreload issues
75-
} else {
76-
// Fallback: defer with setTimeout
77-
setTimeout(() => deferredPreload(), 500);
78-
}
46+
// Monaco and Shiki are NOT preloaded here — they load on demand:
47+
// - Monaco loads when the user first opens/focuses an editor tab
48+
// - Shiki loads when a code block first appears in AI chat or file preview
49+
// This keeps the main bundle lean and avoids blocking startup.
7950

8051
// ============================================================================
8152
// CODE SPLITTING: Lazy-loaded Pages

src/utils/monacoManager.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import type * as Monaco from "monaco-editor";
1515
// Constants
1616
// ============================================================================
1717

18-
/** Number of editor instances to keep in the pool */
18+
/**
19+
* Maximum number of editor instances in the pool.
20+
* Pool is created on demand — no editors are pre-allocated.
21+
* Editors are only instantiated in `acquireEditor()` when a tab is opened.
22+
*/
1923
const EDITOR_POOL_SIZE = 4;
2024

2125
/** Delay before disposing unused models (ms) */

src/utils/shikiHighlighter.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,12 @@ export function detectLanguageFromPath(filePath: string): string {
211211
}
212212

213213
/**
214-
* Preload highlighter in background (call after initial render)
214+
* Lightweight synchronous fallback for rendering code before Shiki loads.
215+
* Returns a plain `<pre>` block with escaped HTML — no syntax highlighting.
216+
* Use this as the initial/placeholder render while `highlightCode()` resolves.
215217
*/
216-
export function preloadHighlighter(): void {
217-
const schedule = typeof requestIdleCallback !== "undefined"
218-
? requestIdleCallback
219-
: (cb: () => void) => setTimeout(cb, 2000);
220-
221-
schedule(() => {
222-
// Just initialize the highlighter, don't preload languages
223-
getHighlighter().catch(() => {});
224-
});
218+
export function getPlainHighlightHtml(code: string, _language?: string): string {
219+
return createPlainHtml(code);
225220
}
226221

227222
/**

0 commit comments

Comments
 (0)