feat(studio): parse AI thinking and tool launch messages in terminal panel
All checks were successful
Beta Release / beta (push) Successful in 40s
All checks were successful
Beta Release / beta (push) Successful in 40s
- Add message type detection: thinking (Reflexion/Thought/>), tool (TOOL_CALL), and normal AI responses - Style thinking messages with italic blue, tool messages with yellow border - Add toolLaunched i18n key for both fr and en locales 💘 Generated with Crush Assisted-by: MiniMax-M2.7 via Crush <crush@charm.land>
This commit is contained in:
@@ -380,13 +380,61 @@ export default function Shell({ api }) {
|
||||
setAiLoading(true)
|
||||
try {
|
||||
const res = await api.runCommand(`echo "AI: ${text}"`, '')
|
||||
setAiMessages(prev => [...prev, { role: 'ai', content: res.output || t('shell.noResponse') }])
|
||||
const output = res.output || t('shell.noResponse')
|
||||
parseAndAddAiMessages(output)
|
||||
} catch (err) {
|
||||
setAiMessages(prev => [...prev, { role: 'ai', content: `${t('shell.error')}: ${err.message}` }])
|
||||
}
|
||||
setAiLoading(false)
|
||||
}
|
||||
|
||||
const parseAndAddAiMessages = (text) => {
|
||||
const lines = text.split('\n')
|
||||
let buffer = ''
|
||||
let inBlock = false
|
||||
|
||||
const flushBuffer = () => {
|
||||
if (buffer.trim()) {
|
||||
setAiMessages(prev => [...prev, { role: 'ai', content: buffer.trim() }])
|
||||
}
|
||||
buffer = ''
|
||||
}
|
||||
|
||||
for (const line of lines) {
|
||||
const toolMatch = line.match(/^\[TOOL_CALL:\{.*\}\]$/)
|
||||
if (toolMatch) {
|
||||
flushBuffer()
|
||||
try {
|
||||
const toolData = JSON.parse(toolMatch[0].slice(10, -1))
|
||||
setAiMessages(prev => [...prev, {
|
||||
role: 'tool',
|
||||
content: `${t('shell.toolLaunched')}: ${toolData.tool || 'tool'}`,
|
||||
args: toolData.task || toolData.args || '',
|
||||
}])
|
||||
} catch {
|
||||
setAiMessages(prev => [...prev, { role: 'tool', content: line, args: '' }])
|
||||
}
|
||||
} else if (line.match(/^(Reflexion|Thought|thinking):/i) || line.startsWith('>')) {
|
||||
if (buffer.trim() && !inBlock) {
|
||||
flushBuffer()
|
||||
}
|
||||
inBlock = true
|
||||
const cleaned = line.replace(/^(Reflexion|Thought|thinking):\s*/i, '').replace(/^>\s*/, '')
|
||||
if (buffer) buffer += ' '
|
||||
buffer += cleaned
|
||||
} else {
|
||||
if (inBlock && buffer.trim()) {
|
||||
setAiMessages(prev => [...prev, { role: 'thinking', content: buffer.trim() }])
|
||||
buffer = ''
|
||||
}
|
||||
inBlock = false
|
||||
if (buffer) buffer += '\n'
|
||||
buffer += line
|
||||
}
|
||||
}
|
||||
flushBuffer()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="shell-layout">
|
||||
<div className="shell-terminal-col">
|
||||
@@ -507,6 +555,7 @@ export default function Shell({ api }) {
|
||||
{aiMessages.map((msg, i) => (
|
||||
<div key={i} className={`ai-message ${msg.role}`}>
|
||||
{msg.content}
|
||||
{msg.args && <div className="tool-args">{msg.args}</div>}
|
||||
</div>
|
||||
))}
|
||||
{aiLoading && <div style={{ textAlign: 'center', padding: 8 }}><span className="spinner" /></div>}
|
||||
|
||||
Reference in New Issue
Block a user