mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
/**
|
|
* Frozen pure-core contract: trigger detection and
|
|
* menu reduction, zero React / DOM / cordis. Types only — implementations
|
|
* live in sibling modules annotated with these
|
|
* aliases; the service shell wires them to ctx.
|
|
*/
|
|
import type { InputTriggerCandidate, TokenSpan, TriggerChar, TriggerGuard, TriggerPosition } from '../types.ts'
|
|
|
|
/** A detected trigger token under the caret. */
|
|
export interface TriggerHit {
|
|
readonly trigger: TriggerChar
|
|
/** Text between the trigger char and the caret, live-filtered. */
|
|
readonly query: string
|
|
/** True only for an open quoted `@file` token. */
|
|
readonly quoted: boolean
|
|
/** leading = draft trimmed (whitespace incl. newlines) starts with the token. */
|
|
readonly position: TriggerPosition
|
|
/** Token span; draftRev injected by the caller. */
|
|
readonly span: TokenSpan
|
|
}
|
|
|
|
/**
|
|
* Detect a trigger token at the caret under the given guard tier.
|
|
* `@` uses the shared file-reference start/whitespace grammar; `/` accepts
|
|
* punctuation boundaries with URL carve-outs. `user@host` and URL `/` do not
|
|
* trigger.
|
|
* Returns null when no trigger is live at the caret.
|
|
*/
|
|
export type DetectTrigger = (draft: string, caret: number, guard: TriggerGuard) => TriggerHit | null
|
|
|
|
/** Menu state: one group per source; empty ready groups auto-close the menu. */
|
|
export interface MenuState {
|
|
readonly open: boolean
|
|
readonly hit: TriggerHit | null
|
|
/** Monotonic per-hit generation; stale source settlements are dropped. */
|
|
readonly generation: number
|
|
readonly groups: readonly {
|
|
readonly source: string
|
|
/** False when candidate section rows own all visible group labeling. */
|
|
readonly showGroupTitle?: boolean
|
|
readonly status: 'pending' | 'ready'
|
|
readonly items: readonly InputTriggerCandidate[]
|
|
}[]
|
|
readonly highlight: { readonly source: string; readonly index: number } | null
|
|
}
|
|
|
|
/** Menu reduction events. Source failure = silent group removal (log only; no error UI tier). */
|
|
export type MenuEvent =
|
|
| { readonly type: 'hit'; readonly hit: TriggerHit | null }
|
|
| { readonly type: 'source-settled'; readonly generation: number; readonly source: string; readonly items?: readonly InputTriggerCandidate[] }
|
|
| { readonly type: 'source-failed'; readonly generation: number; readonly source: string }
|
|
| { readonly type: 'move'; readonly dir: 1 | -1 }
|
|
| { readonly type: 'close' }
|
|
|
|
/** Pure menu reducer; returns the same reference when the event is stale or a no-op. */
|
|
export type MenuReduce = (state: MenuState, ev: MenuEvent) => MenuState
|
|
|
|
/**
|
|
* Exact-name lookup in one source's ready group; null when absent or the
|
|
* group is not ready.
|
|
*/
|
|
export type ExactMatch = (groups: MenuState['groups'], source: string, name: string) => InputTriggerCandidate | null
|