@qilin-agent/client-ui-input-trigger 0.0.0-stage → 3.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +40 -0
- package/README.md +88 -2
- package/README.zh.md +89 -0
- package/lib/client.js +1927 -0
- package/lib/index.js +11 -0
- package/lib/types/client/MenuView.d.ts +11 -0
- package/lib/types/client/contract.d.ts +25 -0
- package/lib/types/client/controller.d.ts +219 -0
- package/lib/types/client/index.d.ts +32 -0
- package/lib/types/client/locales.d.ts +32 -0
- package/lib/types/client/service.d.ts +42 -0
- package/lib/types/client/slots.d.ts +33 -0
- package/lib/types/core/contract.d.ts +77 -0
- package/lib/types/core/detect.d.ts +19 -0
- package/lib/types/core/menu.d.ts +53 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/types.d.ts +214 -0
- package/package.json +70 -3
package/lib/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region lib/types/index.js
|
|
2
|
+
/**
|
|
3
|
+
* Slash trigger plugin, node half. Pure UI plugin: the empty apply exists so
|
|
4
|
+
* the plugin appears in the host cordis.yml / Loader; the browser half ships
|
|
5
|
+
* via exports["./client"], discovered through the package.json qilin.client
|
|
6
|
+
* declaration.
|
|
7
|
+
*/
|
|
8
|
+
/** Host plugin body — no host-side behavior for the slash trigger plugin. */
|
|
9
|
+
function apply() {}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apply };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PropsLocale } from '@qilin-agent/client-ui-slots';
|
|
2
|
+
import type { MenuViewInjected } from './slots.ts';
|
|
3
|
+
/** Full menu props: injected face + the locale seat. */
|
|
4
|
+
export type MenuViewProps = MenuViewInjected & PropsLocale<'slash.menu'>;
|
|
5
|
+
/**
|
|
6
|
+
* Render the candidate menu overlay entry.
|
|
7
|
+
* @param props - injected face (the menu store and the pick route); `t` rides the standard locale seat.
|
|
8
|
+
* @returns the dropdown while open; null while closed.
|
|
9
|
+
*/
|
|
10
|
+
export declare function MenuView({ menu, headers, onPick, onCrumb, onHover, onDismiss, t }: MenuViewProps): import("react").JSX.Element | null;
|
|
11
|
+
//# sourceMappingURL=MenuView.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen service contract of the slash pipeline. Types only. The
|
|
3
|
+
* InputTriggerService implementation publishes this face as `ctx.inputTriggers`; sources
|
|
4
|
+
* see registerSource alone, the conversation wiring layer resolves its
|
|
5
|
+
* per-session controller through sessionOf.
|
|
6
|
+
*/
|
|
7
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
8
|
+
import type { InputTriggerSource } from '../types.ts';
|
|
9
|
+
import type { InputTriggerController } from './controller.ts';
|
|
10
|
+
/** The `ctx.inputTriggers` service face. */
|
|
11
|
+
export interface InputTriggerServiceContract {
|
|
12
|
+
/**
|
|
13
|
+
* Register one trigger source; duplicate trigger/name pairs throw.
|
|
14
|
+
* @param src - source that discovers and resolves slash or reference candidates.
|
|
15
|
+
* @returns effect disposer removing this source.
|
|
16
|
+
*/
|
|
17
|
+
registerSource(src: InputTriggerSource): () => void;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the lazy controller owned by one session scope.
|
|
20
|
+
* @param actx - session-scoped Client context.
|
|
21
|
+
* @returns controller that dies with that scope.
|
|
22
|
+
*/
|
|
23
|
+
sessionOf(actx: ClientContext): InputTriggerController;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InputTriggerController: the per-session half of the trigger pipeline. Owns every
|
|
3
|
+
* piece of mutable interaction state — the authoritative trigger hit (span
|
|
4
|
+
* included; it outlives menu close for space adjudication), the menu store,
|
|
5
|
+
* and the candidate-fetch lifecycle — and executes pick outcomes by
|
|
6
|
+
* dispatching the scoped input-mutation events. The root InputTriggerService keeps
|
|
7
|
+
* only the source roster. One controller per session scope; the service
|
|
8
|
+
* disposes it with the scope fiber.
|
|
9
|
+
*/
|
|
10
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
11
|
+
import { type SnapshotStore } from '@qilin-agent/client-store';
|
|
12
|
+
import type { ArbitrateKey, ArbitrateOutcome, PickOutcome, ReferenceInsert } from '@qilin-agent/client-ui-conversation/client';
|
|
13
|
+
import type { SessionId } from '@qilin-agent/session/types';
|
|
14
|
+
import type { MenuState, TriggerHit } from '../core/contract.ts';
|
|
15
|
+
import type { InputTriggerCrumb, InputTriggerSource, PickAction, SubmitEnvelope, TriggerChar, TriggerGuard } from '../types.ts';
|
|
16
|
+
/** Roster access the controller borrows from the root service (registration order preserved). */
|
|
17
|
+
export interface SourceRoster {
|
|
18
|
+
sources(trigger: string): readonly InputTriggerSource[];
|
|
19
|
+
all(): readonly InputTriggerSource[];
|
|
20
|
+
}
|
|
21
|
+
/** Construction hooks for one controller. */
|
|
22
|
+
export interface InputTriggerControllerDeps {
|
|
23
|
+
/** The owning session scope (event dispatch + teardown registration site). */
|
|
24
|
+
actx: ClientContext;
|
|
25
|
+
/** The session's stable host identity (the projection handed to sources). */
|
|
26
|
+
sessionId: SessionId;
|
|
27
|
+
/** Root-service roster view. */
|
|
28
|
+
roster: SourceRoster;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Per-session trigger pipeline state and orchestration. All mutation stays
|
|
32
|
+
* inside; MenuView renders from {@link InputTriggerController.menu} and routes
|
|
33
|
+
* pointer picks back through {@link InputTriggerController.pick}.
|
|
34
|
+
*/
|
|
35
|
+
export declare class InputTriggerController {
|
|
36
|
+
private readonly deps;
|
|
37
|
+
/** Menu state store (per-session; survives session switches, dies with the scope). */
|
|
38
|
+
readonly menu: SnapshotStore<MenuState>;
|
|
39
|
+
/**
|
|
40
|
+
* Name of the source opened through the programmatic launcher, or null for
|
|
41
|
+
* trigger-detected/closed menus. Composer chrome subscribes to this store
|
|
42
|
+
* for the launcher's expanded state without owning a second menu model.
|
|
43
|
+
*/
|
|
44
|
+
readonly launcher: SnapshotStore<string | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Crumbs published by each header-bearing source for the open menu, keyed
|
|
47
|
+
* by source name. A snapshot store like {@link InputTriggerController.launcher}:
|
|
48
|
+
* the answer changes with every hit, and render-side consumers subscribe
|
|
49
|
+
* instead of re-polling sources during a render.
|
|
50
|
+
*/
|
|
51
|
+
readonly headers: SnapshotStore<ReadonlyMap<string, readonly InputTriggerCrumb[]>>;
|
|
52
|
+
/**
|
|
53
|
+
* Aggregated hot reference lexicon, grouped by trigger (plain-text-reference decision;
|
|
54
|
+
* see .agents/notes/archived/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md):
|
|
55
|
+
* sources implementing the lexicon hook are polled with the session
|
|
56
|
+
* projection; undefined answers (roll not hot yet) are skipped; multiple
|
|
57
|
+
* sources on one trigger concatenate in registration order. A snapshot
|
|
58
|
+
* store because rolls change asynchronously (catalog settles, children
|
|
59
|
+
* spawn/exit) — render-side consumers subscribe instead of re-reading a
|
|
60
|
+
* mutable answer.
|
|
61
|
+
*/
|
|
62
|
+
readonly lexicon: SnapshotStore<ReadonlyMap<TriggerChar, readonly string[]>>;
|
|
63
|
+
/** The authoritative hit: single truth for span CAS material (menu snapshot never carries it alone). */
|
|
64
|
+
private hit;
|
|
65
|
+
/**
|
|
66
|
+
* Identity of the hit whose menu the user dismissed. A dismissal means "not
|
|
67
|
+
* this one, not now": the same token with the same query keeps its menu
|
|
68
|
+
* closed, so restoring the caret after a dismissal cannot reopen it. Typing
|
|
69
|
+
* (a new query) or moving to another token clears it.
|
|
70
|
+
*/
|
|
71
|
+
private dismissed;
|
|
72
|
+
/** Whether the open menu was reached by a drill pick; cleared with the menu. */
|
|
73
|
+
private drilled;
|
|
74
|
+
private fetch;
|
|
75
|
+
private disposed;
|
|
76
|
+
/** Per-source lexicon unsubscribers (sources without the hook never enter). */
|
|
77
|
+
private readonly lexiconOffs;
|
|
78
|
+
constructor(deps: InputTriggerControllerDeps);
|
|
79
|
+
/**
|
|
80
|
+
* Feed a draft/caret change through trigger detection and drive the menu.
|
|
81
|
+
* @param draft - full draft text.
|
|
82
|
+
* @param caret - caret offset into `draft`.
|
|
83
|
+
* @param guard - availability tier derived from the input phase.
|
|
84
|
+
* @param draftRev - the input machine's current draft revision, stamped
|
|
85
|
+
* into the hit span for pick-time CAS.
|
|
86
|
+
*/
|
|
87
|
+
track(draft: string, caret: number, guard: TriggerGuard, draftRev: number): void;
|
|
88
|
+
/**
|
|
89
|
+
* Toggle a menu containing exactly one registered source. The supplied hit
|
|
90
|
+
* is a synthetic selection span rather than a typed trigger token, but
|
|
91
|
+
* picks deliberately reuse the ordinary source callback and scoped input
|
|
92
|
+
* mutation pipeline.
|
|
93
|
+
* @param source - registered source name under `hit.trigger`.
|
|
94
|
+
* @param hit - synthetic hit carrying position and pick-time draft CAS.
|
|
95
|
+
*/
|
|
96
|
+
toggleSource(source: string, hit: TriggerHit): void;
|
|
97
|
+
/**
|
|
98
|
+
* Pointer pick from MenuView: route the clicked candidate through onPick
|
|
99
|
+
* and execute claim/insert outcomes via the scoped input events.
|
|
100
|
+
* @param source - source (group) name.
|
|
101
|
+
* @param index - candidate index within the group.
|
|
102
|
+
* @param action - settling pick (default) or the candidate's drill action.
|
|
103
|
+
*/
|
|
104
|
+
pick(source: string, index: number, action?: PickAction): void;
|
|
105
|
+
/**
|
|
106
|
+
* Pointer pick on one crumb of a source's menu header: route it through the
|
|
107
|
+
* same drill path a folder row takes, so returning to a step and descending
|
|
108
|
+
* into one share one outcome.
|
|
109
|
+
* @param source - source (group) name.
|
|
110
|
+
* @param index - crumb index within that source's published header.
|
|
111
|
+
*/
|
|
112
|
+
pickCrumb(source: string, index: number): void;
|
|
113
|
+
/**
|
|
114
|
+
* Pointer hover from MenuView: park the shared highlight on the hovered
|
|
115
|
+
* candidate (keyboard `move` and pointer hover drive one highlight —
|
|
116
|
+
* last input wins).
|
|
117
|
+
* @param source - source (group) name.
|
|
118
|
+
* @param index - candidate index within the group.
|
|
119
|
+
*/
|
|
120
|
+
hover(source: string, index: number): void;
|
|
121
|
+
/**
|
|
122
|
+
* Keyboard arbitration while the menu is open.
|
|
123
|
+
* @param key - intercepted key.
|
|
124
|
+
* @param composing - inside IME composition: everything passes.
|
|
125
|
+
* @returns `pass` when the browser keeps the key (closed menu, no
|
|
126
|
+
* highlight, or a vanished candidate), `consumed` when the menu handled
|
|
127
|
+
* the key without a settling pick (move, close, drill descent, or a
|
|
128
|
+
* pending-refinement no-op), or `pick-highlighted` when the highlighted
|
|
129
|
+
* candidate settled and the menu closed.
|
|
130
|
+
*/
|
|
131
|
+
arbitrate(key: ArbitrateKey, composing: boolean): ArbitrateOutcome;
|
|
132
|
+
/**
|
|
133
|
+
* Space adjudication over the just-completed leading token: polls sources'
|
|
134
|
+
* matchSpace (hot state, synchronous) and dispatches the outcome itself.
|
|
135
|
+
* @returns true when a claim/insert was actually applied by the input —
|
|
136
|
+
* the caller preventDefaults exactly then.
|
|
137
|
+
*/
|
|
138
|
+
onSpace(): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Serialize one reference occurrence to its model form via the owning
|
|
141
|
+
* source's codec (prompt serialization: registry → explicit
|
|
142
|
+
* call → await). Owner missing or codec-less rejects — the submit attempt
|
|
143
|
+
* blocks instead of silently downgrading to the clipboard text.
|
|
144
|
+
* @param source - owning source name.
|
|
145
|
+
* @param ref - owner-scoped reference id.
|
|
146
|
+
* @param signal - the submit attempt's abort signal.
|
|
147
|
+
* @returns the model representation (e.g. `<skill>name</skill>`).
|
|
148
|
+
*/
|
|
149
|
+
serializeReference(source: string, ref: string, signal: AbortSignal): Promise<string>;
|
|
150
|
+
/**
|
|
151
|
+
* Route a chip to its owner or an editable token to its current lexicon owner.
|
|
152
|
+
* @param source - chip source name; undefined for editable text.
|
|
153
|
+
* @param reference - source-owned id and optional chip glyph.
|
|
154
|
+
* @returns whether an owner accepted the preview, possibly awaiting its catalog.
|
|
155
|
+
*/
|
|
156
|
+
openReference(source: string | undefined, reference: Pick<ReferenceInsert, 'ref' | 'appearance'>): boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Enter last adjudication: polls sources' matchEnter in registration
|
|
159
|
+
* order, first non-undefined wins. The outcome returns to the caller (the
|
|
160
|
+
* input machine applies it inside the same submit attempt — no event).
|
|
161
|
+
* @param line - trimmed draft; the leading char selects the trigger roster.
|
|
162
|
+
* @param signal - attempt-scoped abort from the input machine.
|
|
163
|
+
* @param envelope - non-text submission state accompanying the draft.
|
|
164
|
+
* @returns the winning outcome or undefined (default sink). Rejects when a
|
|
165
|
+
* polled source's warmup fails or the winning source refuses the envelope —
|
|
166
|
+
* the caller must not silently downgrade.
|
|
167
|
+
*/
|
|
168
|
+
adjudicate(line: string, signal: AbortSignal, envelope: SubmitEnvelope): Promise<PickOutcome>;
|
|
169
|
+
/**
|
|
170
|
+
* Drop the menu group of a disposed source (root registry change notification).
|
|
171
|
+
* @param source - the source whose registration was disposed.
|
|
172
|
+
*/
|
|
173
|
+
sourceRemoved(source: InputTriggerSource): void;
|
|
174
|
+
/**
|
|
175
|
+
* Admit a source registered after this controller's birth (root registry
|
|
176
|
+
* change notification): warm it and fold its roll into the live lexicon —
|
|
177
|
+
* the constructor-time prewarm covers only the roster present at scope
|
|
178
|
+
* birth.
|
|
179
|
+
* @param source - the newly registered source.
|
|
180
|
+
*/
|
|
181
|
+
sourceAdded(source: InputTriggerSource): void;
|
|
182
|
+
/** External dismiss (e.g. pointer outside the composer area). */
|
|
183
|
+
dismiss(): void;
|
|
184
|
+
/** Re-fetch the currently open menu without changing its hit or visible rows. */
|
|
185
|
+
refreshOpenMenu(): void;
|
|
186
|
+
/** Scope teardown: close and abort (the service deletes the map entry). */
|
|
187
|
+
dispose(): void;
|
|
188
|
+
/** The session projection handed to sources (agent-backed identity; constant per scope). */
|
|
189
|
+
private project;
|
|
190
|
+
/** Execute a claim/insert/text outcome via the scoped input events (actx as dispatch subject); true = the input applied it. */
|
|
191
|
+
private execute;
|
|
192
|
+
/** Re-poll every lexicon-bearing source and publish the aggregated rolls (see the store doc). */
|
|
193
|
+
private refreshLexicon;
|
|
194
|
+
/** Wire one source's lexicon invalidation channel into refresh (hookless or roll-less sources never notify). */
|
|
195
|
+
private watchLexicon;
|
|
196
|
+
/** Launch the candidate fetch for one hit generation, superseding the previous one. */
|
|
197
|
+
private fetchCandidates;
|
|
198
|
+
private stopFetch;
|
|
199
|
+
/**
|
|
200
|
+
* Run one candidate (or crumb) through its source and apply the outcome.
|
|
201
|
+
*
|
|
202
|
+
* A drill is the one pick that leaves the menu open, so it is also the one
|
|
203
|
+
* that records how the next query was reached; every other pick closes the
|
|
204
|
+
* menu, which clears that record.
|
|
205
|
+
* @param src - the owning source.
|
|
206
|
+
* @param candidate - the picked candidate, or a crumb projected as one.
|
|
207
|
+
* @param hit - the authoritative hit supplying position and span CAS.
|
|
208
|
+
* @param action - settling pick or drill.
|
|
209
|
+
*/
|
|
210
|
+
private settle;
|
|
211
|
+
/** Re-poll every header-bearing source in the hit roster and publish their crumbs. */
|
|
212
|
+
private refreshHeaders;
|
|
213
|
+
private setHeaders;
|
|
214
|
+
/** Record the open menu's identity as dismissed, so a bare re-track cannot revive it. */
|
|
215
|
+
private rememberDismissed;
|
|
216
|
+
private clearLauncher;
|
|
217
|
+
private reduce;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
2
|
+
import { type MenuKey } from './locales.ts';
|
|
3
|
+
export { InputTriggerService } from './service.ts';
|
|
4
|
+
export { InputTriggerController } from './controller.ts';
|
|
5
|
+
export type { InputTriggerControllerDeps, SourceRoster } from './controller.ts';
|
|
6
|
+
export type { MenuViewInjected } from './slots.ts';
|
|
7
|
+
export type { MenuViewProps } from './MenuView.tsx';
|
|
8
|
+
export type { MenuKey } from './locales.ts';
|
|
9
|
+
export type { ArbitrateKey, ArbitrateOutcome, BeginCommandRequest, CandidateRequest, ClientSessionContext, CommandClaim, ConsumeTokenRequest, HeaderRequest, InsertReferenceRequest, PickOutcome, PickVia, ReferenceCodec, ReferenceInsert, InputTriggerCandidate, InputTriggerCrumb, InputTriggerPick, InputTriggerSource, SubmitAttachment, SubmitEnvelope, SubmitOutcome, TokenSpan, TriggerChar, TriggerGuard, TriggerPosition, } from '../types.ts';
|
|
10
|
+
export type { DetectTrigger, ExactMatch, MenuEvent, MenuReduce, MenuState, TriggerHit } from '../core/contract.ts';
|
|
11
|
+
export type { InputTriggerServiceContract } from './contract.ts';
|
|
12
|
+
declare module '@qilin-agent/kylin' {
|
|
13
|
+
interface Context {
|
|
14
|
+
/** The outward face only; the concrete service stays inside this plugin. */
|
|
15
|
+
inputTriggers: import('./contract.ts').InputTriggerServiceContract;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
declare module '@qilin-agent/client-ui-slots' {
|
|
19
|
+
interface LocaleNamespaceMap {
|
|
20
|
+
/** The candidate menu's copy: group titles keyed by source name, the pending row, and the listbox aria. */
|
|
21
|
+
'slash.menu': MenuKey;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Required services: controller resolution reads the session scope tree; the menu copy is localized. */
|
|
25
|
+
export declare const inject: string[];
|
|
26
|
+
/**
|
|
27
|
+
* Client plugin body: mount the service, then register MenuView into the
|
|
28
|
+
* input overlay once its declarer is up.
|
|
29
|
+
* @param ctx - client root context.
|
|
30
|
+
*/
|
|
31
|
+
export declare function apply(ctx: ClientContext): void;
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slash.menu` namespace dictionaries: group titles keyed by source name
|
|
3
|
+
* (the lookup chain returns the key itself, so an unknown source shows its
|
|
4
|
+
* raw name), the pending row, and the listbox and header aria labels.
|
|
5
|
+
*/
|
|
6
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
7
|
+
export declare const zh: {
|
|
8
|
+
command: string;
|
|
9
|
+
skill: string;
|
|
10
|
+
subagent: string;
|
|
11
|
+
loading: string;
|
|
12
|
+
'drill.aria': string;
|
|
13
|
+
'drill.hint': string;
|
|
14
|
+
'drill.key': string;
|
|
15
|
+
'crumbs.aria': string;
|
|
16
|
+
'suggestions.aria': string;
|
|
17
|
+
};
|
|
18
|
+
/** The slash.menu namespace key union. */
|
|
19
|
+
export type MenuKey = keyof typeof zh;
|
|
20
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
21
|
+
export declare const en: {
|
|
22
|
+
command: string;
|
|
23
|
+
skill: string;
|
|
24
|
+
subagent: string;
|
|
25
|
+
loading: string;
|
|
26
|
+
'drill.aria': string;
|
|
27
|
+
'drill.hint': string;
|
|
28
|
+
'drill.key': string;
|
|
29
|
+
'crumbs.aria': string;
|
|
30
|
+
'suggestions.aria': string;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InputTriggerService (`ctx.inputTriggers`): the root half of the trigger pipeline — the
|
|
3
|
+
* stateless source registry plus the per-session controller map. Every piece
|
|
4
|
+
* of mutable interaction state (hit, menu, fetch) lives on the
|
|
5
|
+
* {@link InputTriggerController}; the service only registers sources, resolves
|
|
6
|
+
* controllers by session scope, and relays roster changes.
|
|
7
|
+
*/
|
|
8
|
+
import { Service } from '@qilin-agent/kylin';
|
|
9
|
+
import type { Context } from '@qilin-agent/kylin';
|
|
10
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
11
|
+
import type { InputTriggerSource } from '../types.ts';
|
|
12
|
+
import { InputTriggerController } from './controller.ts';
|
|
13
|
+
import type { InputTriggerServiceContract } from './contract.ts';
|
|
14
|
+
/** The `ctx.inputTriggers` trigger pipeline service (root registry + controller resolution). */
|
|
15
|
+
export declare class InputTriggerService extends Service implements InputTriggerServiceContract {
|
|
16
|
+
static inject: string[];
|
|
17
|
+
private readonly live;
|
|
18
|
+
/**
|
|
19
|
+
* @param ctx - owning root context (the service registers itself as `slash`).
|
|
20
|
+
*/
|
|
21
|
+
constructor(ctx: Context);
|
|
22
|
+
/**
|
|
23
|
+
* Register one trigger source. Live session controllers are notified so a
|
|
24
|
+
* source arriving after scope birth still warms and joins the lexicon.
|
|
25
|
+
* @param src - the source; (trigger, name) must be unique — duplicates throw.
|
|
26
|
+
* @returns the disposer (callers wrap registration in ctx.effect). Disposal
|
|
27
|
+
* while a controller shows the source's menu group drops that group.
|
|
28
|
+
*/
|
|
29
|
+
registerSource(src: InputTriggerSource): () => void;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the per-session controller for one session scope (lazy; the
|
|
32
|
+
* scope disposer removes and disposes it). Construction warms the source
|
|
33
|
+
* roster once — sessions are always agent-backed, so scope birth is the
|
|
34
|
+
* single prewarm moment.
|
|
35
|
+
* @param actx - session-scope ctx.
|
|
36
|
+
* @returns the resident controller.
|
|
37
|
+
* @throws when the Context no longer belongs to a retained Session generation.
|
|
38
|
+
*/
|
|
39
|
+
sessionOf(actx: ClientContext): InputTriggerController;
|
|
40
|
+
private sessions;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { InputTriggerCrumb, PickAction } from '../types.ts';
|
|
2
|
+
import type { SnapshotStore } from '@qilin-agent/client-store';
|
|
3
|
+
import type { MenuState } from '../core/contract.ts';
|
|
4
|
+
/** Injected business face of the MenuView overlay entry (copy rides the standard locale seat, not this face). */
|
|
5
|
+
export interface MenuViewInjected {
|
|
6
|
+
/** The service's menu state store (read-only here; MenuView subscribes). */
|
|
7
|
+
menu: SnapshotStore<MenuState>;
|
|
8
|
+
/** Crumbs published per source for the open menu; sources without a header never appear. */
|
|
9
|
+
headers: SnapshotStore<ReadonlyMap<string, readonly InputTriggerCrumb[]>>;
|
|
10
|
+
/**
|
|
11
|
+
* Pointer pick routed back through the service pipeline.
|
|
12
|
+
* @param source - source (group) name.
|
|
13
|
+
* @param index - candidate index within the group.
|
|
14
|
+
* @param action - settling pick (default) or the candidate's drill action.
|
|
15
|
+
*/
|
|
16
|
+
onPick: (source: string, index: number, action?: PickAction) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Pointer hover routed to the shared highlight (pointer and keyboard drive
|
|
19
|
+
* one highlight — last input wins).
|
|
20
|
+
* @param source - source (group) name.
|
|
21
|
+
* @param index - candidate index within the group.
|
|
22
|
+
*/
|
|
23
|
+
onHover: (source: string, index: number) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Pointer pick on one header crumb, routed back through the source's drill path.
|
|
26
|
+
* @param source - source (group) name.
|
|
27
|
+
* @param index - crumb index within that source's published header.
|
|
28
|
+
*/
|
|
29
|
+
onCrumb: (source: string, index: number) => void;
|
|
30
|
+
/** Dismiss the menu (external pointer outside the composer area). */
|
|
31
|
+
onDismiss: () => void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen pure-core contract: trigger detection and
|
|
3
|
+
* menu reduction, zero React / DOM / cordis. Types only — implementations
|
|
4
|
+
* live in sibling modules annotated with these
|
|
5
|
+
* aliases; the service shell wires them to ctx.
|
|
6
|
+
*/
|
|
7
|
+
import type { TokenSpan } from '@qilin-agent/client-ui-conversation/client';
|
|
8
|
+
import type { InputTriggerCandidate, TriggerChar, TriggerGuard, TriggerPosition } from '../types.ts';
|
|
9
|
+
/** A detected trigger token under the caret. */
|
|
10
|
+
export interface TriggerHit {
|
|
11
|
+
readonly trigger: TriggerChar;
|
|
12
|
+
/** Text between the trigger char and the caret, live-filtered. */
|
|
13
|
+
readonly query: string;
|
|
14
|
+
/** True only for an open quoted `@file` token. */
|
|
15
|
+
readonly quoted: boolean;
|
|
16
|
+
/** leading = draft trimmed (whitespace incl. newlines) starts with the token. */
|
|
17
|
+
readonly position: TriggerPosition;
|
|
18
|
+
/** Token span; draftRev injected by the caller. */
|
|
19
|
+
readonly span: TokenSpan;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detect a trigger token at the caret under the given guard tier.
|
|
23
|
+
* `@` uses the shared file-reference start/whitespace grammar; `/` accepts
|
|
24
|
+
* punctuation boundaries with URL carve-outs. `user@host` and URL `/` do not
|
|
25
|
+
* trigger.
|
|
26
|
+
* Returns null when no trigger is live at the caret.
|
|
27
|
+
*/
|
|
28
|
+
export type DetectTrigger = (draft: string, caret: number, guard: TriggerGuard) => TriggerHit | null;
|
|
29
|
+
/** Menu state: one group per source; empty ready groups auto-close the menu. */
|
|
30
|
+
export interface MenuState {
|
|
31
|
+
readonly open: boolean;
|
|
32
|
+
readonly hit: TriggerHit | null;
|
|
33
|
+
/** Monotonic per-hit generation; stale source settlements are dropped. */
|
|
34
|
+
readonly generation: number;
|
|
35
|
+
readonly groups: readonly {
|
|
36
|
+
readonly source: string;
|
|
37
|
+
/** False when candidate section rows own all visible group labeling. */
|
|
38
|
+
readonly showGroupTitle?: boolean;
|
|
39
|
+
readonly status: 'pending' | 'ready';
|
|
40
|
+
readonly items: readonly InputTriggerCandidate[];
|
|
41
|
+
}[];
|
|
42
|
+
readonly highlight: {
|
|
43
|
+
readonly source: string;
|
|
44
|
+
readonly index: number;
|
|
45
|
+
} | null;
|
|
46
|
+
}
|
|
47
|
+
/** Menu reduction events. Source failure = silent group removal (log only; no error UI tier). */
|
|
48
|
+
export type MenuEvent = {
|
|
49
|
+
readonly type: 'hit';
|
|
50
|
+
readonly hit: TriggerHit | null;
|
|
51
|
+
} | {
|
|
52
|
+
readonly type: 'source-settled';
|
|
53
|
+
readonly generation: number;
|
|
54
|
+
readonly source: string;
|
|
55
|
+
readonly items?: readonly InputTriggerCandidate[];
|
|
56
|
+
} | {
|
|
57
|
+
readonly type: 'source-failed';
|
|
58
|
+
readonly generation: number;
|
|
59
|
+
readonly source: string;
|
|
60
|
+
} | {
|
|
61
|
+
readonly type: 'move';
|
|
62
|
+
readonly dir: 1 | -1;
|
|
63
|
+
} | {
|
|
64
|
+
readonly type: 'hover';
|
|
65
|
+
readonly source: string;
|
|
66
|
+
readonly index: number;
|
|
67
|
+
} | {
|
|
68
|
+
readonly type: 'close';
|
|
69
|
+
};
|
|
70
|
+
/** Pure menu reducer; returns the same reference when the event is stale or a no-op. */
|
|
71
|
+
export type MenuReduce = (state: MenuState, ev: MenuEvent) => MenuState;
|
|
72
|
+
/**
|
|
73
|
+
* Exact-name lookup in one source's ready group; null when absent or the
|
|
74
|
+
* group is not ready.
|
|
75
|
+
*/
|
|
76
|
+
export type ExactMatch = (groups: MenuState['groups'], source: string, name: string) => InputTriggerCandidate | null;
|
|
77
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DetectTrigger } from './contract.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Detect a trigger token at the caret. `@` first uses the shared grammar,
|
|
4
|
+
* including an open quoted token that may span whitespace. Slash detection
|
|
5
|
+
* scans left to the first whitespace; slashes failing the word boundary are
|
|
6
|
+
* treated as ordinary token chars and the scan continues (URL slashes).
|
|
7
|
+
* Guard tiers: plain = both chars live; claimed = '/' fully suppressed,
|
|
8
|
+
* '@' live; frozen = none.
|
|
9
|
+
*
|
|
10
|
+
* @param draft - Full draft text.
|
|
11
|
+
* @param caret - Caret offset into `draft`.
|
|
12
|
+
* @param guard - Availability tier derived from the input phase.
|
|
13
|
+
* @returns The hit with `query` = trigger-to-caret slice and `span` =
|
|
14
|
+
* `{start: triggerIndex, end: caret}`; `span.draftRev` is a placeholder `0`
|
|
15
|
+
* — the calling shell stamps the real revision. Null when no trigger is
|
|
16
|
+
* live at the caret.
|
|
17
|
+
*/
|
|
18
|
+
export declare const detectTrigger: DetectTrigger;
|
|
19
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Menu reduction pure core. One group per source;
|
|
3
|
+
* generation-gated settlement; empty ready groups auto-close. Zero React /
|
|
4
|
+
* DOM / cordis. Stale or no-op events return the same state reference so
|
|
5
|
+
* store subscribers skip re-renders.
|
|
6
|
+
*
|
|
7
|
+
* Roster protocol: the frozen `hit` event carries no source roster, so the
|
|
8
|
+
* reducer cannot invent groups. Opening from a closed state, the shell seeds
|
|
9
|
+
* the roster with {@link seedGroups} and then dispatches `hit`; a `hit`
|
|
10
|
+
* while open (query refinement) resets the existing groups to pending under
|
|
11
|
+
* a new generation while keeping their items on screen until the new fetch
|
|
12
|
+
* settles (stale-while-revalidate — the render layer shows skeletons only
|
|
13
|
+
* for a pending group with no items). Auto-close and explicit close drop
|
|
14
|
+
* the groups.
|
|
15
|
+
*/
|
|
16
|
+
import type { InputTriggerSource } from '../types.ts';
|
|
17
|
+
import type { ExactMatch, MenuReduce, MenuState } from './contract.ts';
|
|
18
|
+
/** Closed rest state with generation 0; store initializer and test seed. */
|
|
19
|
+
export declare const MENU_CLOSED: MenuState;
|
|
20
|
+
/**
|
|
21
|
+
* Replace the group roster with pending groups for `sources`, in order.
|
|
22
|
+
* Shell-side step before dispatching `hit` on a fresh menu open.
|
|
23
|
+
*
|
|
24
|
+
* @param state - Current menu state.
|
|
25
|
+
* @param sources - Sources registered for the hit trigger, in menu order.
|
|
26
|
+
* @returns State carrying the new pending roster; highlight cleared.
|
|
27
|
+
*/
|
|
28
|
+
export declare function seedGroups(state: MenuState, sources: readonly Pick<InputTriggerSource, 'name' | 'showGroupTitle'>[]): MenuState;
|
|
29
|
+
/**
|
|
30
|
+
* Pure menu reducer. `hit` opens a new generation over the seeded roster
|
|
31
|
+
* (null hit closes); `source-settled` outside the current generation, the
|
|
32
|
+
* open menu, or the roster is dropped; a settlement or failure leaving every
|
|
33
|
+
* group ready-and-empty (or no groups) auto-closes; `source-failed` silently
|
|
34
|
+
* removes the group (the shell logs); `move` cycles the highlight across
|
|
35
|
+
* ready items; `hover` parks it on one ready item (pointer and keyboard
|
|
36
|
+
* share the single highlight — last input wins).
|
|
37
|
+
*
|
|
38
|
+
* @param state - Current menu state.
|
|
39
|
+
* @param ev - Menu event.
|
|
40
|
+
* @returns Next state; the same reference when stale or a no-op.
|
|
41
|
+
*/
|
|
42
|
+
export declare const menuReduce: MenuReduce;
|
|
43
|
+
/**
|
|
44
|
+
* Exact-name lookup in one source's ready group.
|
|
45
|
+
*
|
|
46
|
+
* @param groups - Menu groups.
|
|
47
|
+
* @param source - Source (group) name.
|
|
48
|
+
* @param name - Candidate name to match exactly.
|
|
49
|
+
* @returns The candidate, or null when the group is absent, not ready, or
|
|
50
|
+
* has no candidate of that name.
|
|
51
|
+
*/
|
|
52
|
+
export declare const exactMatch: ExactMatch;
|
|
53
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash trigger plugin, node half. Pure UI plugin: the empty apply exists so
|
|
3
|
+
* the plugin appears in the host cordis.yml / Loader; the browser half ships
|
|
4
|
+
* via exports["./client"], discovered through the package.json qilin.client
|
|
5
|
+
* declaration.
|
|
6
|
+
*/
|
|
7
|
+
/** Host plugin body — no host-side behavior for the slash trigger plugin. */
|
|
8
|
+
export declare function apply(): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|