pageturn 0.1.1 → 1.0.0

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.
Files changed (75) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +176 -5
  3. package/dist/astro.js +0 -2
  4. package/dist/chunks/chunk-3ABPYO7H.js +43 -0
  5. package/dist/chunks/chunk-6FXXBNAF.js +197 -0
  6. package/dist/chunks/chunk-AEOAXMU3.js +33 -0
  7. package/dist/chunks/chunk-BOM5VXA5.js +167 -0
  8. package/dist/chunks/chunk-C3T7BGNG.js +30 -0
  9. package/dist/chunks/chunk-FYSSNYA4.js +17 -0
  10. package/dist/chunks/chunk-JHM7F4CH.js +172 -0
  11. package/dist/chunks/chunk-LM3XLSCW.js +799 -0
  12. package/dist/chunks/{chunk-QJ7GGFM4.js → chunk-YAL4MNCU.js} +4 -6
  13. package/dist/core.js +4 -3
  14. package/dist/dev/chunks/chunk-3JAWIMHB.js +184 -0
  15. package/dist/dev/chunks/chunk-3SU4CIKL.js +799 -0
  16. package/dist/dev/chunks/chunk-AEOAXMU3.js +33 -0
  17. package/dist/dev/chunks/chunk-C3T7BGNG.js +30 -0
  18. package/dist/dev/chunks/chunk-FYSSNYA4.js +17 -0
  19. package/dist/dev/chunks/chunk-I4SBUABE.js +175 -0
  20. package/dist/dev/chunks/chunk-U3XN54YY.js +219 -0
  21. package/dist/dev/chunks/chunk-Y6TD2T53.js +43 -0
  22. package/dist/dev/chunks/chunk-YAL4MNCU.js +22 -0
  23. package/dist/dev/core.js +19 -0
  24. package/dist/dev/index.js +21 -0
  25. package/dist/dev/morph-markup.js +34 -0
  26. package/dist/dev/morph.js +457 -0
  27. package/dist/dev/native.js +3 -0
  28. package/dist/dev/next.js +79 -0
  29. package/dist/dev/nuxt-plugin.js +24 -0
  30. package/dist/dev/prefetch.js +2 -0
  31. package/dist/dev/react-router.js +46 -0
  32. package/dist/dev/router.js +9 -0
  33. package/dist/dev/sveltekit.js +32 -0
  34. package/dist/dev/testing.js +48 -0
  35. package/dist/dev/turbo.js +49 -0
  36. package/dist/dev/vue.js +11 -0
  37. package/dist/head-core.js +1 -1
  38. package/dist/head-string.js +6 -6
  39. package/dist/head.js +1 -1
  40. package/dist/index.js +6 -5
  41. package/dist/morph-head.js +1 -1
  42. package/dist/morph-markup.js +11 -9
  43. package/dist/morph.js +145 -287
  44. package/dist/native.js +3 -2
  45. package/dist/next.js +79 -0
  46. package/dist/nuxt-plugin.js +24 -0
  47. package/dist/nuxt.js +34 -0
  48. package/dist/pageturn-morph.js +1 -1
  49. package/dist/pageturn.js +1 -1
  50. package/dist/prefetch.js +2 -2
  51. package/dist/react-router.js +46 -0
  52. package/dist/router.js +5 -183
  53. package/dist/sveltekit-server.js +22 -0
  54. package/dist/sveltekit.js +32 -0
  55. package/dist/testing.js +12 -22
  56. package/dist/turbo.js +49 -0
  57. package/dist/types/astro/integration.d.ts +0 -4
  58. package/dist/types/common/dev.d.ts +6 -0
  59. package/dist/types/common/page.d.ts +5 -0
  60. package/dist/types/common/registry.d.ts +3 -1
  61. package/dist/types/next/next.d.ts +27 -0
  62. package/dist/types/nuxt/module.d.ts +32 -0
  63. package/dist/types/react-router/react-router.d.ts +12 -0
  64. package/dist/types/router/router.d.ts +4 -2
  65. package/dist/types/sveltekit/server.d.ts +21 -0
  66. package/dist/types/sveltekit/sveltekit.d.ts +32 -0
  67. package/dist/types/turbo/turbo.d.ts +1 -0
  68. package/dist/types/vue/vue.d.ts +36 -0
  69. package/dist/vue.js +11 -0
  70. package/package.json +71 -4
  71. package/dist/chunks/chunk-237GNQNZ.js +0 -37
  72. package/dist/chunks/chunk-7LMNJ25V.js +0 -244
  73. package/dist/chunks/chunk-QC36TQP3.js +0 -24
  74. package/dist/chunks/chunk-WTHN3UIQ.js +0 -1000
  75. package/dist/chunks/chunk-YBAP3PHO.js +0 -247
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # pageturn
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - f3ef048: pageturn 1.0: the public API is stable. What a site imports, the marks on `<html>` and
8
+ their custom properties, the events, and the behaviour written in `docs/contract.md` now
9
+ follow semantic versioning: a minor or patch release never breaks them. Nothing changes
10
+ from 0.2.0 in what pageturn does.
11
+
12
+ ## 0.2.0
13
+
14
+ ### Minor Changes
15
+
16
+ - f03333d: Warnings in development: in the build package.json's `development` condition serves,
17
+ pageturn says in the console why a move did not play (no head script, reduced motion, a
18
+ client router with no adapter, a return by Back with no animation, a router's own view
19
+ transition alongside pageturn's). Production builds carry none of it.
20
+ - f03333d: Next.js: `pageturn/next` plays pageturn's moves on the App Router. Its `Link` (as
21
+ `next/link`) and `usePageturnRouter()` (as `useRouter()`) play the leave before the router
22
+ navigates, and `<Pageturn />` in the root layout says when each page is in the DOM. On
23
+ any client router, the page reached is now recorded where the router shows it, base path
24
+ and trailing slash included, so a return by Back finds its `returnAs` there too.
25
+ - f03333d: Nuxt: the `pageturn/nuxt` module puts the head script in every page, adds the stylesheet,
26
+ and plays pageturn's moves on Nuxt's navigations (`<NuxtLink>`, `navigateTo()`, Back and
27
+ Forward), the page reached arriving once Nuxt's `<Suspense>` has resolved.
28
+ - 3b16406: Only `decline()` declines: a move's `leave` or a morph's `ends` that answers `false`, the
29
+ form from before `decline()`, now declines nothing (the types already refused it). Return
30
+ `turn.decline()` or `decline()` instead.
31
+ - f03333d: React Router: `pageturnMiddleware` on the root route and `usePageturn()` in the root
32
+ layout play pageturn's moves on React Router's navigations (`<Link>`, `navigate()`, Back
33
+ and Forward), the route's loaders running during the leave. `react` and `react-router`
34
+ are optional peer dependencies, used by this adapter only.
35
+ - f03333d: SvelteKit: `pageturn/sveltekit` plays pageturn's moves on SvelteKit's own navigations
36
+ (links, `goto()`, Back and Forward), and `pageturn/sveltekit/server` puts the head script
37
+ in every page. Every module of pageturn can now be imported on a server, where it does
38
+ nothing: a framework's layouts run there too.
39
+ - f03333d: Turbo: `pageturn/turbo` plays pageturn's moves on Turbo Drive's visits (links, forms,
40
+ `Turbo.visit()`, Back and Forward), holding Turbo's render until the leave has ended. On
41
+ a page Turbo has cached, the preview is not shown under the leave.
42
+ - f03333d: Vue Router: `pageturn/vue` plays pageturn's moves on the router's navigations
43
+ (`<RouterLink>`, `router.push()`, Back and Forward). On any client router, a navigation
44
+ to a place in the same page plays no move, and one started from code plays the default
45
+ move rather than asking each move's `match`.
46
+
3
47
  ## 0.1.1
