style and display fixes

This commit is contained in:
Rob Colbert 2026-05-12 01:02:09 -04:00
parent dd47212471
commit b3c9579890
3 changed files with 134 additions and 139 deletions

View File

@ -76,12 +76,14 @@ const ChatTurn = memo(function ChatTurn({ turn }: ChatTurnProps) {
{/* Error Alert */}
{turn.errorMessage && turn.status === "error" && (
<div className="max-w-[80%] ml-0 mb-4">
<div className="bg-red-600 text-white rounded p-4 border-2 border-red-400">
<div className="text-sm font-semibold mb-2 flex items-center gap-2">
<div className="max-w-[80%] ml-0 mb-2">
<div className="bg-red-600 text-white rounded p-3 border-2 border-red-400">
<div className="text-xs font-semibold mb-1 flex items-center gap-2">
<span></span> Error
</div>
<div className="whitespace-pre-wrap">{turn.errorMessage}</div>
<div className="whitespace-pre-wrap text-sm">
{turn.errorMessage}
</div>
</div>
</div>
)}
@ -89,51 +91,53 @@ const ChatTurn = memo(function ChatTurn({ turn }: ChatTurnProps) {
{/* User Prompt */}
<div className="max-w-[80%] ml-0 mb-4">
<div className="bg-[#4a0000] text-white rounded p-4">
<div className="text-sm font-semibold mb-2">You</div>
<div className="whitespace-pre-wrap">{turn.prompts?.user || ""}</div>
<div className="text-xs font-semibold mb-2">You</div>
<div className="whitespace-pre-wrap text-sm">
{turn.prompts?.user || ""}
</div>
</div>
</div>
{/* Agent Response */}
{(turn.blocks && turn.blocks.length > 0) && (
{turn.blocks && turn.blocks.length > 0 && (
<div className="max-w-[80%] ml-auto mb-4 overflow-x-hidden">
<div className="bg-bg-tertiary border border-border-default rounded p-4 overflow-x-auto">
<div className="text-sm font-semibold mb-3 text-text-secondary">
<div className="bg-bg-tertiary border border-border-default rounded p-3 overflow-x-auto">
<div className="text-xs font-semibold mb-4 text-text-secondary">
Gadget
</div>
{/* Render blocks in order */}
{turn.blocks.map((block, idx) => {
if (block.mode === 'thinking') {
if (block.mode === "thinking") {
return (
<div key={idx} className="mb-3">
<div key={idx} className="mb-4">
<div className="text-xs text-text-muted mb-1 font-mono">
Thinking
</div>
<div
className="p-3 bg-bg-secondary rounded text-sm text-text-muted whitespace-pre-wrap font-mono text-xs"
dangerouslySetInnerHTML={{
__html: marked.parse(block.content) as string
}}
/>
</div>
);
} else if (block.mode === 'responding') {
return (
<div key={idx} className="mb-3 overflow-x-auto">
<div
className="gadget-markdown"
className="gadget-markdown bg-bg-secondary rounded p-2 text-xs text-text-muted font-mono"
dangerouslySetInnerHTML={{
__html: marked.parse(block.content) as string
__html: marked.parse(block.content) as string,
}}
/>
</div>
);
} else if (block.mode === 'tool') {
} else if (block.mode === "responding") {
return (
<div key={idx} className="mb-4 overflow-x-auto">
<div
className="gadget-markdown text-[13px]"
dangerouslySetInnerHTML={{
__html: marked.parse(block.content) as string,
}}
/>
</div>
);
} else if (block.mode === "tool") {
const toolCall = block.content;
const subagent = toolCall.subagent;
return (
<div key={idx} className="mb-3">
<div key={idx} className="tool-call">
<div className="flex items-center gap-2 text-xs font-mono text-text-secondary">
<span className="text-brand"></span>
<span>{toolCall.name}</span>
@ -144,16 +148,14 @@ const ChatTurn = memo(function ChatTurn({ turn }: ChatTurnProps) {
<span className="text-green-500"></span>
)}
</div>
{subagent && (
<SubagentDisplay subagent={subagent} />
)}
{subagent && <SubagentDisplay subagent={subagent} />}
</div>
);
}
return null;
})}
<div className="mt-2 text-xs text-text-muted">
<div className="mt-1 text-xs text-text-muted">
{startedAt.toLocaleTimeString()}
</div>
</div>
@ -163,7 +165,11 @@ const ChatTurn = memo(function ChatTurn({ turn }: ChatTurnProps) {
);
});
function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool['content']['subagent']> }) {
function SubagentDisplay({
subagent,
}: {
subagent: NonNullable<ChatTurnBlockTool["content"]["subagent"]>;
}) {
const [expanded, setExpanded] = useState(true);
const toggle = useCallback(() => setExpanded((p) => !p), []);
@ -179,7 +185,7 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
};
return (
<div className="ml-4 border-l-2 border-brand/20 pl-2">
<div className="ml-3 border-l-2 border-brand/20 pl-1.5 mt-1">
<button
onClick={toggle}
className="flex items-center gap-1.5 text-xs font-mono text-text-muted hover:text-text-primary transition-colors w-full text-left"
@ -188,7 +194,8 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
<span>Subagent</span>
{subagent.stats && (
<span className="text-text-muted">
({subagent.toolCalls.length} calls · {formatDuration(subagent.stats.durationMs)})
({subagent.toolCalls.length} calls ·{" "}
{formatDuration(subagent.stats.durationMs)})
</span>
)}
</button>
@ -198,9 +205,11 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
{/* Thinking */}
{subagent.thinking && (
<div>
<div className="text-xs text-text-muted font-mono mb-1">Thinking</div>
<div className="text-xs text-text-muted font-mono mb-0.5">
Thinking
</div>
<div
className="subagent-compact gadget-markdown bg-bg-secondary rounded p-1.5 text-xs text-text-muted whitespace-pre-wrap font-mono"
className="gadget-markdown bg-bg-secondary rounded p-1.5 text-xs text-text-muted font-mono"
dangerouslySetInnerHTML={{
__html: marked.parse(subagent.thinking) as string,
}}
@ -211,12 +220,15 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
{/* Subagent Tool Calls */}
{subagent.toolCalls.length > 0 && (
<div>
<div className="text-xs text-text-muted font-mono mb-1">
<div className="text-xs text-text-muted font-mono mb-0.5">
Tool Calls ({subagent.toolCalls.length})
</div>
<div className="space-y-1">
<div className="space-y-0.5">
{subagent.toolCalls.map((tc, i) => (
<div key={tc.callId || i} className="flex items-center gap-2 text-xs font-mono text-text-secondary">
<div
key={tc.callId || i}
className="flex items-center gap-2 text-xs font-mono text-text-secondary"
>
<span className="text-yellow-500"></span>
<span>{tc.name}</span>
{tc.response && <span className="text-green-500"></span>}
@ -229,9 +241,11 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
{/* Response */}
{subagent.response && (
<div>
<div className="text-xs text-text-muted font-mono mb-1">Response</div>
<div className="text-xs text-text-muted font-mono mb-0.5">
Response
</div>
<div
className="subagent-compact gadget-markdown"
className="gadget-markdown text-xs"
dangerouslySetInnerHTML={{
__html: marked.parse(subagent.response) as string,
}}
@ -246,7 +260,9 @@ function SubagentDisplay({ subagent }: { subagent: NonNullable<ChatTurnBlockTool
<span>{formatTokenCount(subagent.stats.inputTokens)} in</span>
<span>{formatTokenCount(subagent.stats.responseTokens)} out</span>
{subagent.stats.thinkingTokenCount > 0 && (
<span>{formatTokenCount(subagent.stats.thinkingTokenCount)} thinking</span>
<span>
{formatTokenCount(subagent.stats.thinkingTokenCount)} thinking
</span>
)}
</div>
)}

View File

@ -12,10 +12,11 @@
--color-border-subtle: #1a1a1a;
--color-border-default: #2a2a2a;
--color-border-highlight: #3a3a3a;
--font-mono: 'Courier New', Courier, monospace;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: "Fira Code", "Courier New", Courier, monospace;
--font-sans:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--spacing-header: 48px;
--spacing-status: 32px;
}
@ -54,7 +55,17 @@ button {
font-size: inherit;
}
input, textarea {
.tool-call {
margin-top: theme("spacing.4");
margin-bottom: theme("spacing.4");
}
.tool-call + .tool-call {
margin-top: 0;
}
input,
textarea {
font-family: inherit;
font-size: inherit;
}
@ -77,8 +88,13 @@ input, textarea {
}
@keyframes strobe {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.4;
}
}
.strobe {
@ -86,20 +102,20 @@ input, textarea {
}
/* ── Gadget Markdown Styles ─────────────────────────────────── */
/* Applied to agent response output inside ChatTurn */
/* Dense, technical display — no blog-style whitespace */
.gadget-markdown {
color: var(--color-text-primary);
line-height: 1.7;
font-size: 14px;
white-space: pre-wrap;
line-height: 1.5;
word-break: break-word;
/* Prevent the markdown root from stretching to fill the scroll container
display:inline-block shrinks it to content width, so the
overflow-x-auto on the parent actually clips and scrolls it. */
display: inline-block;
width: 100%;
max-width: 100%;
margin-bottom: 8px;
:last-child {
margin-bottom: 0;
}
}
.gadget-markdown h1,
@ -110,19 +126,37 @@ input, textarea {
.gadget-markdown h6 {
color: #e8e8e8;
font-weight: 600;
line-height: 1.3;
margin-top: 1.5em;
margin-bottom: 0.5em;
line-height: 1.25;
margin-top: 0.5rem;
margin-bottom: 0.25rem;
}
.gadget-markdown h1 { font-size: 1.4em; border-bottom: 1px solid var(--color-border-default); padding-bottom: 0.3em; }
.gadget-markdown h2 { font-size: 1.2em; border-bottom: 1px solid var(--color-border-default); padding-bottom: 0.25em; }
.gadget-markdown h3 { font-size: 1.05em; }
.gadget-markdown h4 { font-size: 1em; }
.gadget-markdown h1 {
font-size: 1.5em;
/* border-bottom: 1px solid var(--color-border-default); */
padding-bottom: 0.2em;
}
.gadget-markdown h2 {
font-size: 1.4em;
/* border-bottom: 1px solid var(--color-border-default); */
padding-bottom: 0.15em;
}
.gadget-markdown h3 {
font-size: 1.3em;
}
.gadget-markdown h4 {
font-size: 1.2em;
}
.gadget-markdown h5 {
font-size: 1.1em;
}
.gadget-markdown h6 {
font-size: 1em;
}
.gadget-markdown p {
margin-top: 0;
margin-bottom: 0.85em;
margin-bottom: 0.8rem;
}
.gadget-markdown a {
@ -137,23 +171,23 @@ input, textarea {
/* Inline code */
.gadget-markdown code:not(pre code) {
font-family: var(--font-mono);
font-size: 0.875em;
font-size: 0.85em;
background: #1e1e1e;
color: #f472b6;
padding: 0.15em 0.4em;
border-radius: 4px;
color: rgb(219, 219, 7);
padding: 0.1em 0.3em;
border-radius: 3px;
border: 1px solid #2d2d2d;
}
/* Code blocks */
.gadget-markdown pre {
line-height: 1rem;
background: #0d0d0d;
border: 1px solid #2a2a2a;
border-radius: 6px;
padding: 1em 1.2em;
border-radius: 4px;
padding: 0.5rem 0.7rem;
overflow-x: auto;
margin-bottom: 1em;
/* Horizontal scroll fix — parent must have overflow-x-auto or be constrained */
margin-bottom: 0.5rem;
max-width: 100%;
width: 100%;
}
@ -173,14 +207,14 @@ input, textarea {
width: auto;
max-width: 100%;
border-collapse: collapse;
margin-bottom: 1em;
font-size: 0.9em;
margin-bottom: 0.5rem;
font-size: 0.85em;
}
.gadget-markdown th,
.gadget-markdown td {
border: 1px solid #2a2a2a;
padding: 0.5em 0.9em;
padding: 0.35em 0.6em;
text-align: left;
}
@ -200,23 +234,23 @@ input, textarea {
/* Blockquotes */
.gadget-markdown blockquote {
border-left: 3px solid #3a3a3a;
margin: 0 0 1em 0;
padding: 0.5em 1em;
border-left: 2px solid #3a3a3a;
margin: 0 0 0.35rem 0;
padding: 0.25rem 0.6rem;
color: var(--color-text-muted);
background: #111111;
border-radius: 0 4px 4px 0;
border-radius: 0 3px 3px 0;
}
/* Lists */
.gadget-markdown ul,
.gadget-markdown ol {
margin-bottom: 0.85em;
padding-left: 1.5em;
margin-bottom: 0.35rem;
padding-left: 1.2rem;
}
.gadget-markdown li {
margin-bottom: 0.3em;
margin-bottom: 0.15rem;
}
.gadget-markdown li::marker {
@ -227,13 +261,13 @@ input, textarea {
.gadget-markdown hr {
border: none;
border-top: 1px solid var(--color-border-default);
margin: 1.5em 0;
margin: 0.5rem 0;
}
/* Images */
.gadget-markdown img {
max-width: 100%;
border-radius: 6px;
border-radius: 4px;
border: 1px solid var(--color-border-default);
}
@ -243,61 +277,6 @@ input, textarea {
font-weight: 600;
}
.gadget-markdown em {
color: #c4c4c4;
}
/* ── Subagent Compact Markdown ── */
/* Higher-specificity overrides (.subagent-compact.gadget-markdown > .gadget-markdown)
to crush the generous spacing that looks fine at top-level but explodes when nested. */
.subagent-compact.gadget-markdown {
font-size: 12px;
line-height: 1.4;
}
.subagent-compact.gadget-markdown h1,
.subagent-compact.gadget-markdown h2,
.subagent-compact.gadget-markdown h3,
.subagent-compact.gadget-markdown h4,
.subagent-compact.gadget-markdown h5,
.subagent-compact.gadget-markdown h6 {
margin-top: 0.5rem;
margin-bottom: 0.25rem;
line-height: 1.2;
}
.subagent-compact.gadget-markdown p {
margin-top: 0;
margin-bottom: 0.35rem;
}
.subagent-compact.gadget-markdown pre {
margin-top: 0.35rem;
margin-bottom: 0.35rem;
padding: 0.5rem 0.6rem;
}
.subagent-compact.gadget-markdown ul,
.subagent-compact.gadget-markdown ol {
margin-bottom: 0.35rem;
padding-left: 1.1rem;
}
.subagent-compact.gadget-markdown li {
margin-bottom: 0.15rem;
}
.subagent-compact.gadget-markdown blockquote {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
padding: 0.25rem 0.6rem;
}
.subagent-compact.gadget-markdown hr {
margin: 0.4rem 0;
}
.subagent-compact.gadget-markdown table {
margin-bottom: 0.5rem;
}

View File

@ -1009,7 +1009,7 @@ export default function ChatSessionView() {
{/* Chat View (75%) */}
<div className="flex-1 flex flex-col overflow-hidden">
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
<div className="flex-1 overflow-y-auto p-3 space-y-2">
{turns.map((turn) => (
<ChatTurnComponent
key={turn._id}