Contributing
Thank you for your interest in contributing to Elftia. This document describes the complete workflow for participating in development.
Setting Up Your Development Environment
See the Local Development guide for detailed steps. Quick summary:
{/* Prerequisites */}
{/* Node.js v24 (see .nvmrc) + native build tools */}
git clone <repo-url> elftia
cd elftia
npm install
cp .env.example .env
npm run dev
Branch Strategy
| Branch | Purpose | Protection Rules |
|---|---|---|
main | Stable release, always shippable | Requires PR + Review |
dev/* | Feature development branches | - |
fix/* | Bug fix branches | - |
Branch Naming
{/* New feature */}
git checkout -b dev/feature-name
{/* Bug fix */}
git checkout -b fix/bug-description
{/* Examples */}
git checkout -b dev/notes-feature
git checkout -b fix/chat-scroll-issue
Code Standards
See Code Standards for the full specification. Key points:
ESLint
- error-level rules block commits (import ordering,
no-var,eqeqeq,prefer-const, Hook rules) - warn-level rules allow commits but are recommended to fix
- Husky runs
eslint --fixautomatically before commits
File Size
| File Type | Recommended Limit | Hard Limit |
|---|---|---|
| React components | 400 lines | 800 lines |
| Custom Hooks | 300 lines | 600 lines |
| Utility functions | 150 lines | 300 lines |
| Type definitions | 100 lines | 200 lines |
| Services | 300 lines | 600 lines |
Naming
- Components: PascalCase (
UserMessage.tsx) - Hooks: usePascalCase (
useChatActions.ts) - Directories: kebab-case (
chat-messages/) - Semantic tokens (no hardcoded colors)
Commit Convention
Use Conventional Commits format:
<type>: <description>
[optional body]
[optional footer]
Type Reference
| Type | Description | Example |
|---|---|---|
feat | New feature | feat: add notes export feature |
fix | Bug fix | fix: resolve chat scroll position reset |
refactor | Refactor | refactor: split ChatInterface into smaller components |
docs | Documentation | docs: update IPC channels reference |
style | Code formatting (no logic changes) | style: fix import ordering |
test | Tests | test: add unit tests for useFavorites |
chore | Build/tooling changes | chore: update Vite to v7.1 |
Example
git commit -m "feat: add favorites feature with session-scoped bookmarks
- Add FavoritesService with SQLite persistence
- Add FavoritesRouter with Zod validation
- Add useFavorites hook and FavoritesList component
- Add i18n support for en/zh/ja"
Pull Request Requirements
Core Principles
- One PR does one thing — do not mix feature development with bug fixes
- Tests — new features include tests
- Docs — update documentation when necessary
- CI Pass — all checks pass
PR Title
Concisely describe the change using Conventional Commits format:
feat: add favorites feature
fix: resolve chat message duplication
refactor: extract message rendering into separate components
PR Description Template
## Changes
- Brief description of change 1
- Brief description of change 2
## How to Test
- [ ] Manual test step 1
- [ ] Manual test step 2
- [ ] Unit tests pass
## Screenshots (if UI changes)
New Feature Development Checklist
See Adding a Feature for the complete end-to-end flow.
Backend
| Step | File Location | Description |
|---|---|---|
| 1. Shared types | packages/desktop/app/shared/contracts/ | Type definitions shared between frontend and backend |
| 2. Service | packages/desktop/app/main/services/ | Business logic encapsulation |
| 3. Router | packages/desktop/app/main/services/routers/ | IPC routing + Zod validation |
| 4. Registration | routers/index.ts → registerAllRouters() | Create instance and register |
| 5. Preload | packages/desktop/app/preload/index.ts | Expose to frontend |
Frontend
| Step | File Location | Description |
|---|---|---|
| 1. Hook | packages/renderer/src/features/<feature>/hooks/ (cross-feature hooks go in shared/hooks/) | Data fetching and state management |
| 2. Component | packages/renderer/src/features/<feature>/components/ (shared UI in components/ui/) | UI rendering |
| 3. Route | app/lazy/ (pages.tsx etc.) + app/App.tsx | New pages require lazy-load registration (legacy import path from '../lazy' via lazy/index.ts barrel is still compatible) |
| 4. i18n | packages/renderer/src/locales/{en,zh,ja}/ | Three-language translations |
Documentation
| Condition | Needs Update |
|---|---|
| New module added | .claude/skills/architecture-index/SKILL.md |
| User-visible feature | docs/elfi-kb/ knowledge base |
| New route added | docs/elfi-kb/INDEX.md mapping table |
Code Review Focus Areas
Frontend/Backend Separation
- Does the frontend only pass user input and configuration parameters?
- Does the backend handle all external calls?
- Is the returned data in its final form?
- Is transmission of large data like base64 over IPC avoided?
Security
- Are Router parameters validated with Zod?
- Are API keys used only in the main process?
- Are there potential XSS risks?
File Size
- Is the new file within the recommended limit?
- Does the modified file exceed the warning threshold?
- Does it need to be split?
Error Handling
- Do network requests have try-catch?
- Are error messages user-friendly?
- Are there appropriate loading and empty states?
Documentation
- Has the architecture index been updated?
- Has the knowledge base been updated?
- Are all three i18n languages in sync?
Bug Reports
Report Template
## Environment
- OS: Windows 11 / macOS 15 / Ubuntu 24
- Elftia version: v0.5.0
- Node.js version: v24.x
## Steps to Reproduce
1. Open the settings page
2. Switch to the "Appearance" tab
3. Click on wallpaper settings
4. After selecting an image...
## Expected Behavior
The wallpaper should display normally.
## Actual Behavior
The wallpaper does not display; console error: TypeError: Cannot read property 'path' of undefined
## Additional Information
- Diagnostic data export (Settings → Advanced → Export Diagnostic Data)
- Screenshots or screen recordings
- Console error logs
Exporting Diagnostic Data
Export diagnostic data from within the app to help troubleshoot issues:
Settings → Advanced → Diagnostic Tools → Export Diagnostic Data
Or via the DevTools Console:
const data = await window.api.diagnostics.bundle({ includeLogs: true });
Contact
- Project Issues — feature requests and bug reports
- Pull Requests — code contributions
- Discussions — technical discussion and questions