@ape-egg/vibe 4.3.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/package.json +1 -1
- package/runtime/constants.js +1 -1
- package/spa.js +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vibe
|
|
2
2
|
|
|
3
|
-
**Version 4.3.
|
|
3
|
+
**Version 4.3.1** — Runtime-first reactivity for plain HTML. Drop a script tag into any page and get reactive bindings, control flow, and URL-loaded components — no build step required. Compile later if you want; the compiler is a separate, optional package (`@ape-egg/vibe-compiler`).
|
|
4
4
|
|
|
5
5
|
## Security model & CSP
|
|
6
6
|
|
|
@@ -291,7 +291,7 @@ A standalone client-side router built on one contract: `$.page = { path, route,
|
|
|
291
291
|
|
|
292
292
|
**Keyed outlet**: `key` on a fetched `<component>` declares its identity — when the resolved key changes, the component remounts even if `src` is unchanged. With `key="@[page.path]"`, param→param navigation on the same route (`/brawlers/0 → /brawlers/1` — same fragment src) mounts fresh, exactly like an MPA reload on the new URL. `src` and `key` are the wrapper's own contract and are never passed to the component as props.
|
|
293
293
|
|
|
294
|
-
**Link claiming**: one document-level click listener claims same-origin, unmodified, untargeted clicks whose pathname matches a **real** route — pushState + a fresh `$.page` assignment + `document.title` swap when the route carries one, scroll to top. Everything else navigates natively: other origins, modified clicks, `target`/`download` links, same-page hash anchors, and unrouted paths — which is what makes mixed MPA/SPA output work. `'*'` never claims a click; it only resolves deep-link entries and popstate. `popstate` re-resolves (including `'*'`) without scrolling — the browser restores position.
|
|
294
|
+
**Link claiming**: one document-level click listener claims same-origin, unmodified, untargeted clicks whose pathname matches a **real** route — pushState + a fresh `$.page` assignment + `document.title` swap when the route carries one, scroll to top. The entry route gets the same title swap when `setupSpa` runs, so a deep link never shows the shell's title. Everything else navigates natively: other origins, modified clicks, `target`/`download` links, same-page hash anchors, and unrouted paths — which is what makes mixed MPA/SPA output work. `'*'` never claims a click; it only resolves deep-link entries and popstate. `popstate` re-resolves (including `'*'`) without scrolling — the browser restores position.
|
|
295
295
|
|
|
296
296
|
**No match, no `'*'`**: `$.page.src` stays unset and the outlet mounts nothing. There is no built-in 404.
|
|
297
297
|
|
|
@@ -525,7 +525,7 @@ The pages tree becomes three things:
|
|
|
525
525
|
|
|
526
526
|
1. **Page fragments** at `/components/vibe-spa/<pages-relative-path>` — each page's body content, with its head `<style>` tags prepended and its `<script type="module">` carried along byte-identical. No rewrites: `vibe()` itself applies defaults semantics once booted (see App-Lifetime State Defaults above), so page state gets app-lifetime behavior with the page's own import.
|
|
527
527
|
2. **A generated route table** — `pages/brawlers/$index.html` → `{ route: '/brawlers/:index', src: '/components/vibe-spa/brawlers/$index.html', title: <harvested from the page's <title>> }`. `$param` → `:param`, terminal `$$name` → `:name*`, terminal `index` serves its directory path. Sorted most-specific-first.
|
|
528
|
-
3. **A composed shell** at the output root `/index.html` — plain runtime-Vibe code: the deduped union of every page's head resources (`<meta>` keeps only the set common to all pages; page-specific meta is dropped with a verbose note), the `/` route's title, the union of page body attributes minus `vibe-fouc`, one generated boot script (`resolve` seeds `$.page`, `setupSpa` wires navigation — imports reuse the pages' own import style), and the keyed route outlet: `<component src="@[page.src]" key="@[page.path]"></component>` (a path change remounts the fragment even when the route — and so the src — is unchanged: param→param navigation mounts fresh, like the MPA reload it replaces).
|
|
528
|
+
3. **A composed shell** at the output root `/index.html` — plain runtime-Vibe code: the deduped union of every page's head resources (`<meta>` keeps only the set common to all pages; page-specific meta is dropped with a verbose note), the `/` route's title, the union of page body attributes minus `vibe-fouc`, a `window.__MANIFEST__` hint naming the shell's own hyperspeed manifest (the shell is the document at every route, so the runtime loads that one manifest directly instead of probing paths derived from the URL), one generated boot script (`resolve` seeds `$.page`, `setupSpa` wires navigation — imports reuse the pages' own import style), and the keyed route outlet: `<component src="@[page.src]" key="@[page.path]"></component>` (a path change remounts the fragment even when the route — and so the src — is unchanged: param→param navigation mounts fresh, like the MPA reload it replaces).
|
|
529
529
|
|
|
530
530
|
**What SPA mode skips**: per-page stamped HTML, per-page manifests, and the `pages/` output directory. Fragments are runtime-parsed on mount (per-route lazy loading falls out of the reactive src for free); the shell itself gets a manifest but is deliberately left unstamped — it is served at every route path, so its first paint is location-dependent by nature. The shell ships **without** `vibe-fouc`: its absence is the compiled-mode marker (it gates hyperspeed manifest loading), and there is nothing to flash — the outlet is empty and fragments hydrate off-DOM before insertion.
|
|
531
531
|
|
|
@@ -539,7 +539,7 @@ The pages tree becomes three things:
|
|
|
539
539
|
}
|
|
540
540
|
```
|
|
541
541
|
|
|
542
|
-
Dev-server equivalents: any "history API fallback" option (`
|
|
542
|
+
Dev-server equivalents: any "history API fallback" option (`vite preview`, `serve -s`, Caddy `try_files`, or `http-server -P http://localhost:8080?` — the trailing `?` proxies unresolved paths to the root).
|
|
543
543
|
|
|
544
544
|
**Watch mode**: `--spa --watch` re-runs the SPA pass on any pages-tree change — edited pages re-transform, added/removed pages resync the route table and prune orphan fragments, title/head edits recompose the shell, and the shell's manifest refreshes.
|
|
545
545
|
|
package/package.json
CHANGED
package/runtime/constants.js
CHANGED
package/spa.js
CHANGED
|
@@ -57,9 +57,13 @@ export const navigate = (path) =>
|
|
|
57
57
|
activeRouter ? activeRouter.navigate(path) : location.assign(new URL(path, location.origin));
|
|
58
58
|
|
|
59
59
|
export const setupSpa = ({ routes, onNavigate }) => {
|
|
60
|
-
const
|
|
60
|
+
const swapTitle = (resolved) => {
|
|
61
|
+
if (resolved?.title) document.title = resolved.title;
|
|
62
|
+
};
|
|
63
|
+
const apply = onNavigate ?? ((resolved) => {
|
|
64
|
+
const { path, route, params, src, name } = resolved;
|
|
61
65
|
window.$.page = { path, route, params, src, name };
|
|
62
|
-
|
|
66
|
+
swapTitle(resolved);
|
|
63
67
|
});
|
|
64
68
|
|
|
65
69
|
const claim = (target) => {
|
|
@@ -117,6 +121,7 @@ export const setupSpa = ({ routes, onNavigate }) => {
|
|
|
117
121
|
|
|
118
122
|
document.addEventListener('click', onClick);
|
|
119
123
|
addEventListener('popstate', onPopstate);
|
|
124
|
+
if (!onNavigate) swapTitle(resolve(location, routes));
|
|
120
125
|
|
|
121
126
|
const dispose = () => {
|
|
122
127
|
document.removeEventListener('click', onClick);
|