eisland Process Model
eIsland Process Model
Info
eIsland follows Electron's multi-process architecture with strict separation between the Main Process (Node.js), Preload Bridge (Context Bridge), and Renderer Process (Chromium). This architecture ensures security, stability, and clear responsibility boundaries.
Architecture Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ eIsland Application โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Main Process โ โ Preload Bridge โ โ Renderer Process โ โ
โ โ (Node.js) โโโโโบโ (Context Bridge) โโโโโบโ (Chromium) โ โ
โ โ โ โ โ โ โ โ
โ โ โข Window Management โ โ โข API Exposure โ โ โข React UI โ โ
โ โ โข System APIs โ โ โข Type Safety โ โ โข State Managementโ โ
โ โ โข Native Modules โ โ โข Security Bridge โ โ โข DOM Rendering โ โ
โ โ โข IPC Handlers โ โ โข Event Routing โ โ โข User Interactionโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโMain Process
Info
The Main Process is the backbone of the application, running in Node.js with full system access. It manages windows, handles system-level operations, and coordinates all IPC communication.
Responsibilities
| Category | Operations |
|---|---|
| Window Management | Create, destroy, resize, position, transparency |
| System Integration | Clipboard, hotkeys, tray, notifications |
| Native Modules | Process control, fullscreen detection, performance monitoring |
| File Operations | Read/write files, path resolution, media serving |
| Network | HTTP requests, proxy configuration, updates |
| Security | CSP enforcement, protocol registration, sandbox control |
Service Factory Pattern
Tips
Services are created via factory functions with dependency injection through getter/setter options, enabling loose coupling and easy testing.
const mainWindowService = createMainWindowService({
getMainWindow: () => mainWindow,
setMainWindow: (window) => { mainWindow = window; },
getIslandPositionOffset: () => islandPositionOffset,
sizes: { islandWidth: ISLAND_WIDTH, islandHeight: ISLAND_HEIGHT, ... },
});Benefits:
- Loose coupling between services
- Easy to mock for testing
- Clear dependency graph
- No circular dependencies
IPC Handler Registration
Each domain has its own register*Handlers() function that receives a configuration object:
// Clipboard domain
registerClipboardHandlers({ getMainWindow });
// Media domain
registerMediaHandlers({ getMainWindow, getNowPlayingInfo });
// Hotkey domain
registerHotkeyHandlers({ getMainWindow, getIslandPositionOffset });IPC Domains (25+ modules):
| Domain | Operations |
|---|---|
| clipboard | Read/write clipboard, URL detection |
| capture | Screen capture, recording |
| screenshot | Screenshot with region selection |
| app | App lifecycle, restart, quit |
| system | System info, environment variables |
| updater | Auto-update, version check |
| download | File download management |
| media | Media playback control |
| music | Music player integration |
| hotkey | Global shortcut registration |
| island | Island window management |
| theme | Theme switching |
| window | Window state management |
| wallpaper | Wallpaper management |
| Email operations | |
| store | Persistent storage |
| log | Logging |
| format-factory | Media format conversion |
| image-compression | Image optimization |
| net | Network operations |
| hide-process | Process hiding |
| agent | AI agent status |
Native Module Integration
Warning
Native modules (C/C++ addons) are loaded in the Main Process with sandbox disabled. These modules provide low-level Windows system capabilities. For detailed plugin implementations, see Plugins Tech Stack.
| Module | Purpose | Platform |
|---|---|---|
@eisland/windows-processes-attacker | Terminate processes by name/PID | Windows only |
@eisland/windows-fullscreen-detector | Detect fullscreen windows | Windows only |
@eisland/windows-performance-monitor | CPU, memory, temperature snapshots | Windows only |
// Loading native modules in Main Process
const detector = require('@eisland/windows-fullscreen-detector');
if (detector.isAnyFullscreenWindow()) {
mainWindow.hide(); // Auto-hide when fullscreen app detected
}Window Architecture
Info
eIsland uses multiple transparent, frameless, always-on-top windows for different visual contexts.
const mainWindow = new BrowserWindow({
transparent: true,
frame: false,
alwaysOnTop: true,
skipTaskbar: true,
hasShadow: false,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false, // Required for native modules
},
});Window Types:
| Window | Properties | Purpose |
|---|---|---|
| Main Island | transparent, frameless, alwaysOnTop | Primary dynamic island UI |
| Agent Voice Input | fullscreen, transparent, setIgnoreMouseEvents(true) | Voice input overlay |
| CLI Glow Overlay | transparent, alwaysOnTop | Terminal glow effect |
| Standalone Widget | Separate HTML entry | Independent widget window |
| Splash Screen | frameless, transparent, alwaysOnTop, show: false | Startup video playback with IPC synchronization |
Splash Window Lifecycle
Important
The splash window uses an IPC-based ready synchronization pattern to ensure the renderer has mounted and subscribed to playback commands before the main process sends them.
The splash window lifecycle follows a two-phase approach:
Phase 1 โ Creation & Ready Sync:
- Main process creates the splash window with
show: false - A
splashReadyPromiseis created with a 1500ms fallback timer - Renderer mounts, subscribes to
splash:play-video, then sendssplash:renderer-ready - On receiving
splash:renderer-ready(or fallback timeout), the window is revealed
Phase 2 โ Playback & Close:
- Main process waits for
splashReadyPromiseto resolve - Sends
splash:play-videoto renderer - Sets a 5000ms playback fallback timer
- On video end (IPC) or playback timeout โ wait 1s โ fade out โ close
// Renderer side โ signal readiness after mounting
useEffect(() => {
const remove = ipcRenderer.on('splash:play-video', () => {
videoRef.current?.play().catch(() => {});
});
ipcRenderer.send('splash:renderer-ready'); // Signal main process
return remove;
}, []);Tips
The dual-fallback design (ready timeout + playback timeout) ensures the main window is never permanently blocked, even if the renderer fails to load or the video resource is unavailable.
Custom Protocol
A custom eisland-media:// protocol safely serves local wallpaper media files:
protocol.registerFileProtocol('eisland-media', (request, callback) => {
const url = request.url.replace('eisland-media://', '');
const filePath = join(app.getPath('userData'), 'wallpapers', url);
// Path sandboxing: only serve files from userData/wallpapers
callback({ path: filePath });
});Chromium Performance Flags
Applied before app.whenReady():
function applyChromiumPerformanceFlags(app: Electron.App) {
app.commandLine.appendSwitch('enable-features', 'SharedArrayBuffer');
app.commandLine.appendSwitch('disable-features', 'HardwareMediaKeyHandling');
app.commandLine.appendSwitch('enable-gpu-rasterization');
}Preload Bridge
Info
The Preload Bridge is the secure communication layer between Main and Renderer processes. It runs in a privileged context with access to both Node.js APIs and the DOM.
Context Bridge Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Preload Script โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ Node.js APIs โ โ DOM APIs โ โ
โ โ (ipcRenderer) โ โ (window) โ โ
โ โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโ โ
โ โ โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโผโโโโโโโโ โ
โ โ contextBridge โ โ
โ โ exposeInMain โ โ
โ โ World('api') โ โ
โ โโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโ
โ window.api โ
โ (Renderer) โ
โโโโโโโโโโโโโโโโโโโAPI Exposure
The preload script exposes a unified API object through contextBridge.exposeInMainWorld:
contextBridge.exposeInMainWorld('api', {
// Fire-and-forget (one-way)
expandWindow: () => ipcRenderer.send('expandWindow'),
collapseWindow: () => ipcRenderer.send('collapseWindow'),
enableMousePassthrough: () => ipcRenderer.send('enableMousePassthrough'),
// Request-response (two-way)
getMousePosition: () => ipcRenderer.invoke('getMousePosition'),
storeRead: (key: string) => ipcRenderer.invoke('storeRead', key),
hotkeySet: (action: string, accelerator: string) =>
ipcRenderer.invoke('hotkeySet', action, accelerator),
// Event subscriptions (with unsubscribe)
onNowPlayingInfo: (callback: (info: NowPlayingInfo) => void) => {
const handler = (_event: any, info: NowPlayingInfo) => callback(info);
ipcRenderer.on('nowPlayingInfo', handler);
return () => ipcRenderer.removeListener('nowPlayingInfo', handler);
},
onSettingsChanged: (callback: (settings: Settings) => void) => { ... },
});IPC Communication Patterns
Tips
eIsland implements three distinct IPC communication patterns, each optimized for specific use cases.
1. Fire-and-Forget (One-Way)
Direction: Renderer โ Main
Method: ipcRenderer.send() / ipcMain.on()
Use Case: Window control, one-way notifications, actions that don't require a response.
// Renderer Process
window.api.expandWindow();
window.api.collapseWindow();
window.api.enableMousePassthrough();
// Main Process (handler)
ipcMain.on('expandWindow', () => {
mainWindow.setSize(860, 150);
});Characteristics:
- No return value
- Non-blocking
- No confirmation of receipt
- Used for: window control, notifications, logging
2. Request-Response (Two-Way)
Direction: Renderer โ Main โ Renderer
Method: ipcRenderer.invoke() / ipcMain.handle()
Use Case: Data queries, operations with return values, operations requiring confirmation.
// Renderer Process
const mousePos = await window.api.getMousePosition();
const storedValue = await window.api.storeRead('key');
// Main Process (handler)
ipcMain.handle('getMousePosition', () => {
return screen.getCursorScreenPoint();
});
ipcMain.handle('storeRead', (_event, key) => {
return store.get(key);
});Characteristics:
- Returns a Promise
- Async/await support
- Error propagation
- Used for: data queries, file operations, system info
3. Event Subscription (Push-Based)
Direction: Main โ Renderer (continuous)
Method: ipcRenderer.on() + ipcMain.webContents.send()
Use Case: Real-time updates, state changes, continuous data streams.
// Renderer Process (subscription)
const unsubscribe = window.api.onNowPlayingInfo((info) => {
setNowPlaying(info);
});
// Cleanup on unmount
useEffect(() => {
return () => unsubscribe();
}, []);
// Main Process (sender)
mainWindow.webContents.send('nowPlayingInfo', {
title: 'Song Title',
artist: 'Artist Name',
coverUrl: '...',
});Characteristics:
- Continuous updates
- Unsubscribe function returned
- Used for: media info, settings changes, notifications, state updates
Security Model
Caution
The Preload Bridge enforces security by controlling what APIs are exposed to the Renderer process. Direct access to Node.js APIs is blocked.
Security Boundaries:
| Layer | Access | Purpose |
|---|---|---|
| Main Process | Full Node.js, Native Modules | System operations, file I/O, native addons |
| Preload Bridge | Limited Node.js, Context Bridge | Secure API exposure, type safety |
| Renderer Process | DOM, window.api only | UI rendering, user interaction |
Key Security Features:
- Context Isolation: Renderer cannot access Node.js APIs directly
- Sandbox Control: Preload runs with controlled permissions
- API Whitelisting: Only exposed APIs are accessible
- Type Safety: TypeScript definitions for all IPC calls
- Input Validation: Main Process validates all incoming data
Renderer Process
Info
The Renderer Process runs the React UI in a Chromium environment. It handles all visual rendering, user interactions, and client-side state management.
Technology Stack
| Technology | Purpose |
|---|---|
| React 19 | Component-based UI |
| TypeScript | Type safety |
| Zustand | Global state management |
| Tailwind CSS v4 | Utility-first styling |
| GSAP | Programmatic animations |
| i18next | Internationalization |
React Architecture
Coordinator Hook Pattern
Tips
The island UI is orchestrated by a single coordinator hook (useDynamicIslandCoordinator) that composes approximately 16 specialized hooks, each managing one concern.
function useDynamicIslandCoordinator() {
// Shell morphing state machine
const { state, shellClassName, shellStyle, handleIslandClick } =
useDynamicIslandShell();
// Media synchronization
const { nowPlaying } = useIslandNowPlayingSync();
const dominantColor = useIslandDominantColor(nowPlaying?.coverUrl);
// Time display
const { timeStr, dayStr, fullTimeStr, lunarStr } = useIslandTimeStrings();
// Settings synchronization
useIslandSettingsSync();
// Auto-dimming
useIslandAutoDim();
// State bridges (auto-transitions)
useIslandStateBridges();
// Hover interaction
useIslandHoverInteraction();
return {
shellClassName, shellStyle, handleIslandClick,
timeStr, dayStr, fullTimeStr, lunarStr,
};
}Hook Responsibilities:
| Hook | Responsibility |
|---|---|
useDynamicIslandShell | Morphing state machine, click behavior, glow effect |
useIslandNowPlayingSync | SMTC media info synchronization |
useIslandDominantColor | Extract dominant color from album art |
useIslandTimeStrings | Formatted time/date/lunar strings |
useIslandTimerAndAlarm | Timer countdown and alarm logic |
useIslandSettingsSync | IPC settings change listener |
useIslandAutoDim | Auto-dimming after idle period |
useIslandStateBridges | Auto-transitions (e.g., lyrics when music plays) |
useIslandHoverInteraction | Mouse enter/leave with debounced timers |
useIslandShellPresentation | CSS class/style computation |
State-Based Component Rendering
Components are rendered based on the current island state (see State Machine for the full state definitions):
function DynamicIsland() {
const coordinator = useDynamicIslandCoordinator();
return (
<div className={coordinator.shellClassName} style={coordinator.shellStyle}
onClick={coordinator.handleIslandClick}>
{state === 'idle' && <IdleState />}
{state === 'hover' && <HoverState />}
{state === 'notification' && <NotificationState />}
{state === 'expanded' && <ExpandedState />}
{state === 'maxExpand' && <MaxExpandState />}
{state === 'lyrics' && <LyricsState />}
{state === 'login' && <LoginState />}
{state === 'register' && <RegisterState />}
{state === 'agent' && <AgentState />}
{state === 'agentVoiceInput' && <AgentVoiceInputState />}
{state === 'cli' && <CliState />}
{/* ... other states */}
</div>
);
}State Management
Info
The application uses Zustand with a slice composition pattern for global state management. Seven domain-specific slices are composed into a single store.
import { create } from 'zustand';
import type { IIslandStore } from '../types';
const useIslandStore = create<IIslandStore>()((set, get, store) => ({
...createIslandSlice(set, get, store),
...createWeatherSlice(set, get, store),
...createTimerSlice(set, get, store),
...createNotificationSlice(set, get, store),
...createMediaSlice(set, get, store),
...createAiSlice(set, get, store),
...createPomodoroSlice(set, get, store),
}));Store Slices:
| Slice | State Managed |
|---|---|
IslandSlice | Island state machine, UI state |
WeatherSlice | Weather data and location |
TimerSlice | Timers and alarms |
NotificationSlice | Notification queue |
MediaSlice | Music playback, now playing |
AiSlice | AI chat sessions, configuration |
PomodoroSlice | Pomodoro timer state |
HTML Entry Points
Warning
The renderer supports multiple HTML entry points for different visual contexts:
| Entry | File | Purpose |
|---|---|---|
| Main Island | index.html | Primary dynamic island window |
| Standalone Widget | standalone.html | Independent widget window |
| Splash Screen | splash.html | Startup video playback window |
| AI Background | AIbackground.html | AI assistant background effects |
Data Flow
Request-Response Flow
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ Renderer โ โ Preload โ โ Main โ
โ โ โ Bridge โ โ Process โ
โ window.api โโโโโโโโโโบโ ipcRenderer โโโโโโโโโโบโ ipcMain โ
โ .invoke() โ โ .invoke() โ โ .handle() โ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ return โ
โ Promise โ โ Promise โ โ result โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโEvent Subscription Flow
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ Renderer โ โ Preload โ โ Main โ
โ โ โ Bridge โ โ Process โ
โ window.api โโโโโโโโโโบโ ipcRenderer โ โ โ
โ .onXxx() โ โ .on() โ โ โ
โ โ โ โ โ webContents โ
โ callback() โโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ .send() โ
โ โ โ handler โ โ โ
โ unsubscribe โโโโโโโโโโบโ remove โ โ โ
โ โ โ Listener โ โ โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโFire-and-Forget Flow
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ Renderer โ โ Preload โ โ Main โ
โ โ โ Bridge โ โ Process โ
โ window.api โโโโโโโโโโบโ ipcRenderer โโโโโโโโโโบโ ipcMain โ
โ .send() โ โ .send() โ โ .on() โ
โ โ โ โ โ โ
โ (no response)โ โ (no response)โ โ execute โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโBuild Configuration
Info
The project uses electron-vite to configure three separate build targets with a single configuration file.
// electron.vite.config.ts
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: { index: 'src/main/index.ts', smtcWorker: 'src/main/smtcWorker.ts' },
},
},
},
preload: {
plugins: [externalizeDepsPlugin()],
},
renderer: {
root: 'src/renderer',
plugins: [react(), tailwindcss()],
build: {
rollupOptions: {
input: { index: 'index.html', standalone: 'standalone.html', splash: 'splash.html', AIbackground: 'AIbackground.html' },
},
},
},
});Build Targets:
| Target | Entry | Output | Description |
|---|---|---|---|
| main | src/main/index.ts, src/main/smtcWorker.ts | out/main | Node.js main process with SMTC worker |
| preload | src/preload/index.ts | out/preload | Context bridge between main and renderer |
| renderer | index.html, standalone.html, splash.html, AIbackground.html | out/renderer | Chromium renderer with 4 HTML entry points |
Key Features:
- externalizeDepsPlugin: Excludes Node.js dependencies from main/preload bundles
- @vitejs/plugin-react: React Fast Refresh for development
- @tailwindcss/vite: Tailwind CSS v4 integration (no separate config file)
- Multiple HTML entries: Supports main island window, standalone widget, splash screen, and AI background
SMTC Worker
Tips
A dedicated SMTC (System Media Transport Controls) Worker runs as a separate entry point in the Main Process for media synchronization.
// main: {
// rollupOptions: {
// input: { index: 'src/main/index.ts', smtcWorker: 'src/main/smtcWorker.ts' },
// },
// },Purpose:
- Runs in a separate thread
- Handles SMTC events from Windows
- Reports media info to main process
- Prevents main thread blocking
Performance Optimizations
Window Management
- Mouse passthrough: Idle states use
setIgnoreMouseEvents(true)to reduce event processing - Instant resize: Larger-to-smaller transitions skip animation to avoid visual glitches
- Debounced hover: Mouse enter/leave uses debounced timers to prevent rapid state changes
State Management
- Conditional transitions: Guard logic prevents unnecessary re-renders
- Deferred persistence: AI slice defers localStorage writes during streaming
- Cross-tab sync: Storage events synchronize config across windows
Build Optimization
- Code splitting: Multiple HTML entries enable lazy loading
- External dependencies: Main/preload bundles exclude Node.js modules
- Tree shaking: Vite eliminates unused exports
- GPU rasterization: Chromium flags enable hardware acceleration
Changelog
4f6b6-on

