Skip to content

Commit 2201f45

Browse files
feat: show 'The AI stopped Screen Operator' toast when AI ends turn in background
The native toast (UiStringsConfig id toast_ai_stopped_app, used by PhotoReasoningCommandUiNotifier) existed but was never triggered from the WebView. It now fires through the generic WebViewBridge.showToast channel when the app is not in the foreground and the AI ends the turn on its own: - response without any recognized command (unless a popUp() question is still awaiting the user's answer - a pause for input, not a stop) - completed() Toast text honors ui-strings-overrides.json like the native call site.
1 parent 85611f6 commit 2201f45

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3851,6 +3851,36 @@
38513851
_termuxSessionArmed = false;
38523852
}
38533853

3854+
/* ── "The AI stopped Screen Operator" background toast ───── */
3855+
// The toast itself is the native one (PhotoReasoningCommandUiNotifier / UiStringsConfig id
3856+
// "toast_ai_stopped_app"), reached through the generic WebViewBridge.showToast channel - the
3857+
// WebView only decides WHEN it fires: the AI ended the turn on its own (a response without
3858+
// any command, or completed()) while the app is not in the foreground, so the in-page
3859+
// generation-state change cannot be seen.
3860+
const _AI_STOPPED_TOAST_ID = 'toast_ai_stopped_app';
3861+
3862+
function _aiStoppedToastText() {
3863+
// Mirror of UiStringsConfig.get(id, default): an optional ui-strings-overrides.json value
3864+
// wins, non-blank strings only, otherwise the built-in default documented in
3865+
// DEFAULT_UI_STRINGS.
3866+
const fallback = DEFAULT_UI_STRINGS[_AI_STOPPED_TOAST_ID];
3867+
try {
3868+
if (getInAndroid() && typeof Android.getUiStringsOverrides === 'function') {
3869+
const override = JSON.parse(Android.getUiStringsOverrides() || '{}')[_AI_STOPPED_TOAST_ID];
3870+
if (typeof override === 'string' && override.trim()) return override;
3871+
}
3872+
} catch(e) { /* fall through to built-in default */ }
3873+
return fallback;
3874+
}
3875+
3876+
function _showAiStoppedToastInBackground() {
3877+
if (!getInAndroid()) return;
3878+
try {
3879+
if (Bridge.isAppInForeground()) return;
3880+
Bridge.showToast(_aiStoppedToastText(), false);
3881+
} catch(e) { console.warn('[ScreenOperator] AI-stopped toast failed', e); }
3882+
}
3883+
38543884
async function _executeCommandsFromResponse(text) {
38553885
_stopExecution = false;
38563886
// Reset batch answer collection for each new AI response so answers from a previous
@@ -3868,6 +3898,9 @@
38683898
if (window.onGenerationStateChanged) window.onGenerationStateChanged(false, false);
38693899
// Clear any leftover commands bubble from the previous message
38703900
if (window.onCommandStatus) window.onCommandStatus('');
3901+
// The AI stopped the turn - unless a popUp() question from this response is still
3902+
// waiting for the user's answer (that is a pause for input, not a stop).
3903+
if (!_activeAiQuestion && !_aiQuestionQueue.length) _showAiStoppedToastInBackground();
38713904
return;
38723905
}
38733906

@@ -3980,6 +4013,7 @@
39804013
_stopExecution = true;
39814014
generationRunning = false;
39824015
if (window.onGenerationStateChanged) window.onGenerationStateChanged(false, false);
4016+
_showAiStoppedToastInBackground();
39834017
break;
39844018
default:
39854019
console.log('[ScreenOperator] Unknown command type:', cmd.type, cmd);

0 commit comments

Comments
 (0)