A Excalidraw fork with syncing to your Atmosphere account and iroh for sync Https://lexidraw.app
75

Configure Feed

Select the types of activity you want to include in your feed.

README.md

draw-collab-wasm#

Browser (WASM) iroh + iroh-gossip transport for Draw live collaboration. This crate is transport only: it moves opaque byte payloads between browsers over an end-to-end encrypted gossip topic and surfaces presence events. All Draw protocol logic (message subtypes, reconciliation, chunking, file sync) lives in TypeScript.

Browser iroh is relay-only (no UDP in the sandbox): traffic transits n0's public relays, but QUIC connections stay end-to-end encrypted — relays only see ciphertext. The random gossip TopicId inside the ticket is the room capability; it lives only in the URL fragment.

Prerequisites#

  • Rust stable with the wasm32-unknown-unknown target (see rust-toolchain.toml).

  • wasm-pack: cargo install wasm-pack.

  • A wasm-capable clang for building ring (the TLS backend). Apple's system clang does not include the WebAssembly target. On macOS: brew install llvm, then point the C compiler at it:

    export CC_wasm32_unknown_unknown="$(brew --prefix llvm)/bin/clang"
    export AR_wasm32_unknown_unknown="$(brew --prefix llvm)/bin/llvm-ar"
    

Build#

From the repo root:

yarn build:wasm
# == wasm-pack build crates/draw-collab-wasm --target web --release

This emits pkg/ containing the JS glue (with an explicit default init()), the .wasm binary, and .d.ts typings. pkg/ is committed so contributors and CI do not need a Rust toolchain. wasm-pack writes a pkg/.gitignore containing *; empty or delete it after building so the artifacts are tracked.

Consuming from the app#

The app aliases the bare specifier draw-collab-wasm to this crate's pkg/ directory (see excalidraw-app/vite.config.mts and the paths entry in the root tsconfig.json). Load it lazily and call init() before use:

import init, { CollabNode } from "draw-collab-wasm";

await init();
const node = await CollabNode.spawn();

API surface#

  • CollabNode.spawn(): Promise<CollabNode> — bind an endpoint + gossip router.
  • node.node_id(): string — this node's endpoint id.
  • node.create_ticket(): string — mint a fresh room ticket (random topic, self as bootstrap).
  • node.join_room(ticket, wait_joined, on_message, on_neighbor_up, on_neighbor_down): Promise<RoomHandle> — wait_joined = false for the room creator, true for joiners.
  • node.shutdown(): Promise<void>.
  • room.broadcast(payload: Uint8Array): Promise<void> — payloads over 256KB must be chunked by the caller (gossip max_message_size is 256KB).
  • room.ticket(): string — re-mint with self as bootstrap so re-copied links stay fresh.
  • room.close(): void.