fix(suggestion): keep a shown suggestion while the user picks it, and suggest in terminals
A candidate list was cleared about two seconds after it appeared: once typing paused, the not-typing rule dismissed the visible overlay, so a click or Ctrl+Alt+Enter found nothing to insert. The rule now only stops new requests; a visible overlay stays until it is accepted, dismissed or goes stale. Terminals were excluded from suggestions because the whole screen buffer reads as the input and the last line is usually a status bar. The input line is now extracted (Claude Code/Codex >, starship, PowerShell, cmd and POSIX prompts, wrapped continuation lines) and used as the prefix; no prompt means no suggestion. Terminals stay out of phrase learning. Accepting with nothing shown and successful inserts are now logged.
This commit is contained in:
parent
0273c6abaa
commit
7e0c20b510
3 changed files with 143 additions and 5 deletions
|
|
@ -24,7 +24,8 @@ import {
|
|||
parseSuggestionCandidates,
|
||||
sanitizeSuggestionLine,
|
||||
selectPhraseHints,
|
||||
TERMINAL_APPS,
|
||||
isTerminalApp,
|
||||
extractTerminalPromptPrefix,
|
||||
type AnchorKind,
|
||||
type SuggestionCandidate,
|
||||
type SuggestionProvenance,
|
||||
|
|
@ -335,7 +336,11 @@ class SuggestionService extends EventEmitter {
|
|||
*
|
||||
* 정책 판단 → (stale 정리) → 필요하면 생성.
|
||||
*/
|
||||
handleTypingContext(context: TypingContext): void {
|
||||
handleTypingContext(input: TypingContext): void {
|
||||
// 터미널은 화면 전체가 읽힌다 — 입력 줄만 접두로 쓴다. 입력 줄이 없으면 빈 접두(제안 안 함).
|
||||
const context: TypingContext = isTerminalApp(input.appName)
|
||||
? { ...input, prefix: extractTerminalPromptPrefix(input.fullText ?? input.prefix) ?? '' }
|
||||
: input
|
||||
this._lastContext = context
|
||||
const now = Date.now()
|
||||
this._lastContextAt = now
|
||||
|
|
@ -1247,7 +1252,11 @@ class SuggestionService extends EventEmitter {
|
|||
this._activeIndex = index
|
||||
}
|
||||
const candidate = this._candidates[this._activeIndex]
|
||||
if (!candidate) return { ok: false, reason: 'already-visible' }
|
||||
if (!candidate) {
|
||||
// 창이 이미 닫힌 뒤의 클릭·단축키 — 조용히 끝나면 "골라도 안 들어간다" 의 원인이 안 보인다.
|
||||
logger.info(`제안 수락 무시: 표시 중인 후보 없음 (index=${index ?? 'active'})`)
|
||||
return { ok: false, reason: 'already-visible' }
|
||||
}
|
||||
const text = candidate.text
|
||||
const appName = this._appName
|
||||
const windowTitle = this._windowTitle
|
||||
|
|
@ -1277,6 +1286,7 @@ class SuggestionService extends EventEmitter {
|
|||
return { ok: false, reason: 'generation-failed' }
|
||||
}
|
||||
|
||||
logger.info(`제안 수락: ${text.length}자 삽입 (method=${method === 'keyboard' ? 'keyboard' : 'clipboard'}, app=${appName ?? '-'})`)
|
||||
this._markAccepted(text)
|
||||
// 수락한 문장은 사용자 문체의 확실한 표본이다 (학습 동의 시에만 저장됨).
|
||||
getInputTelemetryService().recordExternalText(text, { appName, windowTitle, source: 'suggestion' })
|
||||
|
|
@ -1331,7 +1341,8 @@ class SuggestionService extends EventEmitter {
|
|||
maxCandidates: SUGGESTION_DEFAULTS.maxCandidates,
|
||||
maxChars: SUGGESTION_MAX_OUTPUT_CHARS,
|
||||
// 터미널은 셸 프롬프트라 문장 제안이 의미 없다 — 사용자 목록과 무관하게 뺀다.
|
||||
excludedApps: [...configGet('inputExcludedApps'), ...TERMINAL_APPS]
|
||||
// 터미널도 제안한다(입력 줄만 추출). 학습 제외는 InputTelemetryService 가 따로 한다.
|
||||
excludedApps: [...configGet('inputExcludedApps')]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue