返回 DeepSeek-Reasonix
transcriptContentResolver.ts
根目录 / desktop / frontend / src / lib / transcriptContentResolver.ts
1 // Lazy full-content resolution for snapshot-protocol tabs. A tab wired to the
2 // unified transcript snapshot registers a resolver here; the transcript store
3 // then answers content requests from the snapshot client instead of the
4 // windowed history backend. Tabs without snapshot support fall through to the
5 // store's HistoryContentForTab path.
6
7 export interface TranscriptContentResolver {
8 resolve(entryId: string, field: string): Promise<string | undefined>;
9 enabled(): boolean;
10 }
11
12 export class TranscriptContentResolverRegistry {
13 private readonly resolvers = new Map<string, TranscriptContentResolver>();
14
15 register(
16 tabId: string,
17 resolve: (entryId: string, field: string) => Promise<string | undefined>,
18 enabled: () => boolean = () => true,
19 ): () => void {
20 const entry = { resolve, enabled };
21 this.resolvers.set(tabId, entry);
22 return () => { if (this.resolvers.get(tabId) === entry) this.resolvers.delete(tabId); };
23 }
24
25 active(tabId: string): TranscriptContentResolver | undefined {
26 const resolver = this.resolvers.get(tabId);
27 return resolver?.enabled() ? resolver : undefined;
28 }
29 }
30
31 // Snapshot-protocol item ids are `m:<messageId>` while the windowed store keys
32 // records by backend entryId, so alias them back before lookup.
33 export function resolveTranscriptEntryAlias(
34 sessions: Iterable<{ tabId: string; records: ReadonlyArray<{ entryId: string; message: { messageId?: string } }> }>,
35 tabId: string,
36 entryId: string,
37 ): string {
38 if (!entryId.startsWith("m:")) return entryId;
39 for (const session of sessions) {
40 if (session.tabId !== tabId) continue;
41 const record = session.records.find((candidate) => `m:${candidate.message.messageId}` === entryId);
42 if (record) return record.entryId;
43 }
44 return entryId;
45 }
46
46 lines TYPESCRIPT