Wisp

Wisp is in development and has not been released. Expect changes until 1.0.0.

Type to search every docs page and heading.

PWA and the Dev Loop

Installable and Offline (PWA)

src/manifest.json is served at /manifest.webmanifest and linked by every page; nothing is emitted without it.

{ "name": "Notes", "theme_color": "#7c3aed", "offline": true }
  • Filled in: short_name/name, start_url / (/app/ under a base path, as the manifest link, the worker and the icons are), display standalone, icons from static/icon*.png (size read) and static/icon*.svg.
  • One static/icon.png (512 px or more) suffices: wisp build also writes 192 and 512 px WebP into /_app/img/ with cwebp (absent: a warning, the PNG alone).
  • At startup instead: wisp::app_manifest(r#"{"name": "Notes"}"#)?; in init.

"offline": true adds Wisp's service worker: at install it keeps /, the browser files and static/; pages come from the network and are kept; offline: the kept page or a 503; a new build drops the old cache.

Your own src/service-worker.js (or .ts) replaces it: a classic script at /service-worker.js, registered by every page, importing only 'wisp/sw' (env.PUBLIC_X works). The CSP gets worker-src 'self' and the registering script's hash.

import { build, files, version } from 'wisp/sw'  // browser files, static/ files, hash of both (empty in dev)
self.addEventListener('install', (e) => {
  e.waitUntil(caches.open(`app-${version}`).then((c) => c.addAll([...build, ...files])))
})

Dev: Hot Reload

wisp dev applies a .wisp save with no compile when only browser code or static text changed:

  • A script or {:…} change swaps the file's module (instances rerun it, keeping $state by name, focus, selection and field values).
  • Text alone morphs that file's part between the <!--w:src/components/Card.wisp--> marks.
  • <style> swaps the stylesheet.
  • Else it compiles and morphs.

Instances restart (one console line says why) when:

  • the --- block or {@props} changed;
  • the script has a top-level statement other than declarations, logging, writes to its own names and instance-ending helpers ($effect, onMount, setInterval…; init(), if, new X(), window.x = 1 could run twice);
  • the swap throws.

Components match by creation order, so a reordering list may trade states. Build errors show in a dialog whose src/…:line lines open in the editor; an error the page's code throws (also what onError hears) shows there too. The next successful build closes it. None of this is in release builds.

Devtools

Alt+Shift+W opens dev-only devtools:

  • Component tree with live props and state (editable), stores.
  • A graph per component: what reads each signal (its $derived values by name, how many effects and DOM updates).
  • The route (params, server values, the page's forms), a table of all the app's routes.
  • Timings (dev responses carry Server-Timing: total;dur=…, before;dur=…, handler;dur=…, render;dur=…; WISP_SERVER_TIMING sets it).
  • A server panic or other 5xx opens the same error dialog with its message and file:line; "Open" uses $WISP_EDITOR or $EDITOR, else code -g.

Is This Page Useful?

Edit This Page