@deepseek-ai/dsh-client-ui-conversation 0.1.6-alpha.2 → 0.1.7-alpha.2

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 (33) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +20 -6
  3. package/README.zh.md +20 -6
  4. package/lib/client.js +1374 -370
  5. package/lib/index.js +9 -8
  6. package/lib/types/client/contract/conversation.d.ts +19 -0
  7. package/lib/types/client/contract/groups.d.ts +116 -0
  8. package/lib/types/client/contract/input.d.ts +9 -0
  9. package/lib/types/client/contract/records.d.ts +3 -7
  10. package/lib/types/client/contract/slots.d.ts +51 -17
  11. package/lib/types/client/conversation/assembler.d.ts +16 -1
  12. package/lib/types/client/conversation/assembly.d.ts +3 -0
  13. package/lib/types/client/conversation/group-registry.d.ts +30 -0
  14. package/lib/types/client/conversation/group-store.d.ts +29 -0
  15. package/lib/types/client/conversation/location-index.d.ts +6 -0
  16. package/lib/types/client/index.d.ts +3 -1
  17. package/lib/types/client/input/editor/claim-decor.d.ts +2 -2
  18. package/lib/types/client/input/editor/keymap.d.ts +6 -2
  19. package/lib/types/client/input/editor/runtime.d.ts +13 -0
  20. package/lib/types/client/input/editor/view-binding.d.ts +1 -1
  21. package/lib/types/client/input/facade.d.ts +7 -0
  22. package/lib/types/client/input/submission-policy.d.ts +6 -6
  23. package/lib/types/client/locales.d.ts +384 -4
  24. package/lib/types/client/queue/QueueDock.d.ts +1 -1
  25. package/lib/types/client/service.d.ts +6 -0
  26. package/lib/types/client/skeleton/ConversationHeader.d.ts +8 -0
  27. package/lib/types/client/skeleton/ConversationSession.d.ts +1 -1
  28. package/lib/types/client/skeleton/DefaultConversationViews.d.ts +1 -1
  29. package/lib/types/client/skeleton/control-row-layout.d.ts +10 -0
  30. package/lib/types/client/view-selection.d.ts +4 -0
  31. package/lib/types/index.d.ts +16 -5
  32. package/lib/types/submission-settings.d.ts +9 -1
  33. package/package.json +35 -34
package/lib/index.js CHANGED
@@ -10,18 +10,19 @@ const BUSY_ENTER_BEHAVIORS = ["queue", "steer"];
10
10
  /** Default preserves Enter-as-Queue for running conversations. */
11
11
  const DEFAULT_BUSY_ENTER_BEHAVIOR = "queue";
12
12
  /** Durable conversation schema; also the wire envelope the browser scope validates against. */
13
- const ConversationSettingsSchema = z.object({ [BUSY_ENTER_FIELD]: z.union([...BUSY_ENTER_BEHAVIORS]).default(DEFAULT_BUSY_ENTER_BEHAVIOR) });
13
+ const ConversationSettingsFields = { [BUSY_ENTER_FIELD]: z.union([...BUSY_ENTER_BEHAVIORS]).default(DEFAULT_BUSY_ENTER_BEHAVIOR) };
14
+ z.object(ConversationSettingsFields);
14
15
  //#endregion
15
16
  //#region lib/types/index.js
16
- /** Host registration for browser conversation preferences. */
17
- /**
18
- * Register the durable conversation section when a settings provider exists.
19
- * @param ctx - Host context whose optional settings service owns the section.
17
+ /** Live preferences projected to the browser. */
18
+ const Config = z.object({ [BUSY_ENTER_FIELD]: ConversationSettingsFields[BUSY_ENTER_FIELD].volatile() });
19
+ /** Host preferences are consumed through the configuration form projection.
20
+ * @param ctx Plugin context used for optional settings presentation.
20
21
  */
