eisland State Machine
eIsland State Machine
Info
The eIsland state machine is the core architecture that controls the island's appearance, behavior, and interactions. It manages 21 distinct states, each with defined pixel dimensions, mouse behavior, and transition rules. The dimensions shown below are for Notch mode โ see Shape Modes for Pill mode dimensions.
State Categories
Primary States
| State | Description | Mouse Passthrough | Dimensions (WรH) |
|---|---|---|---|
idle | Default resting state | Yes | 260ร42 px |
minimal | Compact display mode | Yes | 260ร42 px |
hover | Mouse interaction state | No | 500ร60 px |
expanded | Full dashboard view | No | 860ร150 px |
maxExpand | Maximum expansion | No | 860ร400 px |
Content States
| State | Description | Mouse Passthrough | Dimensions (WรH) |
|---|---|---|---|
lyrics | Music lyrics display | Yes | 500ร42 px |
lyricsTranslation | Lyrics with translation | Yes | 500ร60 px |
notification | Alert and message display | No | 500ร88 px |
announcement | System announcements | No | 860ร400 px |
User Flow States
| State | Description | Mouse Passthrough | Dimensions (WรH) |
|---|---|---|---|
guide | First-run tutorial | No | 860ร400 px |
login | Authentication screen | No | 860ร400 px |
register | Account creation | No | 860ร400 px |
resetPassword | Password recovery | No | 860ร400 px |
setPassword | OAuth new user password setup | No | 860ร400 px |
bindOAuth | Bind OAuth to existing account | No | 860ร400 px |
bindEmail | Bind email for OAuth (WeChat, KOOK) | No | 860ร400 px |
payment | Payment processing | No | 860ร400 px |
musicProvidersLogin | Music provider QR code login | No | 860ร400 px |
AI & Input States
| State | Description | Mouse Passthrough | Dimensions (WรH) |
|---|---|---|---|
agent | AI assistant mode | No | 500ร88 px |
agentVoiceInput | Voice command mode | No | 500ร88 px |
stt | Speech-to-text | No | 500ร88 px |
cli | Claude Code / Codex session monitor | No | 500ร88 px |
State Configuration
Tips
Each state defines four key properties that control its behavior:
| Property | Type | Description |
|---|---|---|
mousePassthrough | boolean | Whether mouse events pass through to underlying windows |
expanded | boolean | Whether the island is in expanded visual state |
enterDelay | number | Delay (ms) before entering the state |
leaveDelay | number | Delay (ms) before leaving the state |
Configuration Matrix
export const STATE_CONFIGS: Record<IslandState, StateConfig> = {
idle: { mousePassthrough: true, expanded: false, enterDelay: 0, leaveDelay: 0 },
hover: { mousePassthrough: false, expanded: true, enterDelay: 60, leaveDelay: 80 },
lyrics: { mousePassthrough: true, expanded: true, enterDelay: 50, leaveDelay: 0 },
lyricsTranslation:{ mousePassthrough: true, expanded: true, enterDelay: 50, leaveDelay: 0 },
notification: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
expanded: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
maxExpand: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
minimal: { mousePassthrough: true, expanded: false, enterDelay: 0, leaveDelay: 0 },
agent: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
agentVoiceInput:{ mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
login: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
register: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
resetPassword: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
setPassword: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
bindOAuth: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
bindEmail: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
payment: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
guide: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
announcement: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
stt: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
cli: { mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
musicProvidersLogin:{ mousePassthrough: false, expanded: true, enterDelay: 0, leaveDelay: 0 },
};State Areas
Info
State areas define the pixel footprint (Width ร Height) for each state, used for layout calculations and transition animations.
export const STATE_AREA: Record<string, number> = {
idle: 260 * 42, // 10,920 pxยฒ
hover: 500 * 60, // 30,000 pxยฒ
notification: 500 * 88, // 44,000 pxยฒ
expanded: 860 * 150, // 129,000 pxยฒ
maxExpand: 860 * 400, // 344,000 pxยฒ
lyrics: 500 * 42, // 21,000 pxยฒ
lyricsTranslation: 500 * 60, // 30,000 pxยฒ
minimal: 260 * 42, // 10,920 pxยฒ
agent: 500 * 88, // 44,000 pxยฒ
agentVoiceInput: 500 * 88,// 44,000 pxยฒ
login: 860 * 400, // 344,000 pxยฒ
register: 860 * 400, // 344,000 pxยฒ
resetPassword: 860 * 400, // 344,000 pxยฒ
setPassword: 860 * 400, // 344,000 pxยฒ
bindOAuth: 860 * 400, // 344,000 pxยฒ
bindEmail: 860 * 400, // 344,000 pxยฒ
payment: 860 * 400, // 344,000 pxยฒ
guide: 860 * 400, // 344,000 pxยฒ
announcement: 860 * 400, // 344,000 pxยฒ
stt: 500 * 88, // 44,000 pxยฒ
cli: 500 * 88, // 44,000 pxยฒ
musicProvidersLogin: 860 * 400, // 344,000 pxยฒ
};State Transitions
Warning
State transitions are triggered by user interactions, system events, and application logic. The state machine enforces guards to prevent invalid transitions.
Click Navigation Flow
The island implements a hierarchical click navigation system:
function handleIslandClick() {
switch (state) {
case 'idle':
if (idleClickExpand) setHover();
break;
case 'hover':
setExpanded();
break;
case 'expanded':
case 'maxExpand':
case 'announcement':
if (isOnCliTab && hasActiveSession) setCli();
else setHover();
break;
case 'login':
case 'register':
case 'payment':
// Auth states handle their own navigation
break;
case 'lyrics':
case 'lyricsTranslation':
// Lyrics states use mouse pass-through, no click handling
break;
default:
setIdle();
}
}Transition Rules
| From State | Trigger | To State | Condition |
|---|---|---|---|
idle | Click | hover | idleClickExpand enabled |
hover | Click | expanded | Always |
expanded | Click | hover | Default |
expanded | Click | cli | On CLI tab with active session |
maxExpand | Click | hover | Default |
maxExpand | Click | cli | On CLI tab with active session |
announcement | Click | hover | Default |
announcement | Click | cli | On CLI tab with active session |
login | Click | โ | Self-handled |
register | Click | โ | Self-handled |
payment | Click | โ | Self-handled |
| Any other | Click | idle | Default fallback |
Auto-Transitions
Tips
The state machine includes automatic transitions triggered by system events:
| Trigger | From | To | Description |
|---|---|---|---|
| Music plays | idle | lyrics | Auto-show lyrics when music starts |
| Music plays (with translation) | idle | lyricsTranslation | Auto-show lyrics with translation |
| Translation loaded | lyrics | lyricsTranslation | Upgrade when translation becomes available |
| Translation lost | lyricsTranslation | lyrics | Downgrade when translation unavailable |
| Music stops | lyrics / lyricsTranslation | idle | Return to idle when music stops |
| Notification arrives | Any | notification | Show notification overlay |
| Mouse hover | idle | hover | Expand on mouse enter |
| Mouse leave | hover | idle | Collapse on mouse leave (with delay) |
| Voice input | idle | agentVoiceInput | Activate voice recognition |
| CLI session | expanded | cli | Open terminal if session active |
Animation System
Info
State transitions are animated through a morphing animation system with configurable speeds.
Animation Speeds
| Speed | Duration | Use Case |
|---|---|---|
slow | 1100ms | Dramatic transitions, first-run guide |
medium | 550ms | Standard interactions |
fast | 280ms | Quick responses, frequent transitions |
Morphing Implementation
const MORPH_DURATION_BY_SPEED: Record<string, number> = {
slow: 1100,
medium: 550,
fast: 280,
};
useEffect(() => {
if (prevStateRef.current === state) return;
setFromState(prevStateRef.current);
prevStateRef.current = state;
setMorphing(true);
const id = setTimeout(() => {
setMorphing(false);
setFromState('');
}, MORPH_DURATION_BY_SPEED[animationSpeed] ?? 550);
return () => clearTimeout(id);
}, [state, animationSpeed]);CSS Class Composition
The shell builds a composite CSS class name for styling:
const shellClassName = [
'island-shell',
getStateClassName(state),
morphing && 'morphing',
fromState && `from-${fromState}`,
instantResize && 'instant-resize',
showGlow && 'music-glow',
showGlow === 'paused' && 'music-paused',
springAnimation && 'spring-animation',
`speed-${animationSpeed}`,
].filter(Boolean).join(' ');| Class | Purpose |
|---|---|
island-shell | Base shell styles |
{state} | State-specific dimensions and layout |
morphing | Active morphing animation |
from-{state} | Origin state for morph direction |
instant-resize | Skip animation for size reduction |
music-glow | Music-reactive glow effect |
music-paused | Paused music glow state |
spring-animation | Spring physics animation |
speed-{slow|medium|fast} | Animation speed modifier |
Instant Resize Optimization
Warning
When morphing from a larger state to a smaller one, an instant resize is applied to avoid visual glitches:
const instantResize = morphing && STATE_AREA[fromState] > STATE_AREA[state];State-Specific Behaviors
idle
Info
The idle state is the default resting state when no user interaction occurs. It serves as the entry point for most state transitions.
| Property | Value |
|---|---|
| Dimensions | 260ร42 px |
| Mouse | Pass-through |
| Expanded | No |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- Application startup
- User clicks outside the island
- Escape key pressed from any state
- Auth flow completion (returns to saved state)
- Music stops (from
lyricsstate)
Exit Conditions:
- Mouse hover enters island area โ
hover - Notification arrives โ
notification - Music playback starts โ
lyrics - Voice input activated โ
agentVoiceInput
UI Components Rendered:
- Minimal time display
- Status indicators (if configured)
- Music visualization glow (when playing)
Behavior Details:
- Mouse events pass through to underlying windows
- Minimal visual footprint, nearly invisible
- Auto-dimming after configurable idle period
- Supports click-to-expand (if
idleClickExpandenabled)
hover
Info
The hover state provides quick information preview on mouse hover, serving as a bridge between idle and expanded states. It uses a tab-based architecture with three navigable pages: Time, Lyrics, and Weather.
| Property | Value |
|---|---|
| Dimensions | 500ร60 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 60ms |
| Leave Delay | 80ms |
Entry Conditions:
- Mouse enters island area from
idlestate - Click on
idlestate (ifidleClickExpandenabled) - Return from
expanded/maxExpandon click
Exit Conditions:
- Mouse leaves island area โ
idle(after 80ms delay) - Click on island โ
expanded - Timeout โ
idle(if configured)
Tab Navigation
The hover state contains four navigation dots at the top. Three switch between tab pages; the fourth triggers expansion.
| Tab | Page | Description |
|---|---|---|
time | TimeTab | Time display, lunar calendar, countdown timer, hide/quit buttons |
lyrics | LyricsTab | Music playback controls, album art, artist info, canvas wave animation |
weather | WeatherTab | Current weather, 2-day forecast, click-to-refresh |
expand | โ | Triggers transition to expanded state |
Tips
Users can switch tabs by scrolling the mouse wheel while hovering over the island. The scroll direction cycles through the tab list in order.
Module Structure
hover module file tree
hover/
โโโ index.ts # Module entry point
โโโ types/index.ts # Shared type definitions (HoverContentProps, ActionButtonsProps, etc.)
โโโ config/hoverConfig.ts # Navigation dots configuration
โโโ hooks/useHover.ts # Tab switching, wheel navigation, scroll logic
โโโ components/
โ โโโ HoverContent.tsx # Top-level component, wires hook to form
โ โโโ HoverForm.tsx # Tab layout, nav dots, tab content switching
โโโ utils/
โ โโโ ActionButtons.tsx # Hide island / Quit app buttons
โ โโโ CountdownEdit.tsx # Editable countdown timer
โ โโโ ToolButtons.tsx # Screenshot / Task Manager buttons
โโโ pages/
โโโ time/ # Time tab module
โ โโโ index.ts
โ โโโ types/timeTabTypes.ts
โ โโโ components/TimeTab.tsx
โโโ lyric/ # Lyrics tab module
โ โโโ index.ts
โ โโโ types/silkyWaveTypes.ts
โ โโโ config/silkyWaveConfig.ts
โ โโโ hooks/useSilkyWave.ts
โ โโโ utils/lyricUtils.ts
โ โโโ components/
โ โโโ LyricsTab.tsx
โ โโโ SilkyWave.tsx
โโโ weather/ # Weather tab module
โโโ index.ts
โโโ config/weatherConfig.ts
โโโ utils/weatherUtils.ts
โโโ components/WeatherTab.tsxPage Details
Time Tab โ The default tab on hover entry. Displays the current time and lunar calendar date. Includes an inline countdown timer (scroll wheel to edit digits) and action buttons for hiding the island or quitting the app.
Lyrics Tab โ Shown when music is playing. Displays the album cover as a vinyl disc, song title, artist name, and playback controls (prev/play-pause/next). A canvas-based SilkyWave component renders a multi-layer sinusoidal wave animation that reacts to the playback state.
Weather Tab โ Displays the current temperature, weather description, and location. Shows a 2-day forecast with icons, precipitation probability, and wind speed. Clicking the weather icon triggers a refresh.
Behavior Details:
- 60ms enter delay prevents accidental activation
- 80ms leave delay prevents flicker on quick mouse movements
- Debounced hover detection for smooth transitions
- Shows essential information without full expansion
expanded
Info
The expanded state is the full dashboard view with widgets, controls, and interactive elements.
| Property | Value |
|---|---|
| Dimensions | 860ร150 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- Click on
hoverstate - Direct transition from other states (via API)
Exit Conditions:
- Click on island โ
hover(default) - Click on CLI tab with active session โ
cli - Escape key โ
hover - Mouse leave (if configured) โ
idle
UI Components Rendered:
- Left panel: Shortcuts, todo list, song info, countdown, pomodoro
- Right panel: Weather, calendar, performance monitor, email
- Clock display: Classic, gradient, or minimal styles
- Widget controls: Drag-and-drop reordering
- Tab navigation: Switch between widget groups
Behavior Details:
- Supports drag-and-drop widget reordering
- Multiple clock style options
- Custom background images/videos
- Real-time data updates (weather, music, notifications)
- Responsive layout based on content
maxExpand
Info
The maxExpand state provides maximum screen real estate for settings, tools, and detailed views.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User requests full-screen view from
expandedstate - Settings panel access
- Detailed tool views
Exit Conditions:
- Click on island โ
hover - Escape key โ
expanded - Close button โ
expanded
UI Components Rendered:
- Full settings interface
- Detailed tool panels
- Extended widget configurations
- System information displays
Behavior Details:
- Maximum visual real estate
- Full keyboard navigation support
- Modal-like behavior (blocks underlying interactions)
- Used for complex configurations and detailed views
lyrics
Info
The lyrics state displays synchronized music lyrics, automatically activated during music playback.
| Property | Value |
|---|---|
| Dimensions | 500ร42 px |
| Mouse | Pass-through |
| Expanded | Yes |
| Enter Delay | 50ms |
| Leave Delay | 0ms |
Entry Conditions:
- Music playback starts (auto-trigger)
- SMTC (System Media Transport Controls) reports playing state
- Manual activation from music controls
Exit Conditions:
- Music playback stops โ
idle - Music paused (stays in
lyricswith paused indicator) - Manual dismiss
UI Components Rendered:
- Synchronized lyrics display
- Album art thumbnail
- Song title and artist
- Playback progress indicator
- Music glow effect (reactive to playback)
Behavior Details:
- Real-time lyrics synchronization with playback
- Supports multiple music players (NetEase, QQ Music, Kugou)
- 50ms enter delay for smooth transition
- Music-reactive glow effect (
music-glowCSS class) - Paused state indicator (
music-pausedCSS class) - SMTC worker integration for system-level media control
lyricsTranslation
Info
The lyricsTranslation state displays synchronized lyrics with translation text below each line, automatically activated when translation lyrics are available during music playback.
| Property | Value |
|---|---|
| Dimensions | 500ร60 px |
| Mouse | Pass-through |
| Expanded | Yes |
| Enter Delay | 50ms |
| Leave Delay | 0ms |
Entry Conditions:
- Music playback starts with translation lyrics available (auto-trigger from
idle) - Translation lyrics loaded while in
lyricsstate (auto-upgrade) - Hover leave with translation available
Exit Conditions:
- Translation lyrics become unavailable โ
lyrics(auto-downgrade) - Music playback stops โ
idle - Manual dismiss
UI Components Rendered:
- Synchronized lyrics display (original text)
- Translation text below each lyric line
- Album art thumbnail
- Song title and artist
- Playback progress indicator
- Music glow effect (reactive to playback)
Behavior Details:
- Real-time lyrics and translation synchronization with playback
- Taller window (60px vs 42px) to accommodate translation line
- Automatic upgrade from
lyricswhen translation becomes available - Automatic downgrade to
lyricswhen translation becomes unavailable - Same mouse pass-through behavior as
lyricsstate - Supports Soda Music, NetEase, QQ Music translation sources
notification
Info
The notification state displays alerts and messages with interactive dismissal.
| Property | Value |
|---|---|
| Dimensions | 500ร88 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- System notification arrives
- Email notification received
- Application alert triggered
- IPC notification from main process
Exit Conditions:
- Click on notification โ action (if configured)
- Dismiss gesture โ
idle - Timeout โ
idle(auto-dismiss) - New notification replaces current
UI Components Rendered:
- Notification icon
- Title and message text
- Action buttons (if configured)
- Dismiss button
- Timestamp
Behavior Details:
- Immediate transition (no enter delay)
- Supports multiple notification types
- Auto-dismiss after configurable timeout
- Queued notifications (sequential display)
- Priority-based display order
announcement
Info
The announcement state displays system announcements, updates, and important information.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- System announcement received
- Application update available
- First-run after update
- Scheduled announcements
Exit Conditions:
- Click dismiss โ
hover - Acknowledge button โ
hover - Escape key โ
hover
UI Components Rendered:
- Announcement title
- Rich content (markdown support)
- Version information (for updates)
- Action buttons (update, dismiss, remind later)
- Changelog details
Behavior Details:
- Full announcement panel
- Supports rich content (images, links, formatting)
- Version-specific announcements
- Dismiss tracking (won't show again)
- Integration with auto-updater
guide
Info
The guide state provides an interactive first-run tutorial for new users.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- First application launch
- Manual guide activation
- Guide reset (from settings)
Exit Conditions:
- Complete all steps โ
idle - Skip guide โ
idle - Escape key โ
idle
UI Components Rendered:
- Step indicator (progress bar)
- Tutorial content (per step)
- Interactive demonstrations
- Next/Previous/Skip buttons
- Highlight overlays (pointing to features)
Behavior Details:
- Step-by-step walkthrough
- Interactive element highlighting
- Progress tracking
- Skippable at any point
- Completion state persistence
login
Info
The login state provides user authentication interface, including email/password login and OAuth login (GitHub, Microsoft, WeChat, Gitee, KOOK). For the JWT authentication flow, see JWT Authentication. For the rate limiting, see Redis โ Auth Rate Limiting.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User requests login
- Protected action requires authentication
- Session expired
Exit Conditions:
- Successful authentication โ saved state or
idle - Cancel โ previous state
- Register link โ
register - Reset password link โ
resetPassword - OAuth (GitHub / Microsoft / Gitee) โ
setPassword(new user) orbindOAuth(existing email) - OAuth (WeChat / KOOK) โ
bindEmail(no email returned) orsetPassword/bindOAuth(if email available)
UI Components Rendered:
- Username/email input
- Password input
- Login button
- Register link
- Forgot password link
- GitHub OAuth button (with divider)
- Microsoft OAuth button
- WeChat OAuth button
- Gitee OAuth button
- KOOK OAuth button
- Error messages
Behavior Details:
- Self-handled navigation (does not follow standard click flow)
- Saves return state (
authReturnState) for post-auth redirect - Rate limiting (5 failures per 5-minute window)
- Account lockout protection
- Session token management
- Single device enforcement
OAuth Login Flow (GitHub / Microsoft / WeChat / Gitee / KOOK)
Important
OAuth uses a polling-based architecture: eIsland opens the system default browser for authorization, then polls the backend for the result. All providers follow the same polling flow. WeChat and KOOK add an extra email binding step since they do not return the user's email.
sequenceDiagram
participant U as User
participant E as eIsland (Renderer)
participant B as System Browser
participant P as OAuth Provider
participant S as Backend Server
U->>E: Click OAuth login button
E->>S: GET /auth/oauth/{provider}/authorize
S-->>E: authorizeUrl (no state)
E->>E: Generate random sessionId (UUID)
E->>B: shell.openExternal(authorizeUrl + state=sessionId)
B->>P: User authorizes
P->>S: Redirect to callback?code=xxx&state=sessionId
S->>S: Exchange code for access_token
S->>S: Fetch user info from provider API
S->>S: Store result by sessionId
S-->>B: 302 redirect to configured URL
loop Poll every 2s (max 5 min)
E->>S: GET /auth/oauth/poll?sessionId=xxx
S-->>E: { ready: true }
end
E->>S: GET /auth/oauth/consume?sessionId=xxx
S-->>E: OAuth result (LOGIN / SET_PASSWORD / BIND_OAUTH)
E->>E: Route to appropriate stateBackend Decision Logic:
| Condition | Result | Next State |
|---|---|---|
| OAuth account already bound | LOGIN with JWT token | โ saved state or idle |
| OAuth email matches registered email | BIND_OAUTH with tempToken | โ bindOAuth |
| No matching email found | SET_PASSWORD with tempToken | โ setPassword |
| WeChat / KOOK (no email returned) | SET_PASSWORD with tempToken (null email) | โ bindEmail |
Tips
If the OAuth provider returns a null email (e.g., GitHub privacy settings), the backend falls back to the provider's email API to fetch the user's verified primary email address.
Environment Variables:
| Variable | Description | Example |
|---|---|---|
GITHUB_CLIENT_ID | GitHub OAuth App client ID | Ov23li... |
GITHUB_CLIENT_SECRET | GitHub OAuth App client secret | 4e41d3... |
GITHUB_REDIRECT_URI | GitHub OAuth callback URL | https://server.example.com/api/auth/oauth/github/callback |
MICROSOFT_CLIENT_ID | Microsoft Azure app client ID | abc123... |
MICROSOFT_CLIENT_SECRET | Microsoft Azure app client secret | xyz789... |
MICROSOFT_REDIRECT_URI | Microsoft OAuth callback URL | https://server.example.com/api/auth/oauth/microsoft/callback |
WECHAT_APP_ID | WeChat website application AppID | wx1234567890abcdef |
WECHAT_APP_SECRET | WeChat website application AppSecret | 4e41d3... |
WECHAT_REDIRECT_URI | WeChat OAuth callback URL | https://server.example.com/api/auth/oauth/wechat/callback |
OAUTH_CALLBACK_REDIRECT_URL | Browser redirect after callback | https://www.pyisland.com |
Note
Microsoft OAuth requires an Azure AD app registration with a valid MPN ID if personal Microsoft accounts are supported. See Azure App Registration for setup instructions.
register
Info
The register state provides new account creation interface. For email verification, see Redis โ Email Verification. For CAPTCHA, see Redis โ Slider CAPTCHA.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User clicks register from login
- Direct registration link
Exit Conditions:
- Successful registration โ
loginoridle - Cancel โ
login - Login link โ
login
UI Components Rendered:
- Username input
- Email input
- Password input
- Confirm password input
- Verification code input
- Register button
- Login link
- Terms acceptance checkbox
Behavior Details:
- Email verification required
- Password strength validation
- Username availability check
- Rate limiting (5 attempts per hour per IP)
- CAPTCHA protection (slider verification)
resetPassword
Info
The resetPassword state provides password recovery workflow.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User clicks "Forgot password" from login
- Direct reset link
Exit Conditions:
- Successful password reset โ
login - Cancel โ
login - Timeout โ
login
UI Components Rendered:
- Email input
- Verification code input
- New password input
- Confirm password input
- Reset button
- Back to login link
Behavior Details:
- Email verification required
- Password strength validation
- Rate limiting
- Token expiration (5-minute validity)
- One-time use codes
setPassword
Info
The setPassword state handles OAuth new user registration โ when a user logs in via GitHub or Microsoft for the first time and no matching email account exists, they must set a password to complete registration.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- OAuth callback returns
SET_PASSWORDstatus (GitHub or Microsoft) - No existing email account matches the OAuth provider email
Exit Conditions:
- Password set successfully โ login + redirect to saved state
- Cancel โ
login
UI Components Rendered:
- Username input (editable, pre-filled from OAuth provider)
- Email input (read-only, pre-filled if available)
- Password input with show/hide toggle
- Confirm password input
- Submit button ("Set Password and Register")
- Cancel button ("Cancel Login")
Behavior Details:
- Temporary JWT token (
purpose=set_password) carries OAuth context (provider, providerUserId, accessToken) - Password strength validation (min 8 chars, letters + numbers)
- Username pattern validation (2-32 chars, alphanumeric/CJK/underscore)
- On success: creates user account + OAuth binding, returns JWT token
- Self-handled navigation (does not follow standard click flow)
bindOAuth
Info
The bindOAuth state handles linking a third-party OAuth account to an existing email account โ when a user logs in via GitHub (or Microsoft, when enabled) and a matching email account already exists, they must enter their password to bind the accounts.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- OAuth callback returns
BIND_OAUTHstatus (GitHub or Microsoft) - OAuth provider email matches an existing registered email account
Exit Conditions:
- Password verified โ OAuth binding created โ login + redirect to saved state
- Cancel โ
login
UI Components Rendered:
- Username input (read-only, from existing account)
- Email input (read-only, from existing account)
- Password input with show/hide toggle
- Submit button ("Bind and Login")
- Cancel button ("Cancel Login")
Behavior Details:
- Temporary JWT token (
purpose=bind_oauth) carries OAuth context - Password verification against existing account
- On success: creates OAuth binding record in
user_oauth_bindingtable, returns JWT token - Account must be enabled (not banned)
- Self-handled navigation (does not follow standard click flow)
bindEmail
Info
The bindEmail state handles email binding for OAuth providers that do not return an email address โ currently WeChat and KOOK. When a user logs in via WeChat or KOOK and no email is associated, they must bind an email to complete registration.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- OAuth callback returns
SET_PASSWORDstatus with a null email (WeChat or KOOK) - WeChat or KOOK OAuth login without a bound email address
Exit Conditions:
- Email bound + password set โ
setPassword(new email) orbindOAuth(existing email) - Cancel โ
login
UI Components Rendered:
- Email input
- Verification code input
- Send verification code button (with slider CAPTCHA)
- Submit button ("Verify and Continue")
- Cancel button ("Cancel Login")
Behavior Details:
- Slider CAPTCHA required before sending verification code (once per session)
- Email verification code sent via
BIND_EMAILscene - Backend verifies the code internally (no separate verify endpoint call)
- If the email is already registered โ routes to
bindOAuth - If the email is new โ routes to
setPasswordwith email and username context - Temporary JWT token (
tempToken) carries WeChat OAuth context across states
WeChat OAuth Flow
Important
WeChat OAuth uses the qrconnect flow for website applications. Unlike GitHub/Microsoft, WeChat does not return the user's email, requiring an additional email binding step.
sequenceDiagram
participant U as User
participant E as eIsland (Renderer)
participant B as System Browser
participant W as WeChat Open Platform
participant S as Backend Server
U->>E: Click WeChat login button
E->>S: GET /auth/oauth/wechat/authorize
S-->>E: authorizeUrl (no state)
E->>E: Generate random sessionId (UUID)
E->>B: shell.openExternal(authorizeUrl + state=sessionId)
B->>W: User scans QR code
W->>S: Redirect to callback?code=xxx&state=sessionId
S->>S: Exchange code for access_token
S->>S: Fetch user info (nickname, avatar, openid)
S->>S: Store result by sessionId
S-->>B: 302 redirect to configured URL
loop Poll every 2s (max 5 min)
E->>S: GET /auth/oauth/poll?sessionId=xxx
S-->>E: { ready: true }
end
E->>S: GET /auth/oauth/consume?sessionId=xxx
S-->>E: OAuth result (null email)
E->>E: Route to bindEmail state
U->>E: Enter email + verification code
E->>S: POST /auth/oauth/wechat/bind-email
S-->>E: Result (SET_PASSWORD or BIND_OAUTH)
E->>E: Route to setPassword or bindOAuthBackend Decision Logic (bind-email):
| Condition | Result | Next State |
|---|---|---|
| Email already registered | BIND_OAUTH with tempToken | โ bindOAuth |
| Email not registered | SET_PASSWORD with tempToken | โ setPassword |
Note
WeChat website applications use https://open.weixin.qq.com/connect/qrconnect as the authorization endpoint. The appid parameter is used instead of client_id, and the response uses openid (not id) as the unique identifier.
payment
Info
The payment state handles subscription and payment processing. For the Alipay/WeChat integration, see Payment Processing. For the order lifecycle, see MySQL โ Payment Domain.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User initiates purchase
- Subscription renewal
- Upgrade request
Exit Conditions:
- Payment success โ
idle(with benefits granted) - Payment failure โ
expanded(with error) - Cancel โ previous state
- Timeout โ
idle
UI Components Rendered:
- Product selection
- Price display
- Payment method selection (Alipay, WeChat Pay)
- QR code (for mobile payment)
- Payment status indicator
- Cancel button
Behavior Details:
- Self-handled navigation
- Multiple payment channels (Alipay, WeChat Pay)
- Order creation with idempotency
- Payment timeout (15 minutes default)
- Async notification handling
- Receipt email delivery
agent
Info
The agent state provides the AI assistant chat interface with tool calling capabilities. For the LLM gateway and tool system, see AI Integration. For the billing balance, see Redis โ Agent Balance.
| Property | Value |
|---|---|
| Dimensions | 500ร88 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User opens AI assistant
- Voice command triggers agent
- Keyboard shortcut
Exit Conditions:
- Close button โ
idle - Escape key โ
idle - Minimize โ
idle
UI Components Rendered:
- Chat message history
- Input field
- Send button
- Tool call indicators
- Streaming response display
- Model selector
- Settings button
Behavior Details:
- Real-time streaming responses
- Tool calling (50+ tools available)
- Multi-provider support (DeepSeek, MiMo, MiniMax)
- Thinking mode (chain-of-thought)
- Chat session persistence
- Token usage tracking
- Balance management
agentVoiceInput
Info
The agentVoiceInput state provides voice command recognition interface.
| Property | Value |
|---|---|
| Dimensions | 500ร88 px |
| Mouse | Pass-through |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- Voice input hotkey pressed
- Manual voice activation
- Agent voice command request
Exit Conditions:
- Voice recognition complete โ
agent - Silence timeout โ
idle - Cancel gesture โ
idle - Escape key โ
idle
UI Components Rendered:
- Voice waveform visualization
- Recording indicator
- Cancel button
- Status text ("Listening...", "Processing...")
Behavior Details:
- Fullscreen transparent overlay
- Mouse pass-through (does not block interactions)
- Real-time speech recognition via Tencent Cloud STT
- WebSocket streaming for low latency
- Noise cancellation
- Multi-language support
stt
Info
The stt (Speech-to-Text) state displays transcription results.
| Property | Value |
|---|---|
| Dimensions | 500ร88 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- Speech recognition active
- Transcription request
Exit Conditions:
- Transcription complete โ
agentoridle - Cancel โ
idle - Timeout โ
idle
UI Components Rendered:
- Real-time transcription text
- Confidence indicator
- Edit button
- Copy button
- Cancel button
Behavior Details:
- Real-time transcription display
- Editable results
- Copy to clipboard
- Integration with agent for command processing
cli
Info
The cli state provides real-time monitoring of Claude Code and Codex CLI sessions. For the full architecture, event system, and permission handling, see CLI State & Codex Support.
| Property | Value |
|---|---|
| Dimensions | 500ร88 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- New CLI session detected (auto-transition via notification)
- Permission request received (auto-transition with sound + glow)
- Click on CLI tab with active session (from
expanded,maxExpand, orannouncement)
Exit Conditions:
- Close button โ
idle - Click on body โ
maxExpand(opens full CLI panel) - Escape key โ previous state
UI Components Rendered:
- Provider icon (animated GIF for Claude, static SVG for Codex)
- Session title and phase badge
- Latest event summary or pending permission details
- Provider switch (Claude / Codex toggle)
- Permission buttons (Deny / Allow / Always Allow) โ Claude only
- Synced lyrics overlay (when music is playing and no permission is pending)
Behavior Details:
- Dual-provider support (Claude Code and Codex)
- Real-time event streaming via IPC
- Automatic session detection with notification sound
- CLI glow overlay effect on new sessions
- Permission request auto-prompts with tool details
- Pill mode: content height reduced to 80px (shell stays 100px)
- Click body to expand into full
maxExpandCLI panel with session sidebar, event stream, and activity heatmap
musicProvidersLogin
Info
The musicProvidersLogin state provides a QR code login interface for third-party music providers. It currently supports Soda Music (Qishui) and is designed to be extensible to other providers. The state uses a polling-based architecture to detect when the user scans and confirms the QR code on their phone.
| Property | Value |
|---|---|
| Dimensions | 860ร400 px |
| Mouse | Interactive |
| Expanded | Yes |
| Enter Delay | 0ms |
| Leave Delay | 0ms |
Entry Conditions:
- User initiates music provider login from Settings โ Music โ Providers tab
- Store action
setMusicProvidersLogin(provider)triggers the state transition
Exit Conditions:
- Login confirmed โ return to previous state (via
returnFromAuth) - Back button โ return to previous state
- Escape key โ return to previous state
UI Components Rendered:
- Provider icon and name
- QR code (generated from provider's scan URL)
- Status indicator (waiting / scanned / confirmed / expired / error)
- Success checkmark animation on confirmation
- Refresh button (re-generates QR code)
- Back / Done button
- Report issue button (shown after confirmation)
Behavior Details:
- Self-handled navigation (does not follow standard click flow)
- QR code polling interval: 1.2s initial, then 4.5s default (or provider-specified
retryAfterMs) - Generation-aware polling: stale responses from previous QR sessions are discarded
- Error recovery: on poll failure, retries after 8 seconds
- On mount: checks existing auth status; if already logged in, shows confirmed state immediately
QR Login State Machine
stateDiagram-v2
[*] --> idle
idle --> waiting: createQrCode()
waiting --> scanned: QR scanned by user
waiting --> expired: QR expires
waiting --> rate_limited: Too many requests
waiting --> mfa_cancelled: MFA cancelled
waiting --> error: Poll failure
scanned --> confirmed: User confirms on phone
scanned --> expired: QR expires
confirmed --> [*]
expired --> waiting: refresh()
rate_limited --> waiting: retry (60s cooldown)
error --> waiting: retry (8s backoff)
mfa_cancelled --> idle: BackAuth State Values
| State | Description | Action |
|---|---|---|
idle | Initial state, no QR code active | Show loading spinner |
waiting | QR code displayed, waiting for scan | Show QR code, poll every 4.5s |
scanned | User scanned QR on phone | Show QR code with scanned indicator |
confirmed | Login successful | Show success checkmark, stop polling |
expired | QR code expired | Show expired message, offer refresh |
rate_limited | Too many QR requests | Wait 60s before retry |
mfa_cancelled | User cancelled MFA prompt | Stop polling, show cancellation |
error | Poll or network error | Show error, retry after 8s |
IPC Channels
| Channel | Direction | Description |
|---|---|---|
music-provider-auth:status | Renderer โ Main | Check if provider is already logged in |
music-provider-auth:create-qr | Renderer โ Main | Generate new QR code, returns token + scan URL |
music-provider-auth:check-qr | Renderer โ Main | Poll QR scan status with token |
music-provider-auth:clear | Renderer โ Main | Clear stored auth session |
Module Structure
musicProvidersLogin module file tree
musicProvidersLogin/
โโโ index.ts # Module entry point, re-exports MusicProvidersLoginContent
โโโ config/
โ โโโ providerConfig.ts # Static config per provider (icon, i18n keys)
โโโ hooks/
โ โโโ useMusicProviderQrLogin.ts # QR polling hook, state management
โโโ components/
โโโ MusicProvidersLoginContent.tsx # Login UI with QR code, buttons, statusTips
Adding a new music provider requires: (1) adding the provider to MusicProviderId union type in shared/musicProviderAuth.ts, (2) creating a provider-specific auth service in main/music/providers/, (3) registering it in the PROVIDER_HANDLERS map in main/ipc/media/musicProviderAuth.ts, and (4) adding a config entry in config/providerConfig.ts.
Changelog
bb5b2-on

