@ahggg/dsh-side-chat 0.7.2 → 1.0.0

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 (185) hide show
  1. package/README.md +16 -13
  2. package/README.zh-CN.md +16 -13
  3. package/lib/client/apply.d.ts +0 -1
  4. package/lib/client/apply.js +48 -33
  5. package/lib/client/contracts.d.ts +3 -59
  6. package/lib/client/contracts.js +0 -1
  7. package/lib/client/dsh/ReadOnlyConversation.d.ts +10 -0
  8. package/lib/client/dsh/ReadOnlyConversation.js +91 -0
  9. package/lib/client/dsh/SideChatOverlay.d.ts +7 -0
  10. package/lib/client/{rc6/Rc6SideChatOverlay.js → dsh/SideChatOverlay.js} +26 -57
  11. package/lib/client/{rc6 → dsh}/context.d.ts +7 -3
  12. package/lib/client/dsh/context.js +1 -0
  13. package/lib/client/dsh/conversation-store.d.ts +22 -0
  14. package/lib/client/dsh/conversation-store.js +19 -0
  15. package/lib/client/dsh/primitive-labels.d.ts +2 -0
  16. package/lib/client/dsh/primitive-labels.js +4 -0
  17. package/lib/client/{rc6 → dsh}/remote-adapter.d.ts +2 -3
  18. package/lib/client/dsh/remote-adapter.js +22 -0
  19. package/lib/client/dsh/sessions-adapter.d.ts +56 -0
  20. package/lib/client/dsh/sessions-adapter.js +185 -0
  21. package/lib/client/index.d.ts +2 -3
  22. package/lib/client/index.js +0 -1
  23. package/lib/client/model-preference.d.ts +0 -1
  24. package/lib/client/model-preference.js +0 -1
  25. package/lib/client/panel/SelectionQuote.d.ts +0 -1
  26. package/lib/client/panel/SelectionQuote.js +8 -2
  27. package/lib/client/panel/SendIcon.d.ts +0 -1
  28. package/lib/client/panel/SendIcon.js +0 -1
  29. package/lib/client/panel/SideChatBody.d.ts +0 -1
  30. package/lib/client/panel/SideChatBody.js +0 -1
  31. package/lib/client/panel/SideChatErrorState.d.ts +0 -1
  32. package/lib/client/panel/SideChatErrorState.js +1 -2
  33. package/lib/client/panel/SideChatHeader.d.ts +0 -1
  34. package/lib/client/panel/SideChatHeader.js +0 -1
  35. package/lib/client/panel/SideChatModelSelect.d.ts +0 -1
  36. package/lib/client/panel/SideChatModelSelect.js +37 -19
  37. package/lib/client/panel/SideChatPanel.d.ts +0 -1
  38. package/lib/client/panel/SideChatPanel.js +12 -6
  39. package/lib/client/panel/StopIcon.d.ts +0 -1
  40. package/lib/client/panel/StopIcon.js +0 -1
  41. package/lib/client/panel/messages.d.ts +1 -2
  42. package/lib/client/panel/messages.js +8 -9
  43. package/lib/client/panel/use-auto-growing-textarea.d.ts +0 -1
  44. package/lib/client/panel/use-auto-growing-textarea.js +0 -1
  45. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts +3 -3
  46. package/lib/client/parent-composer/ParentConversationAnnotations.js +71 -28
  47. package/lib/client/parent-composer/add-to-conversation.d.ts +14 -29
  48. package/lib/client/parent-composer/add-to-conversation.js +134 -457
  49. package/lib/client/parent-composer/annotation-persistence.d.ts +8 -9
  50. package/lib/client/parent-composer/annotation-persistence.js +106 -103
  51. package/lib/client/parent-composer/composer-reference.d.ts +19 -48
  52. package/lib/client/parent-composer/composer-reference.js +26 -46
  53. package/lib/client/parent-composer/referenced-conversation.d.ts +5 -8
  54. package/lib/client/parent-composer/referenced-conversation.js +27 -61
  55. package/lib/client/selection/ConversationAnnotationMarkers.d.ts +0 -1
  56. package/lib/client/selection/ConversationAnnotationMarkers.js +5 -2
  57. package/lib/client/selection/SelectionActions.d.ts +0 -1
  58. package/lib/client/selection/SelectionActions.js +2 -2
  59. package/lib/client/selection/conversation-dom.d.ts +5 -0
  60. package/lib/client/selection/conversation-dom.js +24 -0
  61. package/lib/client/selection/selection-controller.d.ts +1 -2
  62. package/lib/client/selection/selection-controller.js +7 -4
  63. package/lib/client/selection/selection-limits.d.ts +0 -1
  64. package/lib/client/selection/selection-limits.js +0 -1
  65. package/lib/client/selection/selection-normalizer.d.ts +0 -1
  66. package/lib/client/selection/selection-normalizer.js +1 -2
  67. package/lib/client/side-chat-controller.d.ts +28 -42
  68. package/lib/client/side-chat-controller.js +245 -338
  69. package/lib/client.js +1202 -8951
  70. package/lib/host/index.d.ts +1 -2
  71. package/lib/host/index.js +1 -2
  72. package/lib/host/read-only-chat-service.d.ts +30 -0
  73. package/lib/host/read-only-chat-service.js +314 -0
  74. package/lib/index.d.ts +16 -9
  75. package/lib/index.js +34 -14
  76. package/lib/remote.d.ts +23 -15
  77. package/lib/remote.js +2 -6
  78. package/lib/shared/constants.d.ts +0 -14
  79. package/lib/shared/constants.js +0 -14
  80. package/lib/shared/contracts.d.ts +52 -20
  81. package/lib/shared/contracts.js +2 -4
  82. package/lib/shared/error-codes.d.ts +1 -2
  83. package/lib/shared/error-codes.js +2 -3
  84. package/lib/shared/observable.d.ts +0 -1
  85. package/lib/shared/observable.js +0 -1
  86. package/lib/shared/{archived-wire.d.ts → side-chat-wire.d.ts} +109 -21
  87. package/lib/shared/side-chat-wire.js +34 -0
  88. package/lib/typert.d.ts +3 -69
  89. package/lib/typert.js +120 -51
  90. package/package.json +80 -38
  91. package/lib/client/apply.d.ts.map +0 -1
  92. package/lib/client/apply.js.map +0 -1
  93. package/lib/client/contracts.d.ts.map +0 -1
  94. package/lib/client/contracts.js.map +0 -1
  95. package/lib/client/index.d.ts.map +0 -1
  96. package/lib/client/index.js.map +0 -1
  97. package/lib/client/model-preference.d.ts.map +0 -1
  98. package/lib/client/model-preference.js.map +0 -1
  99. package/lib/client/panel/SelectionQuote.d.ts.map +0 -1
  100. package/lib/client/panel/SelectionQuote.js.map +0 -1
  101. package/lib/client/panel/SendIcon.d.ts.map +0 -1
  102. package/lib/client/panel/SendIcon.js.map +0 -1
  103. package/lib/client/panel/SideChatBody.d.ts.map +0 -1
  104. package/lib/client/panel/SideChatBody.js.map +0 -1
  105. package/lib/client/panel/SideChatErrorState.d.ts.map +0 -1
  106. package/lib/client/panel/SideChatErrorState.js.map +0 -1
  107. package/lib/client/panel/SideChatHeader.d.ts.map +0 -1
  108. package/lib/client/panel/SideChatHeader.js.map +0 -1
  109. package/lib/client/panel/SideChatModelSelect.d.ts.map +0 -1
  110. package/lib/client/panel/SideChatModelSelect.js.map +0 -1
  111. package/lib/client/panel/SideChatPanel.d.ts.map +0 -1
  112. package/lib/client/panel/SideChatPanel.js.map +0 -1
  113. package/lib/client/panel/StopIcon.d.ts.map +0 -1
  114. package/lib/client/panel/StopIcon.js.map +0 -1
  115. package/lib/client/panel/messages.d.ts.map +0 -1
  116. package/lib/client/panel/messages.js.map +0 -1
  117. package/lib/client/panel/use-auto-growing-textarea.d.ts.map +0 -1
  118. package/lib/client/panel/use-auto-growing-textarea.js.map +0 -1
  119. package/lib/client/parent-composer/ParentConversationAnnotations.d.ts.map +0 -1
  120. package/lib/client/parent-composer/ParentConversationAnnotations.js.map +0 -1
  121. package/lib/client/parent-composer/add-to-conversation.d.ts.map +0 -1
  122. package/lib/client/parent-composer/add-to-conversation.js.map +0 -1
  123. package/lib/client/parent-composer/annotation-persistence.d.ts.map +0 -1
  124. package/lib/client/parent-composer/annotation-persistence.js.map +0 -1
  125. package/lib/client/parent-composer/composer-reference.d.ts.map +0 -1
  126. package/lib/client/parent-composer/composer-reference.js.map +0 -1
  127. package/lib/client/parent-composer/referenced-conversation.d.ts.map +0 -1
  128. package/lib/client/parent-composer/referenced-conversation.js.map +0 -1
  129. package/lib/client/rc6/ArchivedConversation.d.ts +0 -15
  130. package/lib/client/rc6/ArchivedConversation.d.ts.map +0 -1
  131. package/lib/client/rc6/ArchivedConversation.js +0 -230
  132. package/lib/client/rc6/ArchivedConversation.js.map +0 -1
  133. package/lib/client/rc6/Rc6SideChatOverlay.d.ts +0 -8
  134. package/lib/client/rc6/Rc6SideChatOverlay.d.ts.map +0 -1
  135. package/lib/client/rc6/Rc6SideChatOverlay.js.map +0 -1
  136. package/lib/client/rc6/SideChatTool.d.ts +0 -23
  137. package/lib/client/rc6/SideChatTool.d.ts.map +0 -1
  138. package/lib/client/rc6/SideChatTool.js +0 -395
  139. package/lib/client/rc6/SideChatTool.js.map +0 -1
  140. package/lib/client/rc6/context.d.ts.map +0 -1
  141. package/lib/client/rc6/context.js +0 -2
  142. package/lib/client/rc6/context.js.map +0 -1
  143. package/lib/client/rc6/remote-adapter.d.ts.map +0 -1
  144. package/lib/client/rc6/remote-adapter.js +0 -33
  145. package/lib/client/rc6/remote-adapter.js.map +0 -1
  146. package/lib/client/rc6/sessions-adapter.d.ts +0 -61
  147. package/lib/client/rc6/sessions-adapter.d.ts.map +0 -1
  148. package/lib/client/rc6/sessions-adapter.js +0 -370
  149. package/lib/client/rc6/sessions-adapter.js.map +0 -1
  150. package/lib/client/selection/ConversationAnnotationMarkers.d.ts.map +0 -1
  151. package/lib/client/selection/ConversationAnnotationMarkers.js.map +0 -1
  152. package/lib/client/selection/SelectionActions.d.ts.map +0 -1
  153. package/lib/client/selection/SelectionActions.js.map +0 -1
  154. package/lib/client/selection/selection-controller.d.ts.map +0 -1
  155. package/lib/client/selection/selection-controller.js.map +0 -1
  156. package/lib/client/selection/selection-limits.d.ts.map +0 -1
  157. package/lib/client/selection/selection-limits.js.map +0 -1
  158. package/lib/client/selection/selection-normalizer.d.ts.map +0 -1
  159. package/lib/client/selection/selection-normalizer.js.map +0 -1
  160. package/lib/client/side-chat-controller.d.ts.map +0 -1
  161. package/lib/client/side-chat-controller.js.map +0 -1
  162. package/lib/client.js.map +0 -1
  163. package/lib/host/archived-fork-service.d.ts +0 -29
  164. package/lib/host/archived-fork-service.d.ts.map +0 -1
  165. package/lib/host/archived-fork-service.js +0 -264
  166. package/lib/host/archived-fork-service.js.map +0 -1
  167. package/lib/host/index.d.ts.map +0 -1
  168. package/lib/host/index.js.map +0 -1
  169. package/lib/index.d.ts.map +0 -1
  170. package/lib/index.js.map +0 -1
  171. package/lib/remote.d.ts.map +0 -1
  172. package/lib/remote.js.map +0 -1
  173. package/lib/shared/archived-wire.d.ts.map +0 -1
  174. package/lib/shared/archived-wire.js +0 -48
  175. package/lib/shared/archived-wire.js.map +0 -1
  176. package/lib/shared/constants.d.ts.map +0 -1
  177. package/lib/shared/constants.js.map +0 -1
  178. package/lib/shared/contracts.d.ts.map +0 -1
  179. package/lib/shared/contracts.js.map +0 -1
  180. package/lib/shared/error-codes.d.ts.map +0 -1
  181. package/lib/shared/error-codes.js.map +0 -1
  182. package/lib/shared/observable.d.ts.map +0 -1
  183. package/lib/shared/observable.js.map +0 -1
  184. package/lib/typert.d.ts.map +0 -1
  185. package/lib/typert.js.map +0 -1
@@ -1,19 +1,18 @@
1
1
  import type { SessionId } from '../../shared/contracts.js';
2
- import type { ParentComposerInput } from './add-to-conversation.js';
3
- interface AnnotationStorage {
2
+ import type { ParentComposerInput } from './composer-reference.js';
3
+ interface ReferenceStorage {
4
4
  getItem(key: string): string | null;
5
5
  setItem(key: string, value: string): void;
6
6
  removeItem(key: string): void;
7
7
  }
8
- /** Tab-scoped recovery for drafts persisted as a reference clipboard projection. */
8
+ /** Recover the complete reference-bearing draft, not only a leading annotation.
9
+ * DSH persists clipboard text, so conversation chips need recovery as well. */
9
10
  export declare class ConversationAnnotationPersistence {
10
11
  private readonly storage;
11
- private readonly observedSessions;
12
- constructor(storage?: AnnotationStorage | undefined);
12
+ private readonly observedInputs;
13
+ private reconciling;
14
+ constructor(storage?: ReferenceStorage | undefined);
13
15
  reconcile(sessionId: SessionId, input: ParentComposerInput): void;
14
- private read;
15
- private write;
16
- private remove;
16
+ private restore;
17
17
  }
18
18
  export {};
19
- //# sourceMappingURL=annotation-persistence.d.ts.map
@@ -1,135 +1,138 @@
1
- import { conversationAnnotationRecoveryRecord, removeOrphanedConversationAnnotationPlaceholder, restoreConversationAnnotationReference, } from './add-to-conversation.js';
2
- const STORAGE_PREFIX = 'dsh-side-chat:composer-annotations:';
1
+ import { SELECTION_REFERENCE_SOURCE } from './add-to-conversation.js';
2
+ import { SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE } from './referenced-conversation.js';
3
+ const STORAGE_PREFIX = 'dsh-side-chat:composer-references:';
3
4
  function browserSessionStorage() {
4
- if (typeof window === 'undefined')
5
- return;
6
5
  try {
7
- return window.sessionStorage;
6
+ return typeof window === 'undefined' ? undefined : window.sessionStorage;
8
7
  }
9
8
  catch {
10
9
  return;
11
10
  }
12
11
  }
13
- function storageKey(sessionId) {
14
- return `${STORAGE_PREFIX}${encodeURIComponent(sessionId)}`;
15
- }
16
- function recordOf(snapshot) {
17
- const record = conversationAnnotationRecoveryRecord(snapshot);
18
- return record === undefined ? undefined : { version: 2, ...record };
12
+ function ownsReference(snapshot) {
13
+ return snapshot.occurrences.some(occurrence => occurrence.source === SELECTION_REFERENCE_SOURCE
14
+ || occurrence.source === SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE);
19
15
  }
20
- function displayDraft(record) {
21
- return record.version === 1 ? record.draft : record.displayDraft;
16
+ function validOccurrences(draft, value) {
17
+ if (!Array.isArray(value) || value.length === 0)
18
+ return false;
19
+ let end = 0;
20
+ return value.every((candidate) => {
21
+ if (typeof candidate !== 'object' || candidate === null)
22
+ return false;
23
+ const item = candidate;
24
+ if (!Number.isSafeInteger(item.offset) || !Number.isSafeInteger(item.length)
25
+ || item.offset < end || item.length < 0 || item.offset + item.length > draft.length
26
+ || typeof item.source !== 'string' || typeof item.ref !== 'string'
27
+ || typeof item.label !== 'string' || typeof item.clipboardText !== 'string'
28
+ || draft.slice(item.offset, item.offset + item.length) !== item.clipboardText
29
+ || (item.appearance !== undefined && !['session', 'file', 'folder'].includes(item.appearance)))
30
+ return false;
31
+ end = item.offset + item.length;
32
+ return true;
33
+ });
22
34
  }
23
- function mirrorDraft(record) {
24
- return record.version === 1 ? record.projection : record.mirrorDraft;
25
- }
26
- /** Tab-scoped recovery for drafts persisted as a reference clipboard projection. */
35
+ /** Recover the complete reference-bearing draft, not only a leading annotation.
36
+ * DSH persists clipboard text, so conversation chips need recovery as well. */
27
37
  export class ConversationAnnotationPersistence {
28
38
  storage;
29
- observedSessions = new Set();
39
+ observedInputs = new WeakSet();
40
+ reconciling = false;
30
41
  constructor(storage = browserSessionStorage()) {
31
42
  this.storage = storage;
32
43
  }
33
44
  reconcile(sessionId, input) {
34
- const key = storageKey(sessionId);
35
- const snapshot = input.state.getSnapshot();
36
- const current = recordOf(snapshot);
37
- if (current !== undefined) {
38
- this.observedSessions.add(key);
39
- this.write(key, current);
45
+ if (this.reconciling || this.storage === undefined)
40
46
  return;
41
- }
42
- const stored = this.read(key);
43
- if (stored !== undefined) {
44
- const exactDisplayDraft = snapshot.draft === displayDraft(stored);
45
- const storedMirrorDraft = mirrorDraft(stored);
46
- const exactMirrorDraft = storedMirrorDraft !== undefined && snapshot.draft === storedMirrorDraft;
47
- if (exactDisplayDraft || exactMirrorDraft) {
48
- this.observedSessions.add(key);
49
- const restored = restoreConversationAnnotationReference(input, stored.ref, storedMirrorDraft, stored.version === 2 ? stored.baseDraft : undefined);
50
- if (restored)
51
- return;
52
- // A refused insertion may move an exact display record to its exact
53
- // mirror. Keep either v1 or v2 storage retryable from that safe state.
54
- if (storedMirrorDraft !== undefined
55
- && input.state.getSnapshot().draft === storedMirrorDraft)
56
- return;
57
- this.remove(key);
58
- removeOrphanedConversationAnnotationPlaceholder(input);
47
+ this.reconciling = true;
48
+ const key = STORAGE_PREFIX + encodeURIComponent(sessionId);
49
+ try {
50
+ const snapshot = input.state.getSnapshot();
51
+ if (ownsReference(snapshot)) {
52
+ this.observedInputs.add(input);
53
+ this.storage.setItem(key, JSON.stringify({ version: 1, draft: snapshot.draft, occurrences: snapshot.occurrences }));
54
+ return;
55
+ }
56
+ // Deleting a chip in the same editor is authoritative. Never resurrect
57
+ // it just because its plain-text clipboard projection is unchanged.
58
+ if (this.observedInputs.has(input)) {
59
+ this.storage.removeItem(key);
59
60
  return;
60
61
  }
61
- }
62
- // A live occurrence disappeared in this plugin lifetime: removal or send
63
- // is authoritative, including the transition to an empty draft.
64
- if (this.observedSessions.has(key)) {
65
- this.remove(key);
66
- removeOrphanedConversationAnnotationPlaceholder(input);
67
- return;
68
- }
69
- // A new shell starts empty before ConversationSession seeds its persisted
70
- // draft. Keep the tab record until that one mount-time adoption occurs.
71
- if (stored !== undefined && snapshot.draft === '')
72
- return;
73
- if (stored !== undefined)
74
- this.remove(key);
75
- removeOrphanedConversationAnnotationPlaceholder(input);
76
- }
77
- read(key) {
78
- if (this.storage === undefined)
79
- return;
80
- try {
81
62
  const raw = this.storage.getItem(key);
82
63
  if (raw === null)
83
64
  return;
84
- const value = JSON.parse(raw);
85
- if (value.version === 1
86
- && typeof value.draft === 'string'
87
- && typeof value.ref === 'string'
88
- && (value.projection === undefined || typeof value.projection === 'string')) {
89
- return {
90
- version: 1,
91
- draft: value.draft,
92
- ...(value.projection === undefined ? {} : { projection: value.projection }),
93
- ref: value.ref,
94
- };
65
+ const stored = JSON.parse(raw);
66
+ if (stored === null || stored.version !== 1 || typeof stored.draft !== 'string'
67
+ || !validOccurrences(stored.draft, stored.occurrences)) {
68
+ this.storage.removeItem(key);
69
+ return;
95
70
  }
96
- return value.version === 2
97
- && typeof value.displayDraft === 'string'
98
- && typeof value.mirrorDraft === 'string'
99
- && typeof value.baseDraft === 'string'
100
- && typeof value.ref === 'string'
101
- ? {
102
- version: 2,
103
- displayDraft: value.displayDraft,
104
- mirrorDraft: value.mirrorDraft,
105
- baseDraft: value.baseDraft,
106
- ref: value.ref,
107
- }
108
- : undefined;
71
+ // A fresh Session shell starts empty before its persisted draft is seeded.
72
+ if (snapshot.draft === '')
73
+ return;
74
+ if (snapshot.draft !== stored.draft || snapshot.occurrences.length !== 0) {
75
+ this.storage.removeItem(key);
76
+ return;
77
+ }
78
+ if (snapshot.phase !== undefined && snapshot.phase !== 'plain')
79
+ return;
80
+ if (this.restore(input, stored))
81
+ this.observedInputs.add(input);
109
82
  }
110
83
  catch {
111
- return;
84
+ // Storage denial or a stale codec must not alter or erase the user's draft.
112
85
  }
113
- }
114
- write(key, record) {
115
- if (this.storage === undefined)
116
- return;
117
- try {
118
- this.storage.setItem(key, JSON.stringify(record));
119
- }
120
- catch {
121
- // Draft recovery is best-effort; the orphan-prefix guard remains active.
86
+ finally {
87
+ this.reconciling = false;
122
88
  }
123
89
  }
124
- remove(key) {
125
- if (this.storage === undefined)
126
- return;
90
+ restore(input, stored) {
91
+ let ownedSnapshot;
92
+ let complete = false;
127
93
  try {
128
- this.storage.removeItem(key);
94
+ // Right-to-left edits leave every unprocessed clipboard offset unchanged.
95
+ for (const occurrence of [...stored.occurrences].reverse()) {
96
+ const before = input.state.getSnapshot();
97
+ if (before.draft !== stored.draft)
98
+ return false;
99
+ if (!input.insertReference({
100
+ source: occurrence.source, ref: occurrence.ref, label: occurrence.label,
101
+ clipboardText: occurrence.clipboardText,
102
+ ...(occurrence.appearance === undefined ? {} : { appearance: occurrence.appearance }),
103
+ }, { start: occurrence.offset, end: occurrence.offset + occurrence.length, draftRev: before.draftRev }))
104
+ return false;
105
+ const after = input.state.getSnapshot();
106
+ const addedGap = stored.draft[occurrence.offset + occurrence.length] !== ' ';
107
+ const expected = addedGap
108
+ ? stored.draft.slice(0, occurrence.offset + occurrence.length) + ' '
109
+ + stored.draft.slice(occurrence.offset + occurrence.length)
110
+ : stored.draft;
111
+ if (after.draft !== expected)
112
+ return false;
113
+ ownedSnapshot = after;
114
+ if (addedGap) {
115
+ if (!input.replaceText('', {
116
+ start: occurrence.offset + 1, end: occurrence.offset + 2, draftRev: after.draftRev,
117
+ }))
118
+ return false;
119
+ const normalized = input.state.getSnapshot();
120
+ if (normalized.draft !== stored.draft)
121
+ return false;
122
+ ownedSnapshot = normalized;
123
+ }
124
+ }
125
+ complete = true;
126
+ return true;
129
127
  }
130
- catch {
131
- // Storage denial must not block composer cleanup.
128
+ finally {
129
+ // Keep a refused multi-chip restoration retryable; don't save a partial
130
+ // projection, and don't roll back edits from synchronous subscribers.
131
+ const current = input.state.getSnapshot();
132
+ if (!complete && ownedSnapshot === current) {
133
+ const length = current.draft.length - current.occurrences.reduce((sum, item) => sum + item.length - 1, 0);
134
+ input.replaceText(stored.draft, { start: 0, end: length, draftRev: current.draftRev });
135
+ }
132
136
  }
133
137
  }
134
138
  }
135
- //# sourceMappingURL=annotation-persistence.js.map
@@ -1,43 +1,23 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
- export declare const LEGACY_REFERENCE_PLACEHOLDER = "\uFFFC";
3
- export interface ParentComposerOccurrence {
4
- readonly occurrenceId: number;
5
- readonly source: string;
6
- readonly ref: string;
7
- readonly offset: number;
8
- /** Complete inline display-text length in current DSH input snapshots. */
9
- readonly length?: number;
10
- /** Insert-time display label, present in current DSH input snapshots. */
11
- readonly label?: string;
12
- /** Draft-persistence projection cached by current DSH input snapshots. */
13
- readonly clipboardText?: string;
14
- }
15
- export interface ParentComposerInputSnapshot {
16
- readonly draft: string;
17
- readonly draftRev: number;
18
- readonly occurrences: readonly ParentComposerOccurrence[];
19
- }
2
+ import type { InputState, ReferenceInsert, SessionInput, TokenSpan } from '@deepseek-ai/dsh-client-ui-conversation/client';
3
+ export type ParentComposerOccurrence = InputState['occurrences'][number];
4
+ export type ParentComposerSpan = TokenSpan;
5
+ export type ParentComposerInputSnapshot = Pick<InputState, 'draft' | 'draftRev' | 'occurrences'> & Partial<Pick<InputState, 'phase'>>;
6
+ /** DSH 0.2's Lexical editor: published offsets are clipboard coordinates,
7
+ * mutation spans count each reference chip as one character. */
20
8
  export interface ParentComposerInput {
21
9
  readonly state: {
22
10
  getSnapshot(): ParentComposerInputSnapshot;
23
- subscribe?(listener: () => void): () => void;
11
+ subscribe(listener: () => void): () => void;
24
12
  };
25
13
  setDraft(text: string): void;
26
- insertReference(reference: {
27
- readonly source: string;
28
- readonly ref: string;
29
- readonly label: string;
30
- readonly appearance?: 'session' | 'file' | 'folder';
31
- readonly clipboardText: string;
32
- }, span: {
33
- readonly start: number;
34
- readonly end: number;
35
- readonly draftRev: number;
36
- }): boolean;
14
+ replaceText(text: string, span: ParentComposerSpan): boolean;
15
+ insertReference(reference: ReferenceInsert, span: ParentComposerSpan): boolean;
16
+ notify?: SessionInput['notify'];
37
17
  }
38
18
  export interface ParentConversationService {
39
19
  readonly input: {
40
- for(scope: Context): ParentComposerInput;
20
+ for(scope: Context): SessionInput;
41
21
  };
42
22
  }
43
23
  export interface SelectionReferenceSource {
@@ -51,22 +31,13 @@ export interface SelectionReferenceSource {
51
31
  serialize(ref: string, signal: AbortSignal): Promise<string>;
52
32
  };
53
33
  }
54
- export interface OccurrenceRange {
55
- readonly start: number;
56
- readonly end: number;
57
- }
58
- export declare function referenceDisplayText(label: string): string;
59
- /**
60
- * Resolve one occurrence's occupied draft range across both DSH reference
61
- * representations: current full `@label` text with `length`, and the legacy
62
- * one-code-unit U+FFFC placeholder without it.
63
- */
64
- export declare function occurrenceRange(snapshot: ParentComposerInputSnapshot, occurrence: ParentComposerOccurrence, expectedLabel: string): OccurrenceRange | undefined;
34
+ export declare function occurrenceRange(snapshot: ParentComposerInputSnapshot, occurrence: ParentComposerOccurrence, expectedLabel: string): {
35
+ start: number;
36
+ end: number;
37
+ } | undefined;
65
38
  export declare function occurrenceMatchesDraft(snapshot: ParentComposerInputSnapshot, occurrence: ParentComposerOccurrence, expectedLabel: string): boolean;
66
- /** Find the exact occurrence minted by one synchronous insertReference call. */
39
+ /** Convert a reference's clipboard range to the editor's atomic coordinates. */
40
+ export declare function occurrenceEditSpan(_input: ParentComposerInput, snapshot: ParentComposerInputSnapshot, occurrence: ParentComposerOccurrence, options?: {
41
+ readonly consumeFollowingSeparator?: boolean;
42
+ }): ParentComposerSpan | undefined;
67
43
  export declare function newlyInsertedOccurrence(before: ParentComposerInputSnapshot, after: ParentComposerInputSnapshot, source: string, ref: string): ParentComposerOccurrence | undefined;
68
- /** Remove one occurrence's display range and, optionally, its separating ASCII gap. */
69
- export declare function draftWithoutOccurrence(snapshot: ParentComposerInputSnapshot, occurrence: ParentComposerOccurrence, expectedLabel: string, options?: {
70
- readonly consumeAdjacentSpace?: boolean;
71
- }): string | undefined;
72
- //# sourceMappingURL=composer-reference.d.ts.map
@@ -1,57 +1,37 @@
1
- export const LEGACY_REFERENCE_PLACEHOLDER = '\uFFFC';
2
- export function referenceDisplayText(label) {
3
- return `@${label}`;
4
- }
5
- /**
6
- * Resolve one occurrence's occupied draft range across both DSH reference
7
- * representations: current full `@label` text with `length`, and the legacy
8
- * one-code-unit U+FFFC placeholder without it.
9
- */
10
1
  export function occurrenceRange(snapshot, occurrence, expectedLabel) {
11
- const start = occurrence.offset;
12
- if (!Number.isSafeInteger(start) || start < 0 || start > snapshot.draft.length)
2
+ const { offset, length } = occurrence;
3
+ if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length)
4
+ || offset < 0 || length < 0 || offset + length > snapshot.draft.length
5
+ || occurrence.label !== expectedLabel
6
+ || snapshot.draft.slice(offset, offset + length) !== occurrence.clipboardText)
13
7
  return;
14
- if (occurrence.length !== undefined
15
- && Number.isSafeInteger(occurrence.length)
16
- && occurrence.length > 0
17
- && start + occurrence.length <= snapshot.draft.length) {
18
- return { start, end: start + occurrence.length };
19
- }
20
- if (snapshot.draft.startsWith(LEGACY_REFERENCE_PLACEHOLDER, start)) {
21
- return { start, end: start + LEGACY_REFERENCE_PLACEHOLDER.length };
22
- }
23
- const display = referenceDisplayText(expectedLabel);
24
- if (snapshot.draft.startsWith(display, start))
25
- return { start, end: start + display.length };
26
- return;
8
+ return { start: offset, end: offset + length };
27
9
  }
28
10
  export function occurrenceMatchesDraft(snapshot, occurrence, expectedLabel) {
29
- const range = occurrenceRange(snapshot, occurrence, expectedLabel);
11
+ return occurrenceRange(snapshot, occurrence, expectedLabel) !== undefined;
12
+ }
13
+ /** Convert a reference's clipboard range to the editor's atomic coordinates. */
14
+ export function occurrenceEditSpan(_input, snapshot, occurrence, options = {}) {
15
+ const range = occurrenceRange(snapshot, occurrence, occurrence.label);
30
16
  if (range === undefined)
31
- return false;
32
- const text = snapshot.draft.slice(range.start, range.end);
33
- return text === LEGACY_REFERENCE_PLACEHOLDER || text === referenceDisplayText(expectedLabel);
17
+ return;
18
+ let start = range.start;
19
+ for (const previous of snapshot.occurrences) {
20
+ if (previous.offset >= occurrence.offset)
21
+ continue;
22
+ if (occurrenceRange(snapshot, previous, previous.label) === undefined
23
+ || previous.offset + previous.length > occurrence.offset)
24
+ return;
25
+ start -= previous.length - 1;
26
+ }
27
+ let end = start + 1;
28
+ if (options.consumeFollowingSeparator && snapshot.draft[range.end] === ' ')
29
+ end += 1;
30
+ return { start, end, draftRev: snapshot.draftRev };
34
31
  }
35
- /** Find the exact occurrence minted by one synchronous insertReference call. */
36
32
  export function newlyInsertedOccurrence(before, after, source, ref) {
37
33
  const previousIds = new Set(before.occurrences.map(occurrence => occurrence.occurrenceId));
38
34
  const inserted = after.occurrences.filter(occurrence => !previousIds.has(occurrence.occurrenceId)
39
- && occurrence.source === source
40
- && occurrence.ref === ref);
35
+ && occurrence.source === source && occurrence.ref === ref);
41
36
  return inserted.length === 1 ? inserted[0] : undefined;
42
37
  }
43
- /** Remove one occurrence's display range and, optionally, its separating ASCII gap. */
44
- export function draftWithoutOccurrence(snapshot, occurrence, expectedLabel, options = {}) {
45
- const range = occurrenceRange(snapshot, occurrence, expectedLabel);
46
- if (range === undefined)
47
- return;
48
- let { start, end } = range;
49
- if (options.consumeAdjacentSpace === true) {
50
- if (snapshot.draft[end] === ' ')
51
- end += 1;
52
- else if (start > 0 && snapshot.draft[start - 1] === ' ')
53
- start -= 1;
54
- }
55
- return snapshot.draft.slice(0, start) + snapshot.draft.slice(end);
56
- }
57
- //# sourceMappingURL=composer-reference.js.map
@@ -1,5 +1,4 @@
1
- import type { ConversationNode } from '@deepseek-ai/dsh-client-runtime/client';
2
- import type { SessionId } from '../../shared/contracts.js';
1
+ import type { SideChatId, SideChatMessage } from '../../shared/contracts.js';
3
2
  import type { ParentComposerInput, SelectionReferenceSource } from './composer-reference.js';
4
3
  export declare const SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE = "dsh-side-chat-conversation";
5
4
  export interface ReferencedConversationMessage {
@@ -9,7 +8,7 @@ export interface ReferencedConversationMessage {
9
8
  /** One immutable snapshot carried by the parent composer's conversation label. */
10
9
  export interface ReferencedSideChatConversation {
11
10
  readonly version: 1;
12
- readonly conversationId: SessionId;
11
+ readonly conversationId: string;
13
12
  readonly title: string;
14
13
  readonly conversation: readonly ReferencedConversationMessage[];
15
14
  }
@@ -17,12 +16,11 @@ export interface ParsedReferencedConversationPrompt {
17
16
  readonly reference: ReferencedSideChatConversation;
18
17
  readonly message: string;
19
18
  }
20
- /** Project exactly the user/assistant history visible in the Side Chat modal. */
19
+ /** Capture only the plugin transcript, not the parent context or model reasoning. */
21
20
  export declare function referencedSideChatConversation(input: {
22
- readonly conversationId: SessionId;
21
+ readonly conversationId: SideChatId;
23
22
  readonly title: string;
24
- readonly nodes: readonly ConversationNode[];
25
- readonly inheritedThroughSeq: number;
23
+ readonly messages: readonly SideChatMessage[];
26
24
  }): ReferencedSideChatConversation;
27
25
  /** Durable model-facing form of the referenced conversation label. */
28
26
  export declare function serializeReferencedConversation(reference: ReferencedSideChatConversation): string;
@@ -32,4 +30,3 @@ export declare function parseReferencedConversationPrompt(text: string): ParsedR
32
30
  export declare function addReferencedSideChatToConversation(input: ParentComposerInput, reference: ReferencedSideChatConversation): boolean;
33
31
  /** Reference codec used by the native composer chip and submit pipeline. */
34
32
  export declare const sideChatConversationReferenceSource: SelectionReferenceSource;
35
- //# sourceMappingURL=referenced-conversation.d.ts.map
@@ -1,4 +1,4 @@
1
- import { draftWithoutOccurrence } from './composer-reference.js';
1
+ import { newlyInsertedOccurrence, occurrenceEditSpan, } from './composer-reference.js';
2
2
  export const SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE = 'dsh-side-chat-conversation';
3
3
  const OPEN_TAG = '<referenced_conversation>';
4
4
  const CLOSE_TAG = '</referenced_conversation>';
@@ -6,48 +6,16 @@ function normalizedTitle(title) {
6
6
  const normalized = title.trim().replace(/\s+/gu, ' ');
7
7
  return normalized.length === 0 ? 'Side Chat' : normalized;
8
8
  }
9
- function visibleContentText(content) {
10
- return content.map((block) => {
11
- if (typeof block !== 'object' || block === null)
12
- return '';
13
- const value = block;
14
- if (value.type === 'text' && typeof value.text === 'string')
15
- return value.text;
16
- if (value.type === 'image')
17
- return '[Image attachment]';
18
- return '';
19
- }).filter(Boolean).join('\n').trim();
20
- }
21
- function assistantText(node) {
22
- return node.blocks
23
- .flatMap(block => block.kind === 'text' ? [block.text] : [])
24
- .join('\n')
25
- .trim();
26
- }
27
- /** Project exactly the user/assistant history visible in the Side Chat modal. */
9
+ /** Capture only the plugin transcript, not the parent context or model reasoning. */
28
10
  export function referencedSideChatConversation(input) {
29
- const conversation = [];
30
- for (const node of input.nodes) {
31
- if (node.seq <= input.inheritedThroughSeq)
32
- continue;
33
- if (node.kind === 'user' || node.kind === 'steering') {
34
- const content = visibleContentText(node.content);
35
- if (content.length > 0)
36
- conversation.push({ role: 'user', content });
37
- continue;
38
- }
39
- if (node.kind === 'assistant') {
40
- const content = assistantText(node);
41
- if (content.length > 0)
42
- conversation.push({ role: 'assistant', content });
43
- }
44
- }
45
- return {
46
- version: 1,
47
- conversationId: input.conversationId,
48
- title: normalizedTitle(input.title),
49
- conversation,
50
- };
11
+ const conversation = input.messages.flatMap((message) => {
12
+ if (message.status === 'streaming' || message.text.trim().length === 0)
13
+ return [];
14
+ const content = message.selectedText === undefined ? message.text
15
+ : `Selected passage:\n${message.selectedText}\n\nQuestion:\n${message.text}`;
16
+ return [{ role: message.role, content }];
17
+ });
18
+ return { version: 1, conversationId: input.conversationId, title: normalizedTitle(input.title), conversation };
51
19
  }
52
20
  function isReferencedConversationMessage(value) {
53
21
  if (typeof value !== 'object' || value === null)
@@ -144,16 +112,9 @@ function removeOccurrence(input, occurrenceId) {
144
112
  const occurrence = snapshot.occurrences.find(candidate => candidate.occurrenceId === occurrenceId);
145
113
  if (occurrence === undefined)
146
114
  return;
147
- let title;
148
- try {
149
- title = decodeReferencedConversation(occurrence.ref).title;
150
- }
151
- catch {
152
- return;
153
- }
154
- const draft = draftWithoutOccurrence(snapshot, occurrence, title, { consumeAdjacentSpace: true });
155
- if (draft !== undefined)
156
- input.setDraft(draft);
115
+ const span = occurrenceEditSpan(input, snapshot, occurrence);
116
+ if (span !== undefined)
117
+ input.replaceText('', span);
157
118
  }
158
119
  /** Insert or refresh one Side Chat label without duplicating an older snapshot. */
159
120
  export function addReferencedSideChatToConversation(input, reference) {
@@ -164,22 +125,28 @@ export function addReferencedSideChatToConversation(input, reference) {
164
125
  const encoded = encodeReferencedConversation(reference);
165
126
  if (existing.some(occurrence => occurrence.ref === encoded))
166
127
  return true;
128
+ const target = existing[0];
129
+ const span = target === undefined
130
+ ? { start: 0, end: 0, draftRev: before.draftRev }
131
+ : occurrenceEditSpan(input, before, target);
132
+ if (span === undefined)
133
+ return false;
167
134
  const inserted = input.insertReference({
168
135
  source: SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE,
169
136
  ref: encoded,
170
137
  label: reference.title,
171
138
  appearance: 'session',
172
139
  clipboardText: `@${reference.title}`,
173
- }, {
174
- start: 0,
175
- end: 0,
176
- draftRev: before.draftRev,
177
- });
140
+ }, span);
178
141
  if (!inserted)
179
142
  return false;
180
- for (const occurrence of existing)
181
- removeOccurrence(input, occurrence.occurrenceId);
182
- return true;
143
+ const after = input.state.getSnapshot();
144
+ const occurrence = newlyInsertedOccurrence(before, after, SIDE_CHAT_CONVERSATION_REFERENCE_SOURCE, encoded);
145
+ if (occurrence === undefined)
146
+ return false;
147
+ for (const duplicate of existing.slice(1))
148
+ removeOccurrence(input, duplicate.occurrenceId);
149
+ return matchingOccurrences(input.state.getSnapshot(), reference).length === 1;
183
150
  }
184
151
  /** Reference codec used by the native composer chip and submit pipeline. */
185
152
  export const sideChatConversationReferenceSource = {
@@ -197,4 +164,3 @@ export const sideChatConversationReferenceSource = {
197
164
  },
198
165
  },
199
166
  };
200
- //# sourceMappingURL=referenced-conversation.js.map
@@ -6,4 +6,3 @@ export declare function ConversationAnnotationMarkers({ annotations, activeAnnot
6
6
  readonly activeAnnotationIndex?: number;
7
7
  readonly onEdit: (annotation: ConversationSelectionAnnotation, selection: ConversationSelection) => void;
8
8
  }): (import("react/jsx-runtime").JSX.Element | null)[];
9
- //# sourceMappingURL=ConversationAnnotationMarkers.d.ts.map
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { restoreDomConversationSelection } from './selection-controller.js';
4
+ import { conversationChatRoot } from './conversation-dom.js';
4
5
  const ANNOTATION_HIGHLIGHT = 'dsh-side-chat-annotations';
5
6
  const ACTIVE_ANNOTATION_HIGHLIGHT = 'dsh-side-chat-active-annotation';
6
7
  function clamp(value, minimum, maximum) {
@@ -144,7 +145,8 @@ export function ConversationAnnotationMarkers({ annotations, activeAnnotationInd
144
145
  let resizeObserver;
145
146
  const refresh = () => {
146
147
  animationFrame = undefined;
147
- const conversationRoot = document.querySelector('[data-chat-flow]');
148
+ const sessionId = annotations[0]?.selection.parentSessionId;
149
+ const conversationRoot = sessionId === undefined ? null : conversationChatRoot(sessionId) ?? null;
148
150
  if (conversationRoot !== observedConversationRoot) {
149
151
  resizeObserver?.disconnect();
150
152
  observedConversationRoot = conversationRoot;
@@ -157,6 +159,8 @@ export function ConversationAnnotationMarkers({ annotations, activeAnnotationInd
157
159
  const activeRanges = [];
158
160
  if (conversationRoot !== null) {
159
161
  for (const annotation of annotations) {
162
+ if (annotation.selection.parentSessionId !== sessionId)
163
+ continue;
160
164
  const restored = restoreDomConversationSelection({
161
165
  selection: annotation.selection,
162
166
  conversationRoot,
@@ -238,4 +242,3 @@ export function ConversationAnnotationMarkers({ annotations, activeAnnotationInd
238
242
  }, onClick: activate, children: badge }, `${annotation.selection.parentSessionId}:${annotation.selection.fragments[0]?.nodeKey ?? ''}:${String(annotation.annotationIndex)}`));
239
243
  });
240
244
  }
241
- //# sourceMappingURL=ConversationAnnotationMarkers.js.map
@@ -30,4 +30,3 @@ export declare function calculateSelectionActionsPosition(rect: ConversationSele
30
30
  readonly top: number;
31
31
  };
32
32
  export declare function SelectionActions({ selection, touchInteraction, askDisabledReason, annotationNumber, annotationEditor, onAddToChat, onMoreDetails, onAskInSideChat, onAnnotationEditorChange, onRemoveAnnotation, onDismiss, }: SelectionActionsProps): import("react/jsx-runtime").JSX.Element;
33
- //# sourceMappingURL=SelectionActions.d.ts.map