@qilin-agent/client-ui-conversation 3.1.4 → 3.1.6

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.
Files changed (32) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +5 -3
  3. package/README.zh.md +5 -3
  4. package/lib/client.js +4636 -337
  5. package/lib/index.js +13 -9
  6. package/lib/types/client/contract/conversation.d.ts +10 -0
  7. package/lib/types/client/contract/draft-editor.d.ts +23 -0
  8. package/lib/types/client/contract/input.d.ts +34 -10
  9. package/lib/types/client/contract/records.d.ts +8 -3
  10. package/lib/types/client/contract/slots.d.ts +11 -2
  11. package/lib/types/client/contract/views.d.ts +2 -1
  12. package/lib/types/client/conversation/assembler.d.ts +3 -0
  13. package/lib/types/client/draft.d.ts +22 -0
  14. package/lib/types/client/index.d.ts +2 -2
  15. package/lib/types/client/input/decorations.d.ts +1 -1
  16. package/lib/types/client/input/editor/CommandChip.d.ts +13 -0
  17. package/lib/types/client/input/editor/command-chip-node.d.ts +87 -0
  18. package/lib/types/client/input/editor/command-chip-selection.d.ts +20 -0
  19. package/lib/types/client/input/editor/projection.d.ts +3 -0
  20. package/lib/types/client/input/editor/runtime.d.ts +19 -11
  21. package/lib/types/client/input/editor/text-ref.d.ts +2 -7
  22. package/lib/types/client/input/facade.d.ts +50 -17
  23. package/lib/types/client/input/hub.d.ts +2 -1
  24. package/lib/types/client/input/machine.d.ts +9 -5
  25. package/lib/types/client/locales.d.ts +2 -16
  26. package/lib/types/client/skeleton/DefaultConversationViews.d.ts +2 -2
  27. package/lib/types/client/stores.d.ts +8 -1
  28. package/lib/types/conversation-settings.d.ts +4 -0
  29. package/lib/types/index.d.ts +20 -5
  30. package/package.json +35 -35
  31. package/lib/types/client/input/editor/claim-decor.d.ts +0 -23
  32. package/lib/types/client/settings/ContentWidthRow.d.ts +0 -26
package/lib/index.js CHANGED
@@ -26,21 +26,25 @@ const CONTENT_WIDTH_STEP = 20;
26
26
  /** Content width before the user picks one. */
27
27
  const DEFAULT_CONTENT_WIDTH = 0;
28
28
  /** Durable conversation schema; also the wire envelope the browser scope validates against. */
29
- const ConversationSettingsSchema = z.object({
29
+ const ConversationSettingsFields = {
30
30
  [BUSY_ENTER_FIELD]: z.union([...BUSY_ENTER_BEHAVIORS]).default(DEFAULT_BUSY_ENTER_BEHAVIOR),
31
31
  [CONTENT_WIDTH_FIELD]: z.natural().max(CONTENT_WIDTH_MAX).default(0)
32
- });
32
+ };
33
+ z.object(ConversationSettingsFields);
33
34
  //#endregion
34
35
  //#region lib/types/index.js
35
- /** Host registration for browser conversation preferences. */
36
- /**
37
- * Register the durable conversation section when a settings provider exists.
38
- * @param ctx - Host context whose optional settings service owns the section.
36
+ /** Live preferences projected to the browser. */
37
+ const Config = z.object({
38
+ [BUSY_ENTER_FIELD]: ConversationSettingsFields[BUSY_ENTER_FIELD].volatile(),
39
+ [CONTENT_WIDTH_FIELD]: ConversationSettingsFields[CONTENT_WIDTH_FIELD].volatile()
40
+ });
41
+ /** Host preferences are consumed through the configuration form projection.
42
+ * @param ctx Plugin context used for optional settings presentation.
39
43
  */
