Skip to content

feat: lazy load stuffs which are not needed on startup#2366

Open
bajrangCoder wants to merge 2 commits into
mainfrom
lazy-load
Open

feat: lazy load stuffs which are not needed on startup#2366
bajrangCoder wants to merge 2 commits into
mainfrom
lazy-load

Conversation

@bajrangCoder

Copy link
Copy Markdown
Member

No description provided.

@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR defers heavy startup modules — TerminalManager, FileBrowser, Prettier, ads, plugins, and the welcome tab — behind dynamic import() calls and singleton-promise wrappers to reduce initial bundle evaluation time.

  • Lazy wrappers (lazyTerminalManager.js, lazyAds.js) introduce singleton-promise loaders with synchronous "already-loaded" getters; canSaveFile is extracted into its own module to break a startup dependency.
  • src/main.js restructures post-splash init into requestAnimationFrame/setTimeout chains, with restoreTerminalSessions() now awaited inside the 500 ms callback — serializing terminal restoration before applySettings.afterRender() and ad startup.
  • src/lib/commands.js converts ~20 command handlers to async with local import() per invocation, and the welcome tab's EditorFile type is renamed from "page" to "welcome" to enable its own lazy path — but the context-menu innerHTML guard in main.js was not updated to match.

Confidence Score: 3/5

Not safe to merge without addressing the incomplete welcome-tab type migration and the terminal-restore blocking issue.

The welcome tab type was renamed from "page" to "welcome" but the context-menu innerHTML guard in main.js (line 839) was not updated, so the welcome tab will attempt to render editor-specific menu content. Terminal session restoration is now awaited before post-render settings and ad startup, introducing a new serial dependency that can slow perceived startup. The resumeHandler can also invoke handleResume() on an uninitialized adRewards instance if the device is backgrounded during the 500ms+rAF startup window.

src/main.js has three distinct issues: the missed type guard at line 839, the terminal-restore blocking, and the adRewards init race. src/lib/acode.js needs the terminalTouchSelectionMoreOptions.list() method to handle the not-yet-loaded case consistently with its async siblings.

Important Files Changed

Filename Overview
src/main.js Major startup refactor: lazy-loads ads, terminal, plugins, and welcome tab. Three issues: await restoreTerminalSessions() now blocks post-render init; resumeHandler can call handleResume() before init(); context menu innerHTML guard still checks type === "page" instead of "welcome".
src/lib/acode.js Migrates TerminalManager and FileBrowser to lazy proxies. terminalTouchSelectionMoreOptions.list() is the only method left synchronous — returns [] before terminal loads, breaking the API contract for plugins that call it at startup.
src/lib/lazyAds.js New lazy-load wrapper for adRewards and startAd modules; correct singleton-promise pattern with both eager and synchronous getters.
src/components/terminal/lazyTerminalManager.js New lazy-load wrapper for TerminalManager; clean singleton-promise implementation with synchronous getter for already-loaded state.
src/lib/commands.js Converts ~20 commands to async with inline dynamic imports, removing heavy static imports from startup. Each import is local to its command so no shared caching, but correctness is unaffected.
src/utils/helpers.js canShowAds() now returns false when adRewards hasn't loaded yet, silently suppressing early-lifecycle ad impressions for non-pro users.
src/lib/prettierFormatter.js Prettier and all its plugins are now lazy-loaded on first format, using a singleton promise. Straightforward and correct.
src/pages/welcome/welcome.js Welcome tab type changed from "page" to "welcome" to support lazy loading, but the context-menu innerHTML guard in main.js (line 839) still checks type === "page" — not updated here.
src/lib/canSaveFile.js Extracted canSaveFile into its own module to break a circular dependency; logic is unchanged from commands.js.
src/theme/list.js Lazily imports terminalSettings only when a preferred terminal theme is applied on first init; correct and safe.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant App
    participant Splash
    participant rAF as requestAnimationFrame
    participant adRewards
    participant Terminal
    participant Plugins

    App->>Splash: loadApp() + 500ms timeout
    Splash->>Splash: remove loading/splash classes
    Splash->>rAF: schedule initAdRewards()
    Splash->>Terminal: await restoreTerminalSessions()
    Note over Terminal: blocks applySettings, startAds, checkForAppUpdates
    Terminal-->>Splash: resolved
    Splash->>Plugins: setTimeout(0) → loadPlugins()
    Splash->>Splash: applySettings.afterRender()
    Splash->>adRewards: startAds() [fire-and-forget]
    rAF->>adRewards: initAdRewards() → adRewards.init()
    Note over adRewards: resumeHandler can race init()
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant App
    participant Splash
    participant rAF as requestAnimationFrame
    participant adRewards
    participant Terminal
    participant Plugins

    App->>Splash: loadApp() + 500ms timeout
    Splash->>Splash: remove loading/splash classes
    Splash->>rAF: schedule initAdRewards()
    Splash->>Terminal: await restoreTerminalSessions()
    Note over Terminal: blocks applySettings, startAds, checkForAppUpdates
    Terminal-->>Splash: resolved
    Splash->>Plugins: setTimeout(0) → loadPlugins()
    Splash->>Splash: applySettings.afterRender()
    Splash->>adRewards: startAds() [fire-and-forget]
    rAF->>adRewards: initAdRewards() → adRewards.init()
    Note over adRewards: resumeHandler can race init()
