katana-document-viewer 0.1.0

UI-independent document artifact, render evaluation, and export foundation for KatanA.
Documentation
use crate::export_html_ops::ExportHtmlOps;
use crate::export_inline_payload::InlineHtmlWriter;
use crate::theme::KdvThemeSnapshot;
use katana_markdown_model::{KmmNode, KmmNodeKind};

pub(crate) struct FootnoteHtmlWriter;

impl FootnoteHtmlWriter {
    pub(crate) fn is_definition(node: &KmmNode) -> bool {
        matches!(node.kind, KmmNodeKind::FootnoteDefinition(_))
    }

    pub(crate) fn append_definitions(
        html: &mut String,
        nodes: &[KmmNode],
        theme: &KdvThemeSnapshot,
    ) {
        let definitions = nodes
            .iter()
            .filter(|node| Self::is_definition(node))
            .collect::<Vec<_>>();
        if definitions.is_empty() {
            return;
        }
        html.push_str("<section data-kdv-footnotes>\n<ol>\n");
        for node in definitions {
            Self::append_definition(html, node, theme);
        }
        html.push_str("</ol>\n</section>\n");
    }

    fn append_definition(html: &mut String, node: &KmmNode, theme: &KdvThemeSnapshot) {
        let KmmNodeKind::FootnoteDefinition(definition) = &node.kind else {
            return;
        };
        let escaped_label = ExportHtmlOps::escape_html(&definition.label);
        html.push_str(&format!(
            "<li id=\"fn-{escaped_label}\" data-kdv-footnote-definition=\"{escaped_label}\">"
        ));
        if node.children.is_empty() {
            html.push_str(&ExportHtmlOps::escape_html(&definition.text));
        } else {
            InlineHtmlWriter::append_children(html, node, theme);
        }
        html.push_str(&format!(
            " <a href=\"#fnref-{escaped_label}\" data-kdv-footnote-backref=\"{escaped_label}\">↩</a></li>\n"
        ));
    }
}