40
44
  function apply(ctx) {
41
- ctx.inject(["settings"], (settingsCtx) => {
42
- settingsCtx.settings.register(CONVERSATION_SETTINGS_NAMESPACE, ConversationSettingsSchema);
45
+ ctx.inject(["settings"], (child) => {
46
+ child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
43
47
  });
44
48
  }
45
49
  //#endregion
46
- export { BUSY_ENTER_BEHAVIORS, BUSY_ENTER_FIELD, CONTENT_WIDTH_ADAPTIVE, CONTENT_WIDTH_FIELD, CONTENT_WIDTH_MAX, CONTENT_WIDTH_MIN, CONTENT_WIDTH_STEP, CONVERSATION_SETTINGS_NAMESPACE, DEFAULT_BUSY_ENTER_BEHAVIOR, DEFAULT_CONTENT_WIDTH, apply };
50
+ export { BUSY_ENTER_BEHAVIORS, BUSY_ENTER_FIELD, CONTENT_WIDTH_ADAPTIVE, CONTENT_WIDTH_FIELD, CONTENT_WIDTH_MAX, CONTENT_WIDTH_MIN, CONTENT_WIDTH_STEP, CONVERSATION_SETTINGS_NAMESPACE, Config, DEFAULT_BUSY_ENTER_BEHAVIOR, DEFAULT_CONTENT_WIDTH, apply };
@@ -5,6 +5,16 @@ import type { ConversationGroupData, ConversationGroupedView, ConversationGroupI
5
5
  export interface ConversationMatchResult {
6
6
  readonly id: string;
7
7
  readonly role: 'start' | 'update';
8
+ /**
9
+ * Claim this event on the Definition's target: no other Definition of the
10
+ * same target receives it, and that target's unmatched-event fallback does
11
+ * not run. Definitions of other targets and Definitions without a target
12
+ * still receive it. Two claims on one target, or a claim from a Definition
13
+ * without a target, throw during dispatch. A claim returned by the fallback
14
+ * itself has no effect, because the fallback runs only when no Definition of
15
+ * its target matched.
16
+ */
17
+ readonly exclusive?: true;
8
18
  }
9
19
  /** Merge-extensible business values published against one Turn. */
10
20
  export interface ConversationTurnDataMap {
@@ -16,6 +16,29 @@ export interface ReferenceInsert {
16
16
  readonly appearance?: 'session' | 'file' | 'folder';
17
17
  readonly clipboardText: string;
18
18
  }
19
+ /** Persisted reference data without the current editor's occurrence identity. */
20
+ export type DraftReference = Omit<Occurrence, 'occurrenceId'>;
21
+ /**
22
+ * Persisted command chip opening a draft. The claim itself (its submit
23
+ * closure) is not persisted; the input rebuilds it from the token through the
24
+ * trigger pipeline when the draft is restored.
25
+ */
26
+ export interface DraftCommand {
27
+ /** Claim token with its argument separator, e.g. `/goal `; the draft text starts with the token without the separator. */
28
+ readonly token: string;
29
+ }
30
+ /**
31
+ * Editor-independent content. Reference spans are ordered, non-overlapping UTF-16
32
+ * ranges whose text equals clipboardText and that follow a leading command
33
+ * token; runtime node identities are excluded.
34
+ */
35
+ export interface DraftSnapshot {
36
+ readonly text: string;
37
+ readonly references: readonly DraftReference[];
38
+ readonly command?: DraftCommand;
39
+ }
40
+ /** Plain initial text or an existing document with explicit reference identities. */
41
+ export type DraftInput = string | DraftSnapshot;
19
42
  /** Keyboard keys intercepted by an open trigger menu. */
20
43
  export type ArbitrateKey = 'up' | 'down' | 'enter' | 'escape' | 'tab' | 'tabBack';
21
44
  /** Trigger-menu keyboard routing result. */
@@ -7,6 +7,7 @@
7
7
  * here is the submit plane (phase, claim, attempt) alone.
8
8
  */
9
9
  import type { Context } from '@qilin-agent/kylin';
10
+ import type { SessionBinding } from '@qilin-agent/api-session-controller/client';
10
11
  import type { InboxState } from '@qilin-agent/agent/types';
11
12
  import type { ObservableSnapshot, SnapshotStore } from '@qilin-agent/client-store';
12
13
  import type { Branded } from '@qilin-agent/brand';
@@ -35,7 +36,7 @@ export interface SubmitOutcome {
35
36
  export interface CommandClaim {
36
37
  /** Catalog command name without the leading slash (the key of per-command composer copy such as `hint.*`). */
37
38
  readonly name: string;
38
- /** Inserted command text with its argument separator; the bare complete name also retains the claim. */
39
+ /** Command spelling followed by its argument separator; the composer shows the spelling as a chip and inserts the separator after it. */
39
40
  readonly token: string;
40
41
  readonly hint?: string;
41
42
  readonly attachments?: boolean;
@@ -109,6 +110,13 @@ export interface InputTriggerController {
109
110
  adjudicate(line: string, signal: AbortSignal, envelope: {
110
111
  readonly attachments: number;
111
112
  }): Promise<PickOutcome>;
113
+ /**
114
+ * Resolve the claim a persisted command chip names without acting on it.
115
+ * @param token - command spelling without its argument separator, e.g. '/goal'.
116
+ * @param signal - restore-attempt abort.
117
+ * @returns the claim, or undefined when no source claims the token; rejects when catalog warmup fails.
118
+ */
119
+ resolveClaim(token: string, signal: AbortSignal): Promise<CommandClaim | undefined>;
112
120
  /**
113
121
  * @param source - chip owner, or undefined for a text reference.
114
122
  * @param reference - source id and glyph.
@@ -154,14 +162,14 @@ export type DraftAttachmentId = Branded<'DraftAttachmentId'>;
154
162
  * applied the edit after phase and span guards).
155
163
  */
156
164
  export interface InputTarget {
157
- /** Replace the trigger span with claim.token and enter claimed (span-CAS'd). */
165
+ /** Replace the leading trigger span with a command chip and enter claimed (span-CAS'd). */
158
166
  beginCommand(claim: CommandClaim, span: TokenSpan): boolean;
159
167
  /** Replace the trigger span with one reference chip (span-CAS'd). */
160
168
  insertReference(ref: ReferenceInsert, span: TokenSpan): boolean;
161
169
  }
162
170
  /** Per-session input facade owned by the conversation wiring layer. */
163
171
  export interface SessionInput extends InputTarget {
164
- /** Replace the whole draft (persisted-draft seed and programmatic writes). */
172
+ /** Replace the whole draft with plain text, removing inline reference chips. */
165
173
  setDraft(text: string): void;
166
174
  /** Append ordered browser-owned attachment ids; busy admission phases refuse. */
167
175
  addAttachments(ids: readonly DraftAttachmentId[]): boolean;
@@ -196,7 +204,21 @@ export interface SessionInput extends InputTarget {
196
204
  export interface SessionInputResolver {
197
205
  /** Resolve the facade for one session-scope ctx. */
198
206
  for(actx: Context): SessionInput;
207
+ /**
208
+ * Prepare the exact retained Session before navigation publishes it.
209
+ * @param binding - target Session generation, already retained by the caller.
210
+ * @param options - content to adopt and explicit permission to replace existing text.
211
+ * @returns applied, preserved, or blocked by a pending submission or disposal.
212
+ */
213
+ requestDraftInitialization(binding: SessionBinding, options: DraftInitializationOptions): DraftInitializationResult;
199
214
  }
215
+ /** Initial text for a target Session; attachments remain under their existing owner. */
216
+ export interface DraftInitializationOptions {
217
+ readonly prompt?: string;
218
+ readonly clearPreviousDraft?: boolean;
219
+ }
220
+ /** An initialization request never waits for a React mount or a remote catalog. */
221
+ export type DraftInitializationResult = 'applied' | 'preserved' | 'blocked';
200
222
  /**
201
223
  * The public input action face provided to every session-scope slot
202
224
  * component: stable-identity void callbacks, mirroring the
@@ -213,8 +235,10 @@ export interface InputActions {
213
235
  * @returns false when the draft changed or submission locked the editor.
214
236
  */
215
237
  insertText(text: string, span: TokenSpan): boolean;
216
- /** Replace the whole draft (persisted-draft seed and programmatic writes). */
238
+ /** Replace the whole draft with plain text, removing inline reference chips. */
217
239
  setDraft(text: string): void;
240
+ /** Write the current semantic document through the mounted persistence callback. */
241
+ persistDraft(): void;
218
242
  /** Append ordered browser-owned attachment ids; busy admission phases refuse. */
219
243
  addAttachments(ids: readonly DraftAttachmentId[]): boolean;
220
244
  /** Remove one browser-owned attachment id; busy admission phases refuse. */
@@ -272,15 +296,15 @@ export interface SubmitAttempt {
272
296
  /**
273
297
  * Submit-machine input events (the machine's single write path). Text
274
298
  * mutation lives in the editor; the machine only observes the draft through
275
- * event payloads (claim integrity, enter snapshots, settlement decisions).
299
+ * event payloads (leading command chip, enter snapshots, settlement decisions).
276
300
  */
277
301
  export type InputEvent =
278
- /** Clipboard projection changed: the claimed integrity watch runs (zero effects). */
302
+ /** Editor content changed; carries the claim of the leading command chip, if any (zero effects). */
279
303
  {
280
304
  readonly type: 'draft-changed';
281
- readonly draft: string;
305
+ readonly command: CommandClaim | undefined;
282
306
  }
283
- /** The editor applied a claim-token replacement: enter claimed. */
307
+ /** The editor inserted a command chip: enter claimed. */
284
308
  | {
285
309
  readonly type: 'claim';
286
310
  readonly claim: CommandClaim;
@@ -299,12 +323,12 @@ export type InputEvent =
299
323
  readonly attempt: SubmitAttempt;
300
324
  readonly message: string;
301
325
  }
302
- /** Settlement carries the live clipboard projection for suffix-retention and claim re-entry decisions. */
326
+ /** Settlement carries the leading command chip's claim, which the claim follows after a failure. */
303
327
  | {
304
328
  readonly type: 'submit-settled';
305
329
  readonly attempt: SubmitAttempt;
306
330
  readonly ok: boolean;
307
- readonly draft: string;
331
+ readonly command: CommandClaim | undefined;
308
332
  readonly outcome?: SubmitOutcome;
309
333
  readonly message?: string;
310
334
  }
@@ -122,8 +122,9 @@ export type ModelRetryNode = LlmRetryEventData & {
122
122
  retryState: 'scheduled' | 'started' | 'cancelled';
123
123
  };
124
124
  /**
125
- * Durable terminal failure for a turn that ended with an error reason; the
126
- * turn's settled retry chain renders separately and never replaces this node.
125
+ * Durable terminal notice for a turn that ended with an error, a sign-out
126
+ * cancellation, or an interruption by a QiLin stop; the turn's settled retry
127
+ * chain renders separately and never replaces this node.
127
128
  */
128
129
  export interface TurnErrorNode {
129
130
  kind: 'turn-error';
@@ -135,7 +136,11 @@ export interface TurnErrorNode {
135
136
  step: number;
136
137
  /** Sanitized provider message; empty when a known code owns localized copy. */
137
138
  message: string;
138
- /** Stable provider failure code, when recorded. */
139
+ /**
140
+ * Stable provider failure code when recorded, or a Client display code:
141
+ * `ACCOUNT_SIGNED_OUT` for a sign-out cancellation, `INTERRUPTED` for a turn
142
+ * closed after QiLin stopped.
143
+ */
139
144
  code?: string;
140
145
  }
141
146
  /** Durable notice for a turn ended by the per-request output-token cap. */
@@ -11,7 +11,7 @@ import type { SessionId } from '@qilin-agent/session/types';
11
11
  import type { WorkspaceId } from '@qilin-agent/workspace/types';
12
12
  import type { ComposerBlock } from './composer-blocks.ts';
13
13
  import type { DraftAttachmentId, InputActions, InputNotice, InputState } from './input.ts';
14
- import type { ComposerKeyboard, EditSelection } from './draft-editor.ts';
14
+ import type { ComposerKeyboard, DraftSnapshot, EditSelection } from './draft-editor.ts';
15
15
  import type { createConversationStore } from '../stores.ts';
16
16
  import type { BusyEnterBehavior } from './composer-submission.ts';
17
17
  import type { ConversationSnapshot } from './snapshot.ts';
@@ -211,6 +211,11 @@ declare module '@qilin-agent/client-ui-slots' {
211
211
  scope: 'session-maybe';
212
212
  owner: HeroAgentPresetOwnerProps;
213
213
  };
214
+ /** Compact options right-aligned in the New Session row after the workspace and preset controls; rendered only with a Session. */
215
+ 'conversation.hero.options': {
216
+ kind: 'list';
217
+ scope: 'session';
218
+ };
214
219
  /** Full-width entries above the composer card. */
215
220
  'conversation.input.dock': {
216
221
  kind: 'list';
@@ -312,6 +317,10 @@ declare module '@qilin-agent/client-ui-slots' {
312
317
  kind: 'single';
313
318
  scope: 'session-maybe';
314
319
  };
320
+ 'conversation.hero.options': {
321
+ kind: 'list';
322
+ scope: 'session';
323
+ };
315
324
  };
316
325
  inject: ConversationInjected;
317
326
  locale: 'conversation';
@@ -414,7 +423,7 @@ export interface ConversationSessionInjected {
414
423
  readonly conversationViews: ObservableSnapshot<readonly ViewTab[]>;
415
424
  };
416
425
  /** Bind input draft persistence to the Session-owned store instance. */
417
- bindDraftMirror: (write: (text: string) => void) => () => void;
426
+ bindDraftPersistence: (write: (draft: DraftSnapshot) => void) => () => void;
418
427
  /** Select and activate one View while addressing an opaque focus request to it. */
419
428
  openView: (view: string, focus: string) => void;
420
429
  }
@@ -1,4 +1,5 @@
1
1
  /** Conversation view and session-local presentation state. */
2
+ import type { DraftInput } from './draft-editor.ts';
2
3
  /**
3
4
  * One conversation view tab, projected from a 'conversation.view' slot
4
5
  * entry's registration options (label falls back to the entry id).
@@ -27,7 +28,7 @@ export interface ConversationViewRequest {
27
28
  /** The conversation store's own state: the composer draft, the selected View, and one pending focus request. */
28
29
  export interface ConversationStoreState {
29
30
  /** Composer draft (persisted; survives session switches and reloads). */
30
- draft: string;
31
+ draft: DraftInput;
31
32
  /** Preferred `conversation.view` entry id; null resolves to Chat when registered. */
32
33
  view: string | null;
33
34
  /** One-shot focus request addressed to the selected View. */
@@ -43,6 +43,9 @@ export declare class ConversationNodeAssembler implements ConversationViewSnapsh
43
43
  private readonly groups;
44
44
  private readonly pendingGroupStores;
45
45
  private readonly activeTargets;
46
+ /** Matches of the event in dispatch, kept by index; dispatch is not reentrant and reuses both arrays. */
47
+ private readonly matchedDefinitions;
48
+ private readonly matchedResults;
46
49
  private hasMore;
47
50
  private replacePending;
48
51
  private timelineDirty;
@@ -0,0 +1,22 @@
1
+ import type { DraftInput, DraftSnapshot, Occurrence } from './contract/draft-editor.ts';
2
+ /**
3
+ * Copy input into the semantic draft representation without editor runtime identities.
4
+ * @param input - plain text or caller-owned structured content.
5
+ * @returns an owned snapshot ready for editor import.
6
+ */
7
+ export declare function resolveDraftInput(input: DraftInput): DraftSnapshot;
8
+ /**
9
+ * Decode persisted drafts, accepting the earlier plain-string representation.
10
+ * @param value - JSON value read from browser storage.
11
+ * @returns the decoded draft, or undefined for invalid data.
12
+ */
13
+ export declare function parseStoredDraft(value: unknown): DraftSnapshot | undefined;
14
+ /**
15
+ * Export reference identities without their editor-local occurrence ids.
16
+ * @param text - current clipboard projection.
17
+ * @param occurrences - matching reference projections from the same document revision.
18
+ * @param commandToken - claim token of the command chip opening the document, if any.
19
+ * @returns an editor-independent snapshot.
20
+ */
21
+ export declare function snapshotDraft(text: string, occurrences: readonly Occurrence[], commandToken?: string): DraftSnapshot;
22
+ //# sourceMappingURL=draft.d.ts.map
@@ -25,8 +25,8 @@ export type { ConversationLocationDataChange } from './conversation/location-ind
25
25
  export { ConversationViewRegistry } from './conversation/view-registry.ts';
26
26
  export type { ConversationKey } from './locales.ts';
27
27
  export type { ComposerAttachment, ComposerAttachmentsOwnerProps, ComposerAttachmentsProps, ComposerFileAttachment, ComposerImageAttachment, DraftFileUpload, DraftFileUploads, ComposerBarInjected, ComposerBarOwnerProps, ComposerBarProps, ComposerChainProps, ConversationHeaderActionOwnerProps, ConversationHeaderCornerOwnerProps, ConversationHeaderLineageOwnerProps, ConversationContentInputProps, ConversationContentProps, ConversationInjected, ConversationSessionHeaderInjected, ConversationSessionHeaderSlotProps, ConversationSessionInjected, ConversationSessionSlotProps, ConversationSlotProps, ConversationViewsProps, ConversationWidthControlsInputProps, ConversationWidthControlsProps, ConversationStore, ConvViewOwnerProps, ConvViewProps, EmptyWorkspaceOwnerProps, HeroAgentPresetOwnerProps, HeroBrandMarkOwnerProps, InputControlOwnerProps, InputZone, MessageImageLoader, MessageImageSource, MessageImagesOwnerProps, RenderMessageImages, UseConversation, UseConversationViews, } from './contract/slots.ts';
28
- export type { BeginCommandRequest, CommandClaim, ConsumeTokenRequest, DraftAttachmentId, InputActions, InputState, InsertReferenceRequest, InsertTextRequest, PickOutcome, SessionInput, SessionInputResolver, SubmitAttachment, SubmitOutcome, } from './contract/input.ts';
29
- export type { ArbitrateKey, ArbitrateOutcome, ReferenceInsert, TokenSpan } from './contract/draft-editor.ts';
28
+ export type { BeginCommandRequest, CommandClaim, ConsumeTokenRequest, DraftAttachmentId, DraftInitializationOptions, DraftInitializationResult, InputActions, InputState, InsertReferenceRequest, InsertTextRequest, PickOutcome, SessionInput, SessionInputResolver, SubmitAttachment, SubmitOutcome, } from './contract/input.ts';
29
+ export type { ArbitrateKey, ArbitrateOutcome, DraftInput, DraftReference, DraftSnapshot, ReferenceInsert, TokenSpan } from './contract/draft-editor.ts';
30
30
  export type { ComposerBlock, ComposerBlocks } from './contract/composer-blocks.ts';
31
31
  declare module '@qilin-agent/kylin' {
32
32
  interface Context {
@@ -25,7 +25,7 @@ export interface TextRefRange {
25
25
  * exact lexicon member; a `/name` token must end at whitespace or the draft
26
26
  * end ('/name/x' is a path, '/name。' is prose).
27
27
  * @param draft - draft text.
28
- * @param lexicon - per-trigger name lists (a missing trigger scans nothing).
28
+ * @param lexicon - per-trigger name lists; missing catalogs match no named tokens.
29
29
  * @returns matched ranges in draft order.
30
30
  */
31
31
  export declare function scanTextRefs(draft: string, lexicon: ReadonlyMap<'/' | '@', readonly string[]>): TextRefRange[];
@@ -0,0 +1,13 @@
1
+ import type { ReactNode } from 'react';
2
+ /** Display inputs of the command chip. */
3
+ export interface CommandChipProps {
4
+ /** Claimed token without its argument separator, e.g. `/goal`. */
5
+ readonly token: string;
6
+ }
7
+ /**
8
+ * Render the claimed command chip.
9
+ * @param props - the token to display.
10
+ * @returns the chip body.
11
+ */
12
+ export declare function CommandChip({ token }: CommandChipProps): ReactNode;
13
+ //# sourceMappingURL=CommandChip.d.ts.map
@@ -0,0 +1,87 @@
1
+ /**
2
+ * CommandChipNode: the claimed slash command as one atomic Lexical decorator
3
+ * on the document's leading seat. The node carries the claim it was inserted
4
+ * for, so a history restore of the node restores the claim with it.
5
+ * `getTextContent()` answers the token without its argument separator; the
6
+ * separator is an ordinary text node after the chip. The detect projection
7
+ * counts the chip as one U+FFFC; see projection.ts.
8
+ */
9
+ import type { JSX } from 'react';
10
+ import type { LexicalEditor, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from 'lexical';
11
+ import { DecoratorNode } from 'lexical';
12
+ import type { CommandClaim } from '../../contract/input.ts';
13
+ /** JSON form of one command chip (Lexical node serialization contract). */
14
+ export type SerializedCommandChipNode = Spread<{
15
+ text: string;
16
+ }, SerializedLexicalNode>;
17
+ /** One claimed command as an atomic decorator node. */
18
+ export declare class CommandChipNode extends DecoratorNode<JSX.Element> {
19
+ /** The claim this chip anchors; its submit closure has no JSON form. */
20
+ __claim: CommandClaim;
21
+ /** Lexical node registry type tag. */
22
+ static getType(): string;
23
+ /**
24
+ * Clone with identity (Lexical writable-copy contract).
25
+ * @param node - node to clone.
26
+ * @returns a copy carrying the same NodeKey and claim.
27
+ */
28
+ static clone(node: CommandChipNode): CommandChipNode;
29
+ /**
30
+ * Rebuild one chip from its JSON form. JSON cannot carry the claim's
31
+ * submit closure, so an imported command is its literal token text.
32
+ * @param json - serialized chip.
33
+ * @returns a plain text node holding the token.
34
+ */
35
+ static importJSON(json: SerializedCommandChipNode): LexicalNode;
36
+ /**
37
+ * @param claim - the command claim the chip anchors.
38
+ * @param key - Lexical clone-path key; absent for fresh nodes.
39
+ */
40
+ constructor(claim: CommandClaim, key?: NodeKey);
41
+ /** Serialize to the JSON node form (the token text alone). */
42
+ exportJSON(): SerializedCommandChipNode;
43
+ /**
44
+ * Mount the chip's host element; the decorator portal renders into it.
45
+ * @returns an inline, non-editable span carrying the test/e2e anchor.
46
+ */
47
+ createDOM(): HTMLElement;
48
+ /** Host element never changes shape. */
49
+ updateDOM(): boolean;
50
+ /** The chip sits in the text line. */
51
+ isInline(): boolean;
52
+ /**
53
+ * Arrows step across the chip and the default character deletion removes
54
+ * it whole, for the reason documented on ReferenceChipNode.isKeyboardSelectable;
55
+ * registerCommandChipSelection inserts the selecting step before that deletion.
56
+ */
57
+ isKeyboardSelectable(): boolean;
58
+ /** Clipboard / persistence projection: the token without its separator. */
59
+ getTextContent(): string;
60
+ /** The claim this chip anchors. */
61
+ getClaim(): CommandClaim;
62
+ /** React face rendered into the host element by the decorator portal. */
63
+ decorate(): JSX.Element;
64
+ }
65
+ /**
66
+ * Mint one chip node for a claim.
67
+ * @param claim - the command claim.
68
+ * @returns the fresh node.
69
+ */
70
+ export declare function $createCommandChipNode(claim: CommandClaim): CommandChipNode;
71
+ /**
72
+ * Command chip type guard.
73
+ * @param node - any node or nullish.
74
+ * @returns whether the node is a CommandChipNode.
75
+ */
76
+ export declare function $isCommandChipNode(node: LexicalNode | null | undefined): node is CommandChipNode;
77
+ /**
78
+ * Keep command chips on the leading seat. A chip anywhere but the first
79
+ * child of the first block (text, a line break, or a block inserted before
80
+ * it) becomes its literal token, which releases the claim. Inserting a
81
+ * sibling before the chip or moving it to another block marks the chip dirty;
82
+ * a block inserted before the chip's block marks only that block dirty.
83
+ * @param editor - the shell-owned editor.
84
+ * @returns the unregister disposer.
85
+ */
86
+ export declare function registerCommandChipSeat(editor: LexicalEditor): () => void;
87
+ //# sourceMappingURL=command-chip-node.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Two-step command chip deletion. A character deletion whose direction
3
+ * reaches the command chip from a collapsed caret first selects the chip; the
4
+ * next deletion in that direction removes it. Deletions arrive as the
5
+ * character-deletion command (Backspace, Delete, and the platform shortcuts
6
+ * the engine maps to them) or as a deletion input event, which some platforms
7
+ * apply without that command. A key that produces no character deletion, a
8
+ * click, blur, or a caret that no longer touches the chip clears the
9
+ * selection. The selected state is presentation on the chip's host and the
10
+ * root element; the editor selection stays a collapsed range beside the chip,
11
+ * so typing and arrows keep their plain-text behavior.
12
+ */
13
+ import type { LexicalEditor } from 'lexical';
14
+ /**
15
+ * Register two-step deletion for the command chip.
16
+ * @param editor - the shell-owned editor.
17
+ * @returns the unregister disposer, which also clears a selected chip.
18
+ */
19
+ export declare function registerCommandChipSelection(editor: LexicalEditor): () => void;
20
+ //# sourceMappingURL=command-chip-selection.d.ts.map
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import type { LexicalNode, NodeKey, Point } from 'lexical';
11
11
  import type { Occurrence } from '../../contract/draft-editor.ts';
12
+ import type { CommandClaim } from '../../contract/input.ts';
12
13
  /** The detect-projection stand-in for one chip (object replacement character). */
13
14
  export declare const ATOMIC_CHAR = "\uFFFC";
14
15
  /** One leaf (or gap) of the composer document in projection coordinates. */
@@ -75,6 +76,8 @@ export interface EditorProjection {
75
76
  } | null;
76
77
  /** Collapsed caret in detect coordinates; null while the selection is absent or ranged. */
77
78
  readonly caret: number | null;
79
+ /** Claim of the command chip opening the document; undefined without one. */
80
+ readonly command: CommandClaim | undefined;
78
81
  }
79
82
  /**
80
83
  * Fold one selection point to a detect offset.
@@ -1,7 +1,8 @@
1
1
  /** The Composer model's private Lexical editor, projections, and node operations. */
2
2
  import type { ObservableSnapshot } from '@qilin-agent/client-store';
3
3
  import type { LexicalEditor } from 'lexical';
4
- import type { Occurrence, ReferenceInsert } from '../../contract/draft-editor.ts';
4
+ import type { DraftReference, ReferenceInsert } from '../../contract/draft-editor.ts';
5
+ import type { CommandClaim } from '../../contract/input.ts';
5
6
  import type { EditorProjection } from './projection.ts';
6
7
  import type { DetectSpan } from './span-map.ts';
7
8
  type Lexicon = ReadonlyMap<'/' | '@', readonly string[]>;
@@ -9,7 +10,6 @@ type Lexicon = ReadonlyMap<'/' | '@', readonly string[]>;
9
10
  interface DraftEditorRuntimeDeps {
10
11
  readonly onUpdate: () => void;
11
12
  readonly openReference: (source: string | undefined, reference: Pick<ReferenceInsert, 'ref' | 'appearance'>) => boolean;
12
- readonly activeClaimToken: () => string | null;
13
13
  readonly lexicon: () => Lexicon;
14
14
  readonly resolveLexicon: () => ObservableSnapshot<Lexicon> | undefined;
15
15
  }
@@ -24,6 +24,7 @@ export declare class DraftEditorRuntime {
24
24
  private occurrenceSeq;
25
25
  /** Live lexicon subscription disposer; undefined until the controller resolves. */
26
26
  private lexiconOff;
27
+ private lexiconSource;
27
28
  /** @param deps - model callbacks used by editor listeners and transforms. */
28
29
  constructor(deps: DraftEditorRuntimeDeps);
29
30
  /**
@@ -44,12 +45,10 @@ export declare class DraftEditorRuntime {
44
45
  */
45
46
  private applyEdit;
46
47
  /**
47
- * Subscribe the text-ref re-scan to the controller's lexicon once the
48
- * controller resolves. The deps thunk cannot resolve at construction (the
49
- * shell is created inside the sessions provide materialization), so the
50
- * first interactive updates retry until it can.
48
+ * Reconnect decorations when the optional trigger provider changes.
49
+ * The rescan reads the current editor and does not replace its content.
51
50
  */
52
- private ensureLexiconSubscription;
51
+ refreshLexiconSubscription(): void;
53
52
  /**
54
53
  * Re-project inside the existing editor update callback.
55
54
  * @returns the projection preceding this read.
@@ -103,19 +102,28 @@ export declare class DraftEditorRuntime {
103
102
  * @returns whether the range mapped and the edit applied.
104
103
  */
105
104
  insertReference(span: DetectSpan, ref: ReferenceInsert, tail: string): boolean;
106
- /** Refresh claim-token decoration after the model's claim changes. */
107
- refreshClaimDecoration(): void;
105
+ /**
106
+ * Replace a span with a command chip followed by the claim token's
107
+ * argument separator, unless the separator already follows.
108
+ * @param span - detect-coordinate range.
109
+ * @param claim - the command claim the chip anchors.
110
+ * @param tail - the character following the range before editing.
111
+ * @param mergeHistory - merge into the previous history entry (a restored
112
+ * draft's chip is part of its seed, not an undoable step).
113
+ * @returns whether the range mapped and the edit applied.
114
+ */
115
+ insertCommand(span: DetectSpan, claim: CommandClaim, tail: string, mergeHistory?: boolean): boolean;
108
116
  /**
109
117
  * Clear committed content using the model's suffix decision inside the editor update.
110
118
  * @param prefixLength - returns the clipboard-prefix length to remove, or null to clear the root.
111
119
  */
112
120
  clearCommittedDraft(prefixLength: (clipboardText: string) => number | null): void;
113
121
  /**
114
- * Rebuild one model-selected failure snapshot, creating fresh reference nodes.
122
+ * Import semantic content or a model-selected failure snapshot with fresh reference nodes.
115
123
  * @param draft - clipboard text.
116
124
  * @param occurrences - reference occurrences in clipboard order.
117
125
  */
118
- restoreDraft(draft: string, occurrences: readonly Occurrence[]): void;
126
+ restoreDraft(draft: string, occurrences: readonly DraftReference[]): void;
119
127
  /** Cut the editor's undo history after a committed clear or restoration. */
120
128
  clearHistory(): void;
121
129
  }
@@ -28,17 +28,12 @@ export declare class TextRefNode extends TextNode {
28
28
  canInsertTextBefore(): boolean;
29
29
  }
30
30
  /**
31
- * Register the plain-text reference entity transform. The claim decoration
32
- * has precedence on the leading-token seat: while a command claim holds, the
33
- * claimed token must stay a plain TextNode (transforms register per concrete
34
- * node class, so a TextRefNode would never receive the TextNode claim
35
- * transform and the warn color would be lost).
31
+ * Register the plain-text reference entity transform.
36
32
  * @param editor - the shell-owned editor.
37
33
  * @param lexiconOf - live per-trigger name-roll accessor (the controller's aggregated store).
38
- * @param activeToken - live claim token accessor; null while unclaimed.
39
34
  * @returns the unregister disposer.
40
35
  */
41
- export declare function registerTextRefDecoration(editor: LexicalEditor, lexiconOf: () => ReadonlyMap<'/' | '@', readonly string[]>, activeToken: () => string | null): () => void;
36
+ export declare function registerTextRefDecoration(editor: LexicalEditor, lexiconOf: () => ReadonlyMap<'/' | '@', readonly string[]>): () => void;
42
37
  /**
43
38
  * Force a re-scan of the whole document (transforms only visit dirty nodes;
44
39
  * a lexicon roll change dirties nothing on its own). Queued, not discrete —