5.0 KiB
5.0 KiB
Zhiju AI Assistant — Developer Notes
Project Overview
Electron desktop app (Electron Forge + Vite + Vue 3) that wraps a locally-spawned opencode binary (located in resources/). The renderer is a Vue 3 SPA; the main process manages the opencode child process and exposes IPC to the renderer via a preload script.
Build & Configuration
Prerequisites
- Node.js ≥ 18
- The
opencodebinary must exist atresources/windows/x64/opencode.exe(Windows) before packaging. It is not committed to the repo — obtain it separately.
Install dependencies
npm install
Run in development
npm start
This uses electron-forge start, which runs Vite for the renderer and launches Electron. Hot-reload is active for the renderer; changes to src/main/index.js or src/preload/index.js require a manual restart.
Package / distribute
npm run make # builds installers for the current platform
npm run package # produces an unpackaged app directory
Packaged output lands in out/. The opencode.exe binary is bundled via extraResource in forge.config.js and unpacked from ASAR at runtime.
Vite configs
| File | Purpose |
|---|---|
vite.main.config.mjs |
Main process bundle |
vite.preload.config.mjs |
Preload script bundle |
vite.renderer.config.mjs |
Renderer (Vue SPA), alias @ → src/renderer |
Architecture Notes
- Main process (
src/main/index.js): spawnsopencodebinary, resolves a free port starting at4096, waits for TCP readiness, then injectswindow.__opencodeBaseUrlinto the renderer viaexecuteJavaScript. Also runs Bonjour service discovery. - Preload (
src/preload/index.js): bridges IPC between main and renderer usingcontextBridge. - Renderer (
src/renderer/): Vue 3 + Pinia + Vue Router. HTTP calls go throughsrc/renderer/http/(axios-based). SSE streaming is handled insrc/renderer/http/sse.js. - URL constants (
src/renderer/http/url.js): single source of truth for all API endpoint paths.getBaseUrl()readswindow.__opencodeBaseUrl(injected by main) with fallback tohttp://127.0.0.1:4096. - Crypto (
src/renderer/utils/crypto.js): passwords are Base64-encoded then RSA-encrypted (public key hardcoded) before being sent to the login API.
Testing
Framework
Vitest — chosen for native Vite/ESM compatibility, zero extra config needed.
Run all tests
npm test # vitest run (single pass, CI-friendly)
npm run test:watch # vitest watch mode (development)
Or directly:
npx vitest run
Writing tests
- Place test files alongside the source file as
*.test.js(Vitest picks them up automatically). - For pure utility/logic modules (e.g.
url.js,crypto.js) no additional setup is needed. - Modules that depend on
window,electron, or Pinia require mocking. Usevi.stubGlobalforwindowproperties andvi.mockfor module mocks.
Example: testing url.js
// src/renderer/http/url.test.js
import { describe, it, expect } from 'vitest';
import url from './url.js';
describe('url constants', () => {
it('session.detail returns correct path', () => {
expect(url.session.detail('abc123')).toBe('/session/abc123');
});
it('message.send returns correct path', () => {
expect(url.message.send('sess1')).toBe('/session/sess1/message');
});
});
Run with:
npx vitest run src/renderer/http/url.test.js
Testing modules that use window.__opencodeBaseUrl
import { vi, describe, it, expect, beforeEach } from 'vitest';
beforeEach(() => {
vi.stubGlobal('__opencodeBaseUrl', 'http://127.0.0.1:5000');
});
// import and test getBaseUrl() etc.
Code Style
- Formatter: Prettier (config in
package.jsondefaults). Runnpm run formatto auto-fix,npm run format:checkfor CI check. - Commit messages: enforced by commitlint (
@commitlint/config-conventional). Use conventional commits:feat:,fix:,chore:, etc. - Pre-commit hook: husky + lint-staged runs Prettier on staged
*.jsand*.vuefiles automatically. - No ESLint is configured — only Prettier for formatting.
- Vue components use the Composition API (
<script setup>style in newer components, options-stylesetup()returning refs in stores). - Pinia stores use the Setup Store pattern (function returning refs/actions), not the Options Store pattern.
- Chinese comments are common throughout the codebase — maintain them in Chinese when editing existing files.
Key Dependencies
| Package | Role |
|---|---|
electron v41 |
Desktop shell |
electron-forge v7 |
Build/package toolchain |
vite v5 + @vitejs/plugin-vue |
Renderer bundler |
vue v3 + pinia + vue-router |
UI framework |
axios |
HTTP client (renderer) |
element-plus |
UI component library |
bonjour-service |
LAN service discovery |
jsencrypt + js-base64 |
Password encryption before login |
unified / remark / rehype |
Markdown rendering pipeline |
katex |
Math formula rendering in chat |