4
48
 
5
49
  ### Patch Changes
package/README.md CHANGED
@@ -30,7 +30,8 @@ goes wrong, you get an ordinary page load.
30
30
  classic script 4.2 kB. Nothing moves under `prefers-reduced-motion`, and no other host
31
31
  is contacted.
32
32
 
33
- > **Status:** not published yet. What pageturn does in every situation is in
33
+ > **Status:** stable. The public API follows semantic versioning: a minor or patch
34
+ > release never breaks it. What pageturn does in every situation is in
34
35
  > [docs/contract.md](docs/contract.md).
35
36
 
36
37
  ## Contents
@@ -89,6 +90,153 @@ first in every page's `<head>`, so your own inline scripts can read
89
90
  The integration ships only what these options use. Your own scripts import from
90
91
  `pageturn/core` (see [Entries](#entries)).
91
92
 
93
+ ### SvelteKit
94
+
95
+ ```js
96
+ // src/hooks.server.js: the head script first in every page
97
+ import { pageturnHandle } from "pageturn/sveltekit/server";
98
+ export const handle = pageturnHandle();
99
+ ```
100
+
101
+ ```svelte
102
+ <!-- src/routes/+layout.svelte -->
103
+ <script>
104
+ import "pageturn/pageturn.css";
105
+ import { beforeNavigate, onNavigate } from "$app/navigation";
106
+ import { pageturn } from "pageturn/sveltekit";
107
+
108
+ pageturn({ beforeNavigate, onNavigate });
109
+ </script>
110
+ ```
111
+
112
+ SvelteKit's own navigations then play the same moves: links, `goto()`, Back and Forward.
113
+ The leave plays while SvelteKit loads the next page's data, and SvelteKit waits for it
114
+ before it updates the page. A link SvelteKit leaves to the browser
115
+ (`data-sveltekit-reload`) is a real navigation, and plays its move too. With
116
+ `pageturn/no-native.css` or your own CSS, `pageturnHandle({ native: false })`. Under a
117
+ Content-Security-Policy, add `headScriptHash` from `pageturn/head` to `kit.csp`. Turn off
118
+ any SvelteKit view-transition recipe: two systems animating one swap is the likeliest
119
+ bug.
120
+
121
+ ### Rails, with Turbo
122
+
123
+ ```html
124
+ <!-- In the layout's <head>: the head script, then the stylesheet -->
125
+ <script src="/pageturn/head.js"></script>
126
+ <link rel="stylesheet" href="/pageturn/pageturn.css">
127
+ ```
128
+
129
+ ```js
130
+ // app/javascript/application.js
131
+ import "@hotwired/turbo-rails";
132
+ import "pageturn/turbo";
133
+ ```
134
+
135
+ Turbo Drive's visits then play the same moves: links, forms, `Turbo.visit()`, Back and
136
+ Forward. The leave plays while Turbo fetches the page, and Turbo holds its render until
137
+ it has ended. On a page Turbo has cached, its preview is not shown under the leave: the
138
+ page arrives once, from its real render. A link Turbo leaves to the browser
139
+ (`data-turbo="false"`) is a real navigation, with its move. Copy `head.js` and the
140
+ stylesheet from `node_modules/pageturn/dist/` (or inline `headScript` from
141
+ `pageturn/head`), and remove Turbo's `view-transition` meta tag where pageturn plays.
142
+
143
+ ### Vue Router
144
+
145
+ ```js
146
+ // main.js, before the app mounts
147
+ import "pageturn/pageturn.css";
148
+ import { pageturn } from "pageturn/vue";
149
+ pageturn(router);
150
+ ```
151
+
152
+ ```html
153
+ <!-- index.html, first in <head> -->
154
+ <script src="/pageturn/head.js"></script>
155
+ ```
156
+
157
+ The router's navigations then play the same moves: `<RouterLink>`, `router.push()`,
158
+ Back and Forward. The leave starts with the navigation, and the router waits for it once
159
+ the route's components have loaded. A navigation a guard aborts puts the page back. A
160
+ `#place` in the same route plays no move. With Nuxt, use [`pageturn/nuxt`](#nuxt) rather
161
+ than calling this yourself.
162
+
163
+ ### Nuxt
164
+
165
+ ```ts
166
+ // nuxt.config.ts
167
+ export default defineNuxtConfig({
168
+ modules: ["pageturn/nuxt"], // or [["pageturn/nuxt", { css: "no-native" }]]
169
+ });
170
+ ```
171
+
172
+ The module puts the head script first in every page, adds the stylesheet (`css`:
173
+ `true`, `"no-native"` or `false`), and plays the same moves on Nuxt's navigations:
174
+ `<NuxtLink>`, `navigateTo()`, Back and Forward. The page reached arrives once Nuxt's
175
+ `<Suspense>` has resolved (`page:finish`). Turn off `experimental.viewTransition` and any
176
+ `pageTransition` where pageturn plays.
177
+
178
+ ### React Router
179
+
180
+ ```jsx
181
+ import { pageturnMiddleware, usePageturn } from "pageturn/react-router";
182
+
183
+ const router = createBrowserRouter([
184
+ { path: "/", Component: Root, middleware: [pageturnMiddleware], children },
185
+ ]);
186
+
187
+ function Root() {
188
+ usePageturn();
189
+ return <Outlet />;
190
+ }
191
+ ```
192
+
193
+ With the stylesheet imported once and the head script first in the document's `<head>`.
194
+ React Router 7.9 or later, with data routers (`createBrowserRouter`) or framework mode.
195
+ The middleware starts the leave as the navigation does, runs the route's loaders
196
+ meanwhile, and lets the router commit when both are done; the hook says when the route
197
+ reached is in the DOM. `<Link>`, `navigate()`, Back and Forward play the same moves. A
198
+ `<Link reloadDocument>` is a real navigation, with its move. Leave out React Router's
199
+ `viewTransition` where pageturn plays.
200
+
201
+ ### Next.js (App Router)
202
+
203
+ ```jsx
204
+ // app/layout.jsx
205
+ import { coreHeadScript } from "pageturn/head";
206
+ import { Pageturn } from "pageturn/next";
207
+ import "pageturn/no-native.css";
208
+
209
+ export default function Layout({ children }) {
210
+ return (
211
+ <html>
212
+ <head>
213
+ <script dangerouslySetInnerHTML={{ __html: coreHeadScript }} />
214
+ </head>
215
+ <body>
216
+ <Pageturn />
217
+ {children}
218
+ </body>
219
+ </html>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ```jsx
225
+ import { Link, usePageturnRouter } from "pageturn/next";
226
+
227
+ <Link href="/work">Work</Link>; // as next/link
228
+ const router = usePageturnRouter(); // as useRouter()
229
+ router.push("/work");
230
+ ```
231
+
232
+ Next's router cannot wait for a leave, so pageturn's `Link` stops its navigation, plays
233
+ the leave while the page is prefetched, then asks the router again; `usePageturnRouter()`
234
+ does the same from code. `<Pageturn />` says when each page is in the DOM. Back and
235
+ Forward, a plain `next/link` and a bare `router.push` still work: the page arrives (under
236
+ its `returnAs` on Back), with no leave. Put the head script in a plain inline `<script>`:
237
+ `next/script`'s `beforeInteractive` runs it too late, from Next's own code. Next.js 16 or
238
+ later; leave out its experimental view transitions where pageturn plays.
239
+
92
240
  ### Plain HTML, any server
93
241
 
94
242
  ```html
@@ -682,6 +830,13 @@ scroll layer to stop):
682
830
  | `pageturn/morph` | One element from a page to the next. See [docs/morph.md](docs/morph.md) |
683
831
  | `pageturn/morph/markup` | The morph from `data-turn-morph`. Imported for its effect |
684
832
  | `pageturn/router` | The engine for client routers' navigations, which adapters drive. See [docs/router.md](docs/router.md) |
833
+ | `pageturn/sveltekit` | The SvelteKit adapter: `pageturn({ beforeNavigate, onNavigate })`, with the kernel |
834
+ | `pageturn/sveltekit/server` | `pageturnHandle()`: the head script in every page SvelteKit renders |
835
+ | `pageturn/turbo` | The Turbo Drive adapter, with the kernel. Imported for its effect |
836
+ | `pageturn/vue` | The Vue Router adapter: `pageturn(router)`, with the kernel |
837
+ | `pageturn/nuxt` | The Nuxt module: the head script, the stylesheet, and the Vue Router adapter on Nuxt's router |
838
+ | `pageturn/react-router` | The React Router adapter: `pageturnMiddleware` and `usePageturn()`, with the kernel |
839
+ | `pageturn/next` | The Next.js App Router adapter: `Link`, `usePageturnRouter()` and `<Pageturn />`, with the kernel |
685
840
  | `pageturn/testing` | For browser tests of a morph: the layer's parts by name, a travel held at a time or at its end |
686
841
  | `pageturn/astro` | The Astro integration |
687
842
 
@@ -691,6 +846,21 @@ And the files of `dist/` for a page with no bundler: `pageturn.css` or `no-nativ
691
846
  A site that turns prefetch off and writes its own moves imports `pageturn/core` only. The
692
847
  kernel needs the head script: without it, it does nothing, and links navigate normally.
693
848
 
849
+ ### Warnings in development
850
+
851
+ While you develop, pageturn says in the console why a move did not play, once each:
852
+
853
+ - the head script is missing from the page;
854
+ - `prefers-reduced-motion` is on, so nothing plays;
855
+ - a client router took a link and no adapter tells pageturn about it;
856
+ - a page returned to by Back under its `returnAs` has no animation of its own, so it
857
+ shows at once (Back cannot play a leave, which reads as a flash);
858
+ - a router plays its own view transition while pageturn plays a move.
859
+
860
+ They come from the build package.json's `development` condition serves, which Vite,
861
+ webpack and the frameworks on them pick while developing. A production build, and the
862
+ files of `dist/` loaded as they are, carry none of it.
863
+
694
864
  ### Content-Security-Policy
695
865
 
696
866
  - `script-src 'self'`: load `pageturn.js` as a file, or use `head: "external"` in Astro.
@@ -724,10 +894,11 @@ checked by hand before each release ([docs/release-checklist.md](docs/release-ch
724
894
 
725
895
  ## Questions
726
896
 
727
- **Does it work with Next.js, Nuxt, SvelteKit?** Not yet on the links their router
728
- handles: those do not load a page. `pageturn/router` is the engine for them, with the
729
- same moves and CSS; the adapters that connect each router are not written yet
730
- ([docs/router.md](docs/router.md)).
897
+ **Does it work with Next.js, Nuxt, SvelteKit?** Yes, with an adapter for each:
898
+ [SvelteKit](#sveltekit), [Turbo](#rails-with-turbo), [Vue Router](#vue-router),
899
+ [Nuxt](#nuxt), [React Router](#react-router) and [Next.js](#nextjs-app-router), all on
900
+ `pageturn/router` ([docs/router.md](docs/router.md)). pageturn can be imported where a
901
+ framework renders on the server: it does nothing there.
731
902
 
732
903
  **What if JavaScript fails, or the next page is slow?** The click is a real navigation.
733
904
  A leave that throws or takes too long navigates anyway. A page that does not come in
package/dist/astro.js CHANGED
@@ -109,7 +109,5 @@ function pageturn(options = {}) {
109
109
  };
110
110
  }
111
111
  export {
112
- HEAD_SCRIPT_PATH,
113
- MORPH_HEAD_SCRIPT_PATH,
114
112
  pageturn as default
115
113
  };
@@ -0,0 +1,43 @@
1
+ import {
2
+ startNavigation
3
+ } from "./chunk-BOM5VXA5.js";
4
+ import {
5
+ inBrowser
6
+ } from "./chunk-AEOAXMU3.js";
7
+
8
+ // src/vue/vue.ts
9
+ function pageturn(router, options = {}) {
10
+ if (!inBrowser) return () => {
11
+ };
12
+ let current = null, popped = !1, cancel = () => {
13
+ current?.cancelled(), current = null;
14
+ }, stops = [
15
+ // Back and Forward: the router's history says so before its guards run.
16
+ router.options.history.listen((_to, _from, info) => {
17
+ popped = info.type === "pop";
18
+ }),
19
+ router.beforeEach((to, from) => {
20
+ let via = popped ? "history" : "link";
21
+ popped = !1, from.matched.length !== 0 && (current = startNavigation({ to: router.resolve(to).href, via }));
22
+ }),
23
+ router.beforeResolve(() => current?.left),
24
+ router.afterEach((_to, _from, failure) => {
25
+ let turn = current;
26
+ if (current = null, !turn) return;
27
+ if (failure) {
28
+ turn.cancelled();
29
+ return;
30
+ }
31
+ let swap = () => turn.swapped();
32
+ options.whenShown ? options.whenShown(swap) : queueMicrotask(swap);
33
+ }),
34
+ router.onError(cancel)
35
+ ];
36
+ return () => {
37
+ for (let stop of stops) stop();
38
+ };
39
+ }
40
+
41
+ export {
42
+ pageturn
43
+ };
@@ -0,0 +1,197 @@
1
+ import {
2
+ STORE_VERSION,
3
+ choose,
4
+ followArrival,
5
+ leave,
6
+ stopFollowing,
7
+ tellArrival,
8
+ turnOf
9
+ } from "./chunk-JHM7F4CH.js";
10
+ import {
11
+ inBrowser,
12
+ reducesMotion
13
+ } from "./chunk-AEOAXMU3.js";
14
+ import {
15
+ registry
16
+ } from "./chunk-YAL4MNCU.js";
17
+
18
+ // src/kernel/kernel.ts
19
+ var store, current = null, stuckTimer;
20
+ function install() {
21
+ if (!inBrowser) return;
22
+ let published = window.pageturn?.j;
23
+ registry.g || published?.m !== STORE_VERSION || (store = published, registry.g = !0, addEventListener("click", putClickLast, !0), addEventListener("pointerdown", pressed, !0), addEventListener("pagehide", pageHidden, !0), addEventListener("pageshow", pageShown), followArrival());
24
+ }
25
+ function putClickLast() {
26
+ removeEventListener("click", clicked), addEventListener("click", clicked);
27
+ }
28
+ function linkIn(event) {
29
+ for (let target of event.composedPath()) {
30
+ if (target instanceof HTMLAnchorElement)
31
+ return target.hasAttribute("href") ? target : null;
32
+ if (target instanceof SVGAElement) return null;
33
+ }
34
+ return null;
35
+ }
36
+ function isEditable(event) {
37
+ let [target] = event.composedPath();
38
+ return target instanceof HTMLElement && target.isContentEditable;
39
+ }
40
+ function destinationOf(link) {
41
+ let target = link.target || document.querySelector("base[target]")?.getAttribute("target");
42
+ if (target && target !== "_self" || link.hasAttribute("download"))
43
+ return null;
44
+ let to = new URL(link.href), isThisPage = to.pathname === location.pathname && to.search === location.search;
45
+ return to.origin === location.origin && !isThisPage ? to : null;
46
+ }
47
+ function prefetch(to) {
48
+ registry.settings.prefetch && registry.u?.(to);
49
+ }
50
+ function pressed(event) {
51
+ if (event.button !== 0 || isEditable(event)) return;
52
+ let link = linkIn(event), to = link && destinationOf(link);
53
+ to && prefetch(to);
54
+ }
55
+ function clicked(event) {
56
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || isEditable(event))
57
+ return;
58
+ let link = linkIn(event), to = link && destinationOf(link);
59
+ if (!link || !to) return;
60
+ if (current) {
61
+ event.preventDefault();
62
+ return;
63
+ }
64
+ if (store.n(), event.detail === 0 && prefetch(to), reducesMotion()) return;
65
+ let { a: native } = registry, visit = {
66
+ link,
67
+ via: "link",
68
+ from: new URL(location.href),
69
+ to,
70
+ canHandOver: store.q()
71
+ }, name = choose(visit);
72
+ if (name === null) {
73
+ native?.c(null);
74
+ return;
75
+ }
76
+ let turn = turnOf(visit, name);
77
+ if (!document.dispatchEvent(
78
+ new CustomEvent("pageturn:leaving", { cancelable: !0, detail: turn })
79
+ )) {
80
+ native?.c(null);
81
+ return;
82
+ }
83
+ if (name === registry.settings.defaultMove && !registry.b.get(name)?.leave && native?.v(name, to))
84
+ return;
85
+ event.preventDefault();
86
+ let playing = { turn };
87
+ current = playing, leave(turn, (played) => {
88
+ playing.turn = played;
89
+ }).then((handover) => depart(playing, handover));
90
+ }
91
+ function depart(playing, handover) {
92
+ if (current !== playing || playing.handover !== void 0) return;
93
+ let { turn } = playing;
94
+ playing.handover = handover, handover && handover.arriveAs !== null && store.h(
95
+ {
96
+ move: handover.arriveAs ?? turn.move,
97
+ data: handover.data ?? null,
98
+ from: location.pathname,
99
+ at: Date.now()
100
+ },
101
+ turn.to
102
+ ), playing.entry = handover?.returnAs ? {
103
+ move: turn.move,
104
+ to: turn.to.pathname,
105
+ returnAs: handover.returnAs,
106
+ remember: handover.remember ?? null
107
+ } : null, store.i(playing.entry), registry.a?.c(null), location.assign(turn.to.href), stuckTimer = setTimeout(
108
+ () => endWithoutDeparture(playing, "stuck"),
109
+ registry.settings.stuckTimeout
110
+ );
111
+ }
112
+ function endWithoutDeparture(playing, reason) {
113
+ if (current !== playing) return;
114
+ current = null, clearTimeout(stuckTimer), registry.a?.c(void 0), delete document.documentElement.dataset.turnLeaving;
115
+ let stay = { reason, handover: playing.handover ?? null };
116
+ try {
117
+ registry.b.get(playing.turn.move)?.stay?.(playing.turn, stay);
118
+ } catch (error) {
119
+ reportError(error);
120
+ }
121
+ document.dispatchEvent(
122
+ new CustomEvent("pageturn:stayed", {
123
+ detail: { turn: playing.turn, stay }
124
+ })
125
+ );
126
+ }
127
+ function pageHidden() {
128
+ clearTimeout(stuckTimer), stopFollowing();
129
+ let { e: report } = registry;
130
+ if (!report && !window.pageturn?.arrival) return;
131
+ let data = null;
132
+ try {
133
+ data = report?.({ decline: () => registry.f }) ?? null;
134
+ } catch (error) {
135
+ reportError(error);
136
+ }
137
+ let isNone = data === registry.f;
138
+ store.p({
139
+ from: location.pathname,
140
+ at: Date.now(),
141
+ data: isNone ? null : data,
142
+ ...isNone ? { none: !0 } : {}
143
+ });
144
+ }
145
+ function pageShown(event) {
146
+ if (!event.persisted) return;
147
+ registry.a?.c(void 0);
148
+ let playing = current, published = window.pageturn;
149
+ if (!published) return;
150
+ let entry = playing ? playing.entry ?? null : store.r(), arrival2 = store.s(entry, store.o());
151
+ published.arrival = arrival2, arrival2 && (published.t?.(arrival2), published.k?.skipTransition(), followArrival()), playing && endWithoutDeparture(playing, "returned"), arrival2 && tellArrival(arrival2);
152
+ }
153
+
154
+ // src/kernel/core.ts
155
+ function defineMove(name, move) {
156
+ let stored = move;
157
+ registry.b.set(name, stored);
158
+ let reached = inBrowser ? window.pageturn?.arrival : null;
159
+ return reached?.move === name && stored.arrive && queueMicrotask(() => {
160
+ registry.b.get(name) === stored && tellArrival(reached);
161
+ }), () => {
162
+ registry.b.get(name) === stored && registry.b.delete(name);
163
+ };
164
+ }
165
+ function chooseMove(chooser) {
166
+ let stored = chooser;
167
+ return registry.d.push(stored), () => {
168
+ let index = registry.d.indexOf(stored);
169
+ index >= 0 && registry.d.splice(index, 1);
170
+ };
171
+ }
172
+ function arrival() {
173
+ return inBrowser && window.pageturn?.arrival || null;
174
+ }
175
+ function endArrival(move) {
176
+ inBrowser && window.pageturn?.endArrival(move);
177
+ }
178
+ function whenLeft(report) {
179
+ let stored = report;
180
+ return registry.e = stored, () => {
181
+ registry.e === stored && (registry.e = null);
182
+ };
183
+ }
184
+ function configure(settings) {
185
+ for (let [key, value] of Object.entries(settings))
186
+ value !== void 0 && Object.assign(registry.settings, { [key]: value });
187
+ }
188
+ install();
189
+
190
+ export {
191
+ defineMove,
192
+ chooseMove,
193
+ arrival,
194
+ endArrival,
195
+ whenLeft,
196
+ configure
197
+ };
@@ -0,0 +1,33 @@
1
+ // src/common/page.ts
2
+ var inBrowser = typeof document < "u", navigationEntry = () => performance.getEntriesByType("navigation")[0];
3
+ function afterDomContentLoaded(run) {
4
+ let entry = navigationEntry();
5
+ (entry ? entry.domContentLoadedEventStart > 0 : document.readyState !== "loading") ? run() : document.addEventListener("DOMContentLoaded", run, { once: !0 });
6
+ }
7
+ var reduced;
8
+ function reducesMotion() {
9
+ return reduced ??= matchMedia("(prefers-reduced-motion: reduce)"), reduced.matches;
10
+ }
11
+ function notShownAnew() {
12
+ window.pageturn && (window.pageturn.l = !0), delete document.documentElement.dataset.turnEntering;
13
+ }
14
+ var FOCUS_BACK = /* @__PURE__ */ Symbol.for("pageturn.focusBack"), shared = globalThis;
15
+ function giveFocusTo(element, options) {
16
+ shared[FOCUS_BACK] = element;
17
+ try {
18
+ element.focus(options);
19
+ } finally {
20
+ delete shared[FOCUS_BACK];
21
+ }
22
+ }
23
+ var isFocusGivenBack = (element) => shared[FOCUS_BACK] === element;
24
+
25
+ export {
26
+ inBrowser,
27
+ navigationEntry,
28
+ afterDomContentLoaded,
29
+ reducesMotion,
30
+ notShownAnew,
31
+ giveFocusTo,
32
+ isFocusGivenBack
33
+ };