# File & Directory Structure Rules for organizing files and directories across the entire Electron project. ## Repository Root ### Root Directory Rules - **README translations** belong in `docs/readme/`, not at root. Only the main `readme.md` stays at root (GitHub convention) - **Guide documents** (deployment, testing, WebUI, CDP, etc.) belong in `docs/guides/` - **Contributor documentation** (dev setup, code style, file structure, PR workflow) belongs in `docs/contributing/` - **Architecture documentation** belongs in `docs/architecture/` (research notes under `docs/architecture/research/`) - **Feature specs / PRDs / design drafts** belong in `docs/specs/` (or `docs/prds/` for formal PRDs maintained by the product team) - **Config files** (`tsconfig.json`, `package.json`, etc.) stay at root — Node.js/Electron ecosystem convention - **New documentation** should be placed under the appropriate `docs/` subdirectory, not at project root ### Current Root Cleanup Targets | Action | Files | | ------------------------------------------ | ---------------------------------- | | Move readme translations to `docs/readme/` | `readme_{ch,es,jp,ko,pt,tr,tw}.md` | ## Project Layout (`src/`) AionUi is a multi-process Electron app with three core layers: **renderer**, **main process**, and **preload/shared**. ### Target Structure ``` src/ ├── renderer/ # Renderer layer — React UI, no Node.js APIs ├── process/ # Main process layer — all Node.js / Electron business │ ├── bridge/ # IPC handlers │ ├── services/ # Business logic │ ├── database/ # SQLite │ ├── task/ # Agent/task management │ ├── agent/ # AI platform connections │ ├── channels/ # Multi-channel messaging │ ├── extensions/ # Plugin system │ ├── webserver/ # WebUI server │ ├── worker/ # Background workers (fork) │ └── i18n/ # Main-process i18n ├── common/ # Shared layer — cross-process types, adapters, utilities ├── preload.ts # IPC bridge — contextBridge between main ↔ renderer └── index.ts # Main process entry point ``` ### Current Structure All main-process modules now live under `src/process/`. The `src/` root contains only the three core layers (`renderer/`, `process/`, `common/`), the entry files (`index.ts`, `preload.ts`), and the ambient type declaration (`types.d.ts`). ## Directory Naming — Two Conventions by Process This project straddles two ecosystems. Each follows its own convention: | Scope | Directory naming | Reason | | ---------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- | | **Renderer** (`src/renderer/`) | **PascalCase** for component/module dirs | React ecosystem — directory name = component name | | **Everything else** | **lowercase** | Node.js ecosystem | | **Categorical dirs** (everywhere) | **lowercase** | `components/`, `hooks/`, `utils/`, `services/` are categories, not entities | | **Platform dirs** (renderer pages) | **lowercase** | Mirror `src/process/agent//` naming for cross-process consistency | ### Quick test > "Is this directory inside `src/renderer/` AND does it represent a specific component or feature module (not a category)?" > > **YES** → PascalCase. **NO** → lowercase. > > **Exception**: Platform directories (`acp/`, `codex/`, `gemini/`, `nanobot/`, `openclaw/`) always use lowercase, even inside renderer, to match `src/process/agent/`. ### Renderer examples ``` src/renderer/ ├── components/ # categorical → lowercase │ ├── SettingsModal/ # component → PascalCase │ └── EmojiPicker/ # component → PascalCase ├── pages/ # categorical → lowercase │ ├── settings/ # top-level page → lowercase (route segment) │ │ ├── CssThemeSettings/ # feature module → PascalCase │ │ └── McpManagement/ # feature module → PascalCase │ └── conversation/ # top-level page → lowercase │ ├── GroupedHistory/ # feature module → PascalCase │ ├── Workspace/ # feature module → PascalCase │ ├── acp/ # platform dir → lowercase (mirrors src/agent/acp/) │ └── components/ # categorical → lowercase └── hooks/ # categorical → lowercase ``` ### Non-renderer examples ``` src/process/services/cron/ # lowercase src/process/agent/acp/ # lowercase src/process/channels/plugins/dingtalk/ # lowercase ``` ## File Naming — Same Everywhere | Content | Convention | Examples | | ------------------------- | ------------------------------- | ------------------------------------- | | React components, classes | PascalCase | `SettingsModal.tsx`, `CronService.ts` | | Hooks | camelCase with `use` prefix | `useTheme.ts`, `useCronJobs.ts` | | Utilities, helpers | camelCase | `formatDate.ts`, `cronUtils.ts` | | Entry points | `index.ts` / `index.tsx` | Required for directory-based modules | | Config, types, constants | camelCase | `types.ts`, `constants.ts` | | Styles | kebab-case or `Name.module.css` | `chat-layout.css` | ## Process Boundary Rules **Violating these causes runtime crashes.** | Process | Can use | Cannot use | | ---------------------------------- | ----------------------------- | -------------------------------- | | **Main** (`src/process/`) | Node.js, Electron main APIs | DOM APIs, React | | **Renderer** (`src/renderer/`) | DOM APIs, React, browser APIs | Node.js APIs, Electron main APIs | | **Worker** (`src/process/worker/`) | Node.js APIs | DOM APIs, Electron APIs | Cross-process communication MUST go through: - Main ↔ Renderer: IPC via `src/preload.ts` + `src/process/bridge/*.ts` - Main ↔ Worker: fork protocol via `src/process/worker/WorkerProtocol.ts` ## Main Process Naming | Type | Pattern | Examples | | ---------- | --------------------- | --------------------------------- | | Bridge | `Bridge.ts` | `cronBridge.ts`, `webuiBridge.ts` | | Service | `Service.ts` | `CronService.ts`, `McpService.ts` | | Interface | `IService.ts` | `IConversationService.ts` | | Repository | `Repository.ts` | `SqliteConversationRepository.ts` | ## Service Testability Rules ### Pure Logic vs IO Separation Services must separate **pure logic** from **IO operations**: - **Pure logic** (data transformation, validation, formatting) → standalone functions, no `fs`/`db`/`net` imports - **IO operations** (file read, DB query, HTTP call) → thin wrappers in service class or repository - Service methods should receive IO results as parameters rather than calling IO internally ### Dependency Injection Services and bridges that depend on external resources (DB, file system, other services) should accept dependencies as constructor/function parameters: ```typescript // ❌ Hard to test — must mock the entire module import { db } from '@process/database'; function getConversation(id: string) { return db.query('SELECT * FROM conversations WHERE id = ?', id); } // ✅ Easy to test — inject the dependency function getConversation(repo: IConversationRepository, id: string) { return repo.findById(id); } ``` For existing code using direct imports, `vi.mock()` is acceptable. For new code, prefer parameter injection. ## Test File Mapping Test files must mirror the source file they test: | Source | Test | | -------------------------------------------- | ----------------------------------------------- | | `src/process/services/CronService.ts` | `tests/unit/cronService.test.ts` | | `src/process/bridge/fsBridge.ts` | `tests/unit/fsBridge.test.ts` | | `src/renderer/utils/chat/latexDelimiters.ts` | `tests/unit/latexDelimiters.test.ts` | | `src/renderer/hooks/ui/useAutoScroll.ts` | `tests/unit/useAutoScroll.dom.test.ts` | | `src/process/extensions/ExtensionLoader.ts` | `tests/unit/extensions/extensionLoader.test.ts` | When `tests/unit/` exceeds 10 direct children, group into subdirectories matching the source structure (e.g., `tests/unit/extensions/`). New source files with logic should be added to `vitest.config.ts` → `coverage.include`. ## Directory Size Limit A single directory must not contain more than **10** direct children (files + subdirectories). When a directory approaches this limit, split its contents into subdirectories grouped by responsibility. ## UI Library & Icon Standards - **Component library**: `@arco-design/web-react`. All new UI must use Arco components first. - **Icon library**: `@icon-park/react`. All icons must come from this library. - **No raw HTML for interactive elements**: Do not use native `