返回 DeepSeek-Reasonix
editReplay.ts
根目录 / desktop / frontend / src / lib / editReplay.ts
1 import { restoreAttachmentRefsForSubmit } from "./attachmentDisplay";
2 import { invocationSegmentsFromMessage, serializeInvocationSubmit, type ComposerInvocation } from "./invocationDisplay";
3 import { splitSelectedTextContext } from "./selectedTextContext";
4
5 export function replaySubmitText(
6 originalSubmitText: string | undefined,
7 originalDisplayText: string,
8 nextDisplayText: string,
9 fallbackSubmitText: string,
10 ): string {
11 const originalSubmit = (originalSubmitText ?? "").trim();
12 const originalDisplay = originalDisplayText.trim();
13 const nextDisplay = nextDisplayText.trim();
14 const fallbackSubmit = fallbackSubmitText.trim();
15 if (!originalSubmit || originalSubmit === originalDisplay) return fallbackSubmit;
16 if (nextDisplay === originalDisplay) return originalSubmit;
17
18 const invocationSegments = invocationSegmentsFromMessage(originalDisplay, originalSubmit);
19 const invocationItems = invocationSegments.filter((segment) => segment.type === "invocation");
20 if (invocationItems.length > 0) {
21 const scale = originalDisplay.length > 0 ? nextDisplay.length / originalDisplay.length : 0;
22 const invocations: ComposerInvocation[] = invocationItems.map((segment, index) => ({
23 id: `edit-invocation-${index}`,
24 offset: index === 0 ? 0 : Math.min(nextDisplay.length, Math.round(segment.offset * scale)),
25 command: {
26 name: segment.invocation.name,
27 description: "",
28 kind: segment.invocation.kind ?? "skill",
29 },
30 }));
31 const serialized = serializeInvocationSubmit(fallbackSubmit, invocations).trim();
32 // Keep any hidden submit prefix (referenced-session context, memory
33 // framing) exactly like the plain-text branch below: the display maps to
34 // the serialized slash tail of the original submit, and everything before
35 // that tail rides along unchanged. No match (e.g. attachment-expanded
36 // tails) falls back to the bare serialized form.
37 const originalInvocations: ComposerInvocation[] = invocationItems.map((segment, index) => ({
38 id: `edit-invocation-original-${index}`,
39 offset: segment.offset,
40 command: {
41 name: segment.invocation.name,
42 description: "",
43 kind: segment.invocation.kind ?? "skill",
44 },
45 }));
46 const originalSerialized = serializeInvocationSubmit(originalDisplay, originalInvocations).trim();
47 if (originalSerialized && originalSubmit.length > originalSerialized.length && originalSubmit.endsWith(originalSerialized)) {
48 return `${originalSubmit.slice(0, originalSubmit.length - originalSerialized.length)}${serialized}`.trim();
49 }
50 return serialized;
51 }
52
53 const originalFallbackSubmit = restoreAttachmentRefsForSubmit(originalDisplay).trim();
54 if (originalFallbackSubmit && originalSubmit.endsWith(originalFallbackSubmit)) {
55 return `${originalSubmit.slice(0, originalSubmit.length - originalFallbackSubmit.length)}${fallbackSubmit}`.trim();
56 }
57 return fallbackSubmit;
58 }
59
60 export function replaySubmitTextPreservingSelectedContext(
61 originalSubmitText: string | undefined,
62 originalDisplayText: string,
63 nextDisplayText: string,
64 fallbackSubmitText: string,
65 ): string {
66 const selected = splitSelectedTextContext(originalSubmitText);
67 const replayed = replaySubmitText(
68 selected.submitText,
69 originalDisplayText,
70 nextDisplayText,
71 fallbackSubmitText,
72 );
73 return [replayed, selected.contextBlock].filter(Boolean).join("\n\n");
74 }
75
75 lines TYPESCRIPT