vecslide-core 0.1.0

Core library for the .vecslide format: SVG + Opus vector presentations with synchronized audio. Manifest, packing, HTML compilation.
Documentation

vecslide-core

Crates.io docs.rs License: MIT OR Apache-2.0

Core library for the .vecslide format — vector presentations (SVG + Opus audio) playable from a single self-contained HTML file. UI-free, no mandatory filesystem access, and WASM-safe by default.

💡 Prefer the shorter import path vecslide::…? Use the facade crate vecslide — same API, same version.

Installation

[dependencies]
# Default: WASM-safe, no ZIP I/O, no Typst compilation.
vecslide-core = "0.1"

# In-memory ZIP read/write (still WASM-safe, no std::fs).
vecslide-core = { version = "0.1", features = ["zip-io"] }

# Native tooling: std::fs + Typst → SVG + bundled fonts.
vecslide-core = { version = "0.1", features = ["native"] }

native is a superset of zip-io. Do not enable native on WASM targets.

What is .vecslide?

A .vecslide is a renamed ZIP archive with this layout:

lesson.vecslide
├── manifest.yaml           # Slides, timestamps, animations, pointer trail
├── audio/voice.opus        # Opus audio (OGG container), optional
└── vector_assets/*.svg     # SVG slides (or a single split Typst source)

Why this format (1-hour 4K lecture):

4K MP4 VecSlide
Size ~930 MB ~13 MB
Visual quality Compression Perfect at any DPI
Text Baked pixels Selectable, searchable
Offline playback Needs player Any modern browser

Quick start

Parse a manifest

use vecslide_core::manifest::Presentation;

let yaml = r#"
title: "Cell Anatomy"
slides:
  - id: slide_01
    time_start: 0
    svg_file: vector_assets/01.svg
"#;

let p: Presentation = serde_norway::from_str(yaml)?;
assert_eq!(p.slides.len(), 1);

Unpack a .vecslide (feature zip-io)

use vecslide_core::unpack_from_reader;

let bytes = std::fs::read("lesson.vecslide")?;
let unpacked = unpack_from_reader(std::io::Cursor::new(bytes))?;
println!("{} slides", unpacked.presentation.slides.len());

Compile to a single self-contained HTML file

use vecslide_core::{unpack_from_reader, compile_html};

let bytes = std::fs::read("lesson.vecslide")?;
let unpacked = unpack_from_reader(std::io::Cursor::new(bytes))?;
let html: String = compile_html::compile(&unpacked)?;
std::fs::write("lesson.html", html)?;

The resulting .html inlines every SVG slide, embeds the Opus track as a Base64 data URI, and ships the player JS/CSS — no external assets, no CDN.

Pack a folder into a .vecslide (feature zip-io)

use std::fs::File;
use vecslide_core::pack_to_writer;

let out = File::create("lesson.vecslide")?;
pack_to_writer(std::path::Path::new("./source_folder"), out)?;

Modules

Module Default zip-io native Purpose
manifest Presentation, Slide, Animation, YAML/JSON serde
validation Ordered timestamps, referenced files, durations
compile_html UnpackedPresentation → single self-contained HTML
player_template HTML/CSS/JS viewer (include_str!)
pointer Trail: movement threshold, decimation, fade
theme Theme tokens
typst_split Split .typ sources on ----
pack Folder → .vecslide ZIP (in-memory)
unpack .vecslide ZIP → UnpackedPresentation (in-memory)
typst_render Typst source → SVG
typst_fonts Fonts bundled for Typst

Compression

Compression is applied only when packing:

pack.rs           →  .vecslide (ZIP)
  SVG + YAML     :  Deflated  (~75–80% size reduction on XML text)
  Opus audio     :  Stored    (already compressed)

compile_html.rs   →  .html (no ZIP)
  SVG            :  raw text in <script type="text/xml">
  audio          :  inline Base64 data URI

Viewer modes

The compiled HTML supports two playback modes selected by the manifest:

  • Audio modemanifest.audio_track is set. The <audio> element is the master clock; requestAnimationFrame reads audio.currentTime at 60 fps and drives slides, animations, and the pointer trail.
  • Static mode — no audio_track. Navigation is by index: arrows, Space, and swipe advance / go back without depending on audio.

Minimum supported Rust version

Currently 1.90. Bumping the MSRV is not a breaking change and will be documented in the changelog.

License

Licensed under either of

at your option.

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.