Repository navigation
Architecture
hyperpolymath edited this page Sep 27, 2026
·
1 revision
┌──────────────────────────────────────────────┐
│ USER / BROWSER (Firefox, PDF forms) │
└───────────────────────┬──────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ EXTENSION UI LAYER │
│ popup.affine content.affine │
│ components/Block.affine components/FormFiller.affine
└───────────────────────┬──────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ BACKGROUND SERVICE │
│ background.affine core/Storage.affine │
│ core/ProvenMount.affine │
└───────────────────────┬──────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ CORE PROCESSING (Rust → WASM) │
│ rust/pdftool_core │
│ detect_blocks() fill_blocks() │
└───────────────────────┬──────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ DATA LAYER │
│ IndexedDB / local storage │
└──────────────────────────────────────────────┘
| Layer | Reality |
|---|---|
| Rust/WASM core |
Implemented and tested. rust/pdftool_core/src/lib.rs, ~1030 lines, 6 unit tests. |
| AffineScript surfaces |
Prototype. src/*.affine sources exist; no compiler config, no bundle pipeline. |
| Extension packaging |
Absent. public/manifest.json and icons are present; nothing produces a loadable .xpi. |
| Storage |
Prototype. src/core/Storage.affine only. |
Everything the extension does to a PDF goes through exactly two exported functions. That boundary is the whole contract:
#[wasm_bindgen] pub fn detect_blocks(pdf_data: &[u8]) -> Result<JsValue, JsValue>
#[wasm_bindgen] pub fn fill_blocks(pdf_data: &[u8], blocks: JsValue, fields: JsValue)
-> Result<js_sys::Uint8Array, JsValue>-
detect_blockswalks every page'sAnnots, keeps the ones that are widget annotations with a usableRect, and returns aBlock { label, x, y, width, height }per widget. Labels come from the field's/T, falling back to the parent field's/T, falling back tofield_<page>_<index>. -
fill_blockstakes the original PDF bytes plus afield name → valuemap, writes the values into the AcroForm, and returns new PDF bytes. It is name-driven, not coordinate-driven — theblocksargument is parsed and validated but the writeback is keyed on field names.
That last point is worth internalising: blocky-writer's contribution to the document suite is placement into fixed-layout forms, not free-form layout. See Ecosystem.
Failures never cross the WASM boundary as strings. They are structured payloads with a stable machine code, a human message, and optional context:
{ "code": "BW_FILL_NO_MATCHING_FIELDS", "message": "…", "context": "Choice" }There are 20 BW_* codes. Codes are stable API — treat adding or renaming one
as a breaking change, not a tidy-up.