use crate::model::{BlockType, DocumentBlock, DocumentModel}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct OutlineEntry { pub block_id: String, pub level: u8, pub title: String, pub depth: usize, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct VisibilitySnapshot { pub visible_block_ids: Vec, pub outline: Vec, } impl VisibilitySnapshot { pub fn derive(document: &DocumentModel) -> Self { let mut hidden_ancestor_ids: Vec = Vec::new(); let mut visible_block_ids = Vec::new(); let mut outline = Vec::new(); for block in document.blocks() { hidden_ancestor_ids.retain(|ancestor_id| is_ancestor_of(document, ancestor_id, block)); let visible = hidden_ancestor_ids.is_empty(); if visible { visible_block_ids.push(block.id.clone()); } if matches!(block.block_type, BlockType::Heading) { let level = block.heading_level.unwrap_or(1); if visible { outline.push(OutlineEntry { block_id: block.id.clone(), level, title: block.content.text.clone(), depth: block.indent as usize, }); } if block.collapsed { hidden_ancestor_ids.push(block.id.clone()); } } } Self { visible_block_ids, outline, } } } fn is_ancestor_of(document: &DocumentModel, ancestor_id: &str, block: &DocumentBlock) -> bool { document.is_descendant_of(&block.id, ancestor_id) }