SPB Git

spb/prisme Public MIT

Navigateur iOS intelligent — chaque page comprise localement avant d'être affichée. SwiftUI · WebKit · Foundation Models, 100% on-device.

Swift 96.7% JavaScript 3.3%
2.2 KB · 64 lines swift
Raw Blame History
1//2//  DigestCache.swift3//  Prisme4//5//  Author: Simon-Pierre Boucher <contact@spboucher.ai>6//78import CryptoKit9import Foundation1011/// Content-addressed digest cache (CLAUDE.md §4: a page is never distilled12/// twice). Keys are hashes of the extracted text, so a reloaded page with13/// identical content is a hit even if the URL differs. Digests are durable14/// artefacts: kept in memory and on disk, ready to feed history and diffs.15actor DigestCache {16    private struct Entry: Codable {17        let digest: PageDigest18        let blocks: [ContentBlock]19    }2021    private var memory: [String: Entry] = [:]22    private let directory: URL2324    init() {25        let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]26        directory = base.appendingPathComponent("Digests", isDirectory: true)27        try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)28    }2930    static func key(for content: ExtractedContent) -> String {31        var hasher = SHA256()32        hasher.update(data: Data(content.title.utf8))33        for block in content.blocks {34            hasher.update(data: Data(block.text.utf8))35        }36        return hasher.finalize().map { String(format: "%02x", $0) }.joined()37    }3839    func lookup(_ key: String) -> (PageDigest, [ContentBlock])? {40        if let entry = memory[key] {41            return (entry.digest, entry.blocks)42        }43        let file = directory.appendingPathComponent("\(key).json")44        guard let data = try? Data(contentsOf: file),45              let entry = try? JSONDecoder().decode(Entry.self, from: data)46        else { return nil }47        memory[key] = entry48        return (entry.digest, entry.blocks)49    }5051    func store(_ digest: PageDigest, blocks: [ContentBlock], key: String) {52        let entry = Entry(digest: digest, blocks: blocks)53        memory[key] = entry54        if memory.count > 64 {55            // Cheap pressure valve; disk keeps the long tail.56            memory.removeValue(forKey: memory.keys.first!)57        }58        let file = directory.appendingPathComponent("\(key).json")59        if let data = try? JSONEncoder().encode(entry) {60            try? data.write(to: file, options: .atomic)61        }62    }63}64