docs(changelog): merge three orphaned [Unreleased] sections into their releases (#158) (9ac9b56)
docs(readme): say what the client does, not what it enables (#157) (2c3978b)
docs: pin the README CDN snippets, and make the release own them (#156) (ccae125)
fix(release): promote the curated [Unreleased] instead of regenerating (#154) (a1d43e5)
docs(changelog): curate the v0.20.0 entry after release.sh (6281b3f)
Fixed
A field marked lvt-upload-with that cannot reach the handler now says so.
Fields only ever travel on the multipart upload path. Proxied always uses it,
so OnUpload always receives them — but Volume goes over the WebSocket in
chunks while the socket is up (falling back to multipart when it is down), and
Direct PUTs straight to storage and completes with a metadata-only message.
Neither carries form fields, so the server builds the
upload_<name>_complete action's context from an empty map and the handler
sees nothing, with nothing in the markup to explain it. The client now warns
on both transports, naming the fields and the remedy — which differs: Volume
delivers them on its multipart fallback, Direct never delivers them at all.
(#152, livetemplate#508)
Internal
scripts/release.sh runs tests and the build before touching VERSION,
package.json or CHANGELOG.md, and restores those files when a run aborts
before committing. Previously a failing test left them rewritten, and since
the script refuses to start on a dirty tree, the next attempt failed for a
reason unrelated to the first. (#151)
This release jumps 0.19.1 → 0.20.0 to track the core library's v0.20.0, which
scripts/release.sh enforces on major.minor.
Changed
BREAKING feat(upload): form fields travel with a Proxied upload only when
marked lvt-upload-with, replacing the previous serialize-everything-except-
type="password" denylist (#150, livetemplate#452). A Proxied upload auto-fires
on file selection, so the old default silently POSTed every co-located field —
CSRF tokens, hidden secrets — to the upload endpoint with no submit-time moment
for the user to notice. Migration: add lvt-upload-with to each field an
OnUpload handler reads (typically a record id); an unmarked field now
surfaces as a missing value in the handler rather than as a silent leak.
This release jumps 0.18.2 → 0.19.1 to re-sync the client's major.minor with the
core library (livetemplate v0.19.1), which scripts/release.sh enforces.
Changes
fix(event-delegation): bare-key shortcuts no longer fire under Ctrl/Meta/Alt (117b4fc)
Changes
fix(lvt-el): preserve client-applied class/attr state across morphs (#147) (#148) (015aa94)
Changes
ci: pin publish npm back to 11 (npm 12 --provenance is broken) (bc68eab)
Changes
chore(ci): bump CI to Node 22, return publish to npm@latest (#146) (c9141ad)
ci: pin npm to the 11 line in publish workflow (Node 20 compat) (#145) (f1a0c1d)
Changes
feat(dom): text-select on rendered surfaces via data-surface="block" (3412905)
Changes
feat: lvt-fx:viewport-report directive for read-progress reporting (#144) (6377a0e)
Changes
fix: preserve one-shot scroll/autofocus guards across morphdom (#143) (8d5d3ec)
feat: hydrate Declarative Shadow DOM after morphdom inserts templates (#124) (8121860)
Changes
chore(changelog): strip v0.11.4 stub so release.sh regenerates it (55508c5)
feat: per-action loading bar + lvt-fx:scroll=reset-on + lvt-fx:auto-click (#123) (08b37bb)
Changes
feat: lvt-spy directive for scroll-spy in-page navigation (#122) (dfc2cff)
Changes
feat: lvt-spy scroll-spy directive for in-page navigation highlighting
New client-side directive that highlights [lvt-spy-link] anchor elements
as the reader scrolls past corresponding [lvt-spy] targets in the page.
Two modes: container (lvt-spy="h1, h2, h3" watches matching descendants)
and element (empty attribute — the element itself is the target). Active
link receives the lvt-active class. Trigger line is configurable via
--lvt-spy-margin (default 25vh). Pure client-side, no server round-trips
per scroll tick. Mirrors the lifecycle shape of lvt-scroll-away.
Changes
feat: lvt:error CustomEvent for topic_forbidden envelope (livetemplate#415, V14) (#121) (6d1d5b8)
feat: emit explicit submitter on the wire (livetemplate#237 Phase 2) (1385a5e)
Client-side implementation of the explicit-submitter contract documented
in livetemplate/livetemplate's docs/proposals/explicit-submitter.md.
Phase 1 (server-side acceptance) shipped in livetemplate/livetemplate v0.9.0;
this client release adds the client-side emission so the server can route
the clicked-button action without falling back to the empty-value heuristic.
WS action message envelope now includes submitter: <name> when
SubmitEvent.submitter.name is non-empty.
HTTP Tier 1 multipart submissions include an lvt-submitter form key
alongside lvt-action.
New lvt-form:emit-submitter directive (opt-in, paired with
lvt-form:no-intercept) injects a hidden <input type="hidden" name="lvt-submitter"> into natively-submitted forms before the browser
serializes. Wrapper-delegated so DOM swaps preserve it. Logs a one-shot
warn per form when used on a GET form (URL-pollution caveat).
lvt-submitter is now a reserved form field name (same shape as the
existing lvt-action reservation). Apps that previously used a field
named lvt-submitter for user data will see that value routed as the
action; rename the field to avoid the collision.
Changes
fix: stylesheet check must run for non-LiveTemplate destinations too (369a060)
Changes
fix: full reload when SPA navigation crosses an app boundary (#119) (067827a)
refactor: remove dead StaticsMap code (#17) (226811f)
If you're upgrading from < 0.8.13, the attribute surface changed substantially. Run
grep -rn 'lvt-' <your-template-dir> (or grep -rn 'lvt-' . from your app root) to
find call-sites that need updating.
The legacy lvt-no-intercept attribute is recognized on both <a> links and <form>
elements via a shared shim in utils/legacy-attr.ts. Apps that upgrade the client
without renaming all templates in lockstep keep working. The first time a legacy
attribute is encountered the client logs a one-time deprecation warning so app authors
can find call-sites to migrate. The shim will be removed in v0.9.0 — migrate to
lvt-nav:no-intercept (links) and lvt-form:no-intercept (forms) before then.
lvt-ignore attribute: morphdom escape hatch that skips an element and its entire subtree during diff (equivalent to Phoenix LiveView's phx-update="ignore"). Checked on fromEl (live DOM) so both server templates and client JS can use it. Use data-lvt-force-update on the server's version to bypass and resume diffing.
lvt-ignore-attrs attribute: morphdom escape hatch that preserves user-managed attributes (e.g. open on <details>) while still diffing children. Checked on fromEl for consistency with lvt-ignore. Use data-lvt-force-update to bypass.
In-band __navigate__ SPA navigation: same-pathname link clicks send {action:"__navigate__", data:<params>} over the existing WebSocket instead of fetching new HTML. Requires server-side support (livetemplate/livetemplate#344).
DOMParser fallback in updateDOM: HTML containing <script> tags is now parsed via DOMParser to avoid a Chrome innerHTML bug that creates phantom duplicate DOM nodes after script tags.
Breaking Changes
Cross-pathname same-handler navigation now always reconnects. Previously, if two routes shared the same data-lvt-id, navigating between them would do an in-place DOM swap without reconnecting. This fast path has been removed; all cross-pathname navigations (regardless of handler ID) now trigger a full WebSocket reconnect. This is the correct behavior — same-ID across paths means two distinct routes, and sendNavigate cannot express a path change. If your app shares a data-lvt-id across routes, expect a reconnect flash where there was none before.
Deployment note
The __navigate__ in-band action is a no-op on server versions before livetemplate/livetemplate#344. Deploy the server update before or simultaneously with this client version to avoid same-pathname link clicks sending an unrecognized WebSocket action.
Changes
fix(ci): upgrade npm in publish workflow for OIDC trusted publishing (74bd7c5)
feat: polyfill command/commandfor for cross-browser dialog support (#57) (565176c)
Changes
feat: extend livetemplate.css with shared utilities and chat styles (#54) (c12e1e8)
Changes
feat: add data-lvt-target for cross-element targeting in lvt-el: methods (#53) (89aa203)
Added
feat: data-lvt-target attribute for cross-element targeting — lvt-el: methods can now operate on a different element via #id or closest:selector
Changes
chore(release): v0.8.17 (d6b41a4)
feat: extend lvt-el: to support native DOM event triggers (#49) (ddf92c2)
fix: form.name DOM shadowing + skip File objects in FormData parsing (58cf0c2)
Added
feat: lvt-el:{method}:on:{event} now supports any native DOM event as trigger (click, focusin, focusout, mouseenter, mouseleave, keydown, etc.) — no server round-trip, CSP-safe
feat: lvt-fx:{effect}:on:{event} supports DOM event triggers (e.g., lvt-fx:highlight:on:click="flash") and lifecycle triggers (e.g., lvt-fx:highlight:on:success="flash")
Changes
fix: form.name DOM shadowing + skip File objects in FormData parsing (58cf0c2)
Changes
Changes
feat: Tier 1 file uploads — HTTP fetch for forms with file inputs (387e2fe)
Changes
fix: lvt-form:action routing, lvt-nav:no-intercept, unreserve action field (#45) (6598832)