11import Component from '@glimmer/component' ;
22import Logstream from 'codecrafters-frontend/utils/logstream' ;
3+ import fade from 'ember-animated/transitions/fade' ;
4+ import move from 'ember-animated/motions/move' ;
35import type ActionCableConsumerService from 'codecrafters-frontend/services/action-cable-consumer' ;
46import type AutofixRequestModel from 'codecrafters-frontend/models/autofix-request' ;
57import type Store from '@ember-data/store' ;
68import { action } from '@ember/object' ;
9+ import { fadeIn , fadeOut } from 'ember-animated/motions/opacity' ;
10+ import { next } from '@ember/runloop' ;
711import { service } from '@ember/service' ;
812import { tracked } from '@glimmer/tracking' ;
9- import { next } from '@ember/runloop' ;
10- import fade from 'ember-animated/transitions/fade' ;
1113
1214interface Signature {
1315 Element : HTMLDivElement ;
@@ -46,55 +48,66 @@ class ToolCall {
4648 this . status = 'in_progress' ;
4749 }
4850
49- get textComponents ( ) : [ string , string ] {
51+ get text ( ) : string {
5052 switch ( this . tool_name ) {
5153 case 'read' :
5254 if ( this . status === 'in_progress' ) {
53- return [ ' Reading' , 'abcd.rb' ] ;
55+ return ` Reading ${ this . tool_arguments [ 'path' ] ! } ` ;
5456 } else {
55- return [ ' Read' , 'abcd.rb' ] ;
57+ return ` Read ${ this . tool_arguments [ 'path' ] ! } ` ;
5658 }
5759
5860 case 'edit' :
5961 if ( this . status === 'in_progress' ) {
60- return [ 'Editing' , 'abcd.rb' ] ;
62+ return `Editing ${ this . tool_arguments [ 'path' ] ! } ` ;
63+ } else {
64+ return `Edited ${ this . tool_arguments [ 'path' ] ! } ` ;
65+ }
66+
67+ case 'write' :
68+ if ( this . status === 'in_progress' ) {
69+ return `Creating ${ this . tool_arguments [ 'path' ] ! } ` ;
6170 } else {
62- return [ 'Edited' , 'abcd.rb' ] ;
71+ return `Created ${ this . tool_arguments [ 'path' ] ! } ` ;
6372 }
6473
6574 case 'delete' :
6675 if ( this . status === 'in_progress' ) {
67- return [ ' Deleting' , 'abcd.rb' ] ;
76+ return ` Deleting ${ this . tool_arguments [ 'path' ] ! } ` ;
6877 } else {
69- return [ ' Deleted' , 'abcd.rb' ] ;
78+ return ` Deleted ${ this . tool_arguments [ 'path' ] ! } ` ;
7079 }
7180
7281 case 'list' :
7382 if ( this . status === 'in_progress' ) {
74- return [ 'Listing' , '' ] ;
83+ return 'Listing files' ;
7584 } else {
76- return [ 'Listed' , '' ] ;
85+ return 'Listed files' ;
7786 }
7887
7988 case 'bash' :
8089 if ( this . status === 'in_progress' ) {
81- return [ 'Running' , ' tests'] ;
90+ return 'Running tests' ;
8291 } else {
83- return [ 'Ran' , ' tests'] ;
92+ return 'Ran tests' ;
8493 }
8594
8695 // This is a fake tool call that we insert for UI display purposes
8796 case 'analyze' :
8897 if ( this . status === 'in_progress' ) {
89- return [ 'Analyzing' , ' codebase'] ;
98+ return 'Analyzing codebase' ;
9099 } else {
91- return [ 'Analyzed' , ' codebase'] ;
100+ return 'Analyzed codebase' ;
92101 }
93102
94103 default :
95- return [ this . tool_name , '' ] ;
104+ return this . tool_name ;
96105 }
97106 }
107+
108+ isAnalysisAction ( ) : boolean {
109+ return this . tool_name === 'analyze' || this . tool_name === 'read' || this . tool_name === 'list' ;
110+ }
98111}
99112
100113export default class LogstreamSection extends Component < Signature > {
@@ -141,10 +154,6 @@ export default class LogstreamSection extends Component<Signature> {
141154 get toolCalls ( ) : ToolCall [ ] {
142155 const result : ToolCall [ ] = [ ] ;
143156
144- if ( this . events . length === 0 ) {
145- result . push ( new ToolCall ( 'fake-tool-call-id' , 'analyze' , { } ) ) ;
146- }
147-
148157 for ( const event of this . events ) {
149158 if ( event . event === 'tool_call_start' ) {
150159 result . push ( new ToolCall ( event . params . tool_call_id , event . params . tool_name , event . params . tool_arguments || { } ) ) ;
@@ -153,6 +162,25 @@ export default class LogstreamSection extends Component<Signature> {
153162 }
154163 }
155164
165+ if ( result . every ( ( toolCall ) => toolCall . isAnalysisAction ( ) ) ) {
166+ // Show an in-progress analysis action if all tool calls are analysis actions.
167+ result . push ( new ToolCall ( 'fake-tool-call-id' , 'analyze' , { } ) ) ;
168+ } else {
169+ const analysisToolCall = new ToolCall ( 'fake-tool-call-id' , 'analyze' , { } ) ;
170+ analysisToolCall . status = 'completed' ;
171+
172+ let lastAnalysisActionIndex = - 1 ;
173+
174+ for ( let i = result . length - 1 ; i >= 0 ; i -- ) {
175+ if ( result [ i ] ! . isAnalysisAction ( ) ) {
176+ lastAnalysisActionIndex = i ;
177+ break ;
178+ }
179+ }
180+
181+ result . splice ( lastAnalysisActionIndex + 1 , 0 , analysisToolCall ) ;
182+ }
183+
156184 return result ;
157185 }
158186
@@ -161,6 +189,22 @@ export default class LogstreamSection extends Component<Signature> {
161189 this . eventsContainer = eventsContainer ;
162190 }
163191
192+ // @ts -expect-error ember-animated not typed
193+ // eslint-disable-next-line require-yield
194+ * listTransition ( { insertedSprites, keptSprites, removedSprites } ) {
195+ for ( const sprite of keptSprites ) {
196+ move ( sprite ) ;
197+ }
198+
199+ for ( const sprite of insertedSprites ) {
200+ fadeIn ( sprite ) ;
201+ }
202+
203+ for ( const sprite of removedSprites ) {
204+ fadeOut ( sprite ) ;
205+ }
206+ }
207+
164208 willDestroy ( ) {
165209 super . willDestroy ( ) ;
166210
0 commit comments