@@ -43,12 +43,13 @@ import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
4343import { AiHistoryStorage , Conversation , ConversationType } from './AiHistoryStorage.js' ;
4444import { ChangeManager } from './ChangeManager.js' ;
4545import {
46+ type ChatMessage ,
4647 ChatMessageEntity ,
4748 ChatView ,
4849 type ModelChatMessage ,
4950 type Props as ChatViewProps ,
5051 State as ChatViewState ,
51- type Step ,
52+ type Step
5253} from './components/ChatView.js' ;
5354
5455const { html} = Lit ;
@@ -203,7 +204,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
203204 #toggleSearchElementAction: UI . ActionRegistration . Action ;
204205 #contentContainer: HTMLElement ;
205206 #aidaClient: Host . AidaClient . AidaClient ;
206- #viewProps: ChatViewProps ;
207207 #viewOutput: ViewOutput = { } ;
208208 #serverSideLoggingEnabled = isAiAssistanceServerSideLoggingEnabled ( ) ;
209209 #aiAssistanceEnabledSetting: Common . Settings . Setting < boolean > | undefined ;
@@ -228,6 +228,28 @@ export class AiAssistancePanel extends UI.Panel.Panel {
228228 #selectedInsight: InsightContext | null = null ;
229229 #selectedRequest: RequestContext | null = null ;
230230
231+ // Messages displayed in the `ChatView` component.
232+ #messages: ChatMessage [ ] = [ ] ;
233+ // Indicates whether the new conversation context is blocked due to cross-origin restrictions.
234+ // This happens when the conversation's context has a different
235+ // origin than the selected context.
236+ #blockedByCrossOrigin: boolean = false ;
237+ // Whether the UI should show loading or not.
238+ #isLoading: boolean = false ;
239+ // Selected conversation context. The reason we keep this as a
240+ // state field rather than using `#getConversationContext` is that,
241+ // there is a case where the context differs from the selectedElement (or other selected context type).
242+ // Specifically, it allows restoring the previous context when a new selection is cross-origin.
243+ // See `#onContextSelectionChanged` for details.
244+ #selectedContext: ConversationContext < unknown > | null = null ;
245+ // Stores the availability status of the `AidaClient` and the reason for unavailability, if any.
246+ #aidaAvailability: Host . AidaClient . AidaAccessPreconditions ;
247+ // Info of the currently logged in user.
248+ #userInfo: {
249+ accountImage ?: string ,
250+ accountFullName ?: string ,
251+ } ;
252+
231253 constructor ( private view : View = defaultView , { aidaClient, aidaAvailability, syncInfo} : {
232254 aidaClient : Host . AidaClient . AidaClient ,
233255 aidaAvailability : Host . AidaClient . AidaAccessPreconditions ,
@@ -242,31 +264,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
242264 UI . ActionRegistry . ActionRegistry . instance ( ) . getAction ( 'elements.toggle-element-search' ) ;
243265 this . #aidaClient = aidaClient ;
244266 this . #contentContainer = this . contentElement . createChild ( 'div' , 'chat-container' ) ;
245-
246- this . #viewProps = {
247- state : this . #getChatUiState( ) ,
248- aidaAvailability,
249- messages : [ ] ,
250- inspectElementToggled : this . #toggleSearchElementAction. toggled ( ) ,
251- isLoading : false ,
252- onTextSubmit : ( text : string ) => {
253- void this . #startConversation( text ) ;
254- Host . userMetrics . actionTaken ( Host . UserMetrics . Action . AiAssistanceQuerySubmitted ) ;
255- } ,
256- onInspectElementClick : this . #handleSelectElementClick. bind ( this ) ,
257- onFeedbackSubmit : this . #handleFeedbackSubmit. bind ( this ) ,
258- onCancelClick : this . #cancel. bind ( this ) ,
259- onContextClick : this . #handleContextClick. bind ( this ) ,
260- onNewConversation : this . #handleNewChatRequest. bind ( this ) ,
261- canShowFeedbackForm : this . #serverSideLoggingEnabled,
262- userInfo : {
263- accountImage : syncInfo . accountImage ,
264- accountFullName : syncInfo . accountFullName ,
265- } ,
266- selectedContext : null ,
267- blockedByCrossOrigin : false ,
268- stripLinks : false ,
269- isReadOnly : false ,
267+ this . #aidaAvailability = aidaAvailability ;
268+ this . #userInfo = {
269+ accountImage : syncInfo . accountImage ,
270+ accountFullName : syncInfo . accountFullName ,
270271 } ;
271272
272273 this . #conversations = AiHistoryStorage . instance ( ) . getHistory ( ) . map ( item => Conversation . fromSerialized ( item ) ) ;
@@ -437,16 +438,13 @@ export class AiAssistancePanel extends UI.Panel.Panel {
437438 #updateAgentState( agent ?: AiAgent < unknown > ) : void {
438439 if ( this . #currentAgent !== agent ) {
439440 this . #cancel( ) ;
441+ this . #messages = [ ] ;
442+ this . #isLoading = false ;
440443 this . #currentAgent = agent ;
441- this . #viewProps. agentType = this . #currentAgent?. type ;
442- this . #viewProps. messages = [ ] ;
443- this . #viewProps. changeSummary = undefined ;
444- this . #viewProps. isLoading = false ;
445444 if ( this . #currentAgent?. type ) {
446445 this . #currentConversation =
447446 new Conversation ( agentTypeToConversationType ( this . #currentAgent. type ) , [ ] , agent ?. id , false ) ;
448447 this . #conversations. push ( this . #currentConversation) ;
449- this . #viewProps. isReadOnly = false ;
450448 }
451449 }
452450
@@ -460,8 +458,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
460458 this . #viewOutput. chatView ?. focusTextInput ( ) ;
461459 this . #selectDefaultAgentIfNeeded( ) ;
462460 void this . #handleAidaAvailabilityChange( ) ;
463- void this
464- . #handleAiAssistanceEnabledSettingChanged( ) ; // If the setting was switched on/off while the AiAssistancePanel was not shown.
465461 this . #selectedElement =
466462 createNodeContext ( selectedElementFilter ( UI . Context . Context . instance ( ) . flavor ( SDK . DOMModel . DOMNode ) ) ) ,
467463 this . #selectedRequest =
@@ -471,19 +467,12 @@ export class AiAssistancePanel extends UI.Panel.Panel {
471467 this . #selectedInsight =
472468 createPerfInsightContext ( UI . Context . Context . instance ( ) . flavor ( TimelineUtils . InsightAIContext . InsightAIContext ) ) ;
473469 this . #selectedFile = createFileContext ( UI . Context . Context . instance ( ) . flavor ( Workspace . UISourceCode . UISourceCode ) ) ,
474- this . #viewProps = {
475- ...this . #viewProps,
476- agentType : this . #currentAgent?. type ,
477- inspectElementToggled : this . #toggleSearchElementAction. toggled ( ) ,
478- selectedContext : this . #getConversationContext( ) ,
479- } ;
480470 void this . doUpdate ( ) ;
481471
482- this . #aiAssistanceEnabledSetting?. addChangeListener ( this . #handleAiAssistanceEnabledSettingChanged , this ) ;
472+ this . #aiAssistanceEnabledSetting?. addChangeListener ( this . doUpdate , this ) ;
483473 Host . AidaClient . HostConfigTracker . instance ( ) . addEventListener (
484474 Host . AidaClient . Events . AIDA_AVAILABILITY_CHANGED , this . #handleAidaAvailabilityChange) ;
485- this . #toggleSearchElementAction. addEventListener (
486- UI . ActionRegistration . Events . TOGGLED , this . #handleSearchElementActionToggled) ;
475+ this . #toggleSearchElementAction. addEventListener ( UI . ActionRegistration . Events . TOGGLED , this . doUpdate , this ) ;
487476 UI . Context . Context . instance ( ) . addFlavorChangeListener ( SDK . DOMModel . DOMNode , this . #handleDOMNodeFlavorChange) ;
488477 UI . Context . Context . instance ( ) . addFlavorChangeListener (
489478 SDK . NetworkRequest . NetworkRequest , this . #handleNetworkRequestFlavorChange) ;
@@ -507,11 +496,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
507496 }
508497
509498 override willHide ( ) : void {
510- this . #aiAssistanceEnabledSetting?. removeChangeListener ( this . #handleAiAssistanceEnabledSettingChanged , this ) ;
499+ this . #aiAssistanceEnabledSetting?. removeChangeListener ( this . doUpdate , this ) ;
511500 Host . AidaClient . HostConfigTracker . instance ( ) . removeEventListener (
512501 Host . AidaClient . Events . AIDA_AVAILABILITY_CHANGED , this . #handleAidaAvailabilityChange) ;
513- this . #toggleSearchElementAction. removeEventListener (
514- UI . ActionRegistration . Events . TOGGLED , this . #handleSearchElementActionToggled) ;
502+ this . #toggleSearchElementAction. removeEventListener ( UI . ActionRegistration . Events . TOGGLED , this . doUpdate , this ) ;
515503 UI . Context . Context . instance ( ) . removeFlavorChangeListener ( SDK . DOMModel . DOMNode , this . #handleDOMNodeFlavorChange) ;
516504 UI . Context . Context . instance ( ) . removeFlavorChangeListener (
517505 SDK . NetworkRequest . NetworkRequest , this . #handleNetworkRequestFlavorChange) ;
@@ -543,28 +531,18 @@ export class AiAssistancePanel extends UI.Panel.Panel {
543531
544532 #handleAidaAvailabilityChange = async ( ) : Promise < void > => {
545533 const currentAidaAvailability = await Host . AidaClient . AidaClient . checkAccessPreconditions ( ) ;
546- if ( currentAidaAvailability !== this . #viewProps . aidaAvailability ) {
547- this . #viewProps . aidaAvailability = currentAidaAvailability ;
534+ if ( currentAidaAvailability !== this . #aidaAvailability) {
535+ this . #aidaAvailability = currentAidaAvailability ;
548536 const syncInfo = await new Promise < Host . InspectorFrontendHostAPI . SyncInformation > (
549537 resolve => Host . InspectorFrontendHost . InspectorFrontendHostInstance . getSyncInformation ( resolve ) ) ;
550- this . #viewProps . userInfo = {
538+ this . #userInfo = {
551539 accountImage : syncInfo . accountImage ,
552540 accountFullName : syncInfo . accountFullName ,
553541 } ;
554- this . #viewProps. state = this . #getChatUiState( ) ;
555542 void this . doUpdate ( ) ;
556543 }
557544 } ;
558545
559- #handleSearchElementActionToggled = ( ev : Common . EventTarget . EventTargetEvent < boolean > ) : void => {
560- if ( this . #viewProps. inspectElementToggled === ev . data ) {
561- return ;
562- }
563-
564- this . #viewProps. inspectElementToggled = ev . data ;
565- void this . doUpdate ( ) ;
566- } ;
567-
568546 #handleDOMNodeFlavorChange = ( ev : Common . EventTarget . EventTargetEvent < SDK . DOMModel . DOMNode > ) : void => {
569547 if ( this . #selectedElement?. getItem ( ) === ev . data ) {
570548 return ;
@@ -616,19 +594,40 @@ export class AiAssistancePanel extends UI.Panel.Panel {
616594 this . #updateAgentState( this . #currentAgent) ;
617595 } ;
618596
619- #handleAiAssistanceEnabledSettingChanged = ( ) : void => {
620- const nextChatUiState = this . #getChatUiState( ) ;
621- if ( this . #viewProps. state === nextChatUiState ) {
622- return ;
623- }
624-
625- this . #viewProps. state = nextChatUiState ;
626- void this . doUpdate ( ) ;
627- } ;
628-
629597 async doUpdate ( ) : Promise < void > {
630598 this . #updateToolbarState( ) ;
631- this . view ( this . #viewProps, this . #viewOutput, this . #contentContainer) ;
599+ this . view (
600+ {
601+ state : this . #getChatUiState( ) ,
602+ blockedByCrossOrigin : this . #blockedByCrossOrigin,
603+ aidaAvailability : this . #aidaAvailability,
604+ isLoading : this . #isLoading,
605+ messages : this . #messages,
606+ selectedContext : this . #selectedContext,
607+ agentType : this . #currentAgent?. type ,
608+ isReadOnly : this . #currentConversation?. isReadOnly ?? false ,
609+ changeSummary :
610+ ( isAiAssistancePatchingEnabled ( ) && this . #currentAgent && ! this . #currentConversation?. isReadOnly ) ?
611+ this . #changeManager. formatChanges ( this . #currentAgent. id ) :
612+ undefined ,
613+ stripLinks : this . #currentAgent?. type === AgentType . PERFORMANCE ,
614+ inspectElementToggled : this . #toggleSearchElementAction. toggled ( ) ,
615+ userInfo : this . #userInfo,
616+ canShowFeedbackForm : this . #serverSideLoggingEnabled,
617+ onTextSubmit : ( text : string ) => {
618+ void this . #startConversation( text ) ;
619+ Host . userMetrics . actionTaken ( Host . UserMetrics . Action . AiAssistanceQuerySubmitted ) ;
620+ } ,
621+ onInspectElementClick : this . #handleSelectElementClick. bind ( this ) ,
622+ onFeedbackSubmit : this . #handleFeedbackSubmit. bind ( this ) ,
623+ onCancelClick : this . #cancel. bind ( this ) ,
624+ onContextClick : this . #handleContextClick. bind ( this ) ,
625+ onNewConversation : this . #handleNewChatRequest. bind ( this ) ,
626+ onCancelCrossOriginChat : this . #blockedByCrossOrigin && this . #previousSameOriginContext ?
627+ this . #handleCrossOriginChatCancellation. bind ( this ) :
628+ undefined ,
629+ } ,
630+ this . #viewOutput, this . #contentContainer) ;
632631 }
633632
634633 #handleSelectElementClick( ) : void {
@@ -651,7 +650,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
651650 }
652651
653652 #handleContextClick( ) : void | Promise < void > {
654- const context = this . #viewProps . selectedContext ;
653+ const context = this . #selectedContext;
655654 if ( context instanceof RequestContext ) {
656655 const requestLocation = NetworkForward . UIRequestLocation . UIRequestLocation . tab (
657656 context . getItem ( ) , NetworkForward . UIRequestLocation . UIRequestTabs . HEADERS_COMPONENT ) ;
@@ -668,7 +667,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
668667 }
669668
670669 handleAction ( actionId : string ) : void {
671- if ( this . #viewProps . isLoading ) {
670+ if ( this . #isLoading) {
672671 // If running some queries already, focus the input with the abort
673672 // button and do nothing.
674673 this . #viewOutput. chatView ?. focusTextInput ( ) ;
@@ -792,8 +791,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
792791 return ;
793792 }
794793 this . #currentConversation = conversation ;
795- this . #viewProps. messages = [ ] ;
796- this . #viewProps. isReadOnly = this . #currentConversation?. isReadOnly ?? false ;
794+ this . #messages = [ ] ;
797795 await this . #doConversation( conversation . history ) ;
798796 }
799797
@@ -813,29 +811,25 @@ export class AiAssistancePanel extends UI.Panel.Panel {
813811 #runAbortController = new AbortController ( ) ;
814812 #cancel( ) : void {
815813 this . #runAbortController. abort ( ) ;
816- this . #viewProps . isLoading = false ;
814+ this . #isLoading = false ;
817815 void this . doUpdate ( ) ;
818816 }
819817
820818 #onContextSelectionChanged( contextToRestore ?: ConversationContext < unknown > ) : void {
821819 if ( ! this . #currentAgent) {
822- this . #viewProps . blockedByCrossOrigin = false ;
820+ this . #blockedByCrossOrigin = false ;
823821 return ;
824822 }
825823 const currentContext = contextToRestore ?? this . #getConversationContext( ) ;
826- this . #viewProps . selectedContext = currentContext ;
824+ this . #selectedContext = currentContext ;
827825 if ( ! currentContext ) {
828- this . #viewProps . blockedByCrossOrigin = false ;
826+ this . #blockedByCrossOrigin = false ;
829827 return ;
830828 }
831- this . #viewProps . blockedByCrossOrigin = ! currentContext . isOriginAllowed ( this . #currentAgent. origin ) ;
832- if ( ! this . #viewProps . blockedByCrossOrigin ) {
829+ this . #blockedByCrossOrigin = ! currentContext . isOriginAllowed ( this . #currentAgent. origin ) ;
830+ if ( ! this . #blockedByCrossOrigin) {
833831 this . #previousSameOriginContext = currentContext ;
834832 }
835- if ( this . #viewProps. blockedByCrossOrigin && this . #previousSameOriginContext) {
836- this . #viewProps. onCancelCrossOriginChat = this . #handleCrossOriginChatCancellation. bind ( this ) ;
837- }
838- this . #viewProps. stripLinks = this . #viewProps. agentType === AgentType . PERFORMANCE ;
839833 }
840834
841835 #getConversationContext( ) : ConversationContext < unknown > | null {
@@ -917,20 +911,20 @@ export class AiAssistancePanel extends UI.Panel.Panel {
917911 }
918912 }
919913
920- this . #viewProps . isLoading = true ;
914+ this . #isLoading = true ;
921915 for await ( const data of items ) {
922916 step . sideEffect = undefined ;
923917 switch ( data . type ) {
924918 case ResponseType . USER_QUERY : {
925- this . #viewProps . messages . push ( {
919+ this . #messages. push ( {
926920 entity : ChatMessageEntity . USER ,
927921 text : data . query ,
928922 } ) ;
929923 systemMessage = {
930924 entity : ChatMessageEntity . MODEL ,
931925 steps : [ ] ,
932926 } ;
933- this . #viewProps . messages . push ( systemMessage ) ;
927+ this . #messages. push ( systemMessage ) ;
934928 break ;
935929 }
936930 case ResponseType . QUERYING : {
@@ -981,9 +975,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
981975 step . code ??= data . code ;
982976 step . output ??= data . output ;
983977 step . canceled = data . canceled ;
984- if ( isAiAssistancePatchingEnabled ( ) && this . #currentAgent && ! this . #currentConversation?. isReadOnly ) {
985- this . #viewProps. changeSummary = this . #changeManager. formatChanges ( this . #currentAgent. id ) ;
986- }
987978 commitStep ( ) ;
988979 break ;
989980 }
@@ -1019,7 +1010,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
10191010
10201011 // Commit update intermediated step when not
10211012 // in read only mode.
1022- if ( ! this . #viewProps . isReadOnly ) {
1013+ if ( ! this . #currentConversation ? .isReadOnly ) {
10231014 void this . doUpdate ( ) ;
10241015
10251016 // This handles scrolling to the bottom for live conversations when:
@@ -1031,7 +1022,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
10311022 }
10321023 }
10331024
1034- this . #viewProps . isLoading = false ;
1025+ this . #isLoading = false ;
10351026 this . #viewOutput. chatView ?. finishTextAnimations ( ) ;
10361027 void this . doUpdate ( ) ;
10371028 } finally {
0 commit comments