@@ -117,6 +117,14 @@ const UIStringsNotTranslate = {
117
117
learnMoreButton : 'Learn more about auto annotations' ,
118
118
} as const ;
119
119
120
+ const enum AIButtonState {
121
+ ENABLED = 'enabled' ,
122
+ DISABLED = 'disabled' ,
123
+ HIDDEN = 'hidden' ,
124
+ GENERATION_FAILED = 'generation_failed' ,
125
+ GENERATING_LABEL = 'generating_label' ,
126
+ }
127
+
120
128
const str_ = i18n . i18n . registerUIStrings ( 'panels/timeline/overlays/components/EntryLabelOverlay.ts' , UIStrings ) ;
121
129
const i18nString = i18n . i18n . getLocalizedString . bind ( undefined , str_ ) ;
122
130
const lockedString = i18n . i18n . lockedString ;
@@ -201,10 +209,8 @@ export class EntryLabelOverlay extends HTMLElement {
201
209
* consented, hopefully!
202
210
*/
203
211
#inAIConsentDialogFlow = false ;
204
- // Keep track of the AI label loading state to render a loading component if the label is being generated
205
- #isAILabelLoading = false ;
206
- // Set to true when the generation of an AI label failed so we know when to display the 'generation failed' disclaimer
207
- #isAILabelGenerationFailed = false ;
212
+ #currAIButtonState: AIButtonState = AIButtonState . HIDDEN ;
213
+
208
214
/**
209
215
* The entry label overlay consists of 3 parts - the label part with the label string inside,
210
216
* the line connecting the label to the entry, and a black box around an entry to highlight the entry with a label.
@@ -507,6 +513,8 @@ export class EntryLabelOverlay extends HTMLElement {
507
513
508
514
set callTree ( callTree : Utils . AICallTree . AICallTree | null ) {
509
515
this . #callTree = callTree ;
516
+ // If the entry has a calltree, we need to check if we need to show the 'generate label' button.
517
+ this . #setAIButtonRenderState( ) ;
510
518
}
511
519
512
520
// Generate the AI label suggestion if:
@@ -523,7 +531,7 @@ export class EntryLabelOverlay extends HTMLElement {
523
531
}
524
532
try {
525
533
// Trigger a re-render to display the loading component in the place of the button when the label is being generated.
526
- this . #isAILabelLoading = true ;
534
+ this . #currAIButtonState = AIButtonState . GENERATING_LABEL ;
527
535
UI . ARIAUtils . alert ( UIStringsNotTranslate . generatingLabel ) ;
528
536
// Trigger a re-render to put focus back on the input box, otherwise
529
537
// when the button changes to a loading spinner, it loses focus and the
@@ -535,17 +543,16 @@ export class EntryLabelOverlay extends HTMLElement {
535
543
this . #label = await this . #agent. generateAIEntryLabel ( this . #callTree) ;
536
544
this . dispatchEvent ( new EntryLabelChangeEvent ( this . #label) ) ;
537
545
this . #inputField. innerText = this . #label;
538
-
539
- this . #isAILabelLoading = false ;
546
+ this . #placeCursorAtInputEnd( ) ;
547
+ // Reset the button state because we want to hide it if the label is not empty.
548
+ this . #setAIButtonRenderState( ) ;
540
549
// Trigger a re-render to hide the AI Button and display the generated label.
541
550
this . #render( ) ;
542
551
} catch {
543
- this . #isAILabelLoading = false ;
544
- this . #isAILabelGenerationFailed = true ;
552
+ this . #currAIButtonState = AIButtonState . GENERATION_FAILED ;
545
553
void ComponentHelpers . ScheduledRender . scheduleRender ( this , this . #boundRender) ;
546
554
}
547
555
} else {
548
- this . #isAILabelLoading = false ;
549
556
this . #inAIConsentDialogFlow = true ;
550
557
this . #render( ) ;
551
558
const hasConsented = await this . #showUserAiFirstRunDialog( ) ;
@@ -610,7 +617,7 @@ export class EntryLabelOverlay extends HTMLElement {
610
617
return this . #aiAnnotationsEnabledSetting. get ( ) ;
611
618
}
612
619
613
- #shouldRenderAIButton ( ) : 'enabled' | 'disabled' | 'hidden' {
620
+ #setAIButtonRenderState ( ) : void {
614
621
const hasAiExperiment = Boolean ( Root . Runtime . hostConfig . devToolsAiGeneratedTimelineLabels ?. enabled ) ;
615
622
const aiDisabledByEnterprisePolicy = Root . Runtime . hostConfig . aidaAvailability ?. enterprisePolicyValue ===
616
623
Root . Runtime . GenAiEnterprisePolicyValue . DISABLE ;
@@ -625,24 +632,19 @@ export class EntryLabelOverlay extends HTMLElement {
625
632
const labelIsEmpty = this . #label?. length <= 0 ;
626
633
627
634
if ( ! hasAiExperiment || aiDisabledByEnterprisePolicy || ! dataToGenerateLabelAvailable || ! labelIsEmpty ) {
628
- return 'hidden' ;
629
- }
630
-
631
- // To verify whether AI can be used, check if the user is logged in, over 18, in a supported
632
- // location and offline. If the user is not logged in, `blockedByAge` will return true.
633
- const aiAvailable = ! Root . Runtime . hostConfig . aidaAvailability ?. blockedByAge &&
634
- ! Root . Runtime . hostConfig . aidaAvailability ?. blockedByGeo && ! navigator . onLine === false ;
635
- if ( aiAvailable ) {
636
- return 'enabled' ;
637
- }
638
-
639
- // If AI features are not available, we show a disabled button.
640
- if ( ! aiAvailable ) {
641
- return 'disabled' ;
635
+ this . #currAIButtonState = AIButtonState . HIDDEN ;
636
+ } else {
637
+ // To verify whether AI can be used, check if the user is logged in, over 18, in a supported
638
+ // location and offline. If the user is not logged in, `blockedByAge` will return true.
639
+ const aiAvailable = ! Root . Runtime . hostConfig . aidaAvailability ?. blockedByAge &&
640
+ ! Root . Runtime . hostConfig . aidaAvailability ?. blockedByGeo && ! navigator . onLine === false ;
641
+ if ( aiAvailable ) {
642
+ this . #currAIButtonState = AIButtonState . ENABLED ;
643
+ } else {
644
+ // If AI features are not available, we show a disabled button.
645
+ this . #currAIButtonState = AIButtonState . DISABLED ;
646
+ }
642
647
}
643
-
644
- console . error ( '\'Generate label\' button is hidden for an unknown reason' ) ;
645
- return 'hidden' ;
646
648
}
647
649
648
650
#renderAITooltip( opts : { textContent : string , includeSettingsButton : boolean } ) : Lit . TemplateResult {
@@ -668,26 +670,24 @@ export class EntryLabelOverlay extends HTMLElement {
668
670
</ devtools-tooltip > ` ;
669
671
// clang-format on
670
672
}
671
-
672
- #renderAiButton( ) : Lit . LitTemplate {
673
- const noLogging = Root . Runtime . hostConfig . aidaAvailability ?. enterprisePolicyValue ===
674
- Root . Runtime . GenAiEnterprisePolicyValue . ALLOW_WITHOUT_LOGGING ;
675
-
676
- if ( this . #isAILabelLoading) {
677
- // clang-format off
678
- return html `
673
+ #renderGeneratingLabelAiButton( ) : Lit . LitTemplate {
674
+ // clang-format off
675
+ return html `
679
676
< span
680
677
class ="ai-label-loading ">
681
678
< devtools-spinner > </ devtools-spinner >
682
679
< span class ="generate-label-text "> ${ lockedString ( UIStringsNotTranslate . generatingLabel ) } </ span >
683
680
</ span >
684
681
` ;
685
- // clang-format on
686
- }
682
+ // clang-format on
683
+ }
687
684
688
- if ( this . #isAILabelGenerationFailed) {
685
+ #renderAiButton( ) : Lit . LitTemplate {
686
+ const noLogging = Root . Runtime . hostConfig . aidaAvailability ?. enterprisePolicyValue ===
687
+ Root . Runtime . GenAiEnterprisePolicyValue . ALLOW_WITHOUT_LOGGING ;
688
+
689
+ if ( this . #currAIButtonState === AIButtonState . GENERATION_FAILED ) {
689
690
// Only show the error message on the first component render render after the failure.
690
- this . #isAILabelGenerationFailed = false ;
691
691
// clang-format off
692
692
return html `
693
693
< span
@@ -830,6 +830,7 @@ export class EntryLabelOverlay extends HTMLElement {
830
830
@paste=${ this . #handleLabelInputPaste}
831
831
@keyup=${ ( ) => {
832
832
this . #handleLabelInputKeyUp( ) ;
833
+ this . #setAIButtonRenderState( ) ;
833
834
// Rerender the label component when the label text changes because we need to
834
835
// make sure the 'auto annotation' button is only shown when the label is empty.
835
836
this . #render( ) ;
@@ -839,12 +840,16 @@ export class EntryLabelOverlay extends HTMLElement {
839
840
tabindex="0"
840
841
> </ span >
841
842
${ ( ( ) => {
842
- switch ( this . #shouldRenderAIButton ( ) ) {
843
- case 'hidden' :
843
+ switch ( this . #currAIButtonState ) {
844
+ case AIButtonState . HIDDEN :
844
845
return Lit . nothing ;
845
- case 'enabled' :
846
+ case AIButtonState . ENABLED :
847
+ return this . #renderAiButton( ) ;
848
+ case AIButtonState . GENERATING_LABEL :
849
+ return this . #renderGeneratingLabelAiButton( ) ;
850
+ case AIButtonState . GENERATION_FAILED :
846
851
return this . #renderAiButton( ) ;
847
- case 'disabled' :
852
+ case AIButtonState . DISABLED :
848
853
return this . #renderDisabledAiButton( ) ;
849
854
}
850
855
} ) ( ) }
0 commit comments