💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "muyue"
|
Name = "muyue"
|
||||||
Version = "0.4.1"
|
Version = "0.5.0"
|
||||||
Author = "La Légion de Muyue"
|
Author = "La Légion de Muyue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ function getTheme(themeName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createTerminal(container, settings = {}) {
|
function createTerminal(container, settings = {}) {
|
||||||
console.log('[Shell] createTerminal called with settings:', JSON.stringify({ fontSize: settings.fontSize, fontFamily: settings.fontFamily?.slice(0, 30), theme: settings.theme }))
|
|
||||||
const theme = getTheme(settings.theme || 'system')
|
const theme = getTheme(settings.theme || 'system')
|
||||||
const actualFontSize = settings.fontSize || 14
|
const actualFontSize = settings.fontSize || 14
|
||||||
const term = new XTerm({
|
const term = new XTerm({
|
||||||
@@ -528,20 +527,15 @@ export default function Shell({ api, isSudo }) {
|
|||||||
setSystemTerminals(d.system || [])
|
setSystemTerminals(d.system || [])
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
api.getConfig().then(d => {
|
api.getConfig().then(d => {
|
||||||
console.log('[Shell] config response terminal:', JSON.stringify(d?.terminal))
|
|
||||||
if (d.terminal) {
|
if (d.terminal) {
|
||||||
const fontSize = d.terminal.font_size || 14
|
const fontSize = d.terminal.font_size || 14
|
||||||
const fontFamily = d.terminal.font_family || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace"
|
const fontFamily = d.terminal.font_family || "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', 'Menlo', monospace"
|
||||||
const theme = d.terminal.theme || 'system'
|
const theme = d.terminal.theme || 'system'
|
||||||
console.log('[Shell] setting fontSize to:', fontSize, 'from config')
|
|
||||||
setTerminalSettings({ fontSize, fontFamily, theme })
|
setTerminalSettings({ fontSize, fontFamily, theme })
|
||||||
settingsRef.current = { fontSize, fontFamily, theme }
|
settingsRef.current = { fontSize, fontFamily, theme }
|
||||||
baseFontSizeRef.current = fontSize
|
baseFontSizeRef.current = fontSize
|
||||||
} else {
|
|
||||||
console.log('[Shell] no terminal config in response, using defaults')
|
|
||||||
}
|
}
|
||||||
setConfigLoaded(true)
|
setConfigLoaded(true)
|
||||||
console.log('[Shell] configLoaded = true, settingsRef:', JSON.stringify(settingsRef.current))
|
|
||||||
}).catch((err) => { console.warn('[Shell] getConfig failed:', err); setConfigLoaded(true) })
|
}).catch((err) => { console.warn('[Shell] getConfig failed:', err); setConfigLoaded(true) })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -553,7 +547,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
|
|
||||||
const s = settingsRef.current
|
const s = settingsRef.current
|
||||||
const effectiveFontSize = s.fontSize + zoomLevel * 2
|
const effectiveFontSize = s.fontSize + zoomLevel * 2
|
||||||
console.log(`[Shell] initTerminal tab=${tabId}: settingsRef.fontSize=${s.fontSize}, zoomLevel=${zoomLevel}, effectiveFontSize=${effectiveFontSize}`)
|
|
||||||
const { term, fitAddon, searchAddon } = createTerminal(container, {
|
const { term, fitAddon, searchAddon } = createTerminal(container, {
|
||||||
fontSize: effectiveFontSize,
|
fontSize: effectiveFontSize,
|
||||||
fontFamily: s.fontFamily,
|
fontFamily: s.fontFamily,
|
||||||
@@ -646,14 +639,11 @@ export default function Shell({ api, isSudo }) {
|
|||||||
|
|
||||||
const bufferSaveInterval = setInterval(() => { if (!disposed) saveBuffer() }, 5000)
|
const bufferSaveInterval = setInterval(() => { if (!disposed) saveBuffer() }, 5000)
|
||||||
|
|
||||||
console.log(`[Shell] initTerminal tab=${tabId} type=${tab.type} name="${tab.name}" shell="${tab.shell || '(default)'}"`)
|
|
||||||
tabsRef.current[tabId] = { term, fitAddon, searchAddon, ws, resizeObserver, onResize, bufferSaveInterval, saveBuffer, disposed: () => disposed }
|
tabsRef.current[tabId] = { term, fitAddon, searchAddon, ws, resizeObserver, onResize, bufferSaveInterval, saveBuffer, disposed: () => disposed }
|
||||||
tabsRef.current[tabId]._markDisposed = () => { disposed = true }
|
tabsRef.current[tabId]._markDisposed = () => { disposed = true }
|
||||||
console.log(`[Shell] initTerminal tab=${tabId} done, tabsRef keys:`, Object.keys(tabsRef.current))
|
|
||||||
|
|
||||||
const pending = pendingCommandsRef.current[tabId]
|
const pending = pendingCommandsRef.current[tabId]
|
||||||
if (pending && pending.length > 0) {
|
if (pending && pending.length > 0) {
|
||||||
console.log(`[Shell] Flushing ${pending.length} pending commands for tab ${tabId}`)
|
|
||||||
for (const cmd of pending) {
|
for (const cmd of pending) {
|
||||||
if (ws.readyState === WebSocket.OPEN) {
|
if (ws.readyState === WebSocket.OPEN) {
|
||||||
ws.send(JSON.stringify({ type: 'input', data: cmd + '\r' }))
|
ws.send(JSON.stringify({ type: 'input', data: cmd + '\r' }))
|
||||||
@@ -709,7 +699,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
const tryInitTab = (tab, attempt) => {
|
const tryInitTab = (tab, attempt) => {
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
if (attempt > 20) {
|
if (attempt > 20) {
|
||||||
console.warn(`[Shell] max attempts reached for tab ${tab.id}`)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -733,7 +722,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!tabsRef.current[tab.id]) {
|
if (!tabsRef.current[tab.id]) {
|
||||||
console.log(`[Shell] tryInitTab: calling initTerminal for tab ${tab.id}, configLoaded=${configLoaded}`)
|
|
||||||
initTerminal(tab.id, tab)
|
initTerminal(tab.id, tab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +740,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Shell] init effect: tabs=${tabs.length}, configLoaded=${configLoaded}`)
|
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
if (configLoaded && !tabsRef.current[tab.id]) {
|
if (configLoaded && !tabsRef.current[tab.id]) {
|
||||||
tryInitTab(tab, 0)
|
tryInitTab(tab, 0)
|
||||||
@@ -1033,7 +1020,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
pendingCommandsRef.current[targetId].push(code)
|
pendingCommandsRef.current[targetId].push(code)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log(`[Shell] sendToTerminal: tab ${targetId} ← ${code.length} chars`)
|
|
||||||
entry.ws.send(JSON.stringify({ type: 'input', data: code + '\r' }))
|
entry.ws.send(JSON.stringify({ type: 'input', data: code + '\r' }))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -1120,7 +1106,6 @@ export default function Shell({ api, isSudo }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentTab = activeTabRef.current
|
const currentTab = activeTabRef.current
|
||||||
console.log(`[Shell] _sendAiMessage: activeTab=${currentTab}, fromEvent=${fromEvent}, text="${trimmed.slice(0, 50)}"`)
|
|
||||||
setAiMessages(prev => [...prev, { role: 'user', content: trimmed, _tabId: currentTab, _analysis: isAnalysis || undefined }])
|
setAiMessages(prev => [...prev, { role: 'user', content: trimmed, _tabId: currentTab, _analysis: isAnalysis || undefined }])
|
||||||
setAiLoading(true)
|
setAiLoading(true)
|
||||||
|
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ function CodeBlockWithCopy({ part, index, copiedIdx, setCopiedIdx }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function FeedItem({ msg }) {
|
function FeedItem({ msg, activeAgents, onModeChange }) {
|
||||||
const isUser = msg.role === 'user'
|
const isUser = msg.role === 'user'
|
||||||
const isSystem = msg.role === 'system'
|
const isSystem = msg.role === 'system'
|
||||||
const rank = getRank(msg.role)
|
const rank = getRank(msg.role)
|
||||||
@@ -352,7 +352,7 @@ function FeedItem({ msg }) {
|
|||||||
const result = r && (r.content !== undefined || r.is_error !== undefined)
|
const result = r && (r.content !== undefined || r.is_error !== undefined)
|
||||||
? { content: r.content, is_error: r.is_error }
|
? { content: r.content, is_error: r.is_error }
|
||||||
: null
|
: null
|
||||||
return <ToolCallBlock key={`tc${i}`} call={seg.call} result={result} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
return <ToolCallBlock key={`tc${i}`} call={seg.call} result={result} activeAgents={activeAgents} onModeChange={onModeChange} />
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
@@ -365,7 +365,7 @@ function FeedItem({ msg }) {
|
|||||||
const result = resultData
|
const result = resultData
|
||||||
? { content: resultData.result, is_error: resultData.is_error }
|
? { content: resultData.result, is_error: resultData.is_error }
|
||||||
: null
|
: null
|
||||||
return <ToolCallBlock key={tc.tool_call_id || i} call={tc} result={result} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
return <ToolCallBlock key={tc.tool_call_id || i} call={tc} result={result} activeAgents={activeAgents} onModeChange={onModeChange} />
|
||||||
})}
|
})}
|
||||||
{cleanContent && (
|
{cleanContent && (
|
||||||
<div className="feed-content">
|
<div className="feed-content">
|
||||||
@@ -385,7 +385,7 @@ function FeedItem({ msg }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function StreamingItem({ content, thinking, toolCalls, segments }) {
|
function StreamingItem({ content, thinking, toolCalls, segments, activeAgents, onModeChange }) {
|
||||||
const rank = RANKS.general
|
const rank = RANKS.general
|
||||||
const cleanContent = content.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, '')
|
const cleanContent = content.replace(/<think[^>]*>[\s\S]*?<\/think>/gi, '')
|
||||||
const hasToolCalls = toolCalls && toolCalls.length > 0
|
const hasToolCalls = toolCalls && toolCalls.length > 0
|
||||||
@@ -434,14 +434,14 @@ function StreamingItem({ content, thinking, toolCalls, segments }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (seg.type === 'tool') {
|
if (seg.type === 'tool') {
|
||||||
return <ToolCallBlock key={`tc${i}`} call={seg.call} result={seg.result} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
return <ToolCallBlock key={`tc${i}`} call={seg.call} result={seg.result} activeAgents={activeAgents} onModeChange={onModeChange} />
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
})
|
})
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{hasToolCalls && toolCalls.map((tc, i) => (
|
{hasToolCalls && toolCalls.map((tc, i) => (
|
||||||
<ToolCallBlock key={tc.call?.tool_call_id || i} call={tc.call} result={tc.result} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
<ToolCallBlock key={tc.call?.tool_call_id || i} call={tc.call} result={tc.result} activeAgents={activeAgents} onModeChange={onModeChange} />
|
||||||
))}
|
))}
|
||||||
{cleanContent && (
|
{cleanContent && (
|
||||||
<div className="feed-content">
|
<div className="feed-content">
|
||||||
@@ -882,7 +882,7 @@ export default function Studio({ api }) {
|
|||||||
<span className="feed-summary-toggle">{summarizedExpanded ? 'masquer' : 'voir'}</span>
|
<span className="feed-summary-toggle">{summarizedExpanded ? 'masquer' : 'voir'}</span>
|
||||||
</div>
|
</div>
|
||||||
{summarizedExpanded && summarizedMsgs.map(msg => (
|
{summarizedExpanded && summarizedMsgs.map(msg => (
|
||||||
<FeedItem key={msg.id} msg={msg} />
|
<FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -894,7 +894,7 @@ export default function Studio({ api }) {
|
|||||||
<>
|
<>
|
||||||
{renderSummaryBlock()}
|
{renderSummaryBlock()}
|
||||||
{activeMsgs.slice(0, visibleCount).map(msg => (
|
{activeMsgs.slice(0, visibleCount).map(msg => (
|
||||||
<FeedItem key={msg.id} msg={msg} />
|
<FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
||||||
))}
|
))}
|
||||||
<div className="feed-collapsed-messages" onClick={handleToggleCollapsed}>
|
<div className="feed-collapsed-messages" onClick={handleToggleCollapsed}>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
@@ -911,7 +911,7 @@ export default function Studio({ api }) {
|
|||||||
<>
|
<>
|
||||||
{renderSummaryBlock()}
|
{renderSummaryBlock()}
|
||||||
{activeMsgs.map(msg => (
|
{activeMsgs.map(msg => (
|
||||||
<FeedItem key={msg.id} msg={msg} />
|
<FeedItem key={msg.id} msg={msg} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -935,7 +935,7 @@ export default function Studio({ api }) {
|
|||||||
<div className="studio-feed" ref={feedRef}>
|
<div className="studio-feed" ref={feedRef}>
|
||||||
{renderMessages()}
|
{renderMessages()}
|
||||||
{(streaming || streamThinking || loading || streamToolCalls.length > 0) && (
|
{(streaming || streamThinking || loading || streamToolCalls.length > 0) && (
|
||||||
<StreamingItem content={streaming} thinking={streamThinking} toolCalls={streamToolCalls} segments={streamSegments} />
|
<StreamingItem content={streaming} thinking={streamThinking} toolCalls={streamToolCalls} segments={streamSegments} activeAgents={activeAgents} onModeChange={handleToolModeChange} />
|
||||||
)}
|
)}
|
||||||
<div ref={messagesEnd} style={{ height: '24px' }} />
|
<div ref={messagesEnd} style={{ height: '24px' }} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user