refactor(api): split monolithic handlers.go into focused modules
All checks were successful
Beta Release / beta (push) Successful in 44s
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:
@@ -8,37 +8,74 @@ import { useI18n } from '../i18n'
|
||||
|
||||
const MAX_TABS = 7
|
||||
|
||||
const XTERM_THEME = {
|
||||
background: '#0A0A0C',
|
||||
foreground: '#EAE0E2',
|
||||
cursor: '#FF0033',
|
||||
cursorAccent: '#0A0A0C',
|
||||
selectionBackground: '#FF003344',
|
||||
selectionForeground: '#ffffff',
|
||||
black: '#0A0A0C',
|
||||
red: '#FF0033',
|
||||
green: '#00E676',
|
||||
yellow: '#FFD740',
|
||||
blue: '#448AFF',
|
||||
magenta: '#FF1A5E',
|
||||
cyan: '#00BCD4',
|
||||
white: '#EAE0E2',
|
||||
brightBlack: '#5A4F52',
|
||||
brightRed: '#FF5252',
|
||||
brightGreen: '#69F0AE',
|
||||
brightYellow: '#FFFF00',
|
||||
brightBlue: '#82B1FF',
|
||||
brightMagenta: '#FF80AB',
|
||||
brightCyan: '#84FFFF',
|
||||
brightWhite: '#FFFFFF',
|
||||
const THEMES = {
|
||||
default: {
|
||||
background: '#0A0A0C', foreground: '#EAE0E2', cursor: '#FF0033',
|
||||
cursorAccent: '#0A0A0C', selectionBackground: '#FF003344', selectionForeground: '#ffffff',
|
||||
black: '#0A0A0C', red: '#FF0033', green: '#00E676', yellow: '#FFD740',
|
||||
blue: '#448AFF', magenta: '#FF1A5E', cyan: '#00BCD4', white: '#EAE0E2',
|
||||
brightBlack: '#5A4F52', brightRed: '#FF5252', brightGreen: '#69F0AE',
|
||||
brightYellow: '#FFFF00', brightBlue: '#82B1FF', brightMagenta: '#FF80AB',
|
||||
brightCyan: '#84FFFF', brightWhite: '#FFFFFF',
|
||||
},
|
||||
monokai: {
|
||||
background: '#272822', foreground: '#F8F8F2', cursor: '#F8F8F0',
|
||||
cursorAccent: '#272822', selectionBackground: '#F8F8F244', selectionForeground: '#ffffff',
|
||||
black: '#272822', red: '#F92672', green: '#A6E22E', yellow: '#E6DB74',
|
||||
blue: '#66D9EF', magenta: '#AE81FF', cyan: '#A1EFE4', white: '#F8F8F2',
|
||||
brightBlack: '#75715E', brightRed: '#F92672', brightGreen: '#A6E22E',
|
||||
brightYellow: '#E6DB74', brightBlue: '#66D9EF', brightMagenta: '#AE81FF',
|
||||
brightCyan: '#A1EFE4', brightWhite: '#F8F8F2',
|
||||
},
|
||||
gruvbox: {
|
||||
background: '#282828', foreground: '#EBDBB2', cursor: '#FB4934',
|
||||
cursorAccent: '#282828', selectionBackground: '#EBDBB244', selectionForeground: '#ffffff',
|
||||
black: '#282828', red: '#CC241D', green: '#98971A', yellow: '#D79921',
|
||||
blue: '#458588', magenta: '#B16286', cyan: '#689D6A', white: '#EBDBB2',
|
||||
brightBlack: '#928374', brightRed: '#FB4934', brightGreen: '#B8BB26',
|
||||
brightYellow: '#FABC2A', brightBlue: '#83A598', brightMagenta: '#D3869B',
|
||||
brightCyan: '#8EC07C', brightWhite: '#EBDBB2',
|
||||
},
|
||||
nord: {
|
||||
background: '#2E3440', foreground: '#D8DEE9', cursor: '#D8DEE9',
|
||||
cursorAccent: '#2E3440', selectionBackground: '#D8DEE944', selectionForeground: '#ffffff',
|
||||
black: '#2E3440', red: '#BF616A', green: '#A3BE8C', yellow: '#EBCB8B',
|
||||
blue: '#81A1C1', magenta: '#B48EAD', cyan: '#88C0D0', white: '#D8DEE9',
|
||||
brightBlack: '#4C566A', brightRed: '#BF616A', brightGreen: '#A3BE8C',
|
||||
brightYellow: '#EBCB8B', brightBlue: '#81A1C1', brightMagenta: '#B48EAD',
|
||||
brightCyan: '#8FBCBB', brightWhite: '#ECEFF4',
|
||||
},
|
||||
'solarized-dark': {
|
||||
background: '#002B36', foreground: '#839496', cursor: '#D33682',
|
||||
cursorAccent: '#002B36', selectionBackground: '#83949644', selectionForeground: '#ffffff',
|
||||
black: '#002B36', red: '#DC322F', green: '#859900', yellow: '#B58900',
|
||||
blue: '#268BD2', magenta: '#D33682', cyan: '#2AA198', white: '#FDF6E3',
|
||||
brightBlack: '#073642', brightRed: '#CB4B16', brightGreen: '#586E75',
|
||||
brightYellow: '#657B83', brightBlue: '#6C71C4', brightMagenta: '#6C71C4',
|
||||
brightCyan: '#93A1A1', brightWhite: '#FDF6E3',
|
||||
},
|
||||
dracula: {
|
||||
background: '#282A36', foreground: '#F8F8F2', cursor: '#F8F8F2',
|
||||
cursorAccent: '#282A36', selectionBackground: '#F8F8F244', selectionForeground: '#ffffff',
|
||||
black: '#282A36', red: '#FF5555', green: '#50FA7B', yellow: '#F1FA8C',
|
||||
blue: '#BD93F9', magenta: '#FF79C6', cyan: '#8BE9FD', white: '#F8F8F2',
|
||||
brightBlack: '#6272A4', brightRed: '#FF6E6E', brightGreen: '#69FF94',
|
||||
brightYellow: '#FFFFA5', brightBlue: '#D6ACFF', brightMagenta: '#FF92DF',
|
||||
brightCyan: '#A4FFFF', brightWhite: '#FFFFFF',
|
||||
},
|
||||
}
|
||||
|
||||
function createTerminal(container) {
|
||||
function getTheme(themeName) {
|
||||
return THEMES[themeName] || THEMES.default
|
||||
}
|
||||
|
||||
function createTerminal(container, settings = {}) {
|
||||
const theme = getTheme(settings.theme || 'default')
|
||||
const term = new XTerm({
|
||||
cursorBlink: true,
|
||||
fontSize: 14,
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
|
||||
theme: XTERM_THEME,
|
||||
fontSize: settings.fontSize || 14,
|
||||
fontFamily: settings.fontFamily || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
|
||||
theme,
|
||||
allowTransparency: false,
|
||||
scrollback: 5000,
|
||||
})
|
||||
@@ -116,27 +153,30 @@ export default function Shell({ api }) {
|
||||
const [showSshModal, setShowSshModal] = useState(false)
|
||||
const [editingTab, setEditingTab] = useState(null)
|
||||
const [editName, setEditName] = useState('')
|
||||
const [terminalSettings, setTerminalSettings] = useState({
|
||||
fontSize: 14,
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
|
||||
theme: 'default',
|
||||
})
|
||||
|
||||
const [sshForm, setSshForm] = useState({
|
||||
name: '', host: '', port: 22, user: '', key_path: '',
|
||||
})
|
||||
|
||||
const [aiMessages, setAiMessages] = useState([
|
||||
{ role: 'ai', content: t('shell.aiWelcome') }
|
||||
])
|
||||
const [aiInput, setAiInput] = useState('')
|
||||
const [aiLoading, setAiLoading] = useState(false)
|
||||
const aiMessagesRef = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
aiMessagesRef.current?.scrollTo(0, aiMessagesRef.current.scrollHeight)
|
||||
}, [aiMessages])
|
||||
|
||||
useEffect(() => {
|
||||
api.getTerminalSessions().then(d => {
|
||||
setSshConnections(d.ssh || [])
|
||||
setSystemTerminals(d.system || [])
|
||||
}).catch(() => {})
|
||||
api.getConfig().then(d => {
|
||||
if (d.terminal) {
|
||||
setTerminalSettings({
|
||||
fontSize: d.terminal.font_size || 14,
|
||||
fontFamily: d.terminal.font_family || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace",
|
||||
theme: d.terminal.theme || 'default',
|
||||
})
|
||||
}
|
||||
}).catch(() => {})
|
||||
}, [])
|
||||
|
||||
const initTerminal = useCallback((tabId, tab) => {
|
||||
@@ -145,7 +185,11 @@ export default function Shell({ api }) {
|
||||
const container = document.getElementById(`terminal-${tabId}`)
|
||||
if (!container) return
|
||||
|
||||
const { term, fitAddon } = createTerminal(container)
|
||||
const { term, fitAddon } = createTerminal(container, {
|
||||
fontSize: terminalSettings.fontSize,
|
||||
fontFamily: terminalSettings.fontFamily,
|
||||
theme: terminalSettings.theme,
|
||||
})
|
||||
|
||||
let initPayload
|
||||
if (tab.type === 'ssh') {
|
||||
@@ -307,21 +351,6 @@ export default function Shell({ api }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleAiSend = async () => {
|
||||
if (!aiInput.trim() || aiLoading) return
|
||||
const text = aiInput.trim()
|
||||
setAiMessages(prev => [...prev, { role: 'user', content: text }])
|
||||
setAiInput('')
|
||||
setAiLoading(true)
|
||||
try {
|
||||
const res = await api.runCommand(`echo "AI: ${text}"`, '')
|
||||
setAiMessages(prev => [...prev, { role: 'ai', content: res.output || t('shell.noResponse') }])
|
||||
} catch (err) {
|
||||
setAiMessages(prev => [...prev, { role: 'ai', content: `${t('shell.error')}: ${err.message}` }])
|
||||
}
|
||||
setAiLoading(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shell-layout">
|
||||
<div className="shell-terminal-col">
|
||||
@@ -436,27 +465,6 @@ export default function Shell({ api }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="shell-ai-col">
|
||||
<div className="ai-panel-header">{t('shell.aiAssistant')}</div>
|
||||
<div className="ai-panel-messages" ref={aiMessagesRef}>
|
||||
{aiMessages.map((msg, i) => (
|
||||
<div key={i} className={`ai-message ${msg.role}`}>
|
||||
{msg.content}
|
||||
</div>
|
||||
))}
|
||||
{aiLoading && <div style={{ textAlign: 'center', padding: 8 }}><span className="spinner" /></div>}
|
||||
</div>
|
||||
<div className="ai-panel-input">
|
||||
<input
|
||||
value={aiInput}
|
||||
onChange={e => setAiInput(e.target.value)}
|
||||
onKeyDown={e => e.key === 'Enter' && handleAiSend()}
|
||||
placeholder={t('shell.askAi')}
|
||||
/>
|
||||
<button className="sm" onClick={handleAiSend} disabled={!aiInput.trim()}>{t('shell.send')}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showSshModal && (
|
||||
<div className="shell-modal-overlay" onClick={() => setShowSshModal(false)}>
|
||||
<div className="shell-modal" onClick={e => e.stopPropagation()}>
|
||||
|
||||
Reference in New Issue
Block a user