devup-editor-html 1.0.13

HTML ↔ Document conversion + clipboard-mode support (tables, Notion heuristics, data-devup-props round-trip) for devup-editor
Documentation
// The clipboard port mirrors the React `clipboardHtml.ts` module
// structure deliberately; several pedantic lints fire on idiomatic
// patterns (boolean-heavy mark sets, identical match arms for text
// node dispatch, explicit closures that document intent better than
// bare fn references, etc.). We allow them at the crate level so the
// port stays side-by-side comparable with the upstream TS.
//
// This list has been aggressively trimmed. Each remaining allow has a
// justification in this comment block:
//
// - `struct_excessive_bools`: `MarkSet` carries 5 booleans for the 5
//   boolean marks. Refactoring into a bitflags struct would obscure the
//   1:1 correspondence with the TS mark names.
// - `too_many_lines`: `visit_block` is one long dispatch `match` — each
//   tag handler is short but there are many of them.
// - `module_name_repetitions`: `HtmlError` alongside the `html` module.
// - `doc_markdown`: English prose in doc comments contains backtick-free
//   references (Notion, Word) that pedantic lint flags as missing code
//   fences.
// - `match_same_arms`: `handle_dispatch` has multiple arms that return
//   the same paragraph fallback for distinct tag names — explicit for
//   readability.
// - `similar_names`: `open_idx` / `close_idx` are the established naming
//   from the TS source; renaming loses parity.
// - `missing_errors_doc` / `missing_panics_doc`: all public errors are
//   documented on the `HtmlError` enum; per-fn boilerplate is noise.
// - `must_use_candidate`: every public function here has semantics where
//   ignoring the return would be ambiguous (debug logging, side-channel
//   validation in tests). `#[must_use]` is applied selectively where it
//   adds value.
#![allow(
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::module_name_repetitions,
    clippy::doc_markdown,
    clippy::match_same_arms,
    clippy::similar_names,
    clippy::missing_errors_doc,
    clippy::missing_panics_doc,
    clippy::must_use_candidate
)]

//! HTML ↔ [`devup_editor_core::Document`] conversion for the devup editor.
//!
//! Implements [`DocumentExport`] and [`DocumentImport`] from
//! `devup-editor-core`, plus a clipboard-oriented API
//! ([`blocks_to_html`] / [`html_to_copied_blocks`]) that mirrors the
//! behaviour of the React TypeScript `clipboardHtml.ts` exactly so
//! clipboard interop with Word / Notion / Google Docs keeps working.
//!
//! ## Structural support
//!
//! Export and import both cover:
//! - Headings (h1–h6)
//! - Paragraphs (p)
//! - Quotes (blockquote)
//! - Pre / code (language class preserved)
//! - Unordered & ordered lists (with nested indent tracking)
//! - Todo lists (via `<ul data-devup-type="todo">` marker OR
//!   `<input type="checkbox">` heuristic OR Notion `<ul class="to-do-list">`)
//! - Dividers (hr)
//! - Toggle blocks: `<details><summary>`, Notion `<ul class="toggle">`,
//!   plus Notion v3's `<li>`-with-multiple-block-children heuristic
//! - Tables: `colspan`, `rowspan`, `<colgroup>` widths, cell / row /
//!   table styling via `data-devup-props` marker and inline `style=""`
//!
//! Marks on export wrap text in `<strong>`, `<em>`, `<u>`, `<s>`,
//! `<code>`, `<a href="…" rel="noopener noreferrer">`, and
//! `<span style="color:…">` / `<span style="background-color:…">` for
//! color / highlight. Unknown mark types survive via a
//! `<span data-mark="type">` fallback so copy never silently drops text.
//!
//! ## Clipboard-specific features
//!
//! The [`clipboard`](crate::clipboard) module provides:
//! - [`encode_props`] / [`decode_props`] — base64+JSON round-trip marker
//!   (`data-devup-props="…"`) for lossless devup→devup copy/paste of
//!   table prop maps.
//! - [`clean_html`] — strips Word / HWP preprocessing artifacts
//!   (`<!--StartFragment-->`, `<o:p>` tags) before parsing.
//!
//! See [`CopiedBlocks`] for the clipboard subtree shape used by the
//! React layer.

mod clipboard;
mod export;
mod import;
mod slice;

pub use clipboard::{
    CopiedBlocks, clean_html, decode_props, encode_props, looks_like_xml, strip_xml_prolog,
};
pub use export::{Html, blocks_to_html, copied_blocks_to_html, document_to_copied_blocks};
pub use import::html_to_copied_blocks;
pub use slice::slice_content;

/// Test-only re-export of the internal `is_safe_href` helper so the
/// parity integration test (`tests/href_parity.rs`) can reach it. Not
/// part of the stable public API — production callers should use the
/// link-aware serialisation via `blocks_to_html` instead.
#[doc(hidden)]
pub fn __test_is_safe_href(href: &str) -> bool {
    export::is_safe_href(href)
}

use thiserror::Error;

#[derive(Debug, Error)]
pub enum HtmlError {
    #[error("html parse error: {0}")]
    Parse(String),
    #[error("html serialize error: {0}")]
    Serialize(String),
}