mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
341 lines
8.7 KiB
Markdown
341 lines
8.7 KiB
Markdown
# Contributing to Maestro
|
|
|
|
Thank you for your interest in contributing to Maestro! This document provides guidelines, setup instructions, and practical guidance for developers.
|
|
|
|
For architecture details, see [ARCHITECTURE.md](ARCHITECTURE.md). For quick reference while coding, see [CLAUDE.md](CLAUDE.md).
|
|
|
|
## Table of Contents
|
|
|
|
- [Development Setup](#development-setup)
|
|
- [Project Structure](#project-structure)
|
|
- [Development Scripts](#development-scripts)
|
|
- [Common Development Tasks](#common-development-tasks)
|
|
- [Code Style](#code-style)
|
|
- [Debugging Guide](#debugging-guide)
|
|
- [Commit Messages](#commit-messages)
|
|
- [Pull Request Process](#pull-request-process)
|
|
- [Building for Release](#building-for-release)
|
|
|
|
## Development Setup
|
|
|
|
### Prerequisites
|
|
|
|
- Node.js 20+
|
|
- npm or yarn
|
|
- Git
|
|
|
|
### Getting Started
|
|
|
|
```bash
|
|
# Fork and clone the repository
|
|
git clone <your-fork-url>
|
|
cd maestro
|
|
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Run in development mode with hot reload
|
|
npm run dev
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
maestro/
|
|
├── src/
|
|
│ ├── main/ # Electron main process (Node.js backend)
|
|
│ │ ├── index.ts # Entry point, IPC handlers
|
|
│ │ ├── process-manager.ts
|
|
│ │ ├── preload.ts # Secure IPC bridge
|
|
│ │ └── utils/ # Shared utilities
|
|
│ └── renderer/ # React frontend (UI)
|
|
│ ├── App.tsx # Main coordinator
|
|
│ ├── components/ # React components
|
|
│ ├── hooks/ # Custom React hooks
|
|
│ ├── services/ # IPC wrappers (git, process)
|
|
│ ├── contexts/ # React contexts
|
|
│ ├── constants/ # Themes, shortcuts, priorities
|
|
│ ├── types/ # TypeScript definitions
|
|
│ └── utils/ # Frontend utilities
|
|
├── build/ # Application icons
|
|
├── .github/workflows/ # CI/CD automation
|
|
└── dist/ # Build output (generated)
|
|
```
|
|
|
|
## Development Scripts
|
|
|
|
```bash
|
|
npm run dev # Start dev server with hot reload
|
|
npm run build # Full production build
|
|
npm run build:main # Build main process only
|
|
npm run build:renderer # Build renderer only
|
|
npm start # Start built application
|
|
npm run clean # Clean build artifacts
|
|
npm run package # Package for all platforms
|
|
npm run package:mac # Package for macOS
|
|
npm run package:win # Package for Windows
|
|
npm run package:linux # Package for Linux
|
|
```
|
|
|
|
## Common Development Tasks
|
|
|
|
### Adding a New UI Feature
|
|
|
|
1. **Plan the state** - Determine if it's per-session or global
|
|
2. **Add state management** - In `useSettings.ts` (global) or session state
|
|
3. **Create persistence** - Use wrapper function pattern for global settings
|
|
4. **Implement UI** - Follow Tailwind + theme color pattern
|
|
5. **Add keyboard shortcuts** - In `shortcuts.ts` and `App.tsx`
|
|
6. **Test focus flow** - Ensure Escape key navigation works
|
|
|
|
### Adding a New Modal
|
|
|
|
1. Create component in `src/renderer/components/`
|
|
2. Add priority in `src/renderer/constants/modalPriorities.ts`:
|
|
```typescript
|
|
MY_MODAL: 600,
|
|
```
|
|
3. Register with layer stack (see [ARCHITECTURE.md](ARCHITECTURE.md#layer-stack-system))
|
|
4. Use proper ARIA attributes:
|
|
```typescript
|
|
<div role="dialog" aria-modal="true" aria-label="My Modal">
|
|
```
|
|
|
|
### Adding Keyboard Shortcuts
|
|
|
|
1. Add definition in `src/renderer/constants/shortcuts.ts`:
|
|
```typescript
|
|
myShortcut: { id: 'myShortcut', label: 'My Action', keys: ['Meta', 'k'] },
|
|
```
|
|
|
|
2. Add handler in `App.tsx` keyboard event listener:
|
|
```typescript
|
|
else if (isShortcut(e, 'myShortcut')) {
|
|
e.preventDefault();
|
|
// Handler code
|
|
}
|
|
```
|
|
|
|
**Supported modifiers:** `Meta` (Cmd/Win), `Ctrl`, `Alt`, `Shift`
|
|
**Arrow keys:** `ArrowLeft`, `ArrowRight`, `ArrowUp`, `ArrowDown`
|
|
|
|
### Adding a New Setting
|
|
|
|
1. Add state in `useSettings.ts`:
|
|
```typescript
|
|
const [mySetting, setMySettingState] = useState(defaultValue);
|
|
```
|
|
|
|
2. Create wrapper function:
|
|
```typescript
|
|
const setMySetting = (value) => {
|
|
setMySettingState(value);
|
|
window.maestro.settings.set('mySetting', value);
|
|
};
|
|
```
|
|
|
|
3. Load in useEffect:
|
|
```typescript
|
|
const saved = await window.maestro.settings.get('mySetting');
|
|
if (saved !== undefined) setMySettingState(saved);
|
|
```
|
|
|
|
4. Add to return object and export.
|
|
|
|
### Adding a Slash Command
|
|
|
|
Add to `src/renderer/slashCommands.ts`:
|
|
|
|
```typescript
|
|
{
|
|
command: '/mycommand',
|
|
description: 'Does something useful',
|
|
terminalOnly: false, // Optional: restrict to terminal mode
|
|
execute: (context) => {
|
|
const { activeSessionId, setSessions } = context;
|
|
// Your logic
|
|
}
|
|
}
|
|
```
|
|
|
|
### Adding a New Theme
|
|
|
|
Add to `src/renderer/constants/themes.ts`:
|
|
|
|
```typescript
|
|
'my-theme': {
|
|
id: 'my-theme',
|
|
name: 'My Theme',
|
|
mode: 'dark', // or 'light'
|
|
colors: {
|
|
bgMain: '#...',
|
|
bgSidebar: '#...',
|
|
bgActivity: '#...',
|
|
border: '#...',
|
|
textMain: '#...',
|
|
textDim: '#...',
|
|
accent: '#...',
|
|
accentDim: 'rgba(...)',
|
|
accentText: '#...',
|
|
success: '#...',
|
|
warning: '#...',
|
|
error: '#...',
|
|
}
|
|
}
|
|
```
|
|
|
|
Then add the ID to `ThemeId` type in `src/renderer/types/index.ts`.
|
|
|
|
### Adding an IPC Handler
|
|
|
|
1. Add handler in `src/main/index.ts`:
|
|
```typescript
|
|
ipcMain.handle('myNamespace:myAction', async (_, arg1, arg2) => {
|
|
// Implementation
|
|
return result;
|
|
});
|
|
```
|
|
|
|
2. Expose in `src/main/preload.ts`:
|
|
```typescript
|
|
myNamespace: {
|
|
myAction: (arg1, arg2) => ipcRenderer.invoke('myNamespace:myAction', arg1, arg2),
|
|
},
|
|
```
|
|
|
|
3. Add types to `MaestroAPI` interface in preload.ts.
|
|
|
|
## Code Style
|
|
|
|
### TypeScript
|
|
|
|
- Strict mode enabled
|
|
- Interface definitions for all data structures
|
|
- Export types via `preload.ts` for renderer
|
|
|
|
### React Components
|
|
|
|
- Functional components with hooks
|
|
- Keep components focused and small
|
|
- Use Tailwind for layout, inline styles for theme colors
|
|
- Maintain keyboard accessibility
|
|
- Use `tabIndex={-1}` + `outline-none` for programmatic focus
|
|
|
|
### Security
|
|
|
|
- **Always use `execFileNoThrow`** for external commands (never shell-based execution)
|
|
- Keep context isolation enabled
|
|
- Use preload script for all IPC
|
|
- Sanitize all user inputs
|
|
- Use `spawn()` with `shell: false`
|
|
|
|
## Debugging Guide
|
|
|
|
### Focus Not Working
|
|
|
|
1. Add `tabIndex={0}` or `tabIndex={-1}` to element
|
|
2. Add `outline-none` class to hide focus ring
|
|
3. Use `ref={(el) => el?.focus()}` for auto-focus
|
|
4. Check for `e.stopPropagation()` blocking events
|
|
|
|
### Settings Not Persisting
|
|
|
|
1. Ensure wrapper function calls `window.maestro.settings.set()`
|
|
2. Check loading code in `useSettings.ts` useEffect
|
|
3. Verify the key name matches in both save and load
|
|
|
|
### Modal Escape Not Working
|
|
|
|
1. Register modal with layer stack (don't handle Escape locally)
|
|
2. Check priority in `modalPriorities.ts`
|
|
3. Use ref pattern to avoid re-registration:
|
|
```typescript
|
|
const onCloseRef = useRef(onClose);
|
|
onCloseRef.current = onClose;
|
|
```
|
|
|
|
### Theme Colors Not Applying
|
|
|
|
1. Use `style={{ color: theme.colors.textMain }}` instead of Tailwind color classes
|
|
2. Check theme prop is passed to component
|
|
3. Never use hardcoded hex colors for themed elements
|
|
|
|
### Process Output Not Showing
|
|
|
|
1. Check session ID matches (with `-ai` or `-terminal` suffix)
|
|
2. Verify `onData` listener is registered
|
|
3. Check process spawned successfully (check pid > 0)
|
|
4. Look for errors in DevTools console
|
|
|
|
### DevTools
|
|
|
|
Open via Quick Actions (`Cmd+K` → "Toggle DevTools") or set `DEBUG=true` env var.
|
|
|
|
## Commit Messages
|
|
|
|
Use conventional commits:
|
|
|
|
```
|
|
feat: new feature
|
|
fix: bug fix
|
|
docs: documentation changes
|
|
refactor: code refactoring
|
|
test: test additions/changes
|
|
chore: build process or tooling changes
|
|
```
|
|
|
|
Example: `feat: add context usage visualization`
|
|
|
|
## Pull Request Process
|
|
|
|
1. Create a feature branch from `main`
|
|
2. Make your changes following the code style
|
|
3. Test thoroughly (keyboard navigation, themes, focus)
|
|
4. Update documentation if needed
|
|
5. Submit PR with clear description
|
|
6. Wait for review
|
|
|
|
## Building for Release
|
|
|
|
### 1. Prepare Icons
|
|
|
|
Place icons in `build/` directory:
|
|
- `icon.icns` - macOS (512x512 or 1024x1024)
|
|
- `icon.ico` - Windows (256x256)
|
|
- `icon.png` - Linux (512x512)
|
|
|
|
### 2. Update Version
|
|
|
|
Update in `package.json`:
|
|
```json
|
|
{
|
|
"version": "0.1.0"
|
|
}
|
|
```
|
|
|
|
### 3. Build Distributables
|
|
|
|
```bash
|
|
npm run package # All platforms
|
|
npm run package:mac # macOS (.dmg, .zip)
|
|
npm run package:win # Windows (.exe)
|
|
npm run package:linux # Linux (.AppImage, .deb, .rpm)
|
|
```
|
|
|
|
Output in `release/` directory.
|
|
|
|
### GitHub Actions
|
|
|
|
Create a release tag to trigger automated builds:
|
|
|
|
```bash
|
|
git tag v0.1.0
|
|
git push origin v0.1.0
|
|
```
|
|
|
|
GitHub Actions will build for all platforms and create a release.
|
|
|
|
## Questions?
|
|
|
|
Open a GitHub Discussion or create an Issue.
|