AI inline suggest / ghost text

mandatory

Copilot-style ghost text autocomplete in any Koder editor surface. Dimmed inline suggestion + Tab/→ to accept + Esc to discard + partial-accept (word/line). Debounce 300ms. Sandbox-isolated streaming from services/ai/gateway. Required for Kode editor, Kortex notebook, future Kanvas.

Spec — AI inline suggest / ghost text

Pattern: GitHub Copilot ghost text + VS Code Next Edit Suggestions. Streams via streaming-text.kmd. Cost transparency via cost-display.kmd (#112).

Princípios

  1. *ubtle by default*— dimmed (50% opacity) ghost text inline; non-intrusive.
  2. *ast acceptance*— Tab/→ accepts full; Cmd+→ word; Cmd+Shift+→ line.
  3. *asy dismiss*— Esc OR any keystroke (other than accept keys) cancels.
  4. *ebounced trigger*— 300ms default after typing pauses.
  5. *ost-aware*— every suggestion has token attribution; surfaced in status bar.

R1 — Visual

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)█  ← ghost suggestion (dimmed)
                                                ▲
                                              cursor
  • Color: text at 50% opacity (per themes/color-roles.kmd).
  • Font: same as editor body (preserve metrics).
  • Background: NONE (transparent overlay).
  • Underline: NONE.

When cursor moves through ghost → ghost text moves accordingly (anchored to cursor position).

R2 — Trigger

Condition Action
User types → pause 300ms Trigger suggestion request
User keeps typing Cancel pending request
Cursor moves Cancel current ghost
Window blurs Cancel current ghost

Debounce configurable peruser (200500ms range).

R3 — Accept modes

Key Action
Tab Accept full suggestion
→ (arrow right) Accept full (alternative — configurable per-OS)
Cmd/Ctrl + → Accept next word
Cmd/Ctrl + Shift + → Accept next line
Esc Discard suggestion
Any printable key Discard + new input

Partial accept advances cursor; remaining ghost stays.

R4 — Backend integration

Request via services/ai/gateway:

{
  "type": "inline_suggest",
  "prefix": "...lines before cursor...",
  "suffix": "...lines after cursor...",
  "language": "python",
  "file_path": "lib/math.py",
  "max_tokens": 128,
  "stop_sequences": ["\n\n"]
}

Stream response tokenbytoken (crosslink `streamingtext.kmd`):

  • First token within ~500ms (model SLA — surface in cost-display).
  • Display starts rendering at first token.
  • Cancel on any user keystroke after first token already shown.

R5 — Cost transparency

Per-suggestion cost surfaced in editor status bar:

[Editor] · 12 lines · 250 in/45 out · $0.0008

Aggregate session cost via cost-display.kmd (#112).

R6 — Surface enable/disable

User toggle in editor settings:

  • *nabled*(default): ghost suggestions appear.
  • *isabled* no requests sent.
  • *anual trigger*(Ctrl+Space): only on demand.

Workspacelevel + userlevel + perfile setting; perfile > workspace > user > stack default.

R7 — Context window

Gateway receives prefix + suffix:

  • Prefix: last N lines before cursor (default 50).
  • Suffix: next M lines after cursor (default 10).
  • File path + language hint per LSP protocol.

Truncated by model context limit — surface warning if file too large for current model.

R8 — Surface bindings

Surface API
Flutter KoderGhostTextField({onSuggestionAccept, model, gateway}) em koder_kit/lib/src/ai/ghost_text_field.dart
Web <koder-ghost-text-input> (CodeMirror/Monaco extension recommended)
Compose/SwiftUI futuro
Vim / Emacs LSP server (koder-lsp-suggest)

R9 — Acessibilidade

  • Ghost text: aria-hidden="true" (não anuncia continuously); accept = announce "Suggestion accepted: text".
  • Keyboard shortcuts: documented in aria-keyshortcuts.
  • Reducedmotion: ghost appears instant (no fadein).
  • Screen reader users: configurable mode "announce all suggestions" (default OFF — noise).

R10 — i18n

Key en-US pt-BR
ai.suggest.setting.enabled "AI suggestions" "Sugestões de IA"
ai.suggest.setting.manual "Manual trigger only" "Ativar manualmente"
ai.suggest.accept.full "Suggestion accepted" "Sugestão aceita"
ai.suggest.accept.partial "Partial suggestion accepted" "Parte da sugestão aceita"
ai.suggest.cost.label "{in}/{out} · {cost}" "{in}/{out} · {cost}"

R11 — Multi-tenant

Per-file storage/cost attribution per (koder_user_id, workspace_id, file_id).

T-suite

  • *1*Trigger: type + pause 300ms → suggestion appears.
  • *2*Accept full: Tab → ghost text inserted; cursor at end.
  • *3*Accept word: Cmd+→ → only next word inserted; remaining stays ghost.
  • *4*Accept line: Cmd+Shift+→ → only next line; remaining stays.
  • *5*Esc dismisses: ghost gone.
  • *6*Other keystroke dismisses: user types → ghost gone; new trigger debounce starts.
  • *7*Cursor move cancels: arrow key → ghost gone.
  • *8*Disable: setting OFF → no requests sent.
  • *9*Manual trigger: setting "manual" + Ctrl+Space → suggestion appears.
  • *10*Cost surfaced: each suggestion → status bar updates with cost.
  • *11*Streaming: first token < 500ms (SLA gate via model registry).
  • *12*A11y reduced-motion: ghost appears instant.
  • *1*Large file: file > model context → warning surfaced; suggestions disabled until model switch.
  • Companion: streaming-text.kmd (R4 streaming integration), cost-display.kmd (R5 status bar), model-selector.kmd (model switch affects suggestion quality)
  • Backend: services/ai/gateway/
  • Consumers: products/dev/kdev, future products/dev/kanvas, Kortex notebook
  • Refs: GitHub Copilot ghost text, VS Code Next Edit Suggestions

Source: ../home/koder/dev/koder/meta/docs/stack/specs/ai-ui/inline-suggest.kmd