返回 DeepSeek-Reasonix
transcriptMarkdownCache.ts
根目录 / desktop / frontend / src / lib / transcriptMarkdownCache.ts
1 import type { MarkdownBlock } from "./markdownPipeline";
2
3 export interface ParsedMarkdownValue {
4 source: string;
5 blocks: MarkdownBlock[];
6 selectionText: string;
7 selectionRevision: number;
8 /** Source/projection UTF-16 bytes plus the estimated HAST weight. */
9 bytes: number;
10 }
11
12 /**
13 * Byte-bounded LRU whose active selection entries may be pinned. Entries are
14 * addressed by source content, not by row id: a parse is a pure function of
15 * its text, and row ids differ between the live and history projections.
16 */
17 export class TranscriptMarkdownCache {
18 private readonly entries = new Map<number, { value: ParsedMarkdownValue; bytes: number }>();
19 private readonly pins = new Map<number, number>();
20 bytes = 0;
21 evictions = 0;
22
23 constructor(readonly budgetBytes: number) {}
24
25 get(source: string, revision: number): ParsedMarkdownValue | undefined {
26 const entry = this.entries.get(revision);
27 if (!entry || entry.value.source !== source) return undefined;
28 this.entries.delete(revision);
29 this.entries.set(revision, entry);
30 return entry.value;
31 }
32
33 set(revision: number, value: ParsedMarkdownValue): void {
34 const previous = this.entries.get(revision);
35 if (previous) this.bytes -= previous.bytes;
36 const bytes = Math.max(0, value.bytes);
37 this.entries.set(revision, { value, bytes });
38 this.bytes += bytes;
39 this.enforceBudget();
40 }
41
42 pin(revision: number): () => void {
43 this.pins.set(revision, (this.pins.get(revision) ?? 0) + 1);
44 let released = false;
45 return () => {
46 if (released) return;
47 released = true;
48 const count = this.pins.get(revision) ?? 0;
49 if (count <= 1) this.pins.delete(revision);
50 else this.pins.set(revision, count - 1);
51 this.enforceBudget();
52 };
53 }
54
55 size(): number {
56 return this.entries.size;
57 }
58
59 private enforceBudget(): void {
60 while (this.bytes > this.budgetBytes && this.entries.size > 1) {
61 const victimKey = Array.from(this.entries.keys()).find((key) => !this.pins.has(key));
62 if (victimKey === undefined) break;
63 const victim = this.entries.get(victimKey);
64 if (victim) this.bytes -= victim.bytes;
65 this.entries.delete(victimKey);
66 this.evictions += 1;
67 }
68 }
69 }
70
70 lines TYPESCRIPT