Transcript Incremental Projection

Nucleic-Session: 0FFD007B-0696-4517-9429-129C7B0FD5AC
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-06 18:39:05 -07:00
co-authored by Nucleic
parent aa6a8e2670
commit a6c9496dfe
@@ -40,14 +40,19 @@ struct MarkdownText: View {
/// (scrolling, a sibling row streaming) and on every chat reopen, yet structural parsing /// (scrolling, a sibling row streaming) and on every chat reopen, yet structural parsing
/// is independent of `bodySize` — so the source string is a complete key. Bounded; /// is independent of `bodySize` — so the source string is a complete key. Bounded;
/// `NSCache` also evicts under memory pressure. /// `NSCache` also evicts under memory pressure.
///
/// The whole parse pipeline (both caches and the statics below) is `nonisolated`: `prewarm`
/// runs it from a detached background task by design, and as `View` statics they'd otherwise
/// be implicitly MainActor (a Swift 6 error for that call). `(unsafe)` on the caches is
/// sound because `NSCache` is thread-safe and the boxed values are immutable.
private final class ParsedBlocks { let blocks: [Block]; init(_ b: [Block]) { self.blocks = b } } private final class ParsedBlocks { let blocks: [Block]; init(_ b: [Block]) { self.blocks = b } }
private static let blockCache: NSCache<NSString, ParsedBlocks> = { nonisolated(unsafe) private static let blockCache: NSCache<NSString, ParsedBlocks> = {
let cache = NSCache<NSString, ParsedBlocks>() let cache = NSCache<NSString, ParsedBlocks>()
cache.countLimit = 2048 cache.countLimit = 2048
return cache return cache
}() }()
private static func parse(_ markdown: String) -> [Block] { nonisolated private static func parse(_ markdown: String) -> [Block] {
let key = markdown as NSString let key = markdown as NSString
if let hit = blockCache.object(forKey: key) { return hit.blocks } if let hit = blockCache.object(forKey: key) { return hit.blocks }
let blocks = parseUncached(markdown) let blocks = parseUncached(markdown)
@@ -55,7 +60,7 @@ struct MarkdownText: View {
return blocks return blocks
} }
private static func parseUncached(_ markdown: String) -> [Block] { nonisolated private static func parseUncached(_ markdown: String) -> [Block] {
var blocks: [Block] = [] var blocks: [Block] = []
var textBuffer: [String] = [] var textBuffer: [String] = []
func flush() { func flush() {
@@ -100,12 +105,12 @@ struct MarkdownText: View {
/// A GitHub-style table: a `|`-bearing header line immediately followed by a /// A GitHub-style table: a `|`-bearing header line immediately followed by a
/// `|---|:--:|` separator line. /// `|---|:--:|` separator line.
private static func isTableStart(_ lines: [String], _ index: Int) -> Bool { nonisolated private static func isTableStart(_ lines: [String], _ index: Int) -> Bool {
guard lines[index].contains("|"), index + 1 < lines.count else { return false } guard lines[index].contains("|"), index + 1 < lines.count else { return false }
return isSeparatorRow(lines[index + 1]) return isSeparatorRow(lines[index + 1])
} }
private static func isSeparatorRow(_ line: String) -> Bool { nonisolated private static func isSeparatorRow(_ line: String) -> Bool {
let cells = tableCells(line) let cells = tableCells(line)
guard !cells.isEmpty else { return false } guard !cells.isEmpty else { return false }
return cells.allSatisfy { cell in return cells.allSatisfy { cell in
@@ -115,7 +120,7 @@ struct MarkdownText: View {
/// Split a table row into trimmed cells, dropping the empties created by the /// Split a table row into trimmed cells, dropping the empties created by the
/// leading/trailing pipes. /// leading/trailing pipes.
private static func tableCells(_ line: String) -> [String] { nonisolated private static func tableCells(_ line: String) -> [String] {
var trimmed = line.trimmingCharacters(in: .whitespaces) var trimmed = line.trimmingCharacters(in: .whitespaces)
if trimmed.hasPrefix("|") { trimmed.removeFirst() } if trimmed.hasPrefix("|") { trimmed.removeFirst() }
if trimmed.hasSuffix("|") { trimmed.removeLast() } if trimmed.hasSuffix("|") { trimmed.removeLast() }
@@ -189,7 +194,7 @@ struct MarkdownText: View {
/// Classify one raw prose line. Headings are checked before bullets (a heading marker wins), /// Classify one raw prose line. Headings are checked before bullets (a heading marker wins),
/// and the plain case keeps the *raw* line (not the trimmed one) exactly as the old cascade /// and the plain case keeps the *raw* line (not the trimmed one) exactly as the old cascade
/// did — the inline parser preserves leading whitespace under `.inlineOnlyPreservingWhitespace`. /// did — the inline parser preserves leading whitespace under `.inlineOnlyPreservingWhitespace`.
private static func classify(_ raw: String) -> LineStyle { nonisolated private static func classify(_ raw: String) -> LineStyle {
let trimmed = raw.trimmingCharacters(in: .whitespaces) let trimmed = raw.trimmingCharacters(in: .whitespaces)
if trimmed.isEmpty { return .blank } if trimmed.isEmpty { return .blank }
// Headings scale relative to the base prose size so the hierarchy holds at any base and // Headings scale relative to the base prose size so the hierarchy holds at any base and
@@ -219,7 +224,7 @@ struct MarkdownText: View {
} }
/// Returns the content after a `- `, `* `, `+ ` or `N. ` list marker, else nil. /// Returns the content after a `- `, `* `, `+ ` or `N. ` list marker, else nil.
private static func bulletContent(_ trimmed: String) -> String? { nonisolated private static func bulletContent(_ trimmed: String) -> String? {
for marker in ["- ", "* ", "+ "] where trimmed.hasPrefix(marker) { for marker in ["- ", "* ", "+ "] where trimmed.hasPrefix(marker) {
return String(trimmed.dropFirst(marker.count)) return String(trimmed.dropFirst(marker.count))
} }
@@ -241,13 +246,13 @@ struct MarkdownText: View {
/// recur across re-renders and reopens, so memoize the parsed result. Independent of /// recur across re-renders and reopens, so memoize the parsed result. Independent of
/// `bodySize` (callers apply the font), so the source string is a complete key. /// `bodySize` (callers apply the font), so the source string is a complete key.
private final class InlineBox { let value: AttributedString; init(_ v: AttributedString) { self.value = v } } private final class InlineBox { let value: AttributedString; init(_ v: AttributedString) { self.value = v } }
private static let inlineCache: NSCache<NSString, InlineBox> = { nonisolated(unsafe) private static let inlineCache: NSCache<NSString, InlineBox> = {
let cache = NSCache<NSString, InlineBox>() let cache = NSCache<NSString, InlineBox>()
cache.countLimit = 16384 cache.countLimit = 16384
return cache return cache
}() }()
private static func attributedInline(_ string: String) -> AttributedString { nonisolated private static func attributedInline(_ string: String) -> AttributedString {
let key = string as NSString let key = string as NSString
if let hit = inlineCache.object(forKey: key) { return hit.value } if let hit = inlineCache.object(forKey: key) { return hit.value }
let options = AttributedString.MarkdownParsingOptions( let options = AttributedString.MarkdownParsingOptions(
@@ -272,7 +277,7 @@ struct MarkdownText: View {
/// keyed only by the source string (independent of `bodySize`), so a warm value equals what /// keyed only by the source string (independent of `bodySize`), so a warm value equals what
/// the main thread would compute, and a repeat call is a cheap cache hit. Cooperatively /// the main thread would compute, and a repeat call is a cheap cache hit. Cooperatively
/// cancellable — a huge transcript's warm loop bails the moment its owning task is cancelled. /// cancellable — a huge transcript's warm loop bails the moment its owning task is cancelled.
static func prewarm(_ markdown: String) { nonisolated static func prewarm(_ markdown: String) {
for block in parse(markdown) { // also warms the block cache for block in parse(markdown) { // also warms the block cache
if Task.isCancelled { return } if Task.isCancelled { return }
switch block { switch block {