mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-17 18:25:42 +02:00
[eric] dictation: a field that dies mid-decode drops the words instead of typing them into a stranger's box
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { getLastInteractedBrowser } from '@/shared/browserFocus';
|
||||
import { getWebview } from '@/shared/browserRegistry';
|
||||
import { takeInjectSnapshot, setInjectSnapshot } from './injectTargetSnapshot';
|
||||
import { takeInjectSnapshot, setInjectSnapshot, isUsableTarget } from './injectTargetSnapshot';
|
||||
|
||||
// Dictation lands where the user's cursor actually is, like every real dictation tool: a focused
|
||||
// in-app field gets the text typed in (undo-friendly, fires React input events), a focused browser
|
||||
@@ -9,6 +9,9 @@ export type InjectTarget = 'field' | 'webview' | 'composer' | null;
|
||||
|
||||
export function injectAtFocus(text: string): InjectTarget {
|
||||
const snap = takeInjectSnapshot();
|
||||
// The field the user dictated into is gone. Whatever holds focus now is a stranger's box, and
|
||||
// typing their words into it is worse than dropping them, so drop them.
|
||||
if (snap.targetLost && !snap.browserId) return null;
|
||||
const active = snap.el || (document.activeElement as HTMLElement | null);
|
||||
if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.isContentEditable)) {
|
||||
try {
|
||||
@@ -49,8 +52,11 @@ export function injectAtFocus(text: string): InjectTarget {
|
||||
|
||||
/** Called at press-start so the words land where the user was looking, not where focus drifted. */
|
||||
export function snapshotInjectTarget(): void {
|
||||
// Only a typeable element counts as "aimed at". document.activeElement is <body> when nothing is
|
||||
// focused, and storing that would read as a lost target later and swallow the composer fallback.
|
||||
const active = document.activeElement as HTMLElement | null;
|
||||
setInjectSnapshot({
|
||||
el: document.activeElement as HTMLElement | null,
|
||||
el: isUsableTarget(active) ? active : null,
|
||||
browserId: getLastInteractedBrowser(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,10 +21,16 @@ export function isUsableTarget(el: HTMLElement | null): boolean {
|
||||
return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'WEBVIEW' || el.isContentEditable;
|
||||
}
|
||||
|
||||
export interface TakenSnapshot extends InjectSnapshot {
|
||||
/** We aimed at a real field and it died mid-decode. Distinct from never having aimed anywhere. */
|
||||
targetLost: boolean;
|
||||
}
|
||||
|
||||
/** Consumes the snapshot: reading it once is the whole contract, so a stale one can never linger. */
|
||||
export function takeInjectSnapshot(): InjectSnapshot {
|
||||
export function takeInjectSnapshot(): TakenSnapshot {
|
||||
const snap = p_snapshot;
|
||||
p_snapshot = null;
|
||||
if (!snap) return { el: null, browserId: null };
|
||||
return { el: isUsableTarget(snap.el) ? snap.el : null, browserId: snap.browserId };
|
||||
if (!snap) return { el: null, browserId: null, targetLost: false };
|
||||
const usable = isUsableTarget(snap.el);
|
||||
return { el: usable ? snap.el : null, browserId: snap.browserId, targetLost: !!snap.el && !usable };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user