Skip to content

calternal_notes_core::canvas

Lossless Canvas files and bounded scene validation (DESIGN §60, #976).

This pure layer never writes a file. It retains source ranges, so an edit changes only the Drawing and known text/link entries. Unknown Markdown, JSON values, deleted elements and inline image bytes stay in the source. Hosts must cap their read at MAX_FILE_BYTES + 1 before calling open.

Source: crates/calternal-notes-core/src/canvas.rs

pub struct CanvasError(pub &'static str);

A content error has a fixed message; no User content enters logs.

Implements: Debug, Clone, PartialEq, Eq, std::fmt::Display, std::error::Error

Source: crates/calternal-notes-core/src/canvas.rs:20

pub struct CanvasFile

One immutable opened file and its visible scene. Opening is never a save.

Implements: Debug

pub fn open(source: &str) -> Result<Self>

Open plain JSON or Markdown. All limits also run before every save.

pub fn scene(&self) -> &Value

Return the effective scene after Markdown text/link overrides.

pub fn save(&self, scene: &Value) -> Result<String>

Save an actual edit. A semantic no-op returns every original byte. Existing compressed Drawing blocks become plain JSON only on an edit.

Source: crates/calternal-notes-core/src/canvas.rs:38

pub fn decode_event(source: &str) -> Result<Value>

Decode an event with the same duplicate-key and depth checks as a Drawing. Callers first enforce the smaller collaboration frame limit (#976).

Source: crates/calternal-notes-core/src/canvas.rs:210

pub fn file_reference(element: &Value) -> Option<&Value>

Portable Home-file reference, outside the binary files map (#989, §60). Names are display/fallback names only; stable item identity wins on resolve.

Source: crates/calternal-notes-core/src/canvas.rs:711

pub fn is_canvas(path: &str, source: &str) -> bool

Detect a Canvas without changing its bytes (#976, DESIGN §60). The marker also supports imported Markdown whose filename has no suffix.

Source: crates/calternal-notes-core/src/canvas.rs:162

pub fn public_scene(scene: &Value) -> Result<Value>

Read-only public drawing projection (DESIGN §§54, 60, #991). Keep only drawing fields: unknown metadata, deleted content, links and assets never cross this boundary. Until #977 supplies per-viewer cards, every asset or card is a neutral rectangle, including its bound label. Source stays whole.

Source: crates/calternal-notes-core/src/canvas.rs:1396

pub fn searchable_text(scene: &Value) -> Result<String>

Project only visible Canvas text and source links, never JSON/image bytes. A deleted container also hides its bound text (security review #976).

Source: crates/calternal-notes-core/src/canvas.rs:170

pub fn valid_id(id: &str) -> bool

IDs from existing files are never renamed. New IDs use eight base62 chars.

Source: crates/calternal-notes-core/src/canvas.rs:215

pub fn valid_index(index: &str) -> bool

Match rocicorp fractional-indexing’s base62 integer prefix and fraction. Bounds prevent a repeated insert-between operation growing an unbounded key.

Source: crates/calternal-notes-core/src/canvas.rs:237

pub fn valid_link(link: &str) -> bool

Only safe navigation links are shared. No external resource is fetched.

Source: crates/calternal-notes-core/src/canvas.rs:225

pub fn validate_asset_bytes(bytes: &[u8], mime: &str) -> Result<()>

Validate Home assets and legacy images with one allowlist (#989, §60). Container terminators reject appended second formats. SVG stays passive; PDFs are downloadable files and only the thumbnail service renders them.

Source: crates/calternal-notes-core/src/canvas.rs:362

pub fn validate_element(el: &Value) -> Result<()>

Validate a whole element, retaining unknown fields under a small byte cap. Version and nonce are integers; the room resolves their ordering (#976).

Source: crates/calternal-notes-core/src/canvas.rs:542

pub fn validate_scene(scene: &Value) -> Result<()>

Validate geometry and aggregate work before a scene reaches a renderer. This also guards whole-element collaboration events, not just file opens.

Source: crates/calternal-notes-core/src/canvas.rs:256

pub const MAX_DEPTH: usize

Recursion stops before parsing or destroying a deeply nested JSON value.

Source: crates/calternal-notes-core/src/canvas.rs:16

pub const MAX_ELEMENTS: usize

A large Canvas can contain the 5,000 elements in the §60 profile.

Source: crates/calternal-notes-core/src/canvas.rs:14

pub const MAX_FILE_BYTES: usize

The same cap applies to source, decompressed JSON and the saved file.

Source: crates/calternal-notes-core/src/canvas.rs:12