Fix shell block styling in acme theme
- Add shell-block class with white bg, border, proper border-radius - Fix info message bubble width and margin for acme theme - Parse shell output correctly: first $ line is command, rest is output - Style shell output with proper padding and text color
This commit is contained in:
parent
0af153bf4f
commit
0ca3d38ec8
|
|
@ -240,11 +240,11 @@ export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, b
|
|||
<div key={i} class={`message ${msg.role}`}>
|
||||
<div class="msg-bubble">
|
||||
{msg.role === 'user' ? (
|
||||
<div class="user-text"><span class="msg-prefix">u: </span>{msg.content}</div>
|
||||
<div class="md" dangerouslySetInnerHTML={{ __html: linkifyPaths((marked.parse(msg.content) as string).replace(/^<p>/, '<p><span class="msg-prefix">u: </span>'), session.cwd) }} />
|
||||
) : msg.role === 'tool' ? (
|
||||
<ToolCall content={msg.content} />
|
||||
) : msg.role === 'info' ? (
|
||||
<div class="info-text">{msg.content}</div>
|
||||
<InfoMessage content={msg.content} />
|
||||
) : (
|
||||
<div
|
||||
class="md"
|
||||
|
|
@ -349,21 +349,27 @@ function DiagramModal({ html, onClose }: { html: string; onClose: () => void })
|
|||
|
||||
function formatToolContent(raw: string): { name: string; detail: string; body: string; output: string } {
|
||||
// Call is always on the first line; output follows after the newline.
|
||||
const nl = raw.indexOf('\n')
|
||||
const nl = raw.indexOf("\n")
|
||||
const callLine = nl === -1 ? raw : raw.slice(0, nl)
|
||||
const output = nl === -1 ? '' : raw.slice(nl + 1).trimEnd()
|
||||
const output = nl === -1 ? "" : raw.slice(nl + 1).trimEnd()
|
||||
const m = callLine.match(/^(\w+)\((.*)\)$/)
|
||||
if (!m) return { name: '', detail: '', body: raw, output: '' }
|
||||
if (!m) return { name: "", detail: "", body: raw, output: "" }
|
||||
const name = m[1]
|
||||
const argsRaw = m[2]
|
||||
try {
|
||||
const obj = JSON.parse(argsRaw)
|
||||
let detail = ''
|
||||
if (obj.steps?.[0]?.tool) detail = obj.steps[0].tool
|
||||
else if (obj.steps?.[0]?.code) detail = obj.language ?? 'bash'
|
||||
let detail = ""
|
||||
if (name === "call_tool") {
|
||||
detail = obj.calls?.[0]?.tool ?? ""
|
||||
} else if (name === "pipe") {
|
||||
const s = obj.stages?.[0]
|
||||
detail = s?.tool ?? s?.language ?? "bash"
|
||||
} else {
|
||||
detail = obj.steps?.[0]?.language ?? "bash"
|
||||
}
|
||||
return { name, detail, body: JSON.stringify(obj, null, 2), output }
|
||||
} catch {
|
||||
return { name, detail: '', body: argsRaw, output }
|
||||
return { name, detail: "", body: argsRaw, output }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,12 +389,37 @@ const TOOL_ICONS: Record<string, string> = {
|
|||
task_check: '☑️',
|
||||
task_clear: '☑️',
|
||||
bash: '🖥️',
|
||||
python3: '🐍',
|
||||
perl: '🐪',
|
||||
lua: '🌙',
|
||||
awk: '🔧',
|
||||
sed: '🔧',
|
||||
jq: '🔧',
|
||||
bc: '🔢',
|
||||
}
|
||||
|
||||
function toolIcon(detail: string): string {
|
||||
return TOOL_ICONS[detail] ?? '🖥️'
|
||||
}
|
||||
|
||||
function InfoMessage({ content }: { content: string }) {
|
||||
const lines = content.split('\n')
|
||||
const shellLines = lines.filter(l => l.startsWith('$ ')).map(l => l.slice(2))
|
||||
if (shellLines.length === 0) return <div class="info-text">{content}</div>
|
||||
const cmd = shellLines[0]
|
||||
const output = shellLines.slice(1).join('\n')
|
||||
return (
|
||||
<div class="tool-call shell-block">
|
||||
<div class="tool-header">
|
||||
<span class="tool-arrow">🖥️</span>
|
||||
<span class="tool-name">shell</span>
|
||||
<span class="tool-detail">{cmd}</span>
|
||||
</div>
|
||||
<pre class="tool-output">{output || '(no output)'}</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function ToolCall({ content }: { content: string }) {
|
||||
const { name, detail, body, output } = formatToolContent(content)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
|
|
|||
|
|
@ -797,6 +797,28 @@ details[open] > .tool-header::before {
|
|||
[data-theme="acme"] .tool-body {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
[data-theme="acme"] .message.info .msg-bubble {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
[data-theme="acme"] .shell-block {
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
}
|
||||
[data-theme="acme"] .shell-block .tool-header {
|
||||
cursor: default;
|
||||
}
|
||||
[data-theme="acme"] .shell-block .tool-header::before {
|
||||
display: none;
|
||||
}
|
||||
[data-theme="acme"] .shell-block .tool-output {
|
||||
display: block;
|
||||
color: var(--text);
|
||||
font-style: normal;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
[data-theme="acme"] .tool-header {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import preact from '@preact/preset-vite'
|
|||
|
||||
export default defineConfig({
|
||||
plugins: [preact()],
|
||||
base: './',
|
||||
server: {
|
||||
proxy: {
|
||||
'/s/': 'http://localhost:8080',
|
||||
|
|
|
|||
Reference in New Issue