Skip to content

Commit 8f1cfe0

Browse files
committed
docs: add AGENTS.md and CLAUDE.md for AI coding assistants
1 parent ecbb52d commit 8f1cfe0

2 files changed

Lines changed: 254 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
# AGENTS.md
2+
3+
## Project Overview
4+
5+
**Interview Coder CN** (编码面试解题助手) is a desktop application that captures screenshots of coding problems and uses AI (vision models) to generate solutions in real-time. The window is invisible to screen-sharing software, making it suitable for use during coding interviews and online assessments.
6+
7+
Key capabilities:
8+
- Global shortcuts trigger screenshot capture → AI analysis → streamed solution display
9+
- Frameless, transparent, always-on-top overlay window invisible to screen-sharing
10+
- Mouse passthrough mode (window ignores mouse events)
11+
- Multi-screenshot conversation continuity (append screenshots to existing context)
12+
- Follow-up questions within the same conversation
13+
- Configurable AI provider (OpenAI, SiliconFlow, OpenRouter, or any OpenAI-compatible API)
14+
15+
## Tech Stack
16+
17+
| Layer | Technology |
18+
|-------|-----------|
19+
| Framework | Electron 37 (electron-vite 4) |
20+
| Frontend | React 19, TypeScript 5.8 |
21+
| Styling | Tailwind CSS v4, shadcn/ui (New York style), Radix primitives |
22+
| State | Zustand 5 (4 stores, 2 with localStorage persistence) |
23+
| Routing | react-router v7 (HashRouter, 3 routes) |
24+
| AI | Vercel AI SDK (`ai` + `@ai-sdk/openai`), streaming via `streamText()` |
25+
| Build | electron-vite (Vite 7), electron-builder 25 |
26+
| Linting | ESLint 9 (flat config), Prettier |
27+
28+
## Directory Structure
29+
30+
```
31+
src/
32+
├── main/ # Electron main process
33+
│ ├── index.ts # App entry: lifecycle, error handling, app.whenReady()
34+
│ ├── main-window.ts # BrowserWindow creation (frameless, transparent, always-on-top)
35+
│ ├── shortcuts.ts # Global shortcuts registration + AI streaming orchestration (largest file)
36+
│ ├── ai.ts # Vercel AI SDK integration, 3 streaming functions
37+
│ ├── settings.ts # App settings object + IPC handlers
38+
│ ├── state.ts # App state object + IPC handlers
39+
│ ├── take-screenshot.ts # desktopCapturer → base64 PNG
40+
│ ├── auto-updater.ts # electron-updater (non-macOS only)
41+
│ ├── prompts.md # System prompt for AI (copied to build output via vite-plugin-static-copy)
42+
│ └── index.d.ts # global.mainWindow type declaration
43+
├── preload/
44+
│ ├── index.ts # contextBridge API: exposes window.api to renderer
45+
│ └── index.d.ts # Type declarations for window.electron and window.api
46+
└── renderer/
47+
├── index.html # SPA entry
48+
└── src/
49+
├── main.tsx # React root render
50+
├── App.tsx # Router + settings sync + shortcut init + Toaster
51+
├── coder/ # Main page: screenshot display + AI solution stream
52+
│ ├── index.tsx # CoderPage layout + state sync
53+
│ ├── AppHeader.tsx # Draggable title bar with nav buttons
54+
│ ├── AppContent.tsx# Screenshots gallery + markdown solution + error banner
55+
│ ├── AppStatusBar.tsx # Loading indicator, follow-up dialog, shortcut hints
56+
│ └── PrerequisitesChecker.tsx # Modal for API key setup
57+
├── settings/ # Settings page
58+
│ ├── index.tsx # AI config, coding, appearance, shortcuts, privacy
59+
│ ├── SelectLanguage.tsx # Combobox with custom language input
60+
│ ├── SelectModel.tsx # Combobox with custom model input
61+
│ └── CustomShortcuts.tsx # Shortcut key recorder
62+
├── help/ # Help page
63+
│ ├── index.tsx # Quick start guide, shortcuts, FAQ
64+
│ ├── Shortcuts.tsx
65+
│ ├── FAQ.tsx
66+
│ └── components/index.tsx # HelpSection wrapper
67+
├── components/
68+
│ ├── MarkdownRenderer.tsx # react-markdown + remark-gfm + rehype-highlight
69+
│ ├── ShortcutRenderer.tsx # Platform-aware shortcut key badges
70+
│ └── ui/ # shadcn/ui primitives (button, dialog, select, etc.)
71+
├── lib/
72+
│ ├── store/ # Zustand stores
73+
│ │ ├── app.ts # ignoreMouse state, synced from main process
74+
│ │ ├── settings.ts # API config, model, language, opacity (persisted v4)
75+
│ │ ├── shortcuts.ts # Shortcut bindings (persisted v3, with migration)
76+
│ │ └── solution.ts # Loading state, solution chunks, screenshots, errors
77+
│ └── utils/
78+
│ ├── index.ts # cn() helper, getCloneableFields()
79+
│ ├── env.ts # isMac, platformAlt
80+
│ └── keyboard.ts # Accelerator string conversion
81+
└── assets/
82+
├── base.css # Tailwind @import, CSS variables, app layout styles
83+
└── main.css # Tailwind + typography plugin + theme variables (oklch)
84+
```
85+
86+
## Architecture
87+
88+
### Process Model
89+
90+
```
91+
┌─────────────────────────────────────────────────────┐
92+
│ Main Process (src/main/) │
93+
│ ┌──────────┐ ┌──────────┐ ┌───────────────────┐ │
94+
│ │ settings │ │ state │ │ shortcuts.ts │ │
95+
│ │ .ts │ │ .ts │ │ (orchestrator) │ │
96+
│ └────┬─────┘ └────┬─────┘ │ - global hotkeys │ │
97+
│ │ │ │ - AI streaming │ │
98+
│ │ │ │ - conversation │ │
99+
│ │ │ │ management │ │
100+
│ │ │ └──┬───────────┬────┘ │
101+
│ │ │ │ │ │
102+
│ │ │ ┌─────┴──┐ ┌─────┴────┐ │
103+
│ │ │ │ ai.ts │ │take- │ │
104+
│ │ │ │ │ │screenshot│ │
105+
│ │ │ └────────┘ └──────────┘ │
106+
│ └──────────────┼───────────┘ │
107+
│ IPC (ipcMain.handle) │
108+
├─────────────────────────────────────────────────────┤
109+
│ Preload (src/preload/) │
110+
│ contextBridge → window.api │
111+
├─────────────────────────────────────────────────────┤
112+
│ Renderer (src/renderer/) │
113+
│ React app with Zustand stores │
114+
│ window.api.on*() for events from main │
115+
│ window.api.*() for invoke calls to main │
116+
└─────────────────────────────────────────────────────┘
117+
```
118+
119+
### Data Flow: Screenshot → Solution
120+
121+
1. User presses global shortcut (e.g., `Alt+Enter` on macOS)
122+
2. `shortcuts.ts` callback triggers `takeScreenshot()``desktopCapturer` → base64 PNG
123+
3. Main sends `screenshot-taken` and `ai-loading-start` to renderer
124+
4. Main calls `getSolutionStream(base64Image)` → Vercel AI SDK `streamText()`
125+
5. Stream chunks sent to renderer via `solution-chunk` IPC events
126+
6. Renderer accumulates chunks in `useSolutionStore` and renders via `MarkdownRenderer`
127+
7. On completion: `solution-complete`; on error: `solution-error`; on abort: `solution-stopped`
128+
129+
### IPC Channels
130+
131+
**Renderer → Main (invoke):**
132+
- `getAppSettings` / `updateAppSettings` — settings CRUD
133+
- `updateAppState` — sync `inCoderPage`, `ignoreMouse`
134+
- `initShortcuts` / `getShortcuts` / `updateShortcuts` — shortcut management
135+
- `stopSolutionStream` — abort current AI stream
136+
- `sendFollowUpQuestion` — follow-up within conversation
137+
138+
**Main → Renderer (send):**
139+
- `sync-app-state` — push state changes (e.g., mouse ignore toggle)
140+
- `screenshot-taken` / `screenshots-updated` — screenshot data
141+
- `solution-clear` / `solution-chunk` / `solution-complete` / `solution-stopped` / `solution-error` — AI streaming lifecycle
142+
- `ai-loading-start` / `ai-loading-end` — loading state
143+
- `scroll-page-up` / `scroll-page-down` — keyboard-driven scroll
144+
145+
### Zustand Stores
146+
147+
| Store | File | Persisted | Key State |
148+
|-------|------|-----------|-----------|
149+
| `useSettingsStore` | `lib/store/settings.ts` | Yes (v4) | `apiBaseURL`, `apiKey`, `model`, `customModels`, `codeLanguage`, `opacity`, `customPrompt` |
150+
| `useShortcutsStore` | `lib/store/shortcuts.ts` | Yes (v3) | `shortcuts` (action → key mapping with categories) |
151+
| `useSolutionStore` | `lib/store/solution.ts` | No | `isLoading`, `solutionChunks`, `screenshotData`, `errorMessage` |
152+
| `useAppStore` | `lib/store/app.ts` | No | `ignoreMouse` |
153+
154+
Settings are bidirectionally synced: renderer persists to localStorage, and on mount syncs to main process via `updateAppSettings()`. Main process `.env` values serve as initial defaults only.
155+
156+
## Key Patterns & Conventions
157+
158+
### Window Stealth
159+
160+
The app is designed to be invisible to screen-sharing software:
161+
- `BrowserWindow` options: `transparent: true`, `frame: false`, `skipTaskbar: true`
162+
- `setContentProtection(true)` prevents screen capture of the window itself
163+
- `setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true, skipTransformProcessType: true })`
164+
- `keepWindowInFront()` repeatedly reasserts always-on-top for 5 seconds after show
165+
- `showInactive()` on macOS/Windows to avoid stealing focus
166+
167+
### AI Integration
168+
169+
- All AI calls go through `src/main/ai.ts` using Vercel AI SDK's `streamText()`
170+
- Provider: `@ai-sdk/openai` with custom `baseURL` (works with any OpenAI-compatible API)
171+
- Model fallback: `Qwen/Qwen3-VL-32B-Instruct` for SiliconFlow, `gpt-5-mini` otherwise
172+
- System prompt is loaded from `prompts.md` at build time (bundled via `vite-plugin-static-copy`)
173+
- Three streaming functions: `getSolutionStream` (first screenshot), `getFollowUpStream` (follow-up), `getGeneralStream` (multi-screenshot)
174+
- Conversation history (`conversationMessages`) is maintained in `shortcuts.ts` as `ModelMessage[]`
175+
176+
### Stream Abort Pattern
177+
178+
- `StreamContext` with `AbortController` and `reason` (`'user'` | `'new-request'`)
179+
- New requests automatically abort previous streams
180+
- User can manually stop via shortcut or UI button
181+
- Abort reason determines which IPC event to send (`solution-stopped` for user, silent for new-request)
182+
183+
### Shortcut System
184+
185+
- Global shortcuts registered via Electron's `globalShortcut` API
186+
- Renderer stores shortcut config in Zustand (persisted); sends to main on init
187+
- On Windows, `Alt`-based shortcuts also register `Ctrl+Alt` variant for compatibility
188+
- Shortcut actions are string-keyed callbacks in `shortcuts.ts`
189+
- Default shortcuts use `platformAlt` (`Alt` on macOS, `CommandOrControl` on Windows/Linux)
190+
191+
### UI Component Patterns
192+
193+
- shadcn/ui components in `src/renderer/src/components/ui/` — do NOT edit these directly, use the shadcn CLI to add/update
194+
- `cn()` utility (clsx + tailwind-merge) for conditional class merging
195+
- `getCloneableFields()` strips functions from store state before sending over IPC
196+
- Platform-aware shortcut display via `ShortcutRenderer` (⌘, ⌥, ⇧ on Mac; Ctrl, Alt, Shift on Windows)
197+
198+
## Development
199+
200+
### Commands
201+
202+
```bash
203+
npm install # Install dependencies
204+
npm run dev # Start in dev mode (electron-vite dev)
205+
npm run build # Typecheck + build (electron-vite build)
206+
npm run build:mac # Build macOS distributable
207+
npm run build:win # Build Windows distributable
208+
npm run typecheck # Run TypeScript type checking (node + web)
209+
npm run lint # Run ESLint
210+
npm run format # Run Prettier
211+
```
212+
213+
### Configuration
214+
215+
The `.env` file at project root configures the AI provider:
216+
217+
```env
218+
API_BASE_URL="https://openrouter.ai/api/v1" # OpenAI-compatible API endpoint
219+
API_KEY="sk-..." # API key
220+
MODEL="gpt-5-mini" # Optional: override default model
221+
```
222+
223+
These are read by dotenv in the main process and merged with renderer-side settings (renderer settings take priority when set).
224+
225+
### Path Aliases
226+
227+
- `@renderer/*` and `@/*` both resolve to `src/renderer/src/*`
228+
- Configured in `tsconfig.web.json` and `electron.vite.config.ts`
229+
230+
### Code Style
231+
232+
- Prettier: single quotes, no semicolons, 100 char print width, no trailing commas
233+
- ESLint: TypeScript + React + React Hooks + React Refresh rules
234+
- UI text and user-facing strings are in **Chinese** (中文)
235+
- Code comments and variable names are in **English**
236+
237+
## Important Notes for AI Agents
238+
239+
1. **Three TypeScript configs**: `tsconfig.node.json` (main + preload), `tsconfig.web.json` (renderer). The root `tsconfig.json` is a project references file only.
240+
241+
2. **`prompts.md` is bundled**: It lives in `src/main/` but gets copied to the build output via `vite-plugin-static-copy`. Loaded at runtime with `readFileSync(join(import.meta.dirname, 'prompts.md'))`.
242+
243+
3. **`global.mainWindow`**: The main window reference is stored as a global variable, declared in `src/main/index.d.ts`.
244+
245+
4. **Settings flow**: `.env` → main process `settings` object → renderer reads on mount via IPC → renderer persists to localStorage via Zustand. Renderer-side changes are sent back to main via `updateAppSettings`.
246+
247+
5. **No shared types directory**: Main process types (`AppSettings`, `AppState`) are imported directly by the preload script from `../main/settings` and `../main/state`. This works because preload shares the Node.js tsconfig.
248+
249+
6. **Streaming orchestration is in `shortcuts.ts`**: Despite the filename, this 580+ line file is the central orchestrator for both global shortcuts AND AI streaming logic. It manages conversation history, abort controllers, and IPC communication for the entire AI workflow.
250+
251+
7. **Window movement**: The window can be moved via keyboard shortcuts in 200px steps (up/down/left/right).
252+
253+
8. **macOS auto-update is disabled**: `publish: null` in electron-builder.yml for mac target. Auto-update only works on Windows.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@include AGENTS.md

0 commit comments

Comments
 (0)