Skip to content

Repository files navigation

flow-presentation

A reusable engine for node-canvas presentations. A presentation is a graph of nodes on a 3D-tilted canvas (future-dark styling, pointer parallax, zoom-in drill-down); each node opens a pane: a scrolling HTML document, a zoomable sub-graph with side-drawer documents, or a custom React component.

Content lives in one JSON file per deck. The engine owns layout, styling, 3D, and navigation — so presentation improvements and content authoring never collide.

The home canvas — a 3D-tilted node graph with the deck's thesis overlaid

The views

Every node on the home canvas opens one of three pane types; canvas panes add a drawer for per-option documents.

Doc pane Doc panes — scrolling HTML documents built from shared content primitives: cards, tags, callouts, data tables, bars, code. Sequence pane Sequence panes — a flat left→right timeline of interlocking arrow steps with work items beneath, for linear roadmaps.
Canvas pane Canvas panes — a zoomable sub-graph for decisions with options; the home canvas zooms into the node for continuity. Drawer Drawer documents — clicking an option node glides it aside and opens its article in a side panel.

There's also an escape hatch: a pane can mount a custom React component (steppers, configurators, live figures) registered from src/custom/.

Run

npm install
npm run dev        # then open http://localhost:5173/?deck=<name>

Every deck in src/decks/ is bundled automatically; ?deck=<name> switches at runtime (default demo). Navigation: click nodes · step through sections · Esc back out · ◐ theme toggles future-dark / light.

Ship

./ship <deck>      # → dist/<deck>.html, fully self-contained single file

The shipped file inlines everything — JS, CSS, all decks — so you can host it anywhere a static HTML file works (or just send it). <deck> is only the default; ?deck= still switches at runtime.

Where deck data lives

Decks are JSON files in src/decks/ — every file there is bundled automatically, no registration step. Custom pane components live beside them in src/custom/.

For decks you don't want in this repo (personal or confidential content), point DECKS_DIR at a directory outside it — via the environment or a gitignored .env.local — and its decks/*.json and custom/*.tsx are bundled exactly like src/decks/ and src/custom/. Your content can live in its own private repo while the engine stays public.

# .env.local
DECKS_DIR=~/presentations     # containing decks/ and custom/

External custom components import engine types via the @engine alias (import type { SectionComponentProps } from '@engine/deck') and can import their own CSS files. Edit mode saves back to external deck files too.

Edit mode (dev)

Click ✎ edit in the top bar (dev server only) to tweak deck text in place: every text field — thesis, node titles/subs, section headings, pane HTML, drawer articles — becomes contentEditable. Changes save back to the deck's JSON file on blur (Esc blurs; Enter too in single-line fields), as a surgical edit that preserves the file's formatting. Nodes stop navigating while editing; toggle edit off to move around. Two caveats: fields absent from the JSON aren't editable (add the field in the file first), and the browser normalizes an edited field's HTML serialization (e.g. class='x'class="x"), so a field's first edit reformats that one JSON value.

Authoring a deck

Copy src/decks/demo.json to src/decks/<name>.json and replace the content — no code changes. The demo deck is self-documenting — run it and read the panes. Schema lives in src/deck.ts (typed, commented). There's also a Claude Code skill: .claude/skills/flow-presentation/ (works in-repo; symlink it into ~/.claude/skills/ to use it from anywhere).

Deck
├─ meta        title / wordmark
├─ tokens?     CSS var overrides, e.g. { "--brand": "#00b5ad" }
├─ accents?    named accent colors nodes reference, e.g. { "trigger": "var(--accent)" }
├─ thesis      the headline overlaid on the home canvas (HTML allowed)
├─ canvas      nodes [{id,x,y,accent,kind,title,sub,step,big,decision}]
│              edges [[from,to]] · labels [{x,y,text}]
├─ sections[]  ordered; one per canvas node id
│    doc:      { id, num, eyebrow, title, lede, html?, component? }
│    canvas:   { id, type:"canvas", nodes:[{x,y,title,…, article|goto, root?}] }
│    sequence: { id, type:"sequence", steps:[{kind,title,sub,detail[],accent, article|goto}] }
│               or rows:[{kind,title,lede,accent, steps:[…]}]  — stacked chevron rows
└─ articles[]  drawer documents { id, kind, title, html }

Notes:

  • Pane bodies are raw HTML strings (single-quoted attributes keep JSON escaping sane). All content primitives are available: .card (+brand/alt), .callout, .tag, table.data, .bars, pre.code (+.k .s .c .f spans), .grid .cols-2/.cols-3, .pending, .configurator/.readout/.chips.
  • Cross-link any pane/drawer with <a data-goto='section-id'>.
  • Ports and edges are automatic — home-canvas ports derive from edges; sub-canvas edges are drawn root → options.
  • Sequence panes render a flat, left-to-right chain of interlocking arrow segments (deliberately not the 3D node-graph look) — for linear roadmaps / ordered work. Each step lists detail[] work items beneath it and can open an article drawer or goto another section. Per-step accent keys into the deck's accent map. See the demo deck's roadmap section and SequenceSection in src/deck.ts.
  • Custom panes: {"component": "name"} in a section mounts a React component from the registry. A module in src/custom/ (or $DECKS_DIR/custom/) registers by exporting a components name → component map (see src/custom/DemoStepper.tsx). This is where bespoke interactive widgets (configurators, steppers, animated figures) live.

Contributing changes back

The engine/content split only pays off if it's maintained: layout, styling, and interaction improvements belong in src/ (engine) and should be committed back to this repo so every deck inherits them — don't fork engine behavior into a deck. Content-specific interactivity belongs in a registered custom component (src/custom/ or your external custom/), not in the engine.

Trust model

Deck HTML is rendered unsanitized (dangerouslySetInnerHTML) by design — a deck file is first-party authored content, the same trust level as writing the HTML page by hand. Don't render deck JSON from untrusted sources; if that ever becomes a need, add DOMPurify at the three render sites (DetailView, Drawer, NodeButton).

Structure

src/
├─ deck.ts               deck schema (types + accent resolver)
├─ decks.ts              bundles src/decks/*.json (+ $DECKS_DIR/decks/) + HMR deck swaps
├─ edit.tsx              edit mode: Editable fields + save-to-deck-file context (dev only)
├─ App.tsx               navigation state: section, drawer, theme, keyboard, hash links
├─ components/
│  ├─ HomeCanvas.tsx     3D plane, intro flip, parallax, edges, zoom-into transition
│  ├─ SubCanvasSection.tsx  full-stage sub-graph, drawer focus glide, fit-to-viewport
│  ├─ SequenceSection.tsx  flat left→right arrow-segment roadmap
│  ├─ DetailView.tsx     doc panes + data-goto link handling
│  ├─ Drawer.tsx         side panel for option documents
│  ├─ NodeButton.tsx     the node card (home + sub-canvas variants via CSS)
│  └─ TopBar.tsx
├─ styles/
│  ├─ tokens.css         future-dark + light tokens (--brand / --alt / --accent …)
│  ├─ app.css            shell: canvas, nodes, drawer, detail, pager
│  └─ primitives.css     content classes available to pane HTML
├─ custom/DemoStepper.tsx  example registered component
└─ decks/demo.json       self-documenting demo deck

License

MIT

About

Build multi-level, visual presentations with UiPath Flow design vibes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages