// // ContentBlock.swift // Prisme // // Author: Simon-Pierre Boucher // import Foundation /// A typed block produced by the deterministic DOM extraction (Distiller /// step 2). `domPath` is the child-index path from `document.body` to the /// source element โ€” the anchor that lets every generated summary point back /// to the real page (CLAUDE.md ยง4, the easiest constraint to forget). struct ContentBlock: Codable, Hashable, Sendable { enum Kind: String, Codable, Sendable { case heading case paragraph case listItem case quote case code case table case caption } let kind: Kind /// Heading depth (1โ€“6) for headings, 0 otherwise. let level: Int let text: String let domPath: String } /// Everything the extractor returns for one page. struct ExtractedContent: Codable, Sendable { let title: String let lang: String? let blocks: [ContentBlock] /// First sentence of the first paragraph โ€” the deterministic essence /// used wherever the model has nothing better to offer. var leadSentence: String? { guard let paragraph = blocks.first(where: { $0.kind == .paragraph })?.text else { return nil } if let end = paragraph.firstIndex(where: { ".!?".contains($0) }) { return String(paragraph[...end]) } return String(paragraph.prefix(200)) } } /// A text selection captured in the page, with its anchor and context. struct SelectionExcerpt: Codable, Sendable { let text: String let domPath: String? let section: String? }