okf-gem · docs
CLI · serve & setup

okf render

The same graph, as a file you can host anywhere.

When to use it

  • To publish the graph where there is no server to run it: GitHub Pages, an object store, a CI artifact, a file you attach to an email.
  • To hand someone the graph as a single file they open with a double-click. No install, no okf, no service to keep alive.
  • When okf server is the view you want but the host cannot keep a process running. render trades the live reads for a frozen snapshot you can put anywhere static files go.

How it works

okf render <dir> writes the same page okf server serves, as one self-contained HTML file. It prints to stdout by default, so okf render docs/ > public/index.html is the whole publish step, or -o FILE writes the file directly. --title, --link, and --layout tune the header and initial layout exactly as they do for the server, and --map opens the graph with no links and the directories boxed (a dense graph otherwise opens on its spine, the single strongest edge per concept). --title also names the bundle itself wherever the page shows its root: the file tree's root row, the root node in file-tree mode, and the root map in the index layer, all of which read "(root)" when you pass no title.

It is the same template in both modes. What changes is where the browser reads from. Under the server, the page fetches each concept body, its description, the catalog, the index map, and the log live over HTTP as you click. Under render there is no server to answer those reads, so the whole bundle is baked into the file: the same five reads resolve from an embedded payload instead of the network, through the same getter functions, chosen by one injected switch. No server, no build step, nothing external to fetch but the viewer libraries.

Because every body is embedded, the file stands on its own. Open it from disk, from a static host, from a gh-pages branch, and the graph, the panels, search, the filters, and the index and log views all work with no process behind them. Search gains the most from the baking: the static page indexes the bodies it already carries, so full-text search reaches inside concepts offline, which the live server cannot do because it fetches bodies lazily and indexes metadata only. A static graph you can host anywhere shows the whole publish flow.

Try it

okf render docs/ > graph.html

Or write the file and see the count on stderr:

okf render docs/ -o public/index.html
wrote 37 concepts to public/index.html

Open the file in a browser, or commit it to a gh-pages branch and the graph is a URL.

Pitfalls

  • The file carries the whole bundle, so it grows with the bundle. Each body is inlined, so a large bundle makes a large HTML file. That is the price of having no server behind it. For a bundle too big to ship whole, okf server stays the right tool, since it streams bodies on demand.
  • It is a snapshot, not a live view. The server re-reads each body from disk on every click, so edits show on the next click; a rendered file froze the bundle at the moment you ran the command. Re-run render to refresh it.
  • Same trust boundary as the server, no laxer. Every embedded body is escaped so it cannot break out of its <script> tag, and still runs through DOMPurify when rendered, so a hostile bundle cannot script the viewer through the static file any more than through the live one. The page still loads its libraries from a CDN (Cytoscape, marked, and DOMPurify up front, with Mermaid, Panzoom, and MiniSearch lazy loaded on first use), so it needs a network to draw even though the bundle itself is inlined. Search is the exception: when the CDN is out of reach, the search box falls back to its own substring filter rather than going dead. The graph server page has the full write-up.
  • Best-effort, by design. Like server and graph, render skips a file with invalid frontmatter and notes it on stderr under section 9, so run okf validate if the rendered graph is missing a concept.
esc
navigate open