The page script

What assets/devpage.js does in every open page: swapping a page in place, mounting widgets, listening to the live-update stream, following links without reloading, keeping the theme, driving the navbar, and letting hidden tabs go quiet. It is a mechanism page under Live updates, for someone about to change the script.

flowchart LR
  click1["A click on a link"] --> plain{"Plain left click,<br/>same origin, .md?"}
  plain -->|"no"| browser["The browser<br/>loads it"]
  plain -->|"yes"| same{"The page<br/>shown now?"}
  same -->|"yes"| hash["Scroll to<br/>its #fragment"]
  same -->|"no"| swap["load: swap in place,<br/>push history"]
  swap -. "load fails, or<br/>another frame" .-> browser
  click swap href "#swapping-a-page-in-place" "Swapping a page in place"
  click plain href "#links-and-history" "Links and history"

Swapping a page in place

load(url) fetches the URL with cache: "no-store". If the response isn't OK or isn't HTML it returns false; otherwise it parses the HTML and compares its frame — the classes the server put on its <body>, less show-comments, which the script itself adds while the comments are shown — with the open page's. A different frame, as when a document changed kind, is loaded whole. The same frame is swapped in: the script replaces the children of #devpage-page, #devpage-nav and #devpage-navbar with the fresh ones — so the navbar's highlight follows the page — sets the document title, takes the fresh page's window.devpageWidgets — so widgets edited since are fetched at their new versions — mounts the new content's widgets, its Mermaid diagrams included, and starts any design it newly embeds. The rest of the page — the scripts, the event stream, the scroll position — stays as it was.

The swap keeps every embedded design's canvas that is still in the page, outside a widget: each keeps its drawing, and the page's one watch stream stays open while the set of designs it shows is the same. A design embedded inside a widget is the exception: React makes the widget's content again, canvas included, so the new canvas takes over what the old one showed — the design it had navigated to and its way back — and is drawn again from the stream (Design engine).

Mounting widgets

On load and after every swap, each Mermaid fence becomes a Diagram placeholder, and every widget placeholder in the content is mounted with React — the widgets it names loaded first (Widgets). Once every widget has mounted, the script draws the designs the content embeds — those inside widgets exist only now — and the page marks itself ready. The design script, assets/design.js, is loaded the first time a page has a design, and from then on starts every canvas not yet drawn and stops those gone.

The live-update stream

connect() opens an EventSource on /__devpage/events. Each change message calls load(location.href), so the page the tab shows is fetched again and swapped in; a comments message for a file the tab shows fetches that file's threads again. A tab holds at most one stream.

One click listener on the document handles every link, in the text and in clickable diagrams, where pele turns a Mermaid click … href line into an SVG <a href> inside the node. The listener reads the link's href attribute and resolves it against the page's address: an SVG link's href property is an object, not a URL string, so the attribute is the one form both kinds of link share. A click is handled in place when it is a plain left click — no Cmd, Ctrl or Shift, not already handled — on a link to the same origin whose path ends in .md:

Back and forward (popstate) load the address they land on the same way. Every other link — other sites, non-Markdown files such as a doc's or deck's document file — is left to the browser and loads a whole new page. A site's title links to its root page, a Markdown file, so it swaps in place too.

The theme

It keeps the theme the page's head chose current: a click on the theme selector switches data-theme on <html> to the other theme, labels the button with the next switch, and stores the theme in localStorage; until the reader has switched, the page follows the system setting as it changes. A blocked localStorage leaves the switch working for that page.

The navbar

A click on a dropdown's button opens it; ↓ opens it and focuses its first link, ↑ and ↓ move between links, and Escape, a click elsewhere or a followed link closes it. On a narrow screen the menu button shows and hides the links.

Hidden tabs

When a tab is hidden it closes its stream; when it is shown again it reopens the stream and loads its page once, to catch up on changes made while it was hidden. A page being unloaded (pagehide) closes its stream too. A tab opened in the background doesn't connect until it is shown.