mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
58 lines
2.5 KiB
TypeScript
58 lines
2.5 KiB
TypeScript
/**
|
|
* Frozen pure-core contract (design v4, plan §1.2): trigger detection and
|
|
* menu reduction, zero React / DOM / cordis. Types only — T2 implements
|
|
* these signatures in sibling modules (annotate implementations with these
|
|
* aliases); the service shell (T4) wires them to ctx.
|
|
*/
|
|
import type { SlashCandidate, 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
|
|
/** 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.
|
|
* Word-boundary rule: the char before the trigger is start-of-line,
|
|
* whitespace, or punctuation; `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
|
|
readonly status: 'pending' | 'ready'
|
|
readonly items: readonly SlashCandidate[]
|
|
}[]
|
|
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 SlashCandidate[] }
|
|
| { 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) => SlashCandidate | null
|