|
3851 | 3851 | _termuxSessionArmed = false; |
3852 | 3852 | } |
3853 | 3853 |
|
| 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 | + |
3854 | 3884 | async function _executeCommandsFromResponse(text) { |
3855 | 3885 | _stopExecution = false; |
3856 | 3886 | // Reset batch answer collection for each new AI response so answers from a previous |
|
3868 | 3898 | if (window.onGenerationStateChanged) window.onGenerationStateChanged(false, false); |
3869 | 3899 | // Clear any leftover commands bubble from the previous message |
3870 | 3900 | 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(); |
3871 | 3904 | return; |
3872 | 3905 | } |
3873 | 3906 |
|
|
3980 | 4013 | _stopExecution = true; |
3981 | 4014 | generationRunning = false; |
3982 | 4015 | if (window.onGenerationStateChanged) window.onGenerationStateChanged(false, false); |
| 4016 | + _showAiStoppedToastInBackground(); |
3983 | 4017 | break; |
3984 | 4018 | default: |
3985 | 4019 | console.log('[ScreenOperator] Unknown command type:', cmd.type, cmd); |
|
0 commit comments