refactor(api): split monolithic handlers.go into focused modules
All checks were successful
Beta Release / beta (push) Successful in 44s

Break down the 627-line handlers.go into specialized modules:
- handlers_chat.go: chat and streaming endpoints
- handlers_config.go: configuration endpoints
- handlers_common.go: shared utilities
- handlers_info.go: info and status endpoints
- handlers_terminal.go: terminal/shell endpoints
- handlers_tools.go: tool-related endpoints

Also includes config improvements, orchestrator enhancements, and
web component updates.

💘 Generated with Crush

Assisted-by: MiniMax-M2.7 via Crush <crush@charm.land>
This commit is contained in:
Augustin
2026-04-22 18:34:14 +02:00
parent 0b221094f2
commit 04b0fff791
35 changed files with 1338 additions and 779 deletions

View File

@@ -1,11 +1,12 @@
import { useState, useEffect, useCallback } from 'react'
import { User, Brain, RefreshCw, Globe, Wrench } from 'lucide-react'
import { User, Brain, RefreshCw, Globe, Wrench, Monitor } from 'lucide-react'
import { useI18n, LANGUAGES } from '../i18n'
import { getLayoutList } from '../i18n/keyboards'
const PANELS = [
{ id: 'profile', icon: User },
{ id: 'providers', icon: Brain },
{ id: 'terminal', icon: Monitor },
{ id: 'updates', icon: RefreshCw },
{ id: 'locale', icon: Globe },
{ id: 'skills', icon: Wrench },
@@ -26,6 +27,9 @@ export default function Config({ api }) {
const [profileForm, setProfileForm] = useState({})
const [providerForm, setProviderForm] = useState({})
const [toast, setToast] = useState(null)
const [terminalThemes, setTerminalThemes] = useState([])
const [terminalSettings, setTerminalSettings] = useState({ font_size: 14, font_family: '', theme: 'default' })
const [savingTerminal, setSavingTerminal] = useState(false)
const layouts = getLayoutList()
@@ -39,11 +43,19 @@ export default function Config({ api }) {
editor: d.profile?.preferences?.editor || '',
shell: d.profile?.preferences?.shell || '',
})
if (d.terminal) {
setTerminalSettings({
font_size: d.terminal.font_size || 14,
font_family: d.terminal.font_family || '',
theme: d.terminal.theme || 'default',
})
}
}).catch(() => {})
api.getProviders().then(d => setProviders(d.providers || [])).catch(() => {})
api.getSkills().then(d => setSkillList(d.skills || [])).catch(() => {})
api.getUpdates().then(d => setUpdates(d.updates || [])).catch(() => {})
api.getTools().then(d => setTools(d.tools || [])).catch(() => {})
api.getTerminalThemes().then(d => setTerminalThemes(d.themes || [])).catch(() => {})
}, [api])
useEffect(() => { loadData() }, [loadData])
@@ -114,6 +126,18 @@ export default function Config({ api }) {
}
}
const handleSaveTerminalSettings = async () => {
setSavingTerminal(true)
try {
await api.saveTerminalSettings(terminalSettings)
showToast(t('config.saved'))
window.location.reload()
} catch (err) {
showToast(`${t('config.error')}: ${err.message}`)
}
setSavingTerminal(false)
}
const openProviderEdit = (p) => {
setProviderForm({
name: p.name,
@@ -125,8 +149,8 @@ export default function Config({ api }) {
}
const needsUpdateCount = updates.filter(u => u.needsUpdate).length
const installedCount = tools.filter(t => t.installed).length
const missingCount = tools.filter(t => !t.installed).length
const installedCount = tools.filter(tool => tool.installed).length
const missingCount = tools.filter(tool => !tool.installed).length
return (
<div className="config-window">
@@ -189,6 +213,13 @@ export default function Config({ api }) {
{activePanel === 'skills' && (
<PanelSkills skillList={skillList} t={t} />
)}
{activePanel === 'terminal' && (
<PanelTerminal
settings={terminalSettings} setSettings={setTerminalSettings}
themes={terminalThemes} saving={savingTerminal}
onSave={handleSaveTerminalSettings} t={t}
/>
)}
</div>
</div>
</div>
@@ -281,8 +312,8 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
<span className="provider-card-name">{p.name}</span>
{p.apiKey && <span className="badge ok">{t('config.keyConfigured')}</span>}
{!p.apiKey && <span className="badge error">{t('config.noKey')}</span>}
{isValidationTarget && validationStatus.valid && <span className="badge ok">{t('config.keyValid')}</span>}
{isValidationTarget && !validationStatus.valid && <span className="badge error">{validationStatus.error}</span>}
{isValidationTarget && validationStatus?.valid && <span className="badge ok">{t('config.keyValid')}</span>}
{isValidationTarget && !validationStatus?.valid && <span className="badge error">{validationStatus?.error}</span>}
</div>
</div>
@@ -309,7 +340,7 @@ function PanelProviders({ providers, editProvider, providerForm, setProviderForm
>
{validating === p.name ? t('config.validating') : t('config.validateKey')}
</button>
{isValidationTarget && validationStatus.valid && (
{isValidationTarget && validationStatus?.valid && (
<button className="sm" onClick={handleSaveProvider}>{t('config.save')}</button>
)}
</div>
@@ -439,6 +470,102 @@ function PanelSkills({ skillList, t }) {
)
}
function PanelTerminal({ settings, setSettings, themes, saving, onSave, t }) {
const previewTheme = {
background: settings.theme === 'default' ? '#0A0A0C' :
settings.theme === 'monokai' ? '#272822' :
settings.theme === 'gruvbox' ? '#282828' :
settings.theme === 'nord' ? '#2E3440' :
settings.theme === 'solarized-dark' ? '#002B36' :
settings.theme === 'dracula' ? '#282A36' : '#0A0A0C',
foreground: settings.theme === 'default' ? '#EAE0E2' :
settings.theme === 'monokai' ? '#F8F8F2' :
settings.theme === 'gruvbox' ? '#EBDBB2' :
settings.theme === 'nord' ? '#D8DEE9' :
settings.theme === 'solarized-dark' ? '#839496' :
settings.theme === 'dracula' ? '#F8F8F2' : '#EAE0E2',
}
return (
<div className="config-card">
<div className="config-card-group">
<span className="config-card-group-label">{t('config.terminalTheme')}</span>
<div className="chip-row">
{themes.map(th => (
<div
key={th.id}
className={`chip ${settings.theme === th.id ? 'active' : ''}`}
onClick={() => setSettings(s => ({ ...s, theme: th.id }))}
>
{th.name}
</div>
))}
</div>
</div>
<div className="config-card-group">
<span className="config-card-group-label">{t('config.fontSize')}</span>
<div className="chip-row">
{[12, 14, 16, 18, 20, 24].map(size => (
<div
key={size}
className={`chip ${settings.font_size === size ? 'active' : ''}`}
onClick={() => setSettings(s => ({ ...s, font_size: size }))}
>
{size}px
</div>
))}
</div>
</div>
<div className="config-card-group">
<span className="config-card-group-label">{t('config.fontFamily')}</span>
<select
className="config-form-input"
value={settings.font_family}
onChange={e => setSettings(s => ({ ...s, font_family: e.target.value }))}
style={{ maxWidth: 300 }}
>
<option value="">Default (JetBrains Mono)</option>
<option value="'Fira Code', monospace">Fira Code</option>
<option value="'Cascadia Code', 'SF Mono', monospace">Cascadia Code</option>
<option value="'SF Mono', 'Menlo', monospace">SF Mono</option>
<option value="'Source Code Pro', monospace">Source Code Pro</option>
<option value="monospace">System Monospace</option>
</select>
</div>
<div className="config-card-group">
<span className="config-card-group-label">{t('config.preview')}</span>
<div style={{
background: previewTheme.background,
color: previewTheme.foreground,
padding: '16px 20px',
borderRadius: 'var(--radius)',
fontFamily: settings.font_family || "'JetBrains Mono', monospace",
fontSize: settings.font_size || 14,
border: '1px solid var(--border)',
}}>
<span style={{ color: '#00E676' }}></span> <span>~/projects</span>
<span style={{ color: '#448AFF' }}> git status</span>
<br />
<span>On branch </span>
<span style={{ color: '#FFD740' }}>main</span>
<br />
<span style={{ opacity: 0.6 }}>Type a command...</span>
<span style={{ animation: 'blink 1s step-end infinite' }}> </span>
</div>
</div>
<div className="config-card-actions" style={{ marginTop: 16 }}>
<button className="primary sm" onClick={onSave} disabled={saving}>
{saving ? t('config.saving') : t('config.save')}
</button>
</div>
</div>
)
}
function FormInput({ label, value, onChange, type = 'text' }) {
return (
<div className="config-form-field">