33 type Guardrails ,
44 type Model ,
55 type NLSSearchDynamicFilter ,
6+ PromptString ,
67 type SerializedPromptEditorValue ,
78 deserializeContextItem ,
89 isAbortErrorOrSocketHangUp ,
@@ -14,7 +15,6 @@ import {
1415 type FC ,
1516 memo ,
1617 useCallback ,
17- useContext ,
1818 useEffect ,
1919 useImperativeHandle ,
2020 useMemo ,
@@ -25,7 +25,6 @@ import type { UserAccountInfo } from '../Chat'
2525import type { ApiPostMessage } from '../Chat'
2626import { getVSCodeAPI } from '../utils/VSCodeApi'
2727import { SpanManager } from '../utils/spanManager'
28- import { getTraceparentFromSpanContext } from '../utils/telemetry'
2928import { useOmniBox } from '../utils/useOmniBox'
3029import type { CodeBlockActionsProps } from './ChatMessageContent/ChatMessageContent'
3130import {
@@ -37,7 +36,7 @@ import { HumanMessageCell } from './cells/messageCell/human/HumanMessageCell'
3736import { type Context , type Span , context , trace } from '@opentelemetry/api'
3837import { DeepCodyAgentID } from '@sourcegraph/cody-shared/src/models/client'
3938import * as uuid from 'uuid'
40- import { isCodeSearchContextItem } from '../../src/context/openctx/codeSearch'
39+
4140import { useClientActionListener } from '../client/clientState'
4241import { useLocalStorage } from '../components/hooks'
4342
@@ -50,6 +49,7 @@ import { ToolStatusCell } from './cells/toolCell/ToolStatusCell'
5049import { LoadingDots } from './components/LoadingDots'
5150import { ScrollbarMarkers } from './components/ScrollbarMarkers'
5251import { LastEditorContext } from './context'
52+ import { MOCK_LONG_RESPONSE } from './mockData'
5353
5454interface TranscriptProps {
5555 activeChatContext ?: Context
@@ -97,11 +97,11 @@ export const Transcript: FC<TranscriptProps> = props => {
9797 activeChatContext,
9898 setActiveChatContext,
9999 chatEnabled,
100- transcript,
100+ transcript : originalTranscript ,
101101 tokenUsage,
102102 models,
103103 userInfo,
104- messageInProgress,
104+ messageInProgress : originalMessageInProgress ,
105105 guardrails,
106106 postMessage,
107107 copyButtonOnSubmit,
@@ -110,6 +110,66 @@ export const Transcript: FC<TranscriptProps> = props => {
110110 welcomeContent,
111111 } = props
112112
113+ // Simulation state
114+ const [ isSimulating , setIsSimulating ] = useState ( false )
115+ const [ simulationTranscript , setSimulationTranscript ] = useState < ChatMessage [ ] > ( [ ] )
116+ const [ simulationMessageInProgress , setSimulationMessageInProgress ] = useState < ChatMessage | null > (
117+ null
118+ )
119+
120+ // Use simulation state when active, otherwise use original props
121+ const transcript = isSimulating ? simulationTranscript : originalTranscript
122+ const messageInProgress = isSimulating ? simulationMessageInProgress : originalMessageInProgress
123+
124+ // Simulation function
125+ const startSimulation = useCallback ( ( ) => {
126+ setIsSimulating ( true )
127+
128+ // Create a human message
129+ const humanMessage : ChatMessage = {
130+ speaker : 'human' ,
131+ text : PromptString . unsafe_fromUserQuery (
132+ 'Please help me implement a complex feature with multiple code examples.'
133+ ) ,
134+ intent : 'chat' ,
135+ }
136+
137+ // Set initial transcript with human message
138+ setSimulationTranscript ( [ humanMessage ] )
139+
140+ // Start streaming assistant response
141+ const assistantMessage : ChatMessage = {
142+ speaker : 'assistant' ,
143+ text : PromptString . unsafe_fromLLMResponse ( '' ) ,
144+ intent : 'chat' ,
145+ }
146+
147+ setSimulationMessageInProgress ( assistantMessage )
148+
149+ // Long response with multiple code snippets
150+ const fullResponse = MOCK_LONG_RESPONSE
151+
152+ // Simulate streaming by updating text character by character
153+ let currentIndex = 0
154+ const streamingInterval = setInterval ( ( ) => {
155+ if ( currentIndex < fullResponse . length ) {
156+ const currentText = fullResponse . substring ( 0 , currentIndex + 1 )
157+ setSimulationMessageInProgress ( prev =>
158+ prev ? { ...prev , text : PromptString . unsafe_fromLLMResponse ( currentText ) } : null
159+ )
160+ currentIndex ++
161+ } else {
162+ // Streaming complete
163+ clearInterval ( streamingInterval )
164+ setSimulationTranscript ( prev => [
165+ ...prev ,
166+ { ...assistantMessage , text : PromptString . unsafe_fromLLMResponse ( fullResponse ) } ,
167+ ] )
168+ setSimulationMessageInProgress ( null )
169+ }
170+ } , 0 )
171+ } , [ ] )
172+
113173 const interactions = useMemo (
114174 ( ) => transcriptToInteractionPairs ( transcript , messageInProgress ) ,
115175 [ transcript , messageInProgress ]
@@ -243,6 +303,7 @@ export const Transcript: FC<TranscriptProps> = props => {
243303 ? lastHumanEditorRef
244304 : undefined
245305 }
306+ startSimulation = { startSimulation }
246307 />
247308 )
248309 } ,
@@ -260,6 +321,7 @@ export const Transcript: FC<TranscriptProps> = props => {
260321 smartApply ,
261322 interactions ,
262323 messageInProgress ,
324+ startSimulation ,
263325 ]
264326 )
265327
@@ -381,6 +443,7 @@ interface TranscriptInteractionProps
381443 isLastSentInteraction : boolean
382444 priorAssistantMessageIsLoading : boolean
383445 editorRef ?: React . RefObject < PromptEditorRefAPI | null >
446+ startSimulation : ( ) => void
384447}
385448
386449export type RegeneratingCodeBlockState = {
@@ -406,11 +469,11 @@ const TranscriptInteraction: FC<TranscriptInteractionProps> = memo(props => {
406469 copyButtonOnSubmit,
407470 smartApply,
408471 editorRef : parentEditorRef ,
472+ startSimulation,
409473 } = props
410474
411475 const { activeChatContext, setActiveChatContext } = props
412476 const humanEditorRef = useRef < PromptEditorRefAPI | null > ( null )
413- const lastEditorRef = useContext ( LastEditorContext )
414477 useImperativeHandle ( parentEditorRef , ( ) => humanEditorRef . current )
415478
416479 const [ selectedIntent , setSelectedIntent ] = useState < ChatMessage [ 'intent' ] > ( humanMessage ?. intent )
@@ -423,65 +486,6 @@ const TranscriptInteraction: FC<TranscriptInteractionProps> = memo(props => {
423486 }
424487 } , [ humanMessage , isFirstInteraction , isLastInteraction ] )
425488
426- const onUserAction = useCallback (
427- ( action : 'edit' | 'submit' , manuallySelectedIntent : ChatMessage [ 'intent' ] ) => {
428- // Start the span as soon as the user initiates the action
429- const startMark = performance . mark ( 'startSubmit' )
430- const spanManager = new SpanManager ( 'cody-webview' )
431- const span = spanManager . startSpan ( 'chat-interaction' , {
432- attributes : {
433- sampled : true ,
434- 'render.state' : 'started' ,
435- 'startSubmit.mark' : startMark . startTime ,
436- } ,
437- } )
438-
439- if ( ! span ) {
440- throw new Error ( 'Failed to start span for chat interaction' )
441- }
442-
443- const spanContext = trace . setSpan ( context . active ( ) , span )
444- setActiveChatContext ( spanContext )
445- const currentSpanContext = span . spanContext ( )
446-
447- const traceparent = getTraceparentFromSpanContext ( currentSpanContext )
448-
449- // Serialize the editor value after starting the span
450- const editorValue = humanEditorRef . current ?. getSerializedValue ( )
451- if ( ! editorValue ) {
452- console . error ( 'Failed to serialize editor value' )
453- return
454- }
455-
456- const commonProps = {
457- editorValue,
458- traceparent,
459- manuallySelectedIntent,
460- }
461-
462- if ( action === 'edit' ) {
463- // Remove search context chips from the next input so that the user cannot
464- // reference search results that don't exist anymore.
465- // This is a no-op if the input does not contain any search context chips.
466- // NOTE: Doing this for the penultimate input only seems to suffice because
467- // editing a message earlier in the transcript will clear the conversation
468- // and reset the last input anyway.
469- if ( isLastSentInteraction ) {
470- lastEditorRef . current ?. filterMentions ( item => ! isCodeSearchContextItem ( item ) )
471- }
472- editHumanMessage ( {
473- messageIndexInTranscript : humanMessage . index ,
474- ...commonProps ,
475- } )
476- } else {
477- submitHumanMessage ( {
478- ...commonProps ,
479- } )
480- }
481- } ,
482- [ humanMessage , setActiveChatContext , isLastSentInteraction , lastEditorRef ]
483- )
484-
485489 // Omnibox is enabled if the user is not a dotcom user and the omnibox is enabled
486490 const omniboxEnabled = useOmniBox ( ) && ! userInfo ?. isDotComUser
487491
@@ -639,22 +643,13 @@ const TranscriptInteraction: FC<TranscriptInteractionProps> = memo(props => {
639643
640644 const onHumanMessageSubmit = useCallback (
641645 ( intentOnSubmit : ChatMessage [ 'intent' ] ) => {
642- // Current intent is the last selected intent if any or the current intent of the human message
643- const currentIntent = selectedIntent || humanMessage ?. intent
644- // If no intent on submit provided, use the current intent instead
645- const newIntent = intentOnSubmit === undefined ? currentIntent : intentOnSubmit
646- setSelectedIntent ( newIntent )
647- if ( humanMessage . isUnsentFollowup ) {
648- onUserAction ( 'submit' , newIntent )
649- } else {
650- // Use onUserAction directly with the new intent
651- onUserAction ( 'edit' , newIntent )
652- }
646+ // Start simulation instead of actual submission
647+ startSimulation ( )
653648 // Set the unsent followup flag to false after submitting
654649 // to makes sure the last editor for Agent mode gets reset.
655650 humanMessage . isUnsentFollowup = false
656651 } ,
657- [ humanMessage , onUserAction , selectedIntent ]
652+ [ humanMessage , startSimulation ]
658653 )
659654
660655 const onSelectedFiltersUpdate = useCallback (
@@ -870,28 +865,6 @@ export function editHumanMessage({
870865 } , 50 )
871866}
872867
873- function submitHumanMessage ( {
874- editorValue,
875- manuallySelectedIntent,
876- traceparent,
877- } : {
878- editorValue : SerializedPromptEditorValue
879- manuallySelectedIntent ?: ChatMessage [ 'intent' ]
880- traceparent : string
881- } ) : void {
882- getVSCodeAPI ( ) . postMessage ( {
883- command : 'submit' ,
884- text : editorValue . text ,
885- editorState : editorValue . editorState ,
886- contextItems : editorValue . contextItems . map ( deserializeContextItem ) ,
887- manuallySelectedIntent,
888- traceparent,
889- } )
890- setTimeout ( ( ) => {
891- focusLastHumanMessageEditor ( )
892- } , 50 )
893- }
894-
895868function reevaluateSearchWithSelectedFilters ( {
896869 messageIndexInTranscript,
897870 selectedFilters,
0 commit comments