Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

Problem

App startup took 6+ seconds for Electron users because main.ts immediately loaded IpcMain, which transitively imports the entire AI SDK stack (ai, @ai-sdk/anthropic, ai-tokenizer, etc.). These are large modules that weren't needed until the window was created and the user started interacting.

Additionally, the package was being built as "Cmux" instead of "cmux".

Solution

1. Lazy-load IpcMain

Convert static imports to dynamic imports in createWindow(). Config, IpcMain, and tokenizer modules now load on-demand when the window is created, not at app startup.

Before:

import { Config } from './config';
import { IpcMain } from './services/ipcMain';

const config = new Config();
const ipcMain = new IpcMain(config); // Heavy AI SDK loaded immediately

After:

import type { Config } from './config';
import type { IpcMain } from './services/ipcMain';

let config: Config | null = null;
let ipcMain: IpcMain | null = null;

async function createWindow() {
  if (!config || !ipcMain) {
    // Load only when needed
    const [{ Config: ConfigClass }, { IpcMain: IpcMainClass }] = await Promise.all([
      import('./config'),
      import('./services/ipcMain'),
    ]);
    config = new ConfigClass();
    ipcMain = new IpcMainClass(config);
  }
  // ... create window
}

2. Lowercase package name

Changed productName from "Cmux" to "cmux" in package.json build config.

Implementation Details

  • Changed Config/IpcMain/loadTokenizerModules imports to type-only imports
  • Created module-level variables to cache loaded modules
  • Made createWindow() async to await dynamic imports
  • Moved tokenizer loading to after window creation
  • Fixed e2e test userData path (can't use config.rootDir before config loads)
  • Added ESLint justification for dynamic imports

Testing

  • ✅ Unit tests pass (379 tests)
  • ✅ Type checking passes
  • ✅ ESLint passes

Generated with cmux

**Problem:**
App startup took 6+ seconds for Electron users because main.ts immediately
loaded IpcMain, which transitively imports the entire AI SDK stack (ai,
@ai-sdk/anthropic, ai-tokenizer, etc.) - heavy modules that weren't needed
until the window was created and user started interacting.

Additionally, the package was being built as "Cmux" instead of "cmux".

**Solution:**

1. **Lazy-load IpcMain**: Convert static imports to dynamic imports in
   createWindow(). Config, IpcMain, and tokenizer modules now load on-demand
   when the window is created, not at app startup.

2. **Lowercase package name**: Changed productName from "Cmux" to "cmux"
   in package.json build config.

**Implementation details:**
- Changed Config/IpcMain/loadTokenizerModules imports to type-only imports
- Created module-level variables to cache loaded modules
- Made createWindow() async to await dynamic imports
- Moved tokenizer loading to after window creation
- Fixed e2e test userData path (can't use config.rootDir before config loads)
- Added ESLint justification for dynamic imports

**Testing:**
- Unit tests pass (379 tests)
- Type checking passes
- ESLint passes
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

@ammario ammario added this pull request to the merge queue Oct 13, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 13, 2025
@ammario ammario merged commit db98e01 into main Oct 13, 2025
7 checks passed
@ammario ammario deleted the faster-startup-and-lowercase branch October 13, 2025 17:53
ammar-agent added a commit that referenced this pull request Oct 13, 2025
## Problem

Users see dock icon but no window for 6-13s during app startup (varies by machine).
Even with lazy-loaded services (#223), the renderer makes IPC calls on mount which
requires services to be loaded first, so the main window can't appear until everything
is ready.

## Solution

Added native splash screen that appears instantly (<100ms) while services load.

## Implementation

**Three-phase startup:**

1. **Show splash** - Native BrowserWindow with static HTML (<100ms)
   - No React, no IPC, no heavy dependencies
   - Matches app theme colors from colors.tsx
   - Shows "Loading services..." with spinner

2. **Load services** - Happens while splash is visible (~6-13s)
   - Config, IpcMain, AI SDK, tokenizer modules
   - User gets instant visual feedback

3. **Show main window** - Close splash, reveal app
   - Services guaranteed ready when window appears
   - Main window uses "ready-to-show" event to avoid white flash

## Changes

- **Added `static/splash.html`** - Lightweight loading screen matching app theme
- **Modified `src/main.ts`** - Three-phase startup with splash screen
- **Updated `Makefile`** - Copy splash.html to dist during build

## Benefits

- ✅ Instant visual feedback (<100ms vs 6-13s black screen)
- ✅ No user confusion ("is it broken?")
- ✅ Services guaranteed ready (no race conditions)
- ✅ Clean transition to main window
- ✅ All tests pass (410 tests)

_Generated with `cmux`_
ammario pushed a commit that referenced this pull request Oct 13, 2025
## Problem

Users see dock icon but no window for 6-13s during app startup (varies
by machine: ~6s on M3, ~13s on M1).

Even with lazy-loaded services (#223), the renderer makes IPC calls on
mount which requires services to be loaded first, so the main window
can't appear until everything is ready.

## Solution

Added native splash screen that appears instantly (<100ms) while
services load.

## Implementation

**Three-phase startup:**

1. **Show splash** - Native BrowserWindow with static HTML (<100ms)
   - No React, no IPC, no heavy dependencies
   - Matches app theme colors from colors.tsx
   - Shows "Loading services..." with spinner

2. **Load services** - Happens while splash is visible (~6-13s)
   - Config, IpcMain, AI SDK, tokenizer modules
   - User gets instant visual feedback

3. **Show main window** - Close splash, reveal app
   - Services guaranteed ready when window appears
   - Main window uses "ready-to-show" event to avoid white flash

## Changes

- **Added `static/splash.html`** - Lightweight loading screen matching
app theme
- **Modified `src/main.ts`** - Three-phase startup with splash screen
- **Updated `Makefile`** - Copy splash.html to dist during build

## Benefits

- ✅ Instant visual feedback (<100ms vs 6-13s black screen)
- ✅ No user confusion ("is it broken?")
- ✅ Services guaranteed ready (no race conditions)
- ✅ Clean transition to main window
- ✅ All tests pass (410 tests)

## Testing

Manually tested on M3 - splash appears instantly, main window appears
~6s later.

_Generated with `cmux`_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants