返回 DeepSeek-Reasonix
attachDedup.ts
根目录 / desktop / frontend / src / lib / attachDedup.ts
1 // attachDedup centralizes the small deduplication helpers the composer
2 // uses when adding attachments. The composer's image paste/drop already
3 // works, but a user can drop the same file twice (or paste the same
4 // clipboard twice) and end up with two @path references pointing to
5 // the same on-disk blob — which the kernel would re-process. Dedup
6 // keys on the SHA-256 of the file bytes, with a path fallback for the
7 // case where a file:// URL or data: URL is the only available signal.
8
9 const HEX = "0123456789abcdef";
10
11 function bytesToHex(bytes: Uint8Array): string {
12 let out = "";
13 for (let i = 0; i < bytes.length; i++) {
14 const b = bytes[i];
15 out += HEX[(b >> 4) & 0xf] + HEX[b & 0xf];
16 }
17 return out;
18 }
19
20 // sha256 returns the hex SHA-256 of `blob`. The Web Crypto Subtle API
21 // is available in Wails' WebView (Chromium / WebKitGTK 4.1+); we
22 // don't fall back to a JS implementation because a no-op (returning
23 // "") would silently disable dedup, which is worse than no dedup
24 // at all. The caller checks the empty-string return and skips the
25 // dedup step in that case.
26 export async function sha256(blob: Blob): Promise<string> {
27 if (typeof crypto === "undefined" || !crypto.subtle) return "";
28 try {
29 const buf = await blob.arrayBuffer();
30 const digest = await crypto.subtle.digest("SHA-256", buf);
31 return bytesToHex(new Uint8Array(digest));
32 } catch {
33 return "";
34 }
35 }
36
37 // DedupIndex tracks the SHA-256 hashes the user has already attached
38 // in the current composer session (lives for the life of the App
39 // mount; cleared on new session because the user expects a fresh
40 // palette). A path-keyed fallback lets a non-Crypto-capable browser
41 // still dedup by URL when the same path is dropped twice — the
42 // fallback is weaker (the same content from two paths won't match)
43 // but covers the common "dropped the same file twice" case.
44 export class DedupIndex {
45 private hashes = new Set<string>();
46 private paths = new Set<string>();
47
48 seen(hash: string, path: string): boolean {
49 if (hash) {
50 if (this.hashes.has(hash)) return true;
51 return false;
52 }
53 return this.paths.has(path);
54 }
55
56 add(hash: string, path: string): void {
57 if (hash) this.hashes.add(hash);
58 this.paths.add(path);
59 }
60
61 forget(hash: string, path: string): void {
62 if (hash) this.hashes.delete(hash);
63 this.paths.delete(path);
64 }
65
66 clear(): void {
67 this.hashes.clear();
68 this.paths.clear();
69 }
70 }
71
71 lines TYPESCRIPT