21
22
  function apply(ctx) {
22
- ctx.inject(["settings"], (settingsCtx) => {
23
- settingsCtx.settings.register(CONVERSATION_SETTINGS_NAMESPACE, ConversationSettingsSchema);
23
+ ctx.inject(["settings"], (child) => {
24
+ child.effect(() => child.settings.configure({ auto: false }, ctx.fiber));
24
25
  });
25
26
  }
26
27
  //#endregion
27
- export { BUSY_ENTER_BEHAVIORS, BUSY_ENTER_FIELD, CONVERSATION_SETTINGS_NAMESPACE, DEFAULT_BUSY_ENTER_BEHAVIOR, apply };
28
+ export { BUSY_ENTER_BEHAVIORS, BUSY_ENTER_FIELD, CONVERSATION_SETTINGS_NAMESPACE, Config, DEFAULT_BUSY_ENTER_BEHAVIOR, apply };
@@ -1,5 +1,6 @@
1
1
  import type { SessionEventLike } from '@deepseek-ai/dsh-api-session-controller/client';
2
2
  import type { SessionEvent } from '@deepseek-ai/dsh-session/types';
3
+ import type { ConversationGroupData, ConversationGroupedView, ConversationGroupInput } from './groups.ts';
3
4
  /** Definition-local identity and lifecycle role extracted from one event. */
4
5
  export interface ConversationMatchResult {
5
6
  readonly id: string;
@@ -119,6 +120,12 @@ export interface ConversationViewSnapshotMap {
119
120
  export interface ConversationViewSnapshotStore {
120
121
  /** @param target - registered view target. @returns its current snapshot. */
121
122
  get<Target extends Extract<keyof ConversationViewSnapshotMap, string>>(target: Target): ConversationViewSnapshotMap[Target] | undefined;
123
+ /**
124
+ * Read grouping for an activated target without activating another target.
125
+ * @param target - registered View target.
126
+ * @returns its grouped reader, when a Group Definition is active.
127
+ */
128
+ grouped<Target extends string>(target: Target): ConversationGroupedView<ConversationGroupData<Target>> | undefined;
122
129
  }
123
130
  /** Immutable public view of an assembled business Context. */
124
131
  export interface ConversationNodeContext<State = unknown> {
@@ -222,6 +229,7 @@ export interface ConversationViewBuilder<Node extends ConversationViewNode = Con
222
229
  replace(input: {
223
230
  readonly nodes: readonly Node[];
224
231
  readonly timeline: ConversationTimelineSnapshot;
232
+ readonly changedTurns?: readonly number[];
225
233
  }): Snapshot;
226
234
  /**
227
235
  * Apply only Nodes whose materialized values changed in this transaction.
@@ -231,11 +239,22 @@ export interface ConversationViewBuilder<Node extends ConversationViewNode = Con
231
239
  apply(input: {
232
240
  readonly upserts: readonly Node[];
233
241
  readonly timeline: ConversationTimelineSnapshot;
242
+ readonly changedTurns?: readonly number[];
234
243
  }): Snapshot;
244
+ /** @returns the latest target-processed Node inputs; required only for a grouped target. */
245
+ groupInput?(): ConversationGroupInput<Node>;
246
+ /** Publish local sources after all target snapshots and grouping results have been installed. */
247
+ publish?(): void;
235
248
  }
236
249
  /** Registry contribution that creates an isolated builder when a Session first uses this target. */
237
250
  export interface ConversationViewDefinition<Node extends ConversationViewNode = ConversationViewNode, Snapshot = unknown> {
238
251
  readonly target: string;
252
+ /**
253
+ * Address a tool call in this target's inspector; absent for non-inspection views.
254
+ * @param callId - tool-call identity from the Session.
255
+ * @returns the target's opaque focus identity.
256
+ */
257
+ toolCallFocus?(callId: string): string;
239
258
  /** @returns a new Session-owned incremental builder. */
240
259
  create(): ConversationViewBuilder<Node, Snapshot>;
241
260
  /**
@@ -0,0 +1,116 @@
1
+ /** Definition-owned grouping over a target's already materialized Nodes. */
2
+ import type { Branded } from '@deepseek-ai/dsh-brand';
3
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
4
+ import type { ConversationTimelineSnapshot, ConversationViewNode } from './conversation.ts';
5
+ /** Existing Node identity, without allocating another execution Node. */
6
+ export type NodeKey = Branded<'ConversationNodeKey'>;
7
+ /** Definition-local group identity within one Session and target. */
8
+ export type GroupKey = Branded<'ConversationGroupKey'>;
9
+ /** One whole Node or a renderer-owned part of that Node. */
10
+ export interface NodeReference {
11
+ readonly kind: 'node';
12
+ readonly key: NodeKey;
13
+ readonly groupPart?: string;
14
+ }
15
+ /** Root-list reference to a separately observed group. */
16
+ export interface GroupReference {
17
+ readonly kind: 'group';
18
+ readonly key: GroupKey;
19
+ }
20
+ /** The two supported root rendering positions. */
21
+ export type RenderEntry = NodeReference | GroupReference;
22
+ /** Immutable group data and its ordered, non-nested Node references. */
23
+ export interface GroupSnapshot<Data> {
24
+ readonly key: GroupKey;
25
+ readonly data: Data;
26
+ readonly members: readonly NodeReference[];
27
+ }
28
+ /**
29
+ * One atomic grouping update. Replacement input requires entries and a complete
30
+ * group replacement; apply input may omit entries to preserve the root list.
31
+ * Group removal never deletes source Nodes.
32
+ */
33
+ export interface GroupUpdate<Data> {
34
+ /** Complete root sequence; only its Node references and referenced group members render. */
35
+ readonly entries?: readonly RenderEntry[];
36
+ readonly groups: {
37
+ readonly kind: 'replace';
38
+ readonly snapshots: readonly GroupSnapshot<Data>[];
39
+ } | {
40
+ readonly kind: 'apply';
41
+ readonly upserts: readonly GroupSnapshot<Data>[];
42
+ readonly removes: readonly GroupKey[];
43
+ };
44
+ }
45
+ /** Target-processed Node values before and after one update. */
46
+ export interface NodeChange<Node> {
47
+ readonly previous: Node | undefined;
48
+ readonly current: Node;
49
+ }
50
+ /** Owning Turn and immediate neighbours in the target's visible Node order. */
51
+ export interface GroupNodePosition {
52
+ readonly turn: number | undefined;
53
+ readonly previous: NodeKey | undefined;
54
+ readonly next: NodeKey | undefined;
55
+ }
56
+ interface GroupInputNodes<Node> {
57
+ readonly order: readonly NodeKey[];
58
+ /** @param key - target Node identity. @returns its current value during this synchronous update. */
59
+ readonly readNode: (key: NodeKey) => Node | undefined;
60
+ /** @param turn - owning Turn. @returns its visible keys in target order; empty when absent. */
61
+ readonly readTurn: (turn: number) => readonly NodeKey[];
62
+ /** @param key - target Node identity. @returns its visible position, or absence outside the target order. */
63
+ readonly readPosition: (key: NodeKey) => GroupNodePosition | undefined;
64
+ readonly timeline: ConversationTimelineSnapshot;
65
+ }
66
+ /** Target-local inputs; apply includes lifecycle changes without Node upserts. */
67
+ export type ConversationGroupInput<Node> = GroupInputNodes<Node> & ({
68
+ readonly kind: 'replace';
69
+ } | {
70
+ readonly kind: 'apply';
71
+ readonly changes: readonly NodeChange<Node>[];
72
+ readonly changedTurns: readonly number[];
73
+ /** Turns whose visible keys or immediate neighbours changed, including removal and cross-Turn moves. */
74
+ readonly changedTurnOrders: readonly number[];
75
+ });
76
+ /** Framework-owned State for one Session's registered Group Definition. */
77
+ export interface ConversationGroupContext<State> {
78
+ readonly state: State;
79
+ }
80
+ /** Business grouping rules, driven after the target Builder has installed Nodes. */
81
+ export interface ConversationGroupDefinition<Node extends ConversationViewNode = ConversationViewNode, State = unknown, Data = unknown> {
82
+ readonly kind: string;
83
+ readonly target: string;
84
+ /** @returns initial Session-local grouping State. */
85
+ create(): State;
86
+ /**
87
+ * Consume target Node changes, not raw Session events or presentation modes.
88
+ * @param context - current Session-local State.
89
+ * @param input - current target inputs; its readers are valid only during this call.
90
+ * @returns State adopted by the framework.
91
+ */
92
+ update(context: ConversationGroupContext<State>, input: ConversationGroupInput<Node>): State;
93
+ /**
94
+ * Materialize pending grouping changes without changing the business State.
95
+ * @param context - State adopted after update.
96
+ * @returns complete output for replace input; an update or null when unchanged for apply input.
97
+ */
98
+ buildGroups(context: ConversationGroupContext<State>): GroupUpdate<Data> | null;
99
+ }
100
+ /** Merge-extensible group data associated with each registered target. */
101
+ export interface ConversationGroupDataMap {
102
+ }
103
+ /** Group data declared by a target; undeclared targets have no group payload. */
104
+ export type ConversationGroupData<Target extends string> = Target extends keyof ConversationGroupDataMap ? ConversationGroupDataMap[Target] : never;
105
+ /** Stable grouped reader; data-only updates preserve entries and member arrays. */
106
+ export interface ConversationGroupedView<Data> {
107
+ readonly entries: readonly RenderEntry[];
108
+ /**
109
+ * Observe a group without subscribing to the root sequence.
110
+ * @param key - group identity within this target.
111
+ * @returns an identity-stable source, whose value is absent after removal.
112
+ */
113
+ groupSource(key: GroupKey): ObservableSnapshot<GroupSnapshot<Data> | undefined>;
114
+ }
115
+ export {};
116
+ //# sourceMappingURL=groups.d.ts.map
@@ -204,6 +204,15 @@ export interface SessionInputResolver {
204
204
  * paste/…) stay InputBar-private and never ride this face.
205
205
  */
206
206
  export interface InputActions {
207
+ /** @returns a revision-guarded copy of the current editor selection. */
208
+ captureInsertion(): TokenSpan;
209
+ /**
210
+ * Insert asynchronous text without replacing subsequent edits or reference chips.
211
+ * @param text - plain text to insert in one undo step.
212
+ * @param span - captured selection and draft revision.
213
+ * @returns false when the draft changed or submission locked the editor.
214
+ */
215
+ insertText(text: string, span: TokenSpan): boolean;
207
216
  /** Replace the whole draft (persisted-draft seed and programmatic writes). */
208
217
  setDraft(text: string): void;
209
218
  /** Append ordered browser-owned attachment ids; busy admission phases refuse. */
@@ -198,13 +198,9 @@ export interface CompactionSummaryNode {
198
198
  shadowedTokenCount: number | null;
199
199
  }
200
200
  /**
201
- * Fallback for surface events this UI version does not know: the documented
202
- * default arm of `SessionEventMap`, which is merge-extensible, so the
203
- * projection's switch cannot end in `assertNever`. No event produces this node
204
- * because `isAppendSurfaceEvent` admits only the four types in core's
205
- * `SurfaceEventType`, and each has its own arm (`system/message` is claimed by
206
- * a Chat Definition that renders no transcript row) — and it exists so widening
207
- * that set core-side degrades to a raw row instead of dropping the event silently.
201
+ * Fallback for unclaimed append-surface events. The merge-extensible
202
+ * SessionEventMap permits unfamiliar events to retain a raw presentation.
203
+ * Known unsupported developer events throw before fallback selection.
208
204
  */
209
205
  export interface UnknownSurfaceNode {
210
206
  kind: 'unknown';
@@ -56,8 +56,12 @@ export interface ComposerAttachmentsOwnerProps {
56
56
  attachments: readonly ComposerAttachment[];
57
57
  /** Whether a document-level file drop may add attachments now. */
58
58
  canAcceptDrop: boolean;
59
- /** Add one dropped batch through the composer's validation path. */
60
- onAddFiles: (files: readonly File[]) => void;
59
+ /**
60
+ * Add one dropped batch through the composer's validation path.
61
+ * @param files - dropped, pasted, or picked browser files in source order.
62
+ * @param directories - members of `files` the drop source identified as directories.
63
+ */
64
+ onAddFiles: (files: readonly File[], directories?: ReadonlySet<File>) => void;
61
65
  /** Remove one draft attachment through the Conversation service. */
62
66
  onRemoveAttachment: (id: DraftAttachmentId) => void;
63
67
  /** Current per-draft upload states for file-kind attachments. */
@@ -127,10 +131,19 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
127
131
  view?: string;
128
132
  };
129
133
  };
134
+ /** Resident navigation container, including when no Session is selected. */
135
+ 'conversation.header': {
136
+ kind: 'single';
137
+ scope: 'session-maybe';
138
+ };
130
139
  /** Strict per-Session title, actions, and View navigation. */
131
140
  'conversation.session.header': {
132
141
  kind: 'single';
133
142
  scope: 'session';
143
+ owner: {
144
+ /** Parent-owned visibility shared with the header container styling. */
145
+ hideChrome: boolean;
146
+ };
134
147
  };
135
148
  /** Optional replacement for one Session breadcrumb title. */
136
149
  'conversation.session.header.lineage': {
@@ -150,16 +163,10 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
150
163
  scope: 'session';
151
164
  owner: ConversationHeaderActionOwnerProps;
152
165
  };
153
- /**
154
- * Leading seat before the Session breadcrumbs, for window-chrome-adjacent
155
- * controls (macOS desktop sidebar reopen and New Session while the sidebar
156
- * is hidden). The seat is laid out only while its occupant renders
157
- * something, and it stays mounted through the blank-session state so a
158
- * hidden sidebar always keeps a reopen control on screen.
159
- */
160
- 'conversation.session.header.leading': {
166
+ /** Global navigation before the Session title, available without a Session. */
167
+ 'conversation.header.leading': {
161
168
  kind: 'single';
162
- scope: 'session';
169
+ scope: 'root';
163
170
  owner: ConversationHeaderLeadingOwnerProps;
164
171
  };
165
172
  /**
@@ -229,6 +236,12 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
229
236
  kind: 'list';
230
237
  scope: 'session';
231
238
  };
239
+ /** Compact action after the model selector; it can expand across the toolbar while retaining the editor and submit action. */
240
+ 'conversation.input.activity': {
241
+ kind: 'single';
242
+ scope: 'session';
243
+ owner: InputActivityOwnerProps;
244
+ };
232
245
  /** Resident composer body, including the no-Session inert state. */
233
246
  'conversation.composer.bar': {
234
247
  kind: 'single';
@@ -253,7 +266,11 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
253
266
  scope: 'session';
254
267
  owner: InputControlOwnerProps;
255
268
  };
256
- /** Model selector inside the composer tool row. */
269
+ /**
270
+ * Model selector inside the composer tool row. When expanded controls cannot
271
+ * share a line, the row sets --dsh-composer-model-text-display to none and
272
+ * --dsh-composer-model-icon-display to block for an occupant's compact display.
273
+ */
257
274
  'conversation.input.model': {
258
275
  kind: 'single';
259
276
  scope: 'session';
@@ -344,7 +361,7 @@ export interface ConversationHeaderCornerOwnerProps {
344
361
  /** Marker field: the occupant receives no owner-specific values. */
345
362
  children?: never;
346
363
  }
347
- /** The leading seat's occupant derives its state from standard Session props. */
364
+ /** The leading seat exposes global navigation independently of a Session. */
348
365
  export interface ConversationHeaderLeadingOwnerProps {
349
366
  /** Marker field: the occupant receives no owner-specific values. */
350
367
  children?: never;
@@ -365,6 +382,8 @@ export interface InputZone {
365
382
  }
366
383
  /** Conversation View entries obtain their data from registered standard hooks. */
367
384
  export interface ConvViewOwnerProps {
385
+ /** Open a tool call's inspector when an inspection target is available. */
386
+ inspectCall: ((callId: string) => void) | undefined;
368
387
  /** Focus request addressed to the selected View. */
369
388
  viewRequest: import('./views.ts').ConversationViewRequest | null;
370
389
  /** Select a View and address one opaque focus identity to it. */
@@ -388,6 +407,7 @@ export interface ConversationSessionInjected {
388
407
  /** Package-owned View roster source bound only for the Conversation body. */
389
408
  readonly hooks: {
390
409
  readonly conversationViews: ObservableSnapshot<readonly ViewTab[]>;
410
+ readonly inspectCall: ObservableSnapshot<ConvViewOwnerProps['inspectCall']>;
391
411
  };
392
412
  /** Bind input draft persistence to the Session-owned store instance. */
393
413
  bindDraftMirror: (write: (text: string) => void) => () => void;
@@ -426,7 +446,14 @@ export interface ComposerBarOwnerProps {
426
446
  /** Package-private operations injected into the resident composer bar. */
427
447
  export interface ComposerBarInjected {
428
448
  keyboard: ComposerKeyboard | undefined;
429
- addFiles: ((files: readonly File[]) => string | null) | undefined;
449
+ /**
450
+ * Register one picked batch; resolves to the rejection copy or null. Where
451
+ * the browser shell reports host paths (the Desktop application), files
452
+ * and folders with a real path become `@path` references in the draft
453
+ * instead of uploads; `directories` names the members the drop source
454
+ * identified as directories.
455
+ */
456
+ addFiles: ((files: readonly File[], directories?: ReadonlySet<File>) => string | null) | undefined;
430
457
  removeAttachment: ((id: DraftAttachmentId) => void) | undefined;
431
458
  resolveDraftAttachments: ((ids: readonly DraftAttachmentId[]) => readonly ComposerAttachment[]) | undefined;
432
459
  /** Restart one failed file upload; absent without a session. */
@@ -451,8 +478,13 @@ export interface InputControlOwnerProps {
451
478
  /** Whether the composer currently refuses interaction. */
452
479
  locked: boolean;
453
480
  }
481
+ /** A toolbar activity hides ordinary accessory controls while expanded; its occupant must release expansion on unmount. */
482
+ export interface InputActivityOwnerProps extends InputControlOwnerProps {
483
+ /** @param active - whether the occupant needs the toolbar width before the submit action. */
484
+ onActiveChange: (active: boolean) => void;
485
+ }
454
486
  /** Full props of the resident composer bar. */
455
- export type ComposerBarProps = PropsRuntime<'conversation.composer.bar'> & PropsRenderSlots<'conversation.input.attachments' | 'conversation.input.overlay' | 'conversation.input.permission' | 'conversation.input.left' | 'conversation.input.plan' | 'conversation.input.right' | 'conversation.input.model' | 'conversation.composer.dock'> & InjectFace<ComposerBarInjected> & PropsLocale<'conversation'>;
487
+ export type ComposerBarProps = PropsRuntime<'conversation.composer.bar'> & PropsRenderSlots<'conversation.input.attachments' | 'conversation.input.overlay' | 'conversation.input.permission' | 'conversation.input.left' | 'conversation.input.plan' | 'conversation.input.right' | 'conversation.input.model' | 'conversation.input.activity' | 'conversation.composer.dock'> & InjectFace<ComposerBarInjected> & PropsLocale<'conversation'>;
456
488
  /** Owner values used to elect a composer takeover. */
457
489
  export interface ComposerChainProps {
458
490
  /** Current Session identity used by temporary business-owned entries. */
@@ -470,7 +502,7 @@ export interface HeroBrandMarkOwnerProps {
470
502
  className?: string | undefined;
471
503
  }
472
504
  /** Full props of the resident optional-Session Conversation shell. */
473
- export type ConversationSlotProps = PropsRuntime<'main.conversation'> & PropsRenderSlots<'conversation.session.header'> & PropsRenderFactories;
505
+ export type ConversationSlotProps = PropsRuntime<'main.conversation'> & PropsRenderSlots<'conversation.header'> & PropsRenderFactories;
474
506
  /** Inputs shared by main and embedded Conversation content occurrences. */
475
507
  export interface ConversationContentInputProps {
476
508
  variant: 'main' | 'embedded';
@@ -494,8 +526,10 @@ export type ConversationViewsProps = FactoryLocalComponentPropsOf<'conversation.
494
526
  export type ConversationWidthControlsProps = FactoryLocalComponentPropsOf<'conversation.content', 'widthControls'>;
495
527
  /** Full props of the strict Session body. */
496
528
  export type ConversationSessionSlotProps = PropsRuntime<'conversation.session'> & PropsRenderSlots<'conversation.view'> & PropsStore<ConversationStore> & InjectFace<ConversationSessionInjected>;
529
+ /** Full props of the resident navigation header. */
530
+ export type ConversationHeaderProps = PropsRuntime<'conversation.header'> & PropsRenderSlots<'conversation.header.leading' | 'conversation.session.header'>;
497
531
  /** Full props of the strict Session header. */
498
- export type ConversationSessionHeaderSlotProps = PropsRuntime<'conversation.session.header'> & PropsRenderSlots<'conversation.session.header.lineage' | 'conversation.session.header.leading' | 'conversation.session.header.actions' | 'conversation.session.header.utilities' | 'conversation.session.header.corner'> & PropsStore<ConversationStore> & InjectFace<ConversationSessionHeaderInjected> & PropsLocale<'conversation'>;
532
+ export type ConversationSessionHeaderSlotProps = PropsRuntime<'conversation.session.header'> & PropsRenderSlots<'conversation.session.header.lineage' | 'conversation.session.header.actions' | 'conversation.session.header.utilities' | 'conversation.session.header.corner'> & PropsStore<ConversationStore> & InjectFace<ConversationSessionHeaderInjected> & PropsLocale<'conversation'>;
499
533
  /** Full props of the draft-attachment renderer. */
500
534
  export type ComposerAttachmentsProps = PropsRuntime<'conversation.input.attachments'> & PropsLocale<'conversation'>;
501
535
  /** Owner share common to blank-session Workspace pickers. */
@@ -1,6 +1,7 @@
1
1
  import type { SessionAssistantSettlementEntry, SessionEventLikeEntry } from '@deepseek-ai/dsh-api-session-controller/client';
2
2
  import type { LlmAttemptId } from '@deepseek-ai/dsh-llm/brand';
3
3
  import type { ConversationNodeDefinition, ConversationPublication, ConversationViewDefinition, ConversationViewSnapshotMap, ConversationViewSnapshotStore } from '../contract/conversation.ts';
4
+ import type { ConversationGroupData, ConversationGroupDefinition, ConversationGroupedView } from '../contract/groups.ts';
4
5
  /** Event Registry subset consumed by a Session-owned Assembler. */
5
6
  export interface ConversationEventDefinitions {
6
7
  /** @returns ordinary Definitions in registration order. */
@@ -13,6 +14,13 @@ export interface ConversationViewDefinitions {
13
14
  /** @returns view builder factories in registration order. */
14
15
  entries(): readonly ConversationViewDefinition[];
15
16
  }
17
+ /** Registered grouping rules consumed by the target-neutral assembler. */
18
+ export interface ConversationGroupDefinitions {
19
+ /** @returns grouping Definitions in registration order. */
20
+ entries(): readonly ConversationGroupDefinition[];
21
+ /** @param target - View target. @returns its optional Group Definition. */
22
+ forTarget(target: string): ConversationGroupDefinition | undefined;
23
+ }
16
24
  /**
17
25
  * Session-owned incremental engine that assembles business Contexts from a
18
26
  * contiguous Event window and materializes registered view snapshots.
@@ -20,6 +28,7 @@ export interface ConversationViewDefinitions {
20
28
  export declare class ConversationNodeAssembler implements ConversationViewSnapshotStore {
21
29
  private readonly eventDefinitions;
22
30
  private readonly viewDefinitions;
31
+ private readonly groupDefinitions;
23
32
  private readonly contexts;
24
33
  private readonly contextsByKind;
25
34
  private readonly contextsBySeq;
@@ -31,6 +40,8 @@ export declare class ConversationNodeAssembler implements ConversationViewSnapsh
31
40
  private readonly revised;
32
41
  private readonly dependents;
33
42
  private readonly views;
43
+ private readonly groups;
44
+ private readonly pendingGroupStores;
34
45
  private readonly activeTargets;
35
46
  private hasMore;
36
47
  private replacePending;
@@ -38,8 +49,9 @@ export declare class ConversationNodeAssembler implements ConversationViewSnapsh
38
49
  /**
39
50
  * @param eventDefinitions - live Event Definition registry.
40
51
  * @param viewDefinitions - live view builder registry.
52
+ * @param groupDefinitions - optional registered grouping rules, independent of presentation modes.
41
53
  */
42
- constructor(eventDefinitions: ConversationEventDefinitions, viewDefinitions: ConversationViewDefinitions);
54
+ constructor(eventDefinitions: ConversationEventDefinitions, viewDefinitions: ConversationViewDefinitions, groupDefinitions?: ConversationGroupDefinitions);
43
55
  /**
44
56
  * Replace the complete loaded window after open, resync, or gap repair.
45
57
  * @param entries - complete contiguous window.
@@ -91,6 +103,7 @@ export declare class ConversationNodeAssembler implements ConversationViewSnapsh
91
103
  */
92
104
  snapshot(target: string): unknown;
93
105
  get<Target extends Extract<keyof ConversationViewSnapshotMap, string>>(target: Target): ConversationViewSnapshotMap[Target] | undefined;
106
+ grouped<Target extends string>(target: Target): ConversationGroupedView<ConversationGroupData<Target>> | undefined;
94
107
  /**
95
108
  * Read targets whose owners classify their latest snapshot as visible activity.
96
109
  * @returns target ids contributing visible activity.
@@ -118,6 +131,8 @@ export declare class ConversationNodeAssembler implements ConversationViewSnapsh
118
131
  private refreshMatchLocations;
119
132
  private buildNode;
120
133
  private replaceView;
134
+ private updateView;
135
+ private publishViews;
121
136
  private buildTargetNodes;
122
137
  private buildTargetUpserts;
123
138
  private buildLocationData;
@@ -10,6 +10,7 @@ import type { ConversationPromptSnapshot, RequestPromptInspection, SystemPromptN
10
10
  import { type SystemPromptState } from '../contract/system-prompt.ts';
11
11
  import { ConversationEventRegistry } from './event-registry.ts';
12
12
  import { ConversationViewRegistry } from './view-registry.ts';
13
+ import { ConversationGroupRegistry } from './group-registry.ts';
13
14
  /** Observable faces published for one Session's Conversation assembly. */
14
15
  export interface ConversationBinding {
15
16
  readonly snapshot: ObservableSnapshot<ConversationSnapshot>;
@@ -34,6 +35,8 @@ export declare class UiConversation extends Service {
34
35
  readonly events: ConversationEventRegistry;
35
36
  /** Registry of target View definitions. */
36
37
  readonly views: ConversationViewRegistry;
38
+ /** Business grouping rules over already materialized target Nodes. */
39
+ readonly groups: ConversationGroupRegistry;
37
40
  private readonly bindings;
38
41
  private readonly images;
39
42
  /**
@@ -0,0 +1,30 @@
1
+ /** Target-indexed business grouping registrations with the existing effect lifecycle. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { ConversationViewNode } from '../contract/conversation.ts';
4
+ import type { ConversationGroupData, ConversationGroupDefinition } from '../contract/groups.ts';
5
+ import { ConversationDefinitionRegistry } from './definition-registry.ts';
6
+ import type { ConversationViewRegistry } from './view-registry.ts';
7
+ /** One Group Definition per target; registration never instantiates its Builder. */
8
+ export declare class ConversationGroupRegistry extends ConversationDefinitionRegistry<ConversationGroupDefinition> {
9
+ private readonly views;
10
+ /**
11
+ * @param ctx - owning plugin context.
12
+ * @param views - registered target Builder definitions.
13
+ */
14
+ constructor(ctx: Context, views: ConversationViewRegistry);
15
+ /**
16
+ * Register grouping rules for an existing target.
17
+ * @param definition - business State and grouping output for the declared target data.
18
+ * @returns the effect-owned, idempotent registration disposer.
19
+ */
20
+ register<Node extends ConversationViewNode, State, Target extends string>(definition: ConversationGroupDefinition<Node, State, ConversationGroupData<Target>> & {
21
+ readonly target: Target;
22
+ }): () => void;
23
+ /**
24
+ * Find the grouping rules registered for one target.
25
+ * @param target - View target.
26
+ * @returns its grouping Definition, when registered.
27
+ */
28
+ forTarget(target: string): ConversationGroupDefinition | undefined;
29
+ }
30
+ //# sourceMappingURL=group-registry.d.ts.map
@@ -0,0 +1,29 @@
1
+ /** Keyed group publication and incremental validation of rendering positions. */
2
+ import { type ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
3
+ import type { ConversationViewNode } from '../contract/conversation.ts';
4
+ import type { ConversationGroupedView, GroupKey, GroupSnapshot, GroupUpdate, NodeKey, RenderEntry } from '../contract/groups.ts';
5
+ /** Validates a complete batch before installing its group and root-list changes. */
6
+ export declare class ConversationGroupStore<Data> implements ConversationGroupedView<Data> {
7
+ private root;
8
+ private rootGroups;
9
+ private readonly groups;
10
+ private readonly placements;
11
+ private readonly sources;
12
+ private readonly dirty;
13
+ /** @returns the identity-stable ordered root references. */
14
+ get entries(): readonly RenderEntry[];
15
+ groupSource(key: GroupKey): ObservableSnapshot<GroupSnapshot<Data> | undefined>;
16
+ /**
17
+ * Install one validated update without notifying readers.
18
+ * @param update - root replacement and complete or incremental group records.
19
+ * @param readNode - synchronous reader of the current target Nodes.
20
+ */
21
+ prepareAndInstall(update: GroupUpdate<Data>, readNode: (key: NodeKey) => ConversationViewNode | undefined): void;
22
+ /** Publish changed group sources after all related target data has been installed. */
23
+ publish(): void;
24
+ /** Remove grouping without deleting its source Nodes; publication remains deferred. */
25
+ clear(): void;
26
+ private collectRootGroups;
27
+ private collectChanges;
28
+ }
29
+ //# sourceMappingURL=group-store.d.ts.map
@@ -16,6 +16,7 @@ export declare class ConversationLocationIndex {
16
16
  private readonly turnDataStores;
17
17
  private readonly stepDataStores;
18
18
  private readonly dirtyDataStores;
19
+ private readonly changedTurns;
19
20
  private currentTurn;
20
21
  private currentStep;
21
22
  /**
@@ -23,6 +24,11 @@ export declare class ConversationLocationIndex {
23
24
  * @returns current timeline snapshot.
24
25
  */
25
26
  snapshot(): ConversationTimelineSnapshot;
27
+ /**
28
+ * Drain the Turn changes accumulated for one assembly flush.
29
+ * @returns Turn identities changed since the preceding drain.
30
+ */
31
+ takeChangedTurns(): readonly number[];
26
32
  /**
27
33
  * Replace all Definition-owned Location values while preserving reader identities.
28
34
  * @param entries - complete current set of Definition-owned Location values.
@@ -3,6 +3,8 @@ export { apply, Config, inject } from './apply.ts';
3
3
  export type { Config as ConversationConfig } from './apply.ts';
4
4
  export { UiConversation } from './conversation/assembly.ts';
5
5
  export type { ConversationBinding } from './conversation/assembly.ts';
6
+ export type { ConversationGroupRegistry } from './conversation/group-registry.ts';
7
+ export type { ConversationGroupContext, ConversationGroupData, ConversationGroupDataMap, ConversationGroupDefinition, ConversationGroupedView, ConversationGroupInput, GroupKey, GroupNodePosition, GroupReference, GroupSnapshot, GroupUpdate, NodeChange, NodeKey, NodeReference, RenderEntry, } from './contract/groups.ts';
6
8
  export { ConversationController, UnsupportedImageMediaTypeError } from './service.ts';
7
9
  export type { IConversation } from './service.ts';
8
10
  export type { ConversationContextReader, ConversationLocation, ConversationLocationData, ConversationLocationDataScope, ConversationLocationDataSource, ConversationLocationDataStore, ConversationMatch, ConversationMatchResult, ConversationNodeContext, ConversationNodeDefinition, ConversationPreviousContext, ConversationPublication, ConversationStartMatch, ConversationStepDataMap, ConversationTimelineSnapshot, ConversationTurnDataMap, ConversationViewBuilder, ConversationViewDefinition, ConversationViewNode, ConversationViewSnapshotMap, ConversationViewSnapshotStore, StepLocation, TurnLocation, } from './contract/conversation.ts';
@@ -15,7 +17,7 @@ export { inspectRequestPrompt } from './contract/request-inspection.ts';
15
17
  export type { SystemPromptState, SystemPromptInspector } from './contract/system-prompt.ts';
16
18
  export type { ConversationStoreState, ConversationViewRequest, ViewTab } from './contract/views.ts';
17
19
  export { ConversationNodeAssembler } from './conversation/assembler.ts';
18
- export type { ConversationEventDefinitions, ConversationViewDefinitions, } from './conversation/assembler.ts';
20
+ export type { ConversationEventDefinitions, ConversationGroupDefinitions, ConversationViewDefinitions, } from './conversation/assembler.ts';
19
21
  export { ConversationDefinitionRegistry } from './conversation/definition-registry.ts';
20
22
  export { ConversationEventRegistry } from './conversation/event-registry.ts';
21
23
  export { ConversationLocationIndex } from './conversation/location-index.ts';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Claim-token highlight: while a command claim holds, the draft's leading
3
- * token renders in the warn color. A TextNode transform keeps the token in
4
- * its own styled node (splitting when typing merges text into it), and the
3
+ * token renders in the business accent. A TextNode transform keeps the token
4
+ * in its own styled node (splitting when typing merges text into it), and the
5
5
  * shell nudges the first leaf dirty when the claim flips so entering and
6
6
  * leaving claimed restyles without a text edit.
7
7
  */
@@ -28,8 +28,12 @@ export interface ComposerKeymapHandlers {
28
28
  canSubmit(): boolean;
29
29
  /** The Enter gesture after every guard passed; `accelerated` = Ctrl/Cmd held. */
30
30
  submit(accelerated: boolean): void;
31
- /** Pasted files (image intake). */
32
- intakeFiles(files: readonly File[]): void;
31
+ /**
32
+ * Pasted files with directory metadata supplied by the clipboard entry API.
33
+ * @param files - browser files in clipboard order.
34
+ * @param directories - known directory members; absent when no entry identifies a directory.
35
+ */
36
+ intakeFiles(files: readonly File[], directories?: ReadonlySet<File>): void;
33
37
  /** Pasted plain text (sanitized insertion through the shell). */
34
38
  pasteText(text: string): void;
35
39
  }
@@ -88,6 +88,13 @@ export declare class DraftEditorRuntime {
88
88
  * @returns whether the range mapped and the edit applied.
89
89
  */
90
90
  replaceText(span: DetectSpan, text: string): boolean;
91
+ /**
92
+ * Insert an asynchronous text result as one independent undo operation.
93
+ * @param span - owner-validated insertion range.
94
+ * @param text - text sanitized with the same rules as paste.
95
+ * @returns whether the range mapped and the edit applied.
96
+ */
97
+ insertAsyncText(span: DetectSpan, text: string): boolean;
91
98
  /**
92
99
  * Insert a reference chip with the existing trailing-space rule.
93
100
  * @param span - detect-coordinate range.
@@ -96,6 +103,12 @@ export declare class DraftEditorRuntime {
96
103
  * @returns whether the range mapped and the edit applied.
97
104
  */
98
105
  insertReference(span: DetectSpan, ref: ReferenceInsert, tail: string): boolean;
106
+ /**
107
+ * Insert an ordered file-reference batch after the live selection without deleting it.
108
+ * @param references - validated references in source order.
109
+ * @returns whether the live insertion position accepted the batch.
110
+ */
111
+ insertFileReferences(references: readonly ReferenceInsert[]): boolean;
99
112
  /** Refresh claim-token decoration after the model's claim changes. */
100
113
  refreshClaimDecoration(): void;
101
114
  /**
@@ -11,7 +11,7 @@ interface DraftViewGate {
11
11
  running: boolean;
12
12
  steeringAvailable: boolean;
13
13
  busyEnter: BusyEnterBehavior;
14
- intakeFiles: (files: readonly File[]) => void;
14
+ intakeFiles: (files: readonly File[], directories?: ReadonlySet<File>) => void;
15
15
  uploadsPending: boolean;
16
16
  showToast: (text: string) => void;
17
17
  t: ComposerBarProps['t'];