feat(webui): add configuration presets for Claude CLI

- Add backend storage logic in `src/utils/claude-config.js` to save/load/delete presets
- Add API endpoints (`GET`, `POST`, `DELETE`) for presets in `src/webui/index.js`
- Update `public/views/settings.html` with new Presets UI card and modals
- Update `public/js/components/claude-config.js` with auto-load logic and unsaved changes protection
- Add translations (EN/ZH) for new UI elements in `public/js/store.js`
- Add integration tests in `tests/frontend/test-frontend-settings.cjs`
- Update compiled CSS

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
simon-ami
2026-01-12 11:59:32 +01:00
parent 08b332b694
commit e24dff279c
7 changed files with 677 additions and 2 deletions

View File

@@ -18,7 +18,7 @@ import { fileURLToPath } from 'url';
import express from 'express';
import { getPublicConfig, saveConfig, config } from '../config.js';
import { DEFAULT_PORT, ACCOUNT_CONFIG_PATH } from '../constants.js';
import { readClaudeConfig, updateClaudeConfig, replaceClaudeConfig, getClaudeConfigPath } from '../utils/claude-config.js';
import { readClaudeConfig, updateClaudeConfig, replaceClaudeConfig, getClaudeConfigPath, readPresets, savePreset, deletePreset } from '../utils/claude-config.js';
import { logger } from '../utils/logger.js';
import { getAuthorizationUrl, completeOAuthFlow, startCallbackServer } from '../auth/oauth.js';
import { loadAccounts, saveAccounts } from '../account-manager/storage.js';
@@ -484,6 +484,59 @@ export function mountWebUI(app, dirname, accountManager) {
}
});
// ==========================================
// Claude CLI Presets API
// ==========================================
/**
* GET /api/claude/presets - Get all saved presets
*/
app.get('/api/claude/presets', async (req, res) => {
try {
const presets = await readPresets();
res.json({ status: 'ok', presets });
} catch (error) {
res.status(500).json({ status: 'error', error: error.message });
}
});
/**
* POST /api/claude/presets - Save a new preset
*/
app.post('/api/claude/presets', async (req, res) => {
try {
const { name, config: presetConfig } = req.body;
if (!name || typeof name !== 'string' || !name.trim()) {
return res.status(400).json({ status: 'error', error: 'Preset name is required' });
}
if (!presetConfig || typeof presetConfig !== 'object') {
return res.status(400).json({ status: 'error', error: 'Config object is required' });
}
const presets = await savePreset(name.trim(), presetConfig);
res.json({ status: 'ok', presets, message: `Preset "${name}" saved` });
} catch (error) {
res.status(500).json({ status: 'error', error: error.message });
}
});
/**
* DELETE /api/claude/presets/:name - Delete a preset
*/
app.delete('/api/claude/presets/:name', async (req, res) => {
try {
const { name } = req.params;
if (!name) {
return res.status(400).json({ status: 'error', error: 'Preset name is required' });
}
const presets = await deletePreset(name);
res.json({ status: 'ok', presets, message: `Preset "${name}" deleted` });
} catch (error) {
res.status(500).json({ status: 'error', error: error.message });
}
});
/**
* POST /api/models/config - Update model configuration (hidden/pinned/alias)
*/