/** * Journal & Storytelling Types, Parser and Serializer. * Client-safe pure TypeScript module (no Node.js fs dependencies). */ export interface JournalFrontmatter { title?: string; subtitle?: string; date?: string; author?: string; coverAssetId?: string; layout?: string; password?: string; draft?: boolean; } export type JournalBlock = | { type: 'heading'; level: number; text: string } | { type: 'paragraph'; html: string } | { type: 'quote'; text: string; author?: string } | { type: 'photo'; assetId: string; caption?: string; layout: 'fullbleed' | 'wide' | 'contained' } | { type: 'photo-pair'; assetIds: [string, string]; caption?: string }; export interface ParsedJournal { frontmatter: JournalFrontmatter; blocks: JournalBlock[]; referencedAssetIds: string[]; } export interface JournalEntrySummary { slug: string; filename: string; frontmatter: JournalFrontmatter; excerpt: string; wordCount: number; readingTimeMinutes: number; } const HTML_ESCAPES: Record = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', }; /** * Make an author's text inert as HTML. * * This escapes rather than filters. The previous implementation stripped * `