text-document
A rich text document model for Rust, inspired by Qt's QTextDocument/QTextCursor API.
Built on Qleany-generated Clean Architecture with redb (embedded ACID database), full undo/redo, and multi-cursor support.
Features
- Rich text model: Frames, Blocks, InlineElements with
InlineContent::Text | Image - Multi-cursor editing: Qt-style cursors with automatic position adjustment
- Full undo/redo: Snapshot-based, with composite grouping (
begin_edit_block/end_edit_block) - Import/Export: Plain text, Markdown, HTML, LaTeX, DOCX
- Search: Find, find all, regex, replace (undoable)
- Formatting: Character format (
bold,italic,underline, ...), block format (alignment,heading_level, ...), frame format - Event system: Callback-based (
on_change) and polling-based (poll_events) - Thread-safe:
Send + Syncthroughout,Arc<Mutex<...>>interior mutability - Resources: Image and stylesheet storage with base64 encoding
Quick start
use ;
let doc = new;
doc.set_plain_text.unwrap;
// Cursor-based editing
let cursor = doc.cursor;
cursor.move_position;
cursor.insert_text.unwrap; // replaces "Hello"
// Multiple cursors
let c1 = doc.cursor;
let c2 = doc.cursor_at;
c1.insert_text.unwrap;
// c2's position is automatically adjusted
// Undo
doc.undo.unwrap;
// Search
use FindOptions;
let matches = doc.find_all.unwrap;
// Export
let html = doc.to_html.unwrap;
let markdown = doc.to_markdown.unwrap;
CLI
A command-line tool for format conversion and text processing:
# Convert between formats (detected by file extension)
# Show document statistics
# Find text (grep-like output)
# Find and replace
# Print to stdout in a different format
Supported formats:
| Extension | Import | Export |
|---|---|---|
.txt |
yes | yes |
.md |
yes | yes |
.html/.htm |
yes | yes |
.tex/.latex |
- | yes |
.docx |
- | yes |
Document structure
Root
+-- Document
+-- Frame (root frame)
| +-- Block
| | +-- InlineElement (Text "Hello ")
| | +-- InlineElement (Text "world" with bold)
| | +-- InlineElement (Image { name, width, height })
| +-- Block
| +-- InlineElement (Text "Second paragraph")
+-- List (style: Decimal, indent: 1)
+-- Resource (image data, stylesheets)
- Frame: contains Blocks and child Frames.
child_orderinterleaves them. - Block: a paragraph. Contains InlineElements. Has
document_positionfor O(log n) lookup. - InlineElement: either
Text(String),Image { name, width, height, quality }, orEmpty. - List: styling for list items (Disc, Decimal, LowerAlpha, ...). Blocks reference lists via weak relationship.
- Resource: binary data (images, stylesheets) stored as base64.
All format fields are Option<T> — None means "inherit from parent/default", Some(value) means "explicitly set".
Architecture
Generated by Qleany v1.5.1, following Clean Architecture with Package by Feature (Vertical Slice):
crates/
+-- public_api/ # TextDocument, TextCursor, DocumentEvent (the public crate)
+-- cli/ # Command-line tool
+-- frontend/ # AppContext, commands, event hub client
+-- common/ # Entities, database (redb), events, undo/redo, repositories
+-- macros/ # #[uow_action] proc macro
+-- direct_access/ # Entity CRUD controllers + DTOs
+-- document_editing/ # 11 use cases (insert, delete, block, image, frame, list, fragment, ...)
+-- document_formatting/ # 4 use cases (set/merge text format, block format, frame format)
+-- document_io/ # 8 use cases (import/export plain text, markdown, HTML, LaTeX, DOCX)
+-- document_search/ # 3 use cases (find, find_all, replace)
+-- document_inspection/ # 4 use cases (stats, text at position, block at position, extract fragment)
+-- test_harness/ # Shared test setup utilities
Data flow: TextDocument / TextCursor -> frontend::commands -> controllers -> use cases -> UoW -> repositories -> redb
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.