From 0ca3d38ec8cc2bf91cf02432ea0aa3205262293a Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 20 May 2026 17:22:32 +0200 Subject: [PATCH] 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 --- src/components/ChatPane.tsx | 49 ++++++++++++++++++++++++++++++------- src/style.css | 22 +++++++++++++++++ vite.config.ts | 1 + 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/components/ChatPane.tsx b/src/components/ChatPane.tsx index d50e74d..0675158 100644 --- a/src/components/ChatPane.tsx +++ b/src/components/ChatPane.tsx @@ -240,11 +240,11 @@ export function ChatPane({ session, cfg, messages, onSend, onStop, browsePath, b
{msg.role === 'user' ? ( -
u: {msg.content}
+
/, '

u: '), session.cwd) }} /> ) : msg.role === 'tool' ? ( ) : msg.role === 'info' ? ( -

{msg.content}
+ ) : (
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 = { 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
{content}
+ const cmd = shellLines[0] + const output = shellLines.slice(1).join('\n') + return ( +
+
+ 🖥️ + shell + {cmd} +
+
{output || '(no output)'}
+
+ ) +} + function ToolCall({ content }: { content: string }) { const { name, detail, body, output } = formatToolContent(content) const [open, setOpen] = useState(false) diff --git a/src/style.css b/src/style.css index 4246c72..a7f0046 100644 --- a/src/style.css +++ b/src/style.css @@ -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; } diff --git a/vite.config.ts b/vite.config.ts index d13024b..e8cf96b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,6 +3,7 @@ import preact from '@preact/preset-vite' export default defineConfig({ plugins: [preact()], + base: './', server: { proxy: { '/s/': 'http://localhost:8080',