@@ -2102,7 +2102,19 @@ async function renderClearedConversationForTab(tabId) {
21022102let persistTimer = null ;
21032103let persistTimerTabId = null ;
21042104let visibleStateRefreshPending = false ;
2105+ let visibleStateRefreshPromise = Promise . resolve ( ) ;
2106+ let visibleStateRefreshInProgress = false ;
2107+ let visibleStateRefreshGeneration = 0 ;
21052108let lastVisibleTabChatSnapshot = null ;
2109+
2110+ async function waitForVisibleSidePanelStateRefresh ( ) {
2111+ let pendingRefresh ;
2112+ do {
2113+ pendingRefresh = visibleStateRefreshPromise ;
2114+ await pendingRefresh . catch ( ( ) => { } ) ;
2115+ } while ( pendingRefresh !== visibleStateRefreshPromise ) ;
2116+ }
2117+
21062118function schedulePersist ( ) {
21072119 if ( document . visibilityState === 'hidden' ) return ;
21082120 if ( persistTimer ) clearTimeout ( persistTimer ) ;
@@ -3760,19 +3772,30 @@ async function refreshVisibleSidePanelState() {
37603772 addMessage ( 'system' , t ( 'sp.help_message' ) ) ;
37613773 }
37623774 await restoreActiveRunState ( tabId ) ;
3763- if ( document . visibilityState === 'hidden' || ! sameTabId ( currentTabId , tabId ) ) return ;
3764- await consumePendingContextMenuPrompt ( ) ;
3765- drainQueuedContextMenuPrompts ( ) ;
3775+ return document . visibilityState !== 'hidden' && sameTabId ( currentTabId , tabId ) ;
37663776}
37673777
37683778function requestVisibleSidePanelStateRefresh ( ) {
37693779 if ( document . visibilityState === 'hidden' ) return ;
37703780 visibleStateRefreshPending = true ;
3771- Promise . resolve ( ) . then ( ( ) => {
3772- if ( document . visibilityState === 'hidden' || tabSwitchTransitionId != null ) return ;
3781+ visibleStateRefreshInProgress = true ;
3782+ const refreshGeneration = ++ visibleStateRefreshGeneration ;
3783+ syncSendButtonState ( ) ;
3784+ visibleStateRefreshPromise = visibleStateRefreshPromise . catch ( ( ) => { } ) . then ( async ( ) => {
3785+ if ( document . visibilityState === 'hidden' || tabSwitchTransitionId != null ) return false ;
37733786 visibleStateRefreshPending = false ;
3774- refreshVisibleSidePanelState ( ) . catch ( ( ) => { } ) ;
3787+ return refreshVisibleSidePanelState ( ) ;
37753788 } ) ;
3789+ const refreshPromise = visibleStateRefreshPromise ;
3790+ refreshPromise . finally ( ( ) => {
3791+ if ( refreshGeneration !== visibleStateRefreshGeneration ) return ;
3792+ visibleStateRefreshInProgress = false ;
3793+ syncSendButtonState ( ) ;
3794+ } ) . catch ( ( ) => { } ) ;
3795+ refreshPromise . then ( ( refreshed ) => {
3796+ if ( ! refreshed || refreshPromise !== visibleStateRefreshPromise ) return ;
3797+ consumePendingContextMenuPrompt ( ) . then ( ( ) => drainQueuedContextMenuPrompts ( ) ) . catch ( ( ) => { } ) ;
3798+ } ) . catch ( ( ) => { } ) ;
37763799}
37773800
37783801async function restoreActiveRunState ( tabId = currentTabId ) {
@@ -6253,6 +6276,10 @@ function isOutOfBandSlashDraft(value) {
62536276function syncSendButtonState ( ) {
62546277 if ( ! sendBtn ) return ;
62556278 const draft = normalizeScreenshotCommandText ( inputEl ?. value || '' ) . trim ( ) ;
6279+ if ( visibleStateRefreshPending || visibleStateRefreshInProgress ) {
6280+ sendBtn . disabled = true ;
6281+ return ;
6282+ }
62566283 if ( isAwaitingPlanReviewForTab ( ) ) {
62576284 sendBtn . disabled = true ;
62586285 return ;
@@ -6989,11 +7016,25 @@ async function sendMessage(extraChatParams = {}) {
69897016 : null ;
69907017 delete chatExtraParams . sourceGrounding ;
69917018 if ( sourceGrounding ) chatExtraParams . sourceGrounding = sourceGrounding ;
7019+ await waitForVisibleSidePanelStateRefresh ( ) ;
69927020 stopListening ( ) ;
69937021 let text = inputEl . value . trim ( ) ;
69947022 if ( ! text ) return ;
69957023 const submittedText = text ;
69967024 const tabId = currentTabId ;
7025+ let contextMenuClaimOwned = Boolean ( contextMenuClaim ?. promptId && contextMenuClaim ?. claimantId ) ;
7026+ const releaseOwnedContextMenuClaim = async ( ) => {
7027+ if ( ! contextMenuClaimOwned ) return ;
7028+ contextMenuClaimOwned = false ;
7029+ try {
7030+ await sendToBackground ( 'release_context_menu_prompt_claim' , {
7031+ tabId,
7032+ promptId : contextMenuClaim . promptId ,
7033+ claimantId : contextMenuClaim . claimantId ,
7034+ } ) ;
7035+ } catch { /* the durable lease still expires if release fails */ }
7036+ onContextMenuClaimRejected ?. ( { reason : 'panel-hidden' , retryAfterMs : 250 } ) ;
7037+ } ;
69977038 if ( isConversationClearInProgress ( tabId ) ) return false ;
69987039 const permissionSkipContext = permissionSkipCommandContextForDraft ( tabId , text ) ;
69997040 const requestId = createRunRequestId ( tabId ) ;
@@ -7097,27 +7138,14 @@ async function sendMessage(extraChatParams = {}) {
70977138 && sameTabId ( currentTabId , tabId )
70987139 && sameTabId ( renderedTabId , tabId ) ;
70997140 if ( ! renderToCurrentTab ) {
7141+ await releaseOwnedContextMenuClaim ( ) ;
71007142 if ( text ) saveInputDraftForTab ( tabId , text ) ;
71017143 setTabProcessing ( tabId , false ) ;
71027144 setTabAbortRequested ( tabId , false ) ;
71037145 syncSendButtonState ( ) ;
71047146 return false ;
71057147 }
71067148
7107- let renewedContextMenuClaim = false ;
7108- const releaseRenewedContextMenuClaim = async ( ) => {
7109- if ( ! renewedContextMenuClaim ) return ;
7110- renewedContextMenuClaim = false ;
7111- try {
7112- await sendToBackground ( 'release_context_menu_prompt_claim' , {
7113- tabId,
7114- promptId : contextMenuClaim . promptId ,
7115- claimantId : contextMenuClaim . claimantId ,
7116- } ) ;
7117- } catch { /* the durable lease still expires if release fails */ }
7118- onContextMenuClaimRejected ?. ( { reason : 'panel-hidden' , retryAfterMs : 250 } ) ;
7119- } ;
7120-
71217149 if ( contextMenuClaim ?. promptId && contextMenuClaim ?. claimantId ) {
71227150 let renewedClaim = null ;
71237151 try {
@@ -7129,12 +7157,12 @@ async function sendMessage(extraChatParams = {}) {
71297157 } catch {
71307158 renewedClaim = { claimed : false , reason : 'connection' , retryAfterMs : 1_000 } ;
71317159 }
7132- renewedContextMenuClaim = renewedClaim ?. claimed === true ;
7160+ contextMenuClaimOwned = renewedClaim ?. claimed === true ;
71337161 const claimStillVisible = document . visibilityState !== 'hidden'
71347162 && sameTabId ( currentTabId , tabId )
71357163 && sameTabId ( renderedTabId , tabId ) ;
7136- if ( renewedContextMenuClaim && ! claimStillVisible ) {
7137- await releaseRenewedContextMenuClaim ( ) ;
7164+ if ( contextMenuClaimOwned && ! claimStillVisible ) {
7165+ await releaseOwnedContextMenuClaim ( ) ;
71387166 setTabProcessing ( tabId , false ) ;
71397167 setTabAbortRequested ( tabId , false ) ;
71407168 if ( sameTabId ( currentTabId , tabId ) ) syncSendButtonState ( ) ;
@@ -7156,7 +7184,7 @@ async function sendMessage(extraChatParams = {}) {
71567184 && sameTabId ( currentTabId , tabId )
71577185 && sameTabId ( renderedTabId , tabId ) ;
71587186 if ( ! renderToCurrentTab ) {
7159- await releaseRenewedContextMenuClaim ( ) ;
7187+ await releaseOwnedContextMenuClaim ( ) ;
71607188 if ( text ) saveInputDraftForTab ( tabId , text ) ;
71617189 setTabProcessing ( tabId , false ) ;
71627190 setTabAbortRequested ( tabId , false ) ;
0 commit comments