3 The live preview loop
What survives a save (scroll position, kernel state, a running canvas) and how Ctrl-click jumps from the page back to the line that made it.
The reason Taliesin exists is the edit loop: change the source, and the preview reflects it immediately, without reloading the page, losing your scroll, or resetting any live content already on screen. See Section 2.2 for what “immediately” is in milliseconds.
3.1 Incremental updates
On every save the server re-parses the document, diffs the new block list against the old one by content-hash id, and pushes only the blocks that changed over a websocket. An unchanged block is never touched, so:
- your scroll position is preserved,
- a running
{js}cell or Three.js canvas keeps its state, - the page never flashes white.
The block you just edited pulses briefly when it re-renders, so your eye (or your phone) lands on the change.
3.2 Click-to-source and editor sync
In the browser preview, a plain click browses normally; Ctrl-click (Cmd-click
on Mac) any block to open its exact source line in your editor (including across
{{< include >}}d files) via a vscode:// deep link. The client also speaks a
tali-goto / tali-cursor postMessage protocol, so an editor can host the preview and
add cursor sync without forking anything. The in-repo VS Code companion
(editor/vscode/) is exactly that host.
The deep link is vscode:// and there is no configuration key for it, so Ctrl-click does
nothing in Neovim, Helix or Zed. Those editors get everything taliesin lsp serves and the
live preview itself; it is the jump from the page back to the editor that has one target.
Getting the companion. It is not on the VS Code Marketplace; you build the .vsix
from this repository and install it yourself:
&& && &&
Then make sure the taliesin binary is on your PATH, or point the extension’s
taliesin.path setting at it.
A chapter of a book previews as the book. Open the preview on any page that lives
under a _site.yml and the companion serves the whole project and opens it at that
page’s URL, so you get the nav, the breadcrumb and working cross-page links instead of an
orphan page. One server serves every chapter: opening a second one reveals the preview you
already have, moved to that chapter. Moving your cursor into another chapter’s file moves
the preview to it too, and Ctrl-clicking a block resolves against the page you are looking
at, not the one you opened.
The other direction, editor to preview, is split. Moving your cursor marks the matching
block in the preview and deliberately does not move the page, so scrolling somewhere to
compare two figures survives your next keystroke. When you do want the preview to come to
you, press Ctrl+Alt+J (Taliesin: Reveal Cursor in Preview), which scrolls it
there and leaves the keyboard focus in your editor.
If repeated Ctrl-clicks from a browser preview open new tabs instead of moving the cursor
in the tab you already have, add "workbench.editor.revealIfOpen": true to your
settings.json. The companion does not need it.
3.3 What the editor does while you write
- Completion for front-matter keys and values, cell options and their values, cell languages, div classes, cross-references, citations, shortcode names, anchor ids, math commands, and file paths anywhere a path is legal.
- Div attributes, narrowed to the class you wrote.
::: {.callout-noteofferstitle=andcollapse=, the two a callout reads; a plain div offerslayout-ncol=and neither of those. - Lists and blockquotes continue on Enter, keeping the marker you chose (
-,*,+,>, and task items). Pressing Enter on an empty item is how you leave the list. _site.ymlcompletion is your YAML language server’s job, not Taliesin’s: the config schema is a file you point it at with one comment line, the same step in every editor (see Configuration).
3.3.1 What the document’s structure gets you
Neither of these needs a preview window open, and neither is specific to VS Code: they
are the protocol, so cmd = { "taliesin", "lsp" } gets them in Neovim, Helix or Zed too.
- Folding follows the document’s own structure instead of indentation. Front matter,
each heading down to the next of equal or shallower level,
:::divs and code fences each fold as a unit, so you can collapse a chapter to its headings. - Go to definition crosses files.
F12on@sec-priorsjumps to the chapter that defines it, not just to a definition in the file you are in. Your open buffer still wins: an anchor you just typed and have not saved resolves to where you typed it, never to the older copy on disk. Hovering a cross-chapter reference names the page that defines it, since the number belongs to that page and is not known here.
3.3.2 Checking the pages you have not opened
The squiggles cannot do this one for you: the language server only sees files you have opened, so a chapter you have not touched all week is invisible to it. Run the project lint in the terminal instead:
Every located line it prints is clickable in VS Code’s integrated terminal, straight to the file and line, whether or not that page has ever been open.
3.4 The dev menu
Every live preview carries a small dev menu: a </> button tucked in the
bottom corner of the page. It belongs to the preview only, so a static build
never ships it.
The button itself is a connection light: a coloured dot that reads live while the websocket is connected, reconnecting while the server restarts, and error on a failed render. A small badge appears on it when there are diagnostics waiting.
Click it to expand a panel with the preview-only tools:
- Status and a live word count for the prose.
- Restart kernel: drops the warm Jupyter kernel and re-runs every cell against
a fresh one. Reach for it when the kernel wedges, or after you fix
TALIESIN_PYTHONor install a missing package and want a clean replay. - A reminder that Ctrl-click (Cmd-click on Mac) opens any block’s source in your editor.
- A theme toggle (light/dark), for the preview only: see Theming for what a reader’s built page does instead.
- The diagnostics list (detailed below), each entry click-to-source, with the button’s badge tracking the count.
3.5 Live-edit feedback
The loop surfaces what is happening without you watching the terminal:
- A non-fatal problem (a broken
{{< include >}}, a missing kernel, a typo’d extension, a broken[@citation]or@cross-reference) appears in the dev menu’s diagnostics list. - A broken front matter points right at the bad line: the diagnostic shows a small code frame and is clickable to open that line in your editor.
- A failed render throws a prominent, dismissible error overlay over the last good content, so a broken save is impossible to miss. It clears on the next successful render.
- The watcher follows the whole document, not just the served file: editing an
included subsection or the
.bibre-renders the pages that depend on it.
3.6 Reading on any screen
The reading layout is responsive: wide tables, long code, display math and embedded
canvases scroll within their own box instead of stretching the page, motion honours
prefers-reduced-motion, and on a phone the table of contents leaves the sticky
sidebar and appears inline after the article.
The preview binds to loopback only, so it is reachable from the machine running
it and nowhere else. To read a draft on a phone, build it and serve the folder from
somewhere both devices can see.
Pressing Cmd/Ctrl-P gives a clean print/PDF: the dev menu, the TOC handle, and any diagnostics are hidden, the palette forces light, and content flows full width without clipping code, tables, or math.