llms.txt · git:20260912.ea265cc · 2026-09-12 · sha256 dd80c5ff151e1d4e
llms.txt git:20260912.ea265ccA
Immutable. This exact content never changes and is served at /api/v1/blob/dd80c5ff151e1d4e.
# decklet
> Agent-generated, brand-true, real-time editable presentations and assets — one portable HTML file, no office suite.
decklet is a slide engine for agents. The deck is a JSON model; the renderer is the editor; the output is ONE self-contained .html (no dependencies, no network) a human can drag, retype, present and print. Plain Node >= 22 CLI. Playwright is optional and only used by verify/pdf/import-html.
Loop: content + format + style -> model.json -> `node bin/validate.mjs model.json` -> `node bin/create.mjs --model model.json [--style style.json] --out deck.html --format slides|carousel|carousel-4x5|document-letter|document-a4` -> `node bin/verify.mjs deck.html [--refs shots/]` -> hand-off. Revising a deck a human edited: `node bin/edits.mjs deck.html` (read the in-file edit log) -> `node bin/create.mjs --model model.json --out deck.html --from deck.html` (keeps deck/slide/row ids, replays the human's edits — human wins, conflicts reported — and keeps the version history) -> verify. The migrate is the LAST step, never the first.
Formats: slides (960x540 or 1600x900) supported; carousel (1080x1080, 1080x1350) and document (Letter 816x1056, A4 794x1123) experimental (sizing/editing/PDF work; text does not flow across pages).
Model: {w,h,format,styles:{roles,margin,pad},slots,layouts:{name:{slot:{x,y,w,role}}},master:[rows with id],slides:[{layout,bg,hide,els:[rows]}]}. A row is {x,y,w,role,text} plus optional box/tile/bar/line/donut/svg/img/html/anim/nowrap/p/bg/bd/radius/align/color/weight/tt. anim is one of rise|fade|pop|wipe: entrance motion on slide ENTRY, staggered 120ms in model order, off under prefers-reduced-motion and absent from print/PDF/contact sheet/verify (they draw the settled frame); any other value is a validate error. Eight roles (Title,Supertitle,H1,H2,Body,Caption,Label,Stat) are the only source of font/size/lh/ls; rows may not override them (no H3, no Subtitle; Title = display headline for cover/closing slides, H1 = content-slide title). styles.margin is the content inset chrome sits on; master chrome is deck-wide and never varies per layout; a slide override row is partial (only the changed props).
PDF: the ⤓ button writes a true slide-sized PDF inside the file (foreignObject -> canvas -> JPEG -> PDF, zero dependencies; verified in Chromium, Safari unconfirmed -> falls back to print). ⌘P is the paper path: named Letter/A4 pages, one slide per page.
## Files
- SKILL.md: the agent authoring skill — inputs, process, model + style contracts, layouts, master, verification thresholds, anti-patterns, worked examples
- README.md: what/for whom/guarantees/feature matrix/roadmap
- deck.html: the engine with its own explainer deck (12 slides, incl. the motion vocabulary and three inlined GIF clips of the editor) — live at https://grunion-ai.github.io/decklet/deck.html
- docs/demo.gif, docs/demo-poster.png: README demo recording, a build product of deck.html via docs/record-demo.mjs (Playwright + ffmpeg)
- docs/record-clips.mjs: films the three editor clips on the explainer's "filmed" slide from deck.html and writes them into examples/explainer/model.json as data: URIs (Playwright + ffmpeg)
- template.html: the engine with an empty model; create.mjs fills the /*DECK*/ /*TOKENS*/ /*KEY*/ /*ENGINE*/ /*LOG*/ /*VERSIONS*/ /*SPELL*/ markers (the /*EDITS*/ block is the in-file edit core, shared with lib/edits.mjs; the /*BUG*/ block is the bug-report builder, shared with lib/bug.mjs)
- bin/validate.mjs: pure-Node model contract validator (exit 1 on errors; --strict for warnings); includes the gap gate — declared boxes owe neighbours styles.gap (4px), estimates warn with ~
- bin/create.mjs: model (+style) -> deck.html with format presets
- bin/verify.mjs: self-containment + contract + layout parity (always) + AE vs refs (optional); writes results.json
- bin/pdf.mjs: deck.html -> vector PDF via Chromium print engine + px @page (one slide per page, text stays text, hrefs are /Link annotations, HUD hidden by the deck's @media print); gates pages/ratio/HUD/links, exit 1 on failure. The ONLY way an agent makes a PDF — never screenshots stitched by ImageMagick
- bin/edits.mjs: prints the human edit log + version history a deck file carries (read before revising)
- lib/bug.mjs: node side of the bug-report builder (bugReport/bugFacts/scrub lifted from template.html's /*BUG*/ block; engineOf reads a deck's ENGINE literal)
- bin/bug.mjs: decklet bug — prints a prefilled mailto to decklet@grunion.ai (engine version, format, size, slide count, node + OS, a scrubbed --log snippet); never the deck's words
- lib/edits.mjs: node side of the in-file edit core (stampIds/diffDecks/applyLog lifted from template.html's /*EDITS*/ block; blockOf/putBlock for DECK/LOG/VERSIONS)
- bin/import-html.mjs: HTML pages at a fixed viewport -> model.json (master/slots/roles lifted, Title detected on non-content layouts; _lines intent for parity)
- examples/explainer, examples/quarterly-update, examples/launch-carousel, examples/one-pager: brief.md -> model.json (+ style.json)
- test/gate.test.mjs: node --test gate (engine contract, validator, create, import, live proofs); test/edits.test.mjs (ids, log, migrate, create --from); test/editor.test.mjs (live: nibs, PDF arrows, autosave, position, migrate on load, ⌘S write-back, versions, ⌘B)
- CHANGELOG.md, LICENSE (MIT)