feat: better pane management and toolbar api

This commit is contained in:
Brooklyn Nicholson
2026-05-02 15:22:18 -05:00
parent a66303eaef
commit 6dcf5bcbc0
17 changed files with 772 additions and 348 deletions
@@ -3,6 +3,35 @@ import { describe, expect, it } from 'vitest'
import { appendAssistantTextPart, chatMessageText, renderMediaTags, toChatMessages, upsertToolPart } from './chat-messages'
describe('toChatMessages', () => {
it('merges queued tool-only assistant rows into the previous assistant bubble', () => {
const messages = toChatMessages([
{ role: 'assistant', content: 'Planning.', timestamp: 1 },
{
role: 'assistant',
content: '',
timestamp: 2,
tool_calls: [{ id: 'tc-terminal', function: { name: 'terminal', arguments: '{"command":"ls"}' } }]
},
{ role: 'assistant', content: 'Done.', timestamp: 3 }
])
const assistants = messages.filter(m => m.role === 'assistant')
expect(assistants).toHaveLength(1)
expect(chatMessageText(assistants[0])).toContain('Planning')
expect(chatMessageText(assistants[0])).toContain('Done')
expect(assistants[0].parts.some(p => p.type === 'tool-call' && p.toolName === 'terminal')).toBe(true)
const ordered = assistants[0].parts.map(p => p.type)
expect(ordered.filter(t => t === 'text')).toHaveLength(2)
const toolIdx = assistants[0].parts.findIndex(p => p.type === 'tool-call')
expect(ordered.slice(0, toolIdx)).toContain('text')
expect(
assistants[0].parts.findIndex((p, i) => p.type === 'text' && i > toolIdx)
).toBeGreaterThanOrEqual(0)
})
it('hides attached context payloads from user message display', () => {
const [message] = toChatMessages([
{
+16
View File
@@ -514,6 +514,22 @@ export function toChatMessages(messages: SessionMessage[]): ChatMessage[] {
}
if (message.role === 'assistant' && pendingToolParts.length) {
const last = result.at(-1)
// Session logs interleave tool-only assistant rows with later prose; appending onto
// the previous bubble keeps one turn contiguous (vs unshift splitting the UI).
if (last?.role === 'assistant') {
last.parts = [...last.parts, ...pendingToolParts, ...parts]
pendingToolParts = []
pendingToolTimestamp = undefined
if (message.timestamp !== undefined) {
last.timestamp = message.timestamp
}
return
}
parts.unshift(...pendingToolParts)
pendingToolParts = []
pendingToolTimestamp = undefined