feat: add settings gear icon with app settings modal

- Add gear icon button in header-right for app settings
- Create App Settings modal with fields for:
  - Default CLAUDE.md template path
  - Default working directory
- Settings persist to ~/.claudeman/settings.json
- Add GET/PUT /api/settings endpoints
- Keep "+" tab for creating new sessions (useful UX)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-19 02:08:58 +01:00
co-authored by Claude Opus 4.5
parent 0876c2d4e9
commit 5f29455bbb
4 changed files with 150 additions and 2 deletions
+31 -1
View File
@@ -2,7 +2,7 @@ import Fastify, { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
import fastifyStatic from '@fastify/static';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { existsSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
import { existsSync, mkdirSync, writeFileSync, readdirSync, readFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { EventEmitter } from 'node:events';
import { Session, ClaudeMessage, type BackgroundTask } from '../session.js';
@@ -650,6 +650,36 @@ export class WebServer extends EventEmitter {
return { success: false, error: (err as Error).message };
}
});
// ============ App Settings Endpoints ============
const settingsPath = join(homedir(), '.claudeman', 'settings.json');
this.app.get('/api/settings', async () => {
try {
if (existsSync(settingsPath)) {
const content = readFileSync(settingsPath, 'utf-8');
return JSON.parse(content);
}
} catch (err) {
console.error('Failed to read settings:', err);
}
return {};
});
this.app.put('/api/settings', async (req) => {
const settings = req.body as { defaultClaudeMdPath?: string; defaultWorkingDir?: string };
try {
const dir = dirname(settingsPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
return { success: true };
} catch (err) {
return { error: (err as Error).message };
}
});
}
private setupSessionListeners(session: Session): void {