Loading

Comments Outside Diff (1)

  1. src/main.js, line 839-841 (link)

    P1 Welcome tab type not handled in context menu innerHTML

    The welcome tab's EditorFile type was changed from "page" to "welcome" in welcome.js, but this innerHTML guard (which returns "" to suppress the context menu for non-editor tabs) still only checks for "page". When the welcome tab is the active file, this condition is no longer satisfied, so the function proceeds to render encoding/editor fields on a tab that has none — likely producing a broken or partially-rendered context menu entry. The same condition at line 736 was correctly updated to include "welcome", but this one was missed.

Reviews (1): Last reviewed commit: "fixed few issues" | Re-trigger Greptile

Comment thread src/main.js
Comment on lines +923 to +929
async function resumeHandler() {
try {
const adRewards = await loadAdRewards();
adRewards.handleResume();
} catch (error) {
console.error("Failed to resume ad rewards:", error);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 handleResume() can fire before adRewards.init()

initAdRewards() is now deferred through a requestAnimationFrame inside the 500 ms setTimeout. If the user backgrounds and restores the app during that startup window, resumeHandler calls loadAdRewards() and immediately invokes handleResume() before init() has ever been called on that module — while initAdRewards() will later call init() on the exact same cached singleton. Whether handleResume() is safe to call on an uninitialized instance depends entirely on the adRewards module; the old code guaranteed init() finished first.

Suggested change
async function resumeHandler() {
try {
const adRewards = await loadAdRewards();
adRewards.handleResume();
} catch (error) {
console.error("Failed to resume ad rewards:", error);
}
async function resumeHandler() {
try {
// Ensure init has completed before calling handleResume.
await initAdRewards();
const adRewards = await loadAdRewards();
adRewards.handleResume();
} catch (error) {
console.error("Failed to resume ad rewards:", error);
}

Comment thread src/main.js
Comment on lines +287 to 311
await restoreTerminalSessions();

setTimeout(async () => {
try {
await loadPlugins();

setTimeout(() => {
// Ensure at least one sidebar app is active after all plugins are loaded.
sidebarApps.ensureActiveApp();

// Re-emit events for active file after plugins are loaded.
const { activeFile } = editorManager;
if (activeFile?.uri) {
editorManager.emit("file-loaded", activeFile);
editorManager.emit("switch-file", activeFile);
}
}, 0);
} catch (error) {
window.log("error", "Failed to load plugins!");
window.log("error", error);
toast("Failed to load plugins!");
}
} catch (error) {
window.log("error", "Failed to load plugins!");
window.log("error", error);
toast("Failed to load plugins!");
}
}, 0);

applySettings.afterRender();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Terminal restoration now serially blocks post-render init

await restoreTerminalSessions() sits between the splash-removal and applySettings.afterRender() / startAds() / checkForAppUpdates(). Previously restorePersistedSessions() was fire-and-forget. If persisted terminal sessions involve slow SSH/WebSocket reconnects, every subsequent init step (settings, ads, update checks) is delayed for the entire duration of that network operation. Consider not awaiting terminal restoration here, or scheduling it after the remaining post-render work.

Comment thread src/lib/acode.js
Comment on lines +319 to 321
list: () =>
getLoadedTerminalManager()?.getTouchSelectionMoreOptions() || [],
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 terminalTouchSelectionMoreOptions.list() silently returns [] before terminal loads

The add and remove methods on this object are async and await the terminal manager, but list is synchronous and returns getLoadedTerminalManager()?.getTouchSelectionMoreOptions() || []. Any plugin that calls list() before the terminal manager has been lazy-loaded (e.g., to read existing options at startup) will receive an empty array instead of the real list. This is an API contract break relative to the previous always-synchronous TerminalManager.getTouchSelectionMoreOptions() call that always had the manager available.

Comment thread src/utils/helpers.js
Comment on lines 295 to 297
canShowAds() {
return Boolean(!config.HAS_PRO && adRewards.canShowAds());
return Boolean(!config.HAS_PRO && getLoadedAdRewards()?.canShowAds());
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 canShowAds() returns false before adRewards is loaded

getLoadedAdRewards() returns undefined until initAdRewards() resolves (deferred by rAF inside 500 ms timeout). Any call to canShowAds() during that startup window — and therefore any call to showAd() or showInterstitialIfReady() — will silently return false/skip the ad, even for non-pro users. This is a subtle behavior change that may suppress early-lifecycle ad impressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant