@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.
- package/README.md +16 -13
- package/README.zh-CN.md +16 -13
- package/lib/client/apply.d.ts +0 -1
- package/lib/client/apply.js +48 -33
- package/lib/client/contracts.d.ts +3 -59
- package/lib/client/contracts.js +0 -1
- package/lib/client/dsh/ReadOnlyConversation.d.ts +10 -0
- package/lib/client/dsh/ReadOnlyConversation.js +91 -0
- package/lib/client/dsh/SideChatOverlay.d.ts +7 -0
- package/lib/client/{rc6/Rc6SideChatOverlay.js → dsh/SideChatOverlay.js} +26 -57
- package/lib/client/{rc6 → dsh}/context.d.ts +7 -3
- package/lib/client/dsh/context.js +1 -0
- package/lib/client/dsh/conversation-store.d.ts +22 -0
- package/lib/client/dsh/conversation-store.js +19 -0
- package/lib/client/dsh/primitive-labels.d.ts +2 -0
- package/lib/client/dsh/primitive-labels.js +4 -0
- package/lib/client/{rc6 → dsh}/remote-adapter.d.ts +2 -3
- package/lib/client/dsh/remote-adapter.js +22 -0
- package/lib/client/dsh/sessions-adapter.d.ts +56 -0
- package/lib/client/dsh/sessions-adapter.js +185 -0
- package/lib/client/index.d.ts +2 -3
- package/lib/client/index.js +0 -1
- package/lib/client/model-preference.d.ts +0 -1
- package/lib/client/model-preference.js +0 -1
- package/lib/client/panel/SelectionQuote.d.ts +0 -1
- package/lib/client/panel/SelectionQuote.js +8 -2
- package/lib/client/panel/SendIcon.d.ts +0 -1
- package/lib/client/panel/SendIcon.js +0 -1
- package/lib/client/panel/SideChatBody.d.ts +0 -1
- package/lib/client/panel/SideChatBody.js +0 -1
- package/lib/client/panel/SideChatErrorState.d.ts +0 -1
- package/lib/client/panel/SideChatErrorState.js +1 -2
- package/lib/client/panel/SideChatHeader.d.ts +0 -1
- package/lib/client/panel/SideChatHeader.js +0 -1
- package/lib/client/panel/SideChatModelSelect.d.ts +0 -1
- package/lib/client/panel/SideChatModelSelect.js +37 -19
- package/lib/client/panel/SideChatPanel.d.ts +0 -1
- package/lib/client/panel/SideChatPanel.js +12 -6
- package/lib/client/panel/StopIcon.d.ts +0 -1
- package/lib/client/panel/StopIcon.js +0 -1
- package/lib/client/panel/messages.d.ts +1 -2
- package/lib/client/panel/messages.js +8 -9
- package/lib/client/panel/use-auto-growing-textarea.d.ts +0 -1
- package/lib/client/panel/use-auto-growing-textarea.js +0 -1
- package/lib/client/parent-composer/ParentConversationAnnotations.d.ts +3 -3
- package/lib/client/parent-composer/ParentConversationAnnotations.js +71 -28
- package/lib/client/parent-composer/add-to-conversation.d.ts +14 -29
- package/lib/client/parent-composer/add-to-conversation.js +134 -457
- package/lib/client/parent-composer/annotation-persistence.d.ts +8 -9
- package/lib/client/parent-composer/annotation-persistence.js +106 -103
- package/lib/client/parent-composer/composer-reference.d.ts +19 -48
- package/lib/client/parent-composer/composer-reference.js +26 -46
- package/lib/client/parent-composer/referenced-conversation.d.ts +5 -8
- package/lib/client/parent-composer/referenced-conversation.js +27 -61
- package/lib/client/selection/ConversationAnnotationMarkers.d.ts +0 -1
- package/lib/client/selection/ConversationAnnotationMarkers.js +5 -2
- package/lib/client/selection/SelectionActions.d.ts +0 -1
- package/lib/client/selection/SelectionActions.js +2 -2
- package/lib/client/selection/conversation-dom.d.ts +5 -0
- package/lib/client/selection/conversation-dom.js +24 -0
- package/lib/client/selection/selection-controller.d.ts +1 -2
- package/lib/client/selection/selection-controller.js +7 -4
- package/lib/client/selection/selection-limits.d.ts +0 -1
- package/lib/client/selection/selection-limits.js +0 -1
- package/lib/client/selection/selection-normalizer.d.ts +0 -1
- package/lib/client/selection/selection-normalizer.js +1 -2
- package/lib/client/side-chat-controller.d.ts +28 -42
- package/lib/client/side-chat-controller.js +245 -338
- package/lib/client.js +1202 -8951
- package/lib/host/index.d.ts +1 -2
- package/lib/host/index.js +1 -2
- package/lib/host/read-only-chat-service.d.ts +30 -0
- package/lib/host/read-only-chat-service.js +314 -0
- package/lib/index.d.ts +16 -9
- package/lib/index.js +34 -14
- package/lib/remote.d.ts +23 -15
- package/lib/remote.js +2 -6
- package/lib/shared/constants.d.ts +0 -14
- package/lib/shared/constants.js +0 -14
- package/lib/shared/contracts.d.ts +52 -20
- package/lib/shared/contracts.js +2 -4
- package/lib/shared/error-codes.d.ts +1 -2
- package/lib/shared/error-codes.js +2 -3
- package/lib/shared/observable.d.ts +0 -1
- package/lib/shared/observable.js +0 -1
- package/lib/shared/{archived-wire.d.ts → side-chat-wire.d.ts} +109 -21
- package/lib/shared/side-chat-wire.js +34 -0
- package/lib/typert.d.ts +3 -69
- package/lib/typert.js +120 -51
- package/package.json +80 -38
- package/lib/client/apply.d.ts.map +0 -1
- package/lib/client/apply.js.map +0 -1
- package/lib/client/contracts.d.ts.map +0 -1
- package/lib/client/contracts.js.map +0 -1
- package/lib/client/index.d.ts.map +0 -1
- package/lib/client/index.js.map +0 -1
- package/lib/client/model-preference.d.ts.map +0 -1
- package/lib/client/model-preference.js.map +0 -1
- package/lib/client/panel/SelectionQuote.d.ts.map +0 -1
- package/lib/client/panel/SelectionQuote.js.map +0 -1
- package/lib/client/panel/SendIcon.d.ts.map +0 -1
- package/lib/client/panel/SendIcon.js.map +0 -1
- package/lib/client/panel/SideChatBody.d.ts.map +0 -1
- package/lib/client/panel/SideChatBody.js.map +0 -1
- package/lib/client/panel/SideChatErrorState.d.ts.map +0 -1
- package/lib/client/panel/SideChatErrorState.js.map +0 -1
- package/lib/client/panel/SideChatHeader.d.ts.map +0 -1
- package/lib/client/panel/SideChatHeader.js.map +0 -1
- package/lib/client/panel/SideChatModelSelect.d.ts.map +0 -1
- package/lib/client/panel/SideChatModelSelect.js.map +0 -1
- package/lib/client/panel/SideChatPanel.d.ts.map +0 -1
- package/lib/client/panel/SideChatPanel.js.map +0 -1
- package/lib/client/panel/StopIcon.d.ts.map +0 -1
- package/lib/client/panel/StopIcon.js.map +0 -1
- package/lib/client/panel/messages.d.ts.map +0 -1
- package/lib/client/panel/messages.js.map +0 -1
- package/lib/client/panel/use-auto-growing-textarea.d.ts.map +0 -1
- package/lib/client/panel/use-auto-growing-textarea.js.map +0 -1
- package/lib/client/parent-composer/ParentConversationAnnotations.d.ts.map +0 -1
- package/lib/client/parent-composer/ParentConversationAnnotations.js.map +0 -1
- package/lib/client/parent-composer/add-to-conversation.d.ts.map +0 -1
- package/lib/client/parent-composer/add-to-conversation.js.map +0 -1
- package/lib/client/parent-composer/annotation-persistence.d.ts.map +0 -1
- package/lib/client/parent-composer/annotation-persistence.js.map +0 -1
- package/lib/client/parent-composer/composer-reference.d.ts.map +0 -1
- package/lib/client/parent-composer/composer-reference.js.map +0 -1
- package/lib/client/parent-composer/referenced-conversation.d.ts.map +0 -1
- package/lib/client/parent-composer/referenced-conversation.js.map +0 -1
- package/lib/client/rc6/ArchivedConversation.d.ts +0 -15
- package/lib/client/rc6/ArchivedConversation.d.ts.map +0 -1
- package/lib/client/rc6/ArchivedConversation.js +0 -230
- package/lib/client/rc6/ArchivedConversation.js.map +0 -1
- package/lib/client/rc6/Rc6SideChatOverlay.d.ts +0 -8
- package/lib/client/rc6/Rc6SideChatOverlay.d.ts.map +0 -1
- package/lib/client/rc6/Rc6SideChatOverlay.js.map +0 -1
- package/lib/client/rc6/SideChatTool.d.ts +0 -23
- package/lib/client/rc6/SideChatTool.d.ts.map +0 -1
- package/lib/client/rc6/SideChatTool.js +0 -395
- package/lib/client/rc6/SideChatTool.js.map +0 -1
- package/lib/client/rc6/context.d.ts.map +0 -1
- package/lib/client/rc6/context.js +0 -2
- package/lib/client/rc6/context.js.map +0 -1
- package/lib/client/rc6/remote-adapter.d.ts.map +0 -1
- package/lib/client/rc6/remote-adapter.js +0 -33
- package/lib/client/rc6/remote-adapter.js.map +0 -1
- package/lib/client/rc6/sessions-adapter.d.ts +0 -61
- package/lib/client/rc6/sessions-adapter.d.ts.map +0 -1
- package/lib/client/rc6/sessions-adapter.js +0 -370
- package/lib/client/rc6/sessions-adapter.js.map +0 -1
- package/lib/client/selection/ConversationAnnotationMarkers.d.ts.map +0 -1
- package/lib/client/selection/ConversationAnnotationMarkers.js.map +0 -1
- package/lib/client/selection/SelectionActions.d.ts.map +0 -1
- package/lib/client/selection/SelectionActions.js.map +0 -1
- package/lib/client/selection/selection-controller.d.ts.map +0 -1
- package/lib/client/selection/selection-controller.js.map +0 -1
- package/lib/client/selection/selection-limits.d.ts.map +0 -1
- package/lib/client/selection/selection-limits.js.map +0 -1
- package/lib/client/selection/selection-normalizer.d.ts.map +0 -1
- package/lib/client/selection/selection-normalizer.js.map +0 -1
- package/lib/client/side-chat-controller.d.ts.map +0 -1
- package/lib/client/side-chat-controller.js.map +0 -1
- package/lib/client.js.map +0 -1
- package/lib/host/archived-fork-service.d.ts +0 -29
- package/lib/host/archived-fork-service.d.ts.map +0 -1
- package/lib/host/archived-fork-service.js +0 -264
- package/lib/host/archived-fork-service.js.map +0 -1
- package/lib/host/index.d.ts.map +0 -1
- package/lib/host/index.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/remote.d.ts.map +0 -1
- package/lib/remote.js.map +0 -1
- package/lib/shared/archived-wire.d.ts.map +0 -1
- package/lib/shared/archived-wire.js +0 -48
- package/lib/shared/archived-wire.js.map +0 -1
- package/lib/shared/constants.d.ts.map +0 -1
- package/lib/shared/constants.js.map +0 -1
- package/lib/shared/contracts.d.ts.map +0 -1
- package/lib/shared/contracts.js.map +0 -1
- package/lib/shared/error-codes.d.ts.map +0 -1
- package/lib/shared/error-codes.js.map +0 -1
- package/lib/shared/observable.d.ts.map +0 -1
- package/lib/shared/observable.js.map +0 -1
- package/lib/typert.d.ts.map +0 -1
- package/lib/typert.js.map +0 -1
|
@@ -1,38 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const SELECTION_REFERENCE_SOURCE = 'dsh-side-chat-selection';
|
|
1
|
+
import { newlyInsertedOccurrence, occurrenceEditSpan, occurrenceMatchesDraft } from './composer-reference.js';
|
|
2
|
+
export const SELECTION_REFERENCE_SOURCE = 'dsh-side-chat-selection';
|
|
3
3
|
export const SELECTION_REFERENCE_LABEL = '__dsh_side_chat_annotations__';
|
|
4
4
|
function escapeXmlText(value) {
|
|
5
|
-
return value
|
|
6
|
-
.replaceAll('&', '&')
|
|
7
|
-
.replaceAll('<', '<')
|
|
8
|
-
.replaceAll('>', '>');
|
|
5
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
9
6
|
}
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
text: selection.text,
|
|
14
|
-
selection,
|
|
15
|
-
...(trimmedComment === undefined || trimmedComment.length === 0
|
|
16
|
-
? {}
|
|
17
|
-
: { comment: trimmedComment }),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function encodeSelectionReference(annotations) {
|
|
21
|
-
return JSON.stringify({
|
|
22
|
-
version: 2,
|
|
23
|
-
annotations,
|
|
24
|
-
});
|
|
7
|
+
export function unescapeXmlText(value) {
|
|
8
|
+
return value.replaceAll('<', '<').replaceAll('>', '>').replaceAll('&', '&');
|
|
25
9
|
}
|
|
26
10
|
function isSelection(value) {
|
|
27
11
|
if (typeof value !== 'object' || value === null)
|
|
28
12
|
return false;
|
|
29
13
|
const selection = value;
|
|
30
|
-
if (typeof selection.parentSessionId !== 'string'
|
|
31
|
-
||
|
|
32
|
-
||
|
|
33
|
-
|| !Array.isArray(selection.fragments)
|
|
34
|
-
|| typeof selection.rect !== 'object'
|
|
35
|
-
|| selection.rect === null)
|
|
14
|
+
if (typeof selection.parentSessionId !== 'string' || typeof selection.text !== 'string'
|
|
15
|
+
|| !Number.isSafeInteger(selection.atSeq) || !Array.isArray(selection.fragments)
|
|
16
|
+
|| typeof selection.rect !== 'object' || selection.rect === null)
|
|
36
17
|
return false;
|
|
37
18
|
const rect = selection.rect;
|
|
38
19
|
if (![rect.x, rect.y, rect.width, rect.height, rect.viewportWidth, rect.viewportHeight]
|
|
@@ -42,79 +23,38 @@ function isSelection(value) {
|
|
|
42
23
|
if (typeof candidate !== 'object' || candidate === null)
|
|
43
24
|
return false;
|
|
44
25
|
const fragment = candidate;
|
|
45
|
-
return typeof fragment.nodeKey === 'string'
|
|
46
|
-
&& typeof fragment.
|
|
47
|
-
&&
|
|
48
|
-
&& Number.isSafeInteger(fragment.seq)
|
|
49
|
-
&& Number.isSafeInteger(fragment.startOffset)
|
|
50
|
-
&& Number.isSafeInteger(fragment.endOffset)
|
|
26
|
+
return typeof fragment.nodeKey === 'string' && typeof fragment.nodeKind === 'string'
|
|
27
|
+
&& typeof fragment.turnKey === 'string' && Number.isSafeInteger(fragment.seq)
|
|
28
|
+
&& Number.isSafeInteger(fragment.startOffset) && Number.isSafeInteger(fragment.endOffset)
|
|
51
29
|
&& typeof fragment.text === 'string'
|
|
52
30
|
&& ['user', 'assistant', 'context', 'code'].includes(fragment.source ?? '')
|
|
53
|
-
&& typeof fragment.modelVisible === 'boolean'
|
|
54
|
-
&& typeof fragment.settled === 'boolean';
|
|
31
|
+
&& typeof fragment.modelVisible === 'boolean' && typeof fragment.settled === 'boolean';
|
|
55
32
|
});
|
|
56
33
|
}
|
|
57
|
-
function isStoredAnnotation(value) {
|
|
58
|
-
if (typeof value !== 'object' || value === null)
|
|
59
|
-
return false;
|
|
60
|
-
const annotation = value;
|
|
61
|
-
return typeof annotation.text === 'string'
|
|
62
|
-
&& (annotation.comment === undefined || typeof annotation.comment === 'string')
|
|
63
|
-
&& (annotation.selection === undefined || isSelection(annotation.selection));
|
|
64
|
-
}
|
|
65
|
-
function visibleAnnotation(annotation) {
|
|
66
|
-
return {
|
|
67
|
-
text: annotation.text,
|
|
68
|
-
...(annotation.comment === undefined ? {} : { comment: annotation.comment }),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
34
|
function decodeStoredSelectionReference(ref) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|| !Array.isArray(payload.annotations)
|
|
82
|
-
|| payload.annotations.length === 0
|
|
83
|
-
|| !payload.annotations.every(isStoredAnnotation)) {
|
|
35
|
+
const payload = JSON.parse(ref);
|
|
36
|
+
if (payload === null || payload.version !== 2 || !Array.isArray(payload.annotations)
|
|
37
|
+
|| payload.annotations.length === 0 || !payload.annotations.every((value) => {
|
|
38
|
+
if (typeof value !== 'object' || value === null)
|
|
39
|
+
return false;
|
|
40
|
+
const annotation = value;
|
|
41
|
+
return typeof annotation.text === 'string' && isSelection(annotation.selection)
|
|
42
|
+
&& (annotation.comment === undefined || typeof annotation.comment === 'string');
|
|
43
|
+
}))
|
|
84
44
|
throw new Error('The selected conversation annotation is no longer valid.');
|
|
85
|
-
}
|
|
86
45
|
return payload.annotations;
|
|
87
46
|
}
|
|
47
|
+
function visibleAnnotation(annotation) {
|
|
48
|
+
return { text: annotation.text, ...(annotation.comment === undefined ? {} : { comment: annotation.comment }) };
|
|
49
|
+
}
|
|
88
50
|
export function decodeSelectionReference(ref) {
|
|
89
51
|
return decodeStoredSelectionReference(ref).map(visibleAnnotation);
|
|
90
52
|
}
|
|
91
|
-
function selectedContext(annotations) {
|
|
92
|
-
return [
|
|
93
|
-
'<selected_context>',
|
|
94
|
-
...annotations.flatMap((annotation, index) => annotation.comment === undefined
|
|
95
|
-
? [
|
|
96
|
-
`<annotation index="${String(index + 1)}">`,
|
|
97
|
-
escapeXmlText(annotation.text),
|
|
98
|
-
'</annotation>',
|
|
99
|
-
]
|
|
100
|
-
: [
|
|
101
|
-
`<annotation index="${String(index + 1)}">`,
|
|
102
|
-
'<selected_text>',
|
|
103
|
-
escapeXmlText(annotation.text),
|
|
104
|
-
'</selected_text>',
|
|
105
|
-
'<user_comment>',
|
|
106
|
-
escapeXmlText(annotation.comment),
|
|
107
|
-
'</user_comment>',
|
|
108
|
-
'</annotation>',
|
|
109
|
-
]),
|
|
110
|
-
'</selected_context>',
|
|
111
|
-
].join('\n');
|
|
112
|
-
}
|
|
113
53
|
function selectionOccurrences(snapshot) {
|
|
114
54
|
return snapshot.occurrences.filter(occurrence => occurrence.source === SELECTION_REFERENCE_SOURCE);
|
|
115
55
|
}
|
|
116
|
-
function
|
|
117
|
-
return selectionOccurrences(snapshot).flatMap(
|
|
56
|
+
function storedAnnotations(snapshot) {
|
|
57
|
+
return selectionOccurrences(snapshot).flatMap(occurrence => {
|
|
118
58
|
try {
|
|
119
59
|
return [...decodeStoredSelectionReference(occurrence.ref)];
|
|
120
60
|
}
|
|
@@ -123,406 +63,143 @@ function storedConversationAnnotations(snapshot) {
|
|
|
123
63
|
}
|
|
124
64
|
});
|
|
125
65
|
}
|
|
126
|
-
/** Read all plugin annotations represented by the current DSH input occurrence. */
|
|
127
66
|
export function conversationAnnotations(snapshot) {
|
|
128
|
-
return
|
|
67
|
+
return storedAnnotations(snapshot).map(visibleAnnotation);
|
|
129
68
|
}
|
|
130
|
-
/** Read annotations that retain an exact source-selection anchor. */
|
|
131
69
|
export function conversationSelectionAnnotations(snapshot) {
|
|
132
|
-
|
|
133
|
-
storedConversationAnnotations(snapshot).forEach((annotation, annotationIndex) => {
|
|
134
|
-
if (annotation.selection === undefined)
|
|
135
|
-
return;
|
|
136
|
-
anchored.push({
|
|
137
|
-
...visibleAnnotation(annotation),
|
|
138
|
-
annotationIndex,
|
|
139
|
-
selection: annotation.selection,
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
return anchored;
|
|
70
|
+
return storedAnnotations(snapshot).map((annotation, annotationIndex) => ({ ...annotation, annotationIndex }));
|
|
143
71
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const draft = draftWithoutOccurrence(current, occurrence, SELECTION_REFERENCE_LABEL);
|
|
149
|
-
if (draft === undefined)
|
|
150
|
-
continue;
|
|
151
|
-
current = { ...current, draft };
|
|
152
|
-
}
|
|
153
|
-
let draft = current.draft;
|
|
154
|
-
// Add to chat owns the separator immediately after its leading occurrence.
|
|
155
|
-
if (occurrences.some(occurrence => occurrence.offset === 0)) {
|
|
156
|
-
if (draft.startsWith('\n\n'))
|
|
157
|
-
draft = draft.slice(2);
|
|
158
|
-
else if (draft.startsWith('\n') || draft.startsWith(' '))
|
|
159
|
-
draft = draft.slice(1);
|
|
160
|
-
}
|
|
161
|
-
return draft;
|
|
162
|
-
}
|
|
163
|
-
/** Remove every unsent plugin annotation while preserving the user's draft. */
|
|
164
|
-
export function removeConversationAnnotations(input) {
|
|
165
|
-
const snapshot = input.state.getSnapshot();
|
|
166
|
-
if (selectionOccurrences(snapshot).length === 0)
|
|
72
|
+
/** A single atomic chip replacement preserves other references and the user's draft. */
|
|
73
|
+
function writeAnnotations(input, before, annotations) {
|
|
74
|
+
const existing = selectionOccurrences(before);
|
|
75
|
+
if (existing.length > 1)
|
|
167
76
|
return false;
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
export const selectionReferenceSource = {
|
|
173
|
-
trigger: '@',
|
|
174
|
-
name: SELECTION_REFERENCE_SOURCE,
|
|
175
|
-
order: 1_000,
|
|
176
|
-
candidates: async () => [],
|
|
177
|
-
onPick: () => undefined,
|
|
178
|
-
codec: {
|
|
179
|
-
clipboardText: ref => decodeSelectionReference(ref).map(annotation => annotation.text).join('\n\n'),
|
|
180
|
-
serialize: async (ref, signal) => {
|
|
181
|
-
if (signal.aborted)
|
|
182
|
-
throw signal.reason;
|
|
183
|
-
return selectedContext(decodeSelectionReference(ref));
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
function writeConversationAnnotations(input, before, annotations) {
|
|
188
|
-
const draft = draftWithoutSelectionOccurrences(before);
|
|
189
|
-
const insertionState = input.state.getSnapshot();
|
|
190
|
-
if (insertionState.draftRev !== before.draftRev || insertionState.draft !== before.draft)
|
|
77
|
+
const previous = existing[0];
|
|
78
|
+
const span = previous === undefined ? { start: 0, end: 0, draftRev: before.draftRev }
|
|
79
|
+
: occurrenceEditSpan(input, before, previous);
|
|
80
|
+
if (span === undefined)
|
|
191
81
|
return false;
|
|
192
|
-
const ref =
|
|
82
|
+
const ref = JSON.stringify({ version: 2, annotations });
|
|
193
83
|
const inserted = input.insertReference({
|
|
194
|
-
source: SELECTION_REFERENCE_SOURCE,
|
|
195
|
-
ref,
|
|
196
|
-
label: SELECTION_REFERENCE_LABEL,
|
|
84
|
+
source: SELECTION_REFERENCE_SOURCE, ref, label: SELECTION_REFERENCE_LABEL,
|
|
197
85
|
clipboardText: annotations.map(annotation => annotation.text).join('\n\n'),
|
|
198
|
-
},
|
|
199
|
-
start: 0,
|
|
200
|
-
end: 0,
|
|
201
|
-
draftRev: insertionState.draftRev,
|
|
202
|
-
});
|
|
203
|
-
// Existing annotations are intentionally still live here. If DSH rejects the
|
|
204
|
-
// insertion, the operation is a no-op instead of deleting the previous chip.
|
|
86
|
+
}, span);
|
|
205
87
|
if (!inserted)
|
|
206
88
|
return false;
|
|
207
|
-
const
|
|
208
|
-
const occurrence = newlyInsertedOccurrence(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const insertedOccurrence = occurrence === undefined
|
|
212
|
-
? undefined
|
|
213
|
-
: current.occurrences.find(candidate => candidate.occurrenceId === occurrence.occurrenceId);
|
|
214
|
-
if (insertedOccurrence === undefined || insertedOccurrence.offset !== 0)
|
|
215
|
-
return;
|
|
216
|
-
const insertedRange = occurrenceRange(current, insertedOccurrence, SELECTION_REFERENCE_LABEL);
|
|
217
|
-
if (insertedRange === undefined || insertedRange.start !== 0)
|
|
218
|
-
return;
|
|
219
|
-
let tail = current.draft.slice(insertedRange.end);
|
|
220
|
-
if (tail.startsWith(' ') && tail.slice(1) === before.draft)
|
|
221
|
-
tail = before.draft;
|
|
222
|
-
else if (tail !== before.draft)
|
|
223
|
-
return;
|
|
224
|
-
input.setDraft(tail);
|
|
225
|
-
};
|
|
226
|
-
if (occurrence === undefined || occurrence.offset !== 0) {
|
|
227
|
-
rollbackInsertion();
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
const range = occurrenceRange(afterInsert, occurrence, SELECTION_REFERENCE_LABEL);
|
|
231
|
-
if (range === undefined || range.start !== 0) {
|
|
232
|
-
rollbackInsertion();
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
// Current DSH inserts the complete `@label` display text followed by an
|
|
236
|
-
// optional ASCII separating gap. Older releases insert a one-unit U+FFFC
|
|
237
|
-
// placeholder. At this point the old occurrence is still in the tail; one
|
|
238
|
-
// tail replacement preserves the new occurrence and removes the old one.
|
|
239
|
-
let rest = afterInsert.draft.slice(range.end);
|
|
240
|
-
if (rest.startsWith(' ') && rest.slice(1) === before.draft)
|
|
241
|
-
rest = before.draft;
|
|
242
|
-
else if (rest !== before.draft) {
|
|
243
|
-
rollbackInsertion();
|
|
244
|
-
return false;
|
|
245
|
-
}
|
|
246
|
-
const display = afterInsert.draft.slice(range.start, range.end);
|
|
247
|
-
const normalizedDraft = `${display}\n\n${draft}`;
|
|
248
|
-
// insertReference and setDraft are separate public transactions. Re-read the
|
|
249
|
-
// exact occurrence before the second write so a synchronous subscriber cannot
|
|
250
|
-
// make us normalize a stale insertion.
|
|
251
|
-
const current = input.state.getSnapshot();
|
|
252
|
-
const currentOccurrence = current.occurrences.find(candidate => candidate.occurrenceId === occurrence.occurrenceId);
|
|
253
|
-
if (current.draftRev !== afterInsert.draftRev
|
|
254
|
-
|| current.draft !== afterInsert.draft
|
|
255
|
-
|| currentOccurrence === undefined
|
|
256
|
-
|| currentOccurrence.source !== SELECTION_REFERENCE_SOURCE
|
|
257
|
-
|| currentOccurrence.ref !== ref
|
|
258
|
-
|| !occurrenceMatchesDraft(current, currentOccurrence, SELECTION_REFERENCE_LABEL)) {
|
|
259
|
-
rollbackInsertion();
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
input.setDraft(normalizedDraft);
|
|
263
|
-
const normalized = input.state.getSnapshot();
|
|
264
|
-
const retained = normalized.occurrences.find(candidate => candidate.occurrenceId === occurrence.occurrenceId);
|
|
265
|
-
return normalized.draft === normalizedDraft
|
|
266
|
-
&& retained !== undefined
|
|
267
|
-
&& retained.source === SELECTION_REFERENCE_SOURCE
|
|
268
|
-
&& retained.ref === ref
|
|
269
|
-
&& retained.offset === 0
|
|
270
|
-
&& occurrenceMatchesDraft(normalized, retained, SELECTION_REFERENCE_LABEL);
|
|
89
|
+
const after = input.state.getSnapshot();
|
|
90
|
+
const occurrence = newlyInsertedOccurrence(before, after, SELECTION_REFERENCE_SOURCE, ref);
|
|
91
|
+
return occurrence !== undefined && selectionOccurrences(after).length === 1
|
|
92
|
+
&& occurrenceMatchesDraft(after, occurrence, SELECTION_REFERENCE_LABEL);
|
|
271
93
|
}
|
|
272
|
-
/** Add one passage to the parent composer's aggregated annotation occurrence. */
|
|
273
94
|
export function addSelectionToConversation(input, selection, comment) {
|
|
274
95
|
const before = input.state.getSnapshot();
|
|
275
|
-
const
|
|
276
|
-
return
|
|
96
|
+
const trimmed = comment?.trim();
|
|
97
|
+
return writeAnnotations(input, before, [...storedAnnotations(before), {
|
|
98
|
+
text: selection.text, selection, ...(trimmed ? { comment: trimmed } : {}),
|
|
99
|
+
}]);
|
|
100
|
+
}
|
|
101
|
+
export function removeConversationAnnotations(input) {
|
|
102
|
+
const occurrences = selectionOccurrences(input.state.getSnapshot());
|
|
103
|
+
if (occurrences.length === 0)
|
|
104
|
+
return false;
|
|
105
|
+
for (const occurrenceId of occurrences.map(occurrence => occurrence.occurrenceId).reverse()) {
|
|
106
|
+
const snapshot = input.state.getSnapshot();
|
|
107
|
+
const occurrence = snapshot.occurrences.find(candidate => candidate.occurrenceId === occurrenceId);
|
|
108
|
+
if (occurrence === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
// Delete only the chip, never a possibly user-owned leading space/newline.
|
|
111
|
+
const span = occurrenceEditSpan(input, snapshot, occurrence);
|
|
112
|
+
if (span === undefined || !input.replaceText('', span))
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return selectionOccurrences(input.state.getSnapshot()).length === 0;
|
|
277
116
|
}
|
|
278
|
-
/** Remove one unsent selected-passage annotation, retaining the aggregate when needed. */
|
|
279
117
|
export function removeConversationAnnotation(input, annotationIndex) {
|
|
280
118
|
const before = input.state.getSnapshot();
|
|
281
|
-
const annotations = [...
|
|
119
|
+
const annotations = [...storedAnnotations(before)];
|
|
282
120
|
if (!Number.isSafeInteger(annotationIndex) || annotations[annotationIndex] === undefined)
|
|
283
121
|
return false;
|
|
284
122
|
annotations.splice(annotationIndex, 1);
|
|
285
|
-
|
|
286
|
-
input.setDraft(draftWithoutSelectionOccurrences(before));
|
|
287
|
-
return selectionOccurrences(input.state.getSnapshot()).length === 0;
|
|
288
|
-
}
|
|
289
|
-
return writeConversationAnnotations(input, before, annotations);
|
|
123
|
+
return annotations.length === 0 ? removeConversationAnnotations(input) : writeAnnotations(input, before, annotations);
|
|
290
124
|
}
|
|
291
|
-
/** Replace the optional comment on one unsent selected-passage annotation. */
|
|
292
125
|
export function updateConversationAnnotation(input, annotationIndex, comment) {
|
|
293
126
|
const before = input.state.getSnapshot();
|
|
294
|
-
const annotations = [...
|
|
295
|
-
const
|
|
296
|
-
if (!Number.isSafeInteger(annotationIndex) ||
|
|
297
|
-
return false;
|
|
298
|
-
const trimmedComment = comment?.trim();
|
|
299
|
-
annotations[annotationIndex] = {
|
|
300
|
-
text: current.text,
|
|
301
|
-
...(current.selection === undefined ? {} : { selection: current.selection }),
|
|
302
|
-
...(trimmedComment === undefined || trimmedComment.length === 0
|
|
303
|
-
? {}
|
|
304
|
-
: { comment: trimmedComment }),
|
|
305
|
-
};
|
|
306
|
-
return writeConversationAnnotations(input, before, annotations);
|
|
307
|
-
}
|
|
308
|
-
function exactClipboardProjection(snapshot, annotationOccurrence, annotationClipboardText) {
|
|
309
|
-
let projected = '';
|
|
310
|
-
let cursor = 0;
|
|
311
|
-
const occurrences = [...snapshot.occurrences].sort((left, right) => left.offset - right.offset);
|
|
312
|
-
for (const occurrence of occurrences) {
|
|
313
|
-
const expectedLabel = occurrence.occurrenceId === annotationOccurrence.occurrenceId
|
|
314
|
-
? SELECTION_REFERENCE_LABEL
|
|
315
|
-
: occurrence.label;
|
|
316
|
-
if (occurrence.length === undefined
|
|
317
|
-
&& !snapshot.draft.startsWith(LEGACY_REFERENCE_PLACEHOLDER, occurrence.offset)
|
|
318
|
-
&& expectedLabel === undefined)
|
|
319
|
-
return;
|
|
320
|
-
const range = occurrenceRange(snapshot, occurrence, expectedLabel ?? '');
|
|
321
|
-
if (range === undefined || range.start < cursor)
|
|
322
|
-
return;
|
|
323
|
-
const clipboardText = occurrence.occurrenceId === annotationOccurrence.occurrenceId
|
|
324
|
-
? annotationClipboardText
|
|
325
|
-
: occurrence.clipboardText;
|
|
326
|
-
if (clipboardText === undefined)
|
|
327
|
-
return;
|
|
328
|
-
projected += snapshot.draft.slice(cursor, range.start) + clipboardText;
|
|
329
|
-
cursor = range.end;
|
|
330
|
-
}
|
|
331
|
-
return projected + snapshot.draft.slice(cursor);
|
|
332
|
-
}
|
|
333
|
-
function annotationSeparator(draftTail) {
|
|
334
|
-
if (draftTail.startsWith('\n\n'))
|
|
335
|
-
return '\n\n';
|
|
336
|
-
if (draftTail.startsWith('\n'))
|
|
337
|
-
return '\n';
|
|
338
|
-
if (draftTail.startsWith(' '))
|
|
339
|
-
return ' ';
|
|
340
|
-
return '';
|
|
341
|
-
}
|
|
342
|
-
function baseDraftFromMirror(mirrorDraft, clipboardText, expectedBaseDraft) {
|
|
343
|
-
const separators = ['\n\n', '\n', ' ', ''];
|
|
344
|
-
if (expectedBaseDraft !== undefined) {
|
|
345
|
-
return separators.some(separator => mirrorDraft === clipboardText + separator + expectedBaseDraft)
|
|
346
|
-
? expectedBaseDraft
|
|
347
|
-
: undefined;
|
|
348
|
-
}
|
|
349
|
-
for (const separator of separators.slice(0, -1)) {
|
|
350
|
-
const prefix = clipboardText + separator;
|
|
351
|
-
if (mirrorDraft.startsWith(prefix))
|
|
352
|
-
return mirrorDraft.slice(prefix.length);
|
|
353
|
-
}
|
|
354
|
-
return mirrorDraft === clipboardText ? '' : undefined;
|
|
355
|
-
}
|
|
356
|
-
/** Describe both the display draft and DSH's persisted clipboard projection. */
|
|
357
|
-
export function conversationAnnotationRecoveryRecord(snapshot) {
|
|
358
|
-
const occurrence = selectionOccurrences(snapshot).find(candidate => candidate.offset === 0);
|
|
359
|
-
const ref = conversationAnnotationReference(snapshot);
|
|
360
|
-
if (occurrence === undefined || ref === undefined)
|
|
361
|
-
return;
|
|
362
|
-
const range = occurrenceRange(snapshot, occurrence, SELECTION_REFERENCE_LABEL);
|
|
363
|
-
if (range === undefined || range.start !== 0)
|
|
364
|
-
return;
|
|
365
|
-
let annotations;
|
|
366
|
-
try {
|
|
367
|
-
annotations = decodeStoredSelectionReference(ref);
|
|
368
|
-
}
|
|
369
|
-
catch {
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
const clipboardText = annotations.map(annotation => annotation.text).join('\n\n');
|
|
373
|
-
const mirrorDraft = exactClipboardProjection(snapshot, occurrence, clipboardText);
|
|
374
|
-
if (mirrorDraft === undefined)
|
|
375
|
-
return;
|
|
376
|
-
const separator = annotationSeparator(snapshot.draft.slice(range.end));
|
|
377
|
-
const mirrorPrefix = clipboardText + separator;
|
|
378
|
-
if (!mirrorDraft.startsWith(mirrorPrefix))
|
|
379
|
-
return;
|
|
380
|
-
return {
|
|
381
|
-
ref,
|
|
382
|
-
displayDraft: snapshot.draft,
|
|
383
|
-
mirrorDraft,
|
|
384
|
-
baseDraft: mirrorDraft.slice(mirrorPrefix.length),
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
/** Return the valid aggregated reference currently occupying the leading draft slot. */
|
|
388
|
-
export function conversationAnnotationReference(snapshot) {
|
|
389
|
-
const occurrence = selectionOccurrences(snapshot).find(candidate => candidate.offset === 0);
|
|
390
|
-
if (occurrence === undefined
|
|
391
|
-
|| !occurrenceMatchesDraft(snapshot, occurrence, SELECTION_REFERENCE_LABEL))
|
|
392
|
-
return;
|
|
393
|
-
try {
|
|
394
|
-
decodeStoredSelectionReference(occurrence.ref);
|
|
395
|
-
return occurrence.ref;
|
|
396
|
-
}
|
|
397
|
-
catch {
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
function draftWithoutOrphanedAnnotationPrefix(snapshot) {
|
|
402
|
-
if (snapshot.occurrences.some(occurrence => occurrence.offset === 0))
|
|
403
|
-
return;
|
|
404
|
-
const legacyPrefix = `${LEGACY_REFERENCE_PLACEHOLDER}\n\n`;
|
|
405
|
-
if (snapshot.draft.startsWith(legacyPrefix))
|
|
406
|
-
return snapshot.draft.slice(legacyPrefix.length);
|
|
407
|
-
const display = referenceDisplayText(SELECTION_REFERENCE_LABEL);
|
|
408
|
-
if (!snapshot.draft.startsWith(display))
|
|
409
|
-
return;
|
|
410
|
-
let rest = snapshot.draft.slice(display.length);
|
|
411
|
-
if (rest.startsWith('\n\n'))
|
|
412
|
-
rest = rest.slice(2);
|
|
413
|
-
else if (rest.startsWith(' '))
|
|
414
|
-
rest = rest.slice(1);
|
|
415
|
-
else
|
|
416
|
-
return;
|
|
417
|
-
return rest;
|
|
418
|
-
}
|
|
419
|
-
/** Remove the plugin-owned prefix when rc.6 restored its draft without occurrences. */
|
|
420
|
-
export function removeOrphanedConversationAnnotationPlaceholder(input) {
|
|
421
|
-
const draft = draftWithoutOrphanedAnnotationPrefix(input.state.getSnapshot());
|
|
422
|
-
if (draft === undefined)
|
|
127
|
+
const annotations = [...storedAnnotations(before)];
|
|
128
|
+
const annotation = annotations[annotationIndex];
|
|
129
|
+
if (!Number.isSafeInteger(annotationIndex) || annotation === undefined)
|
|
423
130
|
return false;
|
|
424
|
-
|
|
425
|
-
|
|
131
|
+
const trimmed = comment?.trim();
|
|
132
|
+
annotations[annotationIndex] = { text: annotation.text, selection: annotation.selection,
|
|
133
|
+
...(trimmed ? { comment: trimmed } : {}) };
|
|
134
|
+
return writeAnnotations(input, before, annotations);
|
|
426
135
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
return false;
|
|
435
|
-
}
|
|
436
|
-
const snapshot = input.state.getSnapshot();
|
|
437
|
-
const orphanDraft = draftWithoutOrphanedAnnotationPrefix(snapshot);
|
|
438
|
-
const clipboardText = annotations.map(annotation => annotation.text).join('\n\n');
|
|
439
|
-
const mirrorBaseDraft = expectedMirrorDraft === undefined
|
|
440
|
-
? undefined
|
|
441
|
-
: baseDraftFromMirror(expectedMirrorDraft, clipboardText, expectedBaseDraft);
|
|
442
|
-
const draft = orphanDraft
|
|
443
|
-
?? (expectedMirrorDraft !== undefined
|
|
444
|
-
&& snapshot.occurrences.length === 0
|
|
445
|
-
&& snapshot.draft === expectedMirrorDraft
|
|
446
|
-
&& mirrorBaseDraft !== undefined
|
|
447
|
-
? mirrorBaseDraft
|
|
448
|
-
: undefined);
|
|
449
|
-
if (draft === undefined)
|
|
450
|
-
return false;
|
|
451
|
-
if (draft !== snapshot.draft)
|
|
452
|
-
input.setDraft(draft);
|
|
453
|
-
const beforeWrite = input.state.getSnapshot();
|
|
454
|
-
if (beforeWrite.draft !== draft || beforeWrite.occurrences.length !== 0)
|
|
455
|
-
return false;
|
|
456
|
-
if (writeConversationAnnotations(input, beforeWrite, annotations))
|
|
457
|
-
return true;
|
|
458
|
-
// Recovery must be content preserving. If DSH rejects insertion, put the
|
|
459
|
-
// exact mirror projection back rather than leaving a partially stripped draft.
|
|
460
|
-
const afterFailure = input.state.getSnapshot();
|
|
461
|
-
if (afterFailure.occurrences.length === 0 && expectedMirrorDraft !== undefined) {
|
|
462
|
-
input.setDraft(expectedMirrorDraft);
|
|
463
|
-
}
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
function unescapeXmlText(value) {
|
|
467
|
-
return value
|
|
468
|
-
.replaceAll('<', '<')
|
|
469
|
-
.replaceAll('>', '>')
|
|
470
|
-
.replaceAll('&', '&');
|
|
471
|
-
}
|
|
472
|
-
function parseAnnotationBody(body) {
|
|
473
|
-
const selectedText = /^<selected_text>\n([\s\S]*?)\n<\/selected_text>(?:\n<user_comment>\n([\s\S]*?)\n<\/user_comment>)?$/u.exec(body);
|
|
474
|
-
if (selectedText === null)
|
|
475
|
-
return { text: unescapeXmlText(body) };
|
|
476
|
-
const comment = selectedText[2];
|
|
477
|
-
return {
|
|
478
|
-
text: unescapeXmlText(selectedText[1] ?? ''),
|
|
479
|
-
...(comment === undefined ? {} : { comment: unescapeXmlText(comment) }),
|
|
480
|
-
};
|
|
136
|
+
function selectedContext(annotations) {
|
|
137
|
+
return ['<selected_context>', ...annotations.flatMap((annotation, index) => [
|
|
138
|
+
`<annotation index="${String(index + 1)}">`,
|
|
139
|
+
'<selected_text>', escapeXmlText(annotation.text), '</selected_text>',
|
|
140
|
+
...(annotation.comment === undefined ? [] : ['<user_comment>', escapeXmlText(annotation.comment), '</user_comment>']),
|
|
141
|
+
'</annotation>',
|
|
142
|
+
]), '</selected_context>'].join('\n');
|
|
481
143
|
}
|
|
482
|
-
|
|
144
|
+
export const selectionReferenceSource = {
|
|
145
|
+
trigger: '@', name: SELECTION_REFERENCE_SOURCE, order: 1_000,
|
|
146
|
+
candidates: async () => [], onPick: () => undefined,
|
|
147
|
+
codec: {
|
|
148
|
+
clipboardText: ref => decodeSelectionReference(ref).map(annotation => annotation.text).join('\n\n'),
|
|
149
|
+
serialize: async (ref, signal) => {
|
|
150
|
+
if (signal.aborted)
|
|
151
|
+
throw signal.reason;
|
|
152
|
+
return selectedContext(decodeSelectionReference(ref));
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
};
|
|
483
156
|
export function parseAnnotatedConversationPrompt(text) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
157
|
+
// Stored messages outlive a plugin update. Reading their durable text does
|
|
158
|
+
// not require an old DSH runtime or an old composer implementation.
|
|
159
|
+
const prefix = /^\s*<selected_context(?: source="current-conversation" event-seq="\d+")?>\r?\n([\s\S]*?)\r?\n<\/selected_context>\s*/u.exec(text);
|
|
160
|
+
if (prefix === null)
|
|
161
|
+
return;
|
|
162
|
+
const annotations = [];
|
|
163
|
+
if (prefix[0].trimStart().startsWith('<selected_context source=')) {
|
|
164
|
+
annotations.push({ text: unescapeXmlText(prefix[1] ?? '') });
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
for (const match of prefix[1]?.matchAll(/<annotation(?: index="\d+")?>\r?\n([\s\S]*?)\r?\n<\/annotation>/gu) ?? []) {
|
|
168
|
+
const body = match[1] ?? '';
|
|
169
|
+
const parts = /^<selected_text>\r?\n([\s\S]*?)\r?\n<\/selected_text>(?:\r?\n<user_comment>\r?\n([\s\S]*?)\r?\n<\/user_comment>)?$/u.exec(body);
|
|
170
|
+
annotations.push({ text: unescapeXmlText(parts?.[1] ?? body),
|
|
171
|
+
...(parts?.[2] === undefined ? {} : { comment: unescapeXmlText(parts[2]) }) });
|
|
493
172
|
}
|
|
494
173
|
}
|
|
495
|
-
|
|
496
|
-
if (legacyXml !== null) {
|
|
497
|
-
return {
|
|
498
|
-
annotations: [{ text: unescapeXmlText(legacyXml[2] ?? '') }],
|
|
499
|
-
message: text.slice(legacyXml[0].length).trim(),
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
const legacyText = /^Selected passage from the current conversation \(message (\d+)\):\n([\s\S]*?)\nEnd selected passage\.\s*/u.exec(text);
|
|
503
|
-
if (legacyText === null)
|
|
504
|
-
return undefined;
|
|
505
|
-
return {
|
|
506
|
-
annotations: [{ text: legacyText[2] ?? '' }],
|
|
507
|
-
message: text.slice(legacyText[0].length).trim(),
|
|
508
|
-
};
|
|
174
|
+
return annotations.length === 0 ? undefined : { annotations, message: text.slice(prefix[0].length).trim() };
|
|
509
175
|
}
|
|
510
|
-
/**
|
|
176
|
+
/** The first prompt uses the same durable annotation format as the main composer. */
|
|
511
177
|
export function buildSideChatPrompt(selection, question) {
|
|
512
178
|
const trimmed = question.trim();
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
179
|
+
return [{ type: 'text', text: selection === undefined ? trimmed : [
|
|
180
|
+
selectedContext([{ text: selection.text }]), '',
|
|
181
|
+
'<user_question>', escapeXmlText(trimmed), '</user_question>',
|
|
182
|
+
].join('\n') }];
|
|
183
|
+
}
|
|
184
|
+
/** Decode a question only after recognizing this plugin's annotation prefix. */
|
|
185
|
+
export function annotationMessageText(message) {
|
|
186
|
+
const question = /^<user_question>(?:\r?\n)?([\s\S]*?)(?:\r?\n)?<\/user_question>(?=$|\s)/u.exec(message);
|
|
187
|
+
if (question === null)
|
|
188
|
+
return message;
|
|
189
|
+
const text = unescapeXmlText((question[1] ?? '').trim());
|
|
190
|
+
// Attachments or additional text blocks must not make the question wrapper
|
|
191
|
+
// reappear, nor be discarded while projecting the model-facing payload.
|
|
192
|
+
const suffix = message.slice(question[0].length).trim();
|
|
193
|
+
return [text, suffix].filter(part => part.length > 0).join('\n');
|
|
194
|
+
}
|
|
195
|
+
/** One projection supplies both the annotation capsule and the visible question. */
|
|
196
|
+
export function parseSideChatPrompt(text) {
|
|
197
|
+
const annotated = parseAnnotatedConversationPrompt(text);
|
|
198
|
+
return annotated === undefined ? undefined : {
|
|
199
|
+
annotations: annotated.annotations,
|
|
200
|
+
message: annotationMessageText(annotated.message),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
export function sideChatQuestionText(text) {
|
|
204
|
+
return parseSideChatPrompt(text)?.message ?? text;
|
|
527
205
|
}
|
|
528
|
-
//# sourceMappingURL=add-to-conversation.js.map
|