fix(suggestion): paste after the shortcut keys are released and keep typing detection stable
Ctrl+Alt+Enter pasted while Ctrl+Alt were still down, so the target app got Ctrl+Alt+V; accepting now closes the panel and waits for the modifiers to be released. Candidates are accepted on pointer press because the list is redrawn as new candidates stream in, which swallowed clicks. The typing gate identified the focused field by its bounds, so chat boxes that grow while typing looked like a new field on every keystroke and were reported as "not typing". Fields are now keyed by window, control type and name, and a mouse click re-baselines the text instead. The decision log includes both gate values. The live-caption model selector moves to the caption section of the General tab, next to the other caption settings.
This commit is contained in:
parent
4b0f685941
commit
da0da98285
8 changed files with 144 additions and 55 deletions
|
|
@ -165,6 +165,8 @@ class InputTelemetryService extends EventEmitter {
|
|||
private _lastFocusKey = ''
|
||||
/** 현재 포커스에 들어왔을 때의 텍스트 (비밀번호는 저장하지 않는다) — 편집 여부 판정 기준선. */
|
||||
private _textAtFocus = ''
|
||||
/** 클릭 뒤 첫 스냅샷에서 기준선을 다시 잡는다 */
|
||||
private _rebaseFocusText = false
|
||||
private _lastActiveTickAt = 0
|
||||
private _lastMouse: { x: number; y: number } | null = null
|
||||
private _lastClickAt = 0
|
||||
|
|
@ -407,6 +409,8 @@ class InputTelemetryService extends EventEmitter {
|
|||
this._lastClickAt = now
|
||||
this._lastClickButton = button
|
||||
this._lastClickPos = position
|
||||
// 클릭은 다른 칸으로 옮겨 갔을 수 있다 — 다음 스냅샷의 텍스트를 새 기준으로 삼는다.
|
||||
this._rebaseFocusText = true
|
||||
}
|
||||
|
||||
private _handleMouseUp(event: UiohookMouseEvent): void {
|
||||
|
|
@ -607,15 +611,16 @@ class InputTelemetryService extends EventEmitter {
|
|||
: tail
|
||||
this._caretFallback = !caretKnown
|
||||
|
||||
// 포커스 식별: 바뀌었으면 그 시점 텍스트를 기준선으로 저장한다(비밀번호는 비운다).
|
||||
// 마우스로 필드에 들어오기만 하고 아무것도 안 쳤으면, 이 기준선과 현재 텍스트가
|
||||
// 같아 editedSinceFocus 가 false 로 남는다(실측: YouTube 검색창 클릭만 했는데
|
||||
// 옛 검색어로 제안이 뜸).
|
||||
const focusKey = computeFocusKey(this._foreground.hwnd, snapshot.controlType ?? null, snapshot.elementRect)
|
||||
// 포커스 식별: 칸이 바뀌었거나 클릭이 있었으면 그 시점 텍스트를 기준선으로 저장한다
|
||||
// (비밀번호는 비운다). 마우스로 칸에 들어오기만 하고 아무것도 안 쳤으면 기준선과
|
||||
// 현재 텍스트가 같아 editedSinceFocus 가 false 로 남는다(실측: YouTube 검색창 클릭만
|
||||
// 했는데 옛 검색어로 제안이 뜸).
|
||||
const focusKey = computeFocusKey(this._foreground.hwnd, snapshot.controlType ?? null, snapshot.controlName ?? null)
|
||||
const textForFocus = snapshot.isPassword ? '' : snapshot.text
|
||||
if (focusKey !== this._lastFocusKey) {
|
||||
if (focusKey !== this._lastFocusKey || this._rebaseFocusText) {
|
||||
this._lastFocusKey = focusKey
|
||||
this._textAtFocus = textForFocus
|
||||
this._rebaseFocusText = false
|
||||
}
|
||||
context.editedSinceFocus = textForFocus !== this._textAtFocus
|
||||
|
||||
|
|
@ -1385,16 +1390,14 @@ function lastLineOf(text: string): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 포커스된 컨트롤 식별 키 — 창(hwnd) + 컨트롤 종류 + 위치.
|
||||
* 포커스된 컨트롤 식별 키 — 창(hwnd) + 컨트롤 종류 + 컨트롤 이름.
|
||||
*
|
||||
* 높이는 뺀다 — 여러 줄 입력창은 타이핑에 따라 높이가 자라, 높이를 포함하면
|
||||
* 같은 필드인데도 칠 때마다 "새 포커스" 로 오인돼 기준선이 계속 리셋된다.
|
||||
* 위치·크기는 쓰지 않는다 — 채팅 입력창은 글이 늘면 자라고 움직여, 넣으면 같은 칸인데도
|
||||
* 칠 때마다 "새 칸" 으로 오인돼 기준선이 리셋되고 제안이 거의 막혔다(실측: Agent
|
||||
* Switchboard 에서 not-typing 반복). 같은 창 안의 칸 이동은 클릭 재기준으로 잡는다.
|
||||
*/
|
||||
function computeFocusKey(hwnd: number | null, controlType: string | null, elementRect: UiRect | null): string {
|
||||
const rect = elementRect
|
||||
? `${Math.round(elementRect.x)},${Math.round(elementRect.y)},${Math.round(elementRect.width)}`
|
||||
: 'none'
|
||||
return `${hwnd ?? ''}|${controlType ?? ''}|${rect}`
|
||||
function computeFocusKey(hwnd: number | null, controlType: string | null, controlName: string | null): string {
|
||||
return `${hwnd ?? ''}|${controlType ?? ''}|${controlName ?? ''}`
|
||||
}
|
||||
|
||||
function formatLocalDate(date: Date): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue