Easily create and install custom templates based on Typst.
Write a worksheet as a handful of Typst macros, or let the AI stage convert an HTML worksheet into them. The forge lays it out for the Paper Pro Move, guards against clipped lines and cut-off screen folds, and installs it as a native template in the device's picker or as a screen-size PDF. No Developer Mode, no SSH: templates go in through the reMarkable desktop app and sync like any other document.
On the Paper Pro Move. Left: the generated templates sit in the stock picker with their own icons, installed without Developer Mode. Middle: a hand-authored sheet with rating scales and checkboxes. Right: an HTML worksheet converted by the AI stage, rendered with the #masthead week blank and a #logtable.
forge convert sheet.html --slug my-sheet # AI: HTML -> worksheets/my-sheet/sheet.typ
forge build my-sheet # deterministic: template + A4 PDF + Move PDF + preview
forge install my-sheet # to the device (recommends template vs PDF)
forge uninstall my-sheet # move the installed template to the trash
forge rebind my-sheet # move existing notebooks to the current template id
forge all sheet.html --slug my-sheet # everything
Requirements differ by stage. convert and build run on any OS with
Python 3.11+ and Typst. Installing as a template needs Windows or macOS
and the reMarkable desktop app, because the installer closes the app, injects
the template into its document store, and restarts it so the app syncs it to
the cloud and the device. Installing as a PDF works anywhere: it uploads
over the device's USB web interface, and you can also drag the PDF into the
desktop app on any platform.
- Install Typst (
winget install Typst.Typston Windows,brew install typston macOS) and uv. - From the checkout,
uv sync(orpip install -e .). This installs theforgecommand into the project's virtual environment; run it asuv run forge ..., or activate.venvand callforge ...directly. The tool runs from the checkout -worksheet-lib.typ,assets/and theworksheets/output tree are resolved relative to the repository root. copy config.example.toml config.tomland fill in your OpenAI key (or setOPENAI_API_KEY). The key is only read, never logged.
All commands read config.toml from the checkout and keep their state under
worksheets/<slug>/.
| Command | What it does |
|---|---|
forge convert <input.html> [--slug NAME] |
Stage 1. Sends the HTML to OpenAI, validates the returned Typst against the vocabulary parser and typst compile (one retry), writes worksheets/<slug>/sheet.typ and keeps a copy of the input as src.html. Slug defaults to the input file name. |
forge build <slug> |
Stage 2, no AI. Parses sheet.typ, lays it out, writes the .template, the picker icon SVG, an A4 PDF, a Move-screen-size PDF, preview.png, and build.json (height, whether it fits the template canvas, widest line). Fails instead of clipping if a line might exceed the screen edge. |
forge install <slug> [--template | --pdf] [--new-id] |
Stage 3. Prompts with a recommendation (template if the sheet fits the canvas cap, PDF otherwise); --template or --pdf skip the prompt. If stdin is not a terminal the recommendation is used without asking. Template installs update the same store item on re-runs; --new-id publishes under a fresh id and trashes the old copy, for when clients keep showing a cached old layout. PDF installs upload over USB. |
forge uninstall <slug> |
Moves the installed template to the trash in the desktop app store (reversible until the trash is emptied). Requires a previous template install. |
forge rebind <slug> [--dry-run] [--include-trashed] |
After install --new-id, rewrites notebook pages that still reference an earlier id of this template so they show the current copy. --dry-run only lists affected notebooks; --include-trashed also rewrites notebooks in the trash. |
forge all <input.html> [--slug NAME] [--template | --pdf] [--new-id] |
The three stages in sequence, with the install flags passed through. |
python -m forge ... is equivalent to forge ....
- Stage 1 (the only AI stage): OpenAI turns the HTML into Typst restricted
to the macro vocabulary in
worksheet-lib.typ(#masthead,#intro,#numberedlist,#checklist,#sect,#capsfield,#boxedpair,#logtable,#callout,#sheetfooter). Output is validated by the parser +typst compile, one auto-retry on failure.worksheets/<slug>/sheet.typis the editable source of truth — tweak the wording or line counts there and rebuild. - Stage 2 (deterministic, no AI):
forge/typparse.pyparses the macro calls (any raw Typst = hard error);forge/sheet.pylays them out in device pixels with the safety guards learned the hard way (pessimistic width check that fails the build instead of clipping, fold-aware placement at every 1696px screen boundary, 2550px canvas-cap budget); outputs a native TemplateType.template(free-form "Worksheets" category, deterministic SVG picker icon), an A4 print PDF, an exact-screen-size Move PDF, and a preview rendered with the device's own font. - Stage 3: height ≤ 2550px → recommends the golden-path template
install (TemplateType triplet into the desktop app's store; the app syncs
it to the cloud and the device picker — no Developer Mode). Taller sheets →
the Move-size PDF, uploaded via the device's USB web interface or dragged
into the desktop app.
--template/--pdfskip the prompt. Re-running install for a slug updates the same template in place (uuid persisted inworksheets/<slug>/state.json). Clients cache the rendered background per template id, so if an edited sheet keeps showing the old layout useinstall --new-id, which publishes a fresh id and trashes the old copy. Notebook pages bind to the template id, so pages created before that still show the old copy:rebind <slug>rewrites those page references in the desktop store to the current id (--dry-runlists them first;--include-trashedalso covers trashed notebooks).
Note: the template installer closes and restarts the reMarkable desktop app.
The document-store and app paths are detected automatically on Windows and
macOS; [paths] app_store and remarkable_app remain available as overrides.
Three sheets ship under worksheets/ so you can try the pipeline without an
OpenAI key. Everything else in that folder is ignored by git; the examples
publish only their sources and a preview.png of the device rendering.
| Slug | What it is | Build |
|---|---|---|
achievement-log |
Weekly wins log: a five-column table with Personal/Work tick boxes and a notes field. | forge build achievement-log then forge install achievement-log. |
difficult-conversation |
Hand-authored workbook page (status boxes, rating scales, an observations table across the fold). Layout is beyond the macro vocabulary, so it has no sheet.typ. |
python worksheets/difficult-conversation/gen_conversation_workbook.py writes both hand-made templates; then forge install difficult-conversation --template. forge build does not apply. |
conversation-nvc-goal |
Second page of the same workbook: the goal in plain words plus the four NVC stems. | Generated by the script above; forge install conversation-nvc-goal --template. |
The two hand-made slugs also carry raw Typst editions of the workbook
(conversation-workbook.typ for A4, conversation-workbook-rmpp-move.typ
for Move-size pages) that compile with plain typst compile.
Left: the examples offered when creating a notebook. Middle: the Difficult Conversation sheet scrolled to its second screen, the table placed fold-aware so no row is cut. Right: Conversation NVC Goal, a one-screen template.
The device geometry in forge/constants.py (authoring scale 1.1615, visible
authoring space 821 x 1460 units per screen, 2550-unit canvas cap) was
measured on a reMarkable Paper Pro Move, firmware 3.28 (tentacruel), on
2026-08-30 with a labeled ruler template (scripts/cal_gen.py). A firmware
change or a different device may move these numbers; re-run the ruler
template to check, and treat the constants as a device profile to adjust.
Background, device findings, and the full format reference: docs/remarkable-template-guide.md.





