Skip to content

Commit f5d1abe

Browse files
loop: simple-chat-mode completed after 1 iterations
1 parent b61a3c8 commit f5d1abe

6 files changed

Lines changed: 376 additions & 44 deletions

File tree

frontend/src/api/types/settings.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ export interface UserPreferences {
4848
defaultModel?: string
4949
defaultAgent?: string
5050
autoScroll: boolean
51-
showReasoning: boolean
52-
expandToolCalls: boolean
5351
expandDiffs: boolean
52+
expandToolCalls: boolean
53+
showReasoning: boolean
54+
simpleChatMode: boolean
5455
leaderKey?: string
5556
directShortcuts?: string[]
5657
keyboardShortcuts: Record<string, string>

frontend/src/components/message/FileToolRender.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ export function FileToolRender({ part, filediff, filePath, content, toolName, on
6666
const { preferences } = useSettings()
6767
const isReadTool = toolName === 'Read'
6868
const isEditTool = toolName === 'Edit'
69+
const isWriteTool = toolName === 'Write'
6970
const hasExpandableContent = !isReadTool && (filediff || content)
7071

71-
const defaultExpanded = isEditTool
72-
? (preferences?.expandDiffs ?? true)
72+
const isFileMutatingTool = isEditTool || isWriteTool
73+
const defaultExpanded = isFileMutatingTool
74+
? (preferences?.expandDiffs ?? true)
7375
: (preferences?.expandToolCalls ?? false)
7476
const [expanded, setExpanded] = useState(defaultExpanded)
7577

frontend/src/components/message/MessagePart.test.tsx

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import type { MessagePart as MessagePartType } from '@/api/types'
55

66
const mocks = vi.hoisted(() => ({
77
useTTS: vi.fn(),
8+
useSettings: vi.fn(),
89
}))
910

1011
vi.mock('@/hooks/useTTS', () => ({
1112
useTTS: mocks.useTTS,
1213
}))
1314

15+
vi.mock('@/hooks/useSettings', () => ({
16+
useSettings: mocks.useSettings,
17+
}))
18+
1419
interface MockTTSReturn {
1520
speakMessage: ReturnType<typeof vi.fn>
1621
stop: ReturnType<typeof vi.fn>
@@ -20,13 +25,42 @@ interface MockTTSReturn {
2025
isEnabled: boolean
2126
}
2227

28+
interface MockSettingsReturn {
29+
preferences: {
30+
simpleChatMode: boolean
31+
showReasoning: boolean
32+
expandToolCalls: boolean
33+
expandDiffs: boolean
34+
autoScroll: boolean
35+
theme: 'dark' | 'light' | 'system'
36+
mode: 'plan' | 'build'
37+
} | undefined
38+
isLoading: boolean
39+
updateSettings: ReturnType<typeof vi.fn>
40+
isUpdating: boolean
41+
}
42+
2343
describe('MessagePart', () => {
2444
const mockSpeakMessage = vi.fn()
2545
const mockStop = vi.fn()
2646

2747
beforeEach(() => {
2848
mockSpeakMessage.mockClear()
2949
mockStop.mockClear()
50+
mocks.useSettings.mockReturnValue({
51+
preferences: {
52+
simpleChatMode: false,
53+
showReasoning: false,
54+
expandToolCalls: false,
55+
expandDiffs: true,
56+
autoScroll: true,
57+
theme: 'dark' as const,
58+
mode: 'build' as const,
59+
},
60+
isLoading: false,
61+
updateSettings: vi.fn(),
62+
isUpdating: false,
63+
})
3064
})
3165

3266
const setup = (options: {
@@ -47,6 +81,15 @@ describe('MessagePart', () => {
4781
mocks.useTTS.mockReturnValue(mockTTS)
4882
}
4983

84+
const setupSettings = (preferences: MockSettingsReturn['preferences']) => {
85+
mocks.useSettings.mockReturnValue({
86+
preferences,
87+
isLoading: false,
88+
updateSettings: vi.fn(),
89+
isUpdating: false,
90+
})
91+
}
92+
5093
const createStepFinishPart = (messageID: string): MessagePartType => ({
5194
type: 'step-finish',
5295
messageID,
@@ -223,4 +266,261 @@ describe('MessagePart', () => {
223266

224267
expect(screen.getByRole('button')).not.toHaveClass('bg-red-500/20')
225268
})
269+
270+
describe('simpleChatMode', () => {
271+
const createToolPart = (): MessagePartType => ({
272+
type: 'tool',
273+
tool: 'edit',
274+
sessionID: 'test-session',
275+
state: {
276+
status: 'completed',
277+
input: { filePath: '/test/file.txt' },
278+
time: { start: Date.now(), end: Date.now() + 100 },
279+
},
280+
})
281+
282+
const createPatchPart = (): MessagePartType => ({
283+
type: 'patch',
284+
hash: 'abc123',
285+
files: ['/test/file.txt'],
286+
sessionID: 'test-session',
287+
})
288+
289+
const createReasoningPart = (): MessagePartType => ({
290+
type: 'reasoning',
291+
text: 'This is the reasoning text',
292+
sessionID: 'test-session',
293+
})
294+
295+
const createSnapshotPart = (): MessagePartType => ({
296+
type: 'snapshot',
297+
snapshot: 'snapshot-data',
298+
sessionID: 'test-session',
299+
})
300+
301+
const createAgentPart = (): MessagePartType => ({
302+
type: 'agent',
303+
name: 'test-agent',
304+
sessionID: 'test-session',
305+
})
306+
307+
const createTextPart = (): MessagePartType => ({
308+
type: 'text',
309+
text: 'Hello, this is a text message',
310+
sessionID: 'test-session',
311+
})
312+
313+
it('renders null for tool part when simpleChatMode is true', () => {
314+
setupSettings({
315+
simpleChatMode: true,
316+
showReasoning: false,
317+
expandToolCalls: false,
318+
expandDiffs: true,
319+
autoScroll: true,
320+
theme: 'dark',
321+
mode: 'build',
322+
})
323+
324+
const part = createToolPart()
325+
const { container } = render(<MessagePart part={part} />)
326+
327+
expect(container.firstChild).toBeNull()
328+
})
329+
330+
it('renders null for patch part when simpleChatMode is true', () => {
331+
setupSettings({
332+
simpleChatMode: true,
333+
showReasoning: false,
334+
expandToolCalls: false,
335+
expandDiffs: true,
336+
autoScroll: true,
337+
theme: 'dark',
338+
mode: 'build',
339+
})
340+
341+
const part = createPatchPart()
342+
const { container } = render(<MessagePart part={part} />)
343+
344+
expect(container.firstChild).toBeNull()
345+
})
346+
347+
it('renders null for reasoning part when simpleChatMode is true', () => {
348+
setupSettings({
349+
simpleChatMode: true,
350+
showReasoning: true,
351+
expandToolCalls: false,
352+
expandDiffs: true,
353+
autoScroll: true,
354+
theme: 'dark',
355+
mode: 'build',
356+
})
357+
358+
const part = createReasoningPart()
359+
const { container } = render(<MessagePart part={part} />)
360+
361+
expect(container.firstChild).toBeNull()
362+
})
363+
364+
it('renders null for snapshot part when simpleChatMode is true', () => {
365+
setupSettings({
366+
simpleChatMode: true,
367+
showReasoning: false,
368+
expandToolCalls: false,
369+
expandDiffs: true,
370+
autoScroll: true,
371+
theme: 'dark',
372+
mode: 'build',
373+
})
374+
375+
const part = createSnapshotPart()
376+
const { container } = render(<MessagePart part={part} />)
377+
378+
expect(container.firstChild).toBeNull()
379+
})
380+
381+
it('renders null for agent part when simpleChatMode is true', () => {
382+
setupSettings({
383+
simpleChatMode: true,
384+
showReasoning: false,
385+
expandToolCalls: false,
386+
expandDiffs: true,
387+
autoScroll: true,
388+
theme: 'dark',
389+
mode: 'build',
390+
})
391+
392+
const part = createAgentPart()
393+
const { container } = render(<MessagePart part={part} />)
394+
395+
expect(container.firstChild).toBeNull()
396+
})
397+
398+
it('renders text part when simpleChatMode is true', () => {
399+
setupSettings({
400+
simpleChatMode: true,
401+
showReasoning: false,
402+
expandToolCalls: false,
403+
expandDiffs: true,
404+
autoScroll: true,
405+
theme: 'dark',
406+
mode: 'build',
407+
})
408+
409+
const part = createTextPart()
410+
render(<MessagePart part={part} />)
411+
412+
expect(screen.getByText('Hello, this is a text message')).toBeInTheDocument()
413+
})
414+
415+
it('renders tool part when simpleChatMode is false', () => {
416+
setupSettings({
417+
simpleChatMode: false,
418+
showReasoning: false,
419+
expandToolCalls: false,
420+
expandDiffs: true,
421+
autoScroll: true,
422+
theme: 'dark',
423+
mode: 'build',
424+
})
425+
426+
const part = createToolPart()
427+
const { container } = render(<MessagePart part={part} />)
428+
429+
expect(container.firstChild).not.toBeNull()
430+
})
431+
432+
it('renders patch part when simpleChatMode is false', () => {
433+
setupSettings({
434+
simpleChatMode: false,
435+
showReasoning: false,
436+
expandToolCalls: false,
437+
expandDiffs: true,
438+
autoScroll: true,
439+
theme: 'dark',
440+
mode: 'build',
441+
})
442+
443+
const part = createPatchPart()
444+
const { container } = render(<MessagePart part={part} />)
445+
446+
expect(container.firstChild).not.toBeNull()
447+
})
448+
449+
it('renders snapshot part when simpleChatMode is false', () => {
450+
setupSettings({
451+
simpleChatMode: false,
452+
showReasoning: false,
453+
expandToolCalls: false,
454+
expandDiffs: true,
455+
autoScroll: true,
456+
theme: 'dark',
457+
mode: 'build',
458+
})
459+
460+
const part = createSnapshotPart()
461+
const { container } = render(<MessagePart part={part} />)
462+
463+
expect(container.firstChild).not.toBeNull()
464+
})
465+
466+
it('renders agent part when simpleChatMode is false', () => {
467+
setupSettings({
468+
simpleChatMode: false,
469+
showReasoning: false,
470+
expandToolCalls: false,
471+
expandDiffs: true,
472+
autoScroll: true,
473+
theme: 'dark',
474+
mode: 'build',
475+
})
476+
477+
const part = createAgentPart()
478+
const { container } = render(<MessagePart part={part} />)
479+
480+
expect(container.firstChild).not.toBeNull()
481+
})
482+
})
483+
484+
describe('showReasoning', () => {
485+
const createReasoningPart = (): MessagePartType => ({
486+
type: 'reasoning',
487+
text: 'This is the reasoning text',
488+
sessionID: 'test-session',
489+
})
490+
491+
it('renders null for reasoning part when showReasoning is false', () => {
492+
setupSettings({
493+
simpleChatMode: false,
494+
showReasoning: false,
495+
expandToolCalls: false,
496+
expandDiffs: true,
497+
autoScroll: true,
498+
theme: 'dark',
499+
mode: 'build',
500+
})
501+
502+
const part = createReasoningPart()
503+
const { container } = render(<MessagePart part={part} />)
504+
505+
expect(container.firstChild).toBeNull()
506+
})
507+
508+
it('renders reasoning part when showReasoning is true', () => {
509+
setupSettings({
510+
simpleChatMode: false,
511+
showReasoning: true,
512+
expandToolCalls: false,
513+
expandDiffs: true,
514+
autoScroll: true,
515+
theme: 'dark',
516+
mode: 'build',
517+
})
518+
519+
const part = createReasoningPart()
520+
render(<MessagePart part={part} />)
521+
522+
expect(screen.getByText('This is the reasoning text')).toBeInTheDocument()
523+
expect(screen.getByText('Reasoning')).toBeInTheDocument()
524+
})
525+
})
226526
})

0 commit comments

Comments
 (0)