@llui/vike 0.0.15 → 0.0.17

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 CHANGED
@@ -57,6 +57,168 @@ export const onRenderClient = createOnRenderClient({
57
57
  })
58
58
  ```
59
59
 
60
+ ### Persistent Layouts
61
+
62
+ Declare app chrome (header, sidebar, dialogs, session state) as a `Layout` component that stays mounted across client navigation. The route-scoped `Page` swaps in and out at the layout's `pageSlot()` position while the surrounding layout subtree — and every DOM node, focus trap, portal, and effect subscription inside it — is untouched.
63
+
64
+ > **Do not name your layout file `+Layout.ts`.** Vike reserves the `+` prefix for its own framework-adapter config conventions, and `+Layout.ts` specifically is interpreted by `vike-react` / `vike-vue` / `vike-solid` as a framework-native layout config. `@llui/vike` isn't a framework adapter in that sense — it's a render adapter, and `createOnRenderClient({ Layout })` consumes the layout component directly. Name your file `Layout.ts`, `app-layout.ts`, or place it anywhere outside `/pages` that Vike won't scan, then import it from `+onRenderClient.ts` / `+onRenderHtml.ts` by path.
65
+
66
+ ```ts
67
+ // pages/Layout.ts ← not +Layout.ts
68
+ import { component, div, header, main } from '@llui/dom'
69
+ import { pageSlot } from '@llui/vike/client'
70
+
71
+ export const AppLayout = component<LayoutState, LayoutMsg>({
72
+ name: 'AppLayout',
73
+ init: () => [{ session: null }, []],
74
+ update: layoutUpdate,
75
+ view: ({ send }) => [
76
+ div({ class: 'app-shell' }, [
77
+ header([
78
+ /* persistent chrome */
79
+ ]),
80
+ main([pageSlot()]), // ← where the route's Page renders
81
+ ]),
82
+ ],
83
+ })
84
+ ```
85
+
86
+ ```ts
87
+ // pages/+onRenderClient.ts
88
+ import { createOnRenderClient } from '@llui/vike/client'
89
+ import { AppLayout } from './Layout'
90
+
91
+ export const onRenderClient = createOnRenderClient({
92
+ Layout: AppLayout,
93
+ })
94
+ ```
95
+
96
+ ```ts
97
+ // pages/+onRenderHtml.ts — server renders layout + page as one tree
98
+ import { createOnRenderHtml } from '@llui/vike/server'
99
+ import { AppLayout } from './Layout'
100
+
101
+ export const onRenderHtml = createOnRenderHtml({
102
+ Layout: AppLayout,
103
+ })
104
+ ```
105
+
106
+ Call `pageSlot()` exactly once in each layout's view, at the position where nested content should render. It's an ordinary structural primitive — composes naturally inside `show()`, `branch()`, `provide()`, and any other view tree.
107
+
108
+ #### Nested layouts
109
+
110
+ Pass an array to stack layouts outer-to-inner. Each layout except the innermost calls its own `pageSlot()`. The innermost layer is always the route's `Page`.
111
+
112
+ ```ts
113
+ createOnRenderClient({
114
+ Layout: [AppLayout, DashboardLayout],
115
+ })
116
+ ```
117
+
118
+ For per-route chains — e.g. `/dashboard/*` routes use `[AppLayout, DashboardLayout]` while `/settings` uses `[AppLayout]` — pass a resolver function instead:
119
+
120
+ ```ts
121
+ createOnRenderClient({
122
+ Layout: (pageContext) =>
123
+ pageContext.urlPathname.startsWith('/dashboard') ? [AppLayout, DashboardLayout] : [AppLayout],
124
+ })
125
+ ```
126
+
127
+ The chain diff on each nav walks old and new chains in parallel and finds the first mismatch. Every layer before that mismatch stays mounted; every layer at or after it is torn down innermost-first and re-mounted outermost-first. Navigating from `/dashboard/reports` to `/dashboard/overview` only disposes the `Page` — `AppLayout` and `DashboardLayout` stay alive. Navigating to `/settings` disposes `DashboardLayout` and the `Page`, keeping only `AppLayout`.
128
+
129
+ #### Layout ↔ Page communication
130
+
131
+ Layouts and pages are independent component instances with their own state, update, and `send`. They share state and expose cross-cutting operations via **context**, not via direct messaging.
132
+
133
+ The scope-tree integration makes this natural: `pageSlot()` creates its slot as a child of the layout's render scope, and the page's `rootScope` is parented inside that slot. `useContext` from within the page walks up through the slot and finds any providers the layout installed above it.
134
+
135
+ Common pattern — a layout-owned toast system:
136
+
137
+ ```ts
138
+ // pages/Layout.ts
139
+ import { component, div, main, provide, createContext } from '@llui/dom'
140
+ import { pageSlot } from '@llui/vike/client'
141
+
142
+ interface ToastDispatchers {
143
+ show: (msg: string) => void
144
+ dismiss: (id: string) => void
145
+ }
146
+ export const ToastContext = createContext<ToastDispatchers>(undefined, 'Toast')
147
+
148
+ // Note: import { provideValue, useContextValue } from '@llui/dom' for
149
+ // the stable-dispatcher pattern below — they're the static-bag
150
+ // companions to the reactive provide / useContext primitives.
151
+
152
+ export const AppLayout = component<LayoutState, LayoutMsg>({
153
+ name: 'AppLayout',
154
+ init: () => [{ toasts: [] }, []],
155
+ update: layoutUpdate,
156
+ view: ({ send }) => [
157
+ div({ class: 'app-shell' }, [
158
+ ToastStack(), // reads from layout state
159
+ ...provideValue(
160
+ ToastContext,
161
+ {
162
+ show: (msg) => send({ type: 'toast/show', msg }),
163
+ dismiss: (id) => send({ type: 'toast/dismiss', id }),
164
+ },
165
+ () => [main([pageSlot()])],
166
+ ),
167
+ ]),
168
+ ],
169
+ })
170
+ ```
171
+
172
+ ```ts
173
+ // Any page below the layout can now use the toast dispatcher.
174
+ // pages/studio/+Page.ts
175
+ import { component, button, text, useContextValue } from '@llui/dom'
176
+ import { ToastContext } from '../Layout'
177
+
178
+ export const StudioPage = component<StudioState, StudioMsg>({
179
+ name: 'StudioPage',
180
+ init: () => [{ saved: false }, []],
181
+ update: (s, m) => {
182
+ if (m.type === 'saveSucceeded') {
183
+ // ...
184
+ }
185
+ return [s, []]
186
+ },
187
+ view: ({ send }) => {
188
+ const toast = useContextValue(ToastContext)
189
+ return [button({ onClick: () => toast.show('Saved') }, [text('Save')])]
190
+ },
191
+ })
192
+ ```
193
+
194
+ `provideValue` and `useContextValue` are companions to the reactive `provide` / `useContext` for the common case of publishing a stable dispatcher bag — anything that doesn't depend on the parent's state. Use them for toast queues, session managers, breadcrumb dispatchers, and any other pattern where a page calls into layout-owned operations through a closure-captured `send`. The reactive `provide(ctx, accessor, children)` and `useContext(ctx)` forms still exist for context values that DO depend on state (e.g. `provide(ThemeContext, (s) => s.theme, () => [...])`).
195
+
196
+ Toast state machines, global progress indicators, breadcrumb/title bars, modal-takeover chrome toggles, and session-expired banners all fall out of this pattern naturally — the layout owns the state, provides a dispatcher via context, and any page can trigger layout operations without touching the layout's internals.
197
+
198
+ For the rarer case where a layout needs to **probe a page** (e.g. "is your form dirty? can we navigate away?"), use **addressed effects** — the page registers an address on mount, the layout dispatches a targeted effect to it.
199
+
200
+ #### Layout data
201
+
202
+ Layouts can have their own server-fetched data alongside per-page `+data.ts` by populating `pageContext.lluiLayoutData` as an array matching the layout chain (outermost first). Each layout's `init(layoutData)` receives its slice.
203
+
204
+ Wire this from Vike's config mechanism however you like — the adapter just reads `pageContext.lluiLayoutData` when present.
205
+
206
+ #### Hydration envelope
207
+
208
+ With a `Layout` configured, `window.__LLUI_STATE__` is chain-aware:
209
+
210
+ ```js
211
+ window.__LLUI_STATE__ = {
212
+ layouts: [
213
+ { name: 'AppLayout', state: { session: 'alice' } },
214
+ { name: 'DashboardLayout', state: { active: 'reports' } },
215
+ ],
216
+ page: { name: 'ReportsPage', state: { view: 'summary' } },
217
+ }
218
+ ```
219
+
220
+ The client matches each layer by component `name` when hydrating — server/client chain mismatches throw with a clear error instead of silently binding the wrong state to the wrong instance. Pages written against the pre-layout flat envelope shape continue to hydrate correctly when no `Layout` is configured.
221
+
60
222
  ### Page Transitions
61
223
 
62
224
  `createOnRenderClient` accepts `onLeave` and `onEnter` hooks that fire around the dispose-and-remount cycle on client navigation. `onLeave` is awaited — return a promise to defer the swap until a leave animation finishes:
@@ -110,12 +272,13 @@ Hydrates the server-rendered HTML on the client. Attaches event listeners and re
110
272
 
111
273
  ## API
112
274
 
113
- | Export | Sub-path | Description |
114
- | ---------------------- | ------------------- | ---------------------------------------------------------------- |
115
- | `onRenderHtml` | `@llui/vike/server` | Default server hook — minimal HTML template |
116
- | `createOnRenderHtml` | `@llui/vike/server` | Factory for custom document templates |
117
- | `onRenderClient` | `@llui/vike/client` | Default client hook — hydrate or mount |
118
- | `createOnRenderClient` | `@llui/vike/client` | Factory for custom container + `onLeave` / `onEnter` / `onMount` |
119
- | `fromTransition` | `@llui/vike/client` | Adapter: `TransitionOptions` `{ onLeave, onEnter }` hook pair |
275
+ | Export | Sub-path | Description |
276
+ | ---------------------- | ------------------- | --------------------------------------------------------------- |
277
+ | `onRenderHtml` | `@llui/vike/server` | Default server hook — minimal HTML template |
278
+ | `createOnRenderHtml` | `@llui/vike/server` | Factory for custom document templates + persistent layouts |
279
+ | `onRenderClient` | `@llui/vike/client` | Default client hook — hydrate or mount |
280
+ | `createOnRenderClient` | `@llui/vike/client` | Factory for custom container + layouts + transition hooks |
281
+ | `pageSlot` | `@llui/vike/client` | Structural primitive declares where a layout renders its page |
282
+ | `fromTransition` | `@llui/vike/client` | Adapter: `TransitionOptions` → `{ onLeave, onEnter }` hook pair |
120
283
 
121
284
  The barrel export (`@llui/vike`) re-exports everything, but prefer sub-path imports to avoid bundling jsdom into the client.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { onRenderHtml, createOnRenderHtml } from './on-render-html.js';
2
- export type { PageContext, DocumentContext, RenderHtmlResult } from './on-render-html.js';
2
+ export type { PageContext, DocumentContext, RenderHtmlResult, RenderHtmlOptions, } from './on-render-html.js';
3
3
  export { onRenderClient, createOnRenderClient, fromTransition } from './on-render-client.js';
4
4
  export type { ClientPageContext, RenderClientOptions } from './on-render-client.js';
5
+ export { pageSlot } from './page-slot.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACtE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEzF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC5F,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACtE,YAAY,EACV,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC5F,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAEnF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { onRenderHtml, createOnRenderHtml } from './on-render-html.js';
2
2
  export { onRenderClient, createOnRenderClient, fromTransition } from './on-render-client.js';
3
+ export { pageSlot } from './page-slot.js';
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAGtE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA","sourcesContent":["export { onRenderHtml, createOnRenderHtml } from './on-render-html.js'\nexport type { PageContext, DocumentContext, RenderHtmlResult } from './on-render-html.js'\n\nexport { onRenderClient, createOnRenderClient, fromTransition } from './on-render-client.js'\nexport type { ClientPageContext, RenderClientOptions } from './on-render-client.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAQtE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAG5F,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["export { onRenderHtml, createOnRenderHtml } from './on-render-html.js'\nexport type {\n PageContext,\n DocumentContext,\n RenderHtmlResult,\n RenderHtmlOptions,\n} from './on-render-html.js'\n\nexport { onRenderClient, createOnRenderClient, fromTransition } from './on-render-client.js'\nexport type { ClientPageContext, RenderClientOptions } from './on-render-client.js'\n\nexport { pageSlot } from './page-slot.js'\n"]}
@@ -1,124 +1,178 @@
1
- import type { ComponentDef, TransitionOptions } from '@llui/dom';
1
+ import type { AnyComponentDef, TransitionOptions } from '@llui/dom';
2
+ export { pageSlot } from './page-slot.js';
2
3
  declare global {
3
4
  interface Window {
4
5
  __LLUI_STATE__?: unknown;
5
6
  }
6
7
  }
8
+ /**
9
+ * Page context shape as seen by `@llui/vike`'s client-side hooks. The
10
+ * `Page` and `data` fields come from whichever `+Page.ts` and `+data.ts`
11
+ * Vike resolved for the current route.
12
+ *
13
+ * `lluiLayoutData` is optional and carries per-layer data for the layout
14
+ * chain configured via `createOnRenderClient({ Layout })`. It's indexed
15
+ * outermost-to-innermost, one entry per layout layer. Absent entries
16
+ * mean the corresponding layout's `init()` receives `undefined`. Users
17
+ * wire this from their Vike `+data.ts` files by merging layout-owned
18
+ * data under the `lluiLayoutData` key.
19
+ */
7
20
  export interface ClientPageContext {
8
- Page: ComponentDef<unknown, unknown, unknown, unknown>;
21
+ Page: AnyComponentDef;
9
22
  data?: unknown;
23
+ lluiLayoutData?: readonly unknown[];
10
24
  isHydration?: boolean;
11
25
  }
26
+ type LayoutChain = ReadonlyArray<AnyComponentDef>;
12
27
  /**
13
- * Page-lifecycle hooks that fire around the dispose → clear mount
14
- * sequence on client navigation. Use these to animate page transitions,
15
- * save scroll state, emit analytics events, or defer the swap behind
16
- * any async work that must complete before the next page appears.
28
+ * Page-lifecycle hooks that fire around the dispose → mount cycle on
29
+ * client navigation. With persistent layouts in play the cycle only
30
+ * tears down the *divergent* suffix of the layout chain any layers
31
+ * shared between the old and new routes stay mounted.
17
32
  *
18
- * The sequence is:
33
+ * Navigation sequence for an already-mounted app:
19
34
  *
20
35
  * ```
21
36
  * client nav triggered
22
37
  * │
23
38
  * ▼
24
- * onLeave(el) ← awaited if it returns a promise
25
- * │ (the outgoing page's DOM is still mounted here)
39
+ * compare old chain to new chain → find first mismatch index K
40
+ * │
26
41
  * ▼
27
- * currentHandle.dispose()
28
- * │ (all scopes torn down portals, focus traps,
29
- * │ onMount cleanups all fire synchronously here)
42
+ * onLeave(leaveTarget) ← awaited; leaveTarget is the slot element
43
+ * │ at depth K-1 (or the root container if K=0)
44
+ * │ whose contents are about to be replaced
30
45
  * ▼
31
- * el.textContent = ''
32
- * │ (old DOM removed)
46
+ * dispose chainHandles[K..end] innermost first
47
+ * │
33
48
  * ▼
34
- * mountApp(el, Page, data)
35
- * │ (new page mounted)
49
+ * leaveTarget.textContent = ''
50
+ * │
51
+ * ▼
52
+ * mount newChain[K..end] into leaveTarget, outermost first
53
+ * │
36
54
  * ▼
37
- * onEnter(el) ← not awaited; animate in-place
55
+ * onEnter(leaveTarget) ← fire-and-forget; fresh DOM in place
38
56
  * │
39
57
  * ▼
40
- * onMount() ← legacy shim, still fires last
58
+ * onMount()
41
59
  * ```
42
60
  *
43
- * On the initial render (hydration), `onLeave` and `onEnter` are NOT
61
+ * On the initial hydration render, `onLeave` and `onEnter` are NOT
44
62
  * called — there's no outgoing page to leave and no animation to enter.
45
- * If you need to run code after hydration, use `onMount`.
63
+ * Use `onMount` for code that should run on every render including the
64
+ * initial one.
46
65
  */
47
66
  export interface RenderClientOptions {
48
- /** CSS selector for the mount container. Default: '#app' */
67
+ /** CSS selector for the mount container. Default: `'#app'`. */
49
68
  container?: string;
50
69
  /**
51
- * Called on the outgoing page's container BEFORE dispose + clear + mount.
52
- * Return a promise to defer the swap until the leave animation finishes.
53
- * The container element is passed as the argument — its children are
54
- * still the previous page's DOM at this point.
70
+ * Persistent layout chain. One of:
71
+ *
72
+ * - A single `ComponentDef` becomes a one-layout chain.
73
+ * - An array of `ComponentDef`s outermost layout first, innermost
74
+ * layout last. Every layer except the innermost must call
75
+ * `pageSlot()` in its view to declare where nested content renders.
76
+ * - A function that returns a chain from the current `pageContext` —
77
+ * lets different routes use different chains, e.g. by reading
78
+ * Vike's `pageContext.urlPathname` or `pageContext.config.Layout`.
79
+ *
80
+ * Layers that are shared between the previous and next navigation
81
+ * stay mounted. Only the divergent suffix is disposed and re-mounted.
82
+ * Dialogs, focus traps, and effect subscriptions rooted in a surviving
83
+ * layer are unaffected by the nav.
84
+ */
85
+ Layout?: AnyComponentDef | LayoutChain | ((pageContext: ClientPageContext) => LayoutChain);
86
+ /**
87
+ * Called on the slot element whose contents are about to be replaced,
88
+ * BEFORE the divergent suffix is disposed and re-mounted. The slot's
89
+ * current DOM is still attached when this runs — the only moment a
90
+ * leave animation can read/write it. Return a promise to defer the
91
+ * swap until the animation completes.
55
92
  *
93
+ * For a plain no-layout setup, the slot element is the root container.
56
94
  * Not called on the initial hydration render.
57
95
  */
58
96
  onLeave?: (el: HTMLElement) => void | Promise<void>;
59
97
  /**
60
- * Called after the new page is mounted into the container. Use this to
61
- * kick off an enter animation on the freshly-rendered content. Not
62
- * awaitedif you return a promise, the resolution is ignored.
98
+ * Called after the new divergent suffix is mounted, on the same slot
99
+ * element that was passed to `onLeave`. Use this to kick off an enter
100
+ * animation. Fire-and-forget — promise returns are ignored.
63
101
  *
64
102
  * Not called on the initial hydration render.
65
103
  */
66
104
  onEnter?: (el: HTMLElement) => void;
67
105
  /**
68
106
  * Called after mount or hydration completes. Fires on every render
69
- * including the initial hydration. Use this for per-render side
70
- * effects that don't fit the animation hooks (analytics, focus
71
- * management, etc.).
107
+ * including the initial hydration. Use for per-render side effects
108
+ * that don't fit the animation hooks.
72
109
  */
73
110
  onMount?: () => void;
74
111
  }
75
112
  /**
76
113
  * Adapt a `TransitionOptions` object (e.g. the output of
77
- * `routeTransition()` from `@llui/transitions`, or any preset like
78
- * `fade()` / `slide()`) into the `onLeave` / `onEnter` shape expected
79
- * by `createOnRenderClient`.
114
+ * `routeTransition()` from `@llui/transitions`, or a preset like `fade`
115
+ * / `slide`) into the `onLeave` / `onEnter` pair expected by
116
+ * `createOnRenderClient`.
80
117
  *
81
- * ```typescript
118
+ * ```ts
82
119
  * import { createOnRenderClient, fromTransition } from '@llui/vike/client'
83
120
  * import { routeTransition } from '@llui/transitions'
84
121
  *
85
122
  * export const onRenderClient = createOnRenderClient({
123
+ * Layout: AppLayout,
86
124
  * ...fromTransition(routeTransition({ duration: 200 })),
87
125
  * })
88
126
  * ```
89
127
  *
90
- * The transition operates on the container element itself its
91
- * opacity / transform fades out the outgoing page, then the new page
92
- * fades in when it mounts. If the preset doesn't restore its starting
93
- * style on `leave`, the container may still carry leftover properties
94
- * when the new page mounts; use `enter` to reset them explicitly or
95
- * pick presets that self-clean.
128
+ * The transition operates on the slot element — in a no-layout setup,
129
+ * the root container; in a layout setup, the innermost surviving
130
+ * layer's `pageSlot()` element. Opacity / transform fades apply to the
131
+ * outgoing page content, then the new page fades in.
96
132
  */
97
133
  export declare function fromTransition(t: TransitionOptions): Pick<RenderClientOptions, 'onLeave' | 'onEnter'>;
98
134
  /**
99
- * @internal — test helper. Disposes the current handle (if any) and clears
100
- * the module-level state so subsequent calls behave as a first mount.
101
- * Not part of the public API; subject to change without notice.
135
+ * @internal — test helper. Disposes every layer in the current chain
136
+ * and clears the module state so subsequent calls behave as a first
137
+ * mount. Not part of the public API; subject to change without notice.
138
+ */
139
+ export declare function _resetChainForTest(): void;
140
+ /**
141
+ * Back-compat alias for the pre-layout test helper name.
142
+ * @internal
143
+ * @deprecated — use `_resetChainForTest` instead.
102
144
  */
103
145
  export declare function _resetCurrentHandleForTest(): void;
104
146
  /**
105
- * Default onRenderClient hook — no animation hooks. Hydrates if
106
- * `isHydration` is true, otherwise mounts fresh. Use `createOnRenderClient`
147
+ * Default onRenderClient hook — no layout, no animation hooks. Hydrates
148
+ * on first load, mounts fresh on subsequent navs. Use `createOnRenderClient`
107
149
  * for the customizable factory form.
108
150
  */
109
151
  export declare function onRenderClient(pageContext: ClientPageContext): Promise<void>;
110
152
  /**
111
- * Factory to create a customized onRenderClient hook.
153
+ * Factory to create a customized onRenderClient hook. See `RenderClientOptions`
154
+ * for the full option surface — this is the entry point for persistent
155
+ * layouts, route transitions, and lifecycle hooks.
112
156
  *
113
- * ```typescript
157
+ * **Do not name your layout file `+Layout.ts`.** Vike reserves the `+`
158
+ * prefix for its own framework config conventions, and `+Layout.ts` is
159
+ * interpreted by `vike-react` / `vike-vue` / `vike-solid` framework
160
+ * adapters as a native layout config. `@llui/vike` isn't a framework
161
+ * adapter in that sense — it's a render adapter, and `createOnRenderClient`
162
+ * consumes the layout component directly via the `Layout` option. Name
163
+ * the file `Layout.ts`, `app-layout.ts`, or anywhere outside `/pages`
164
+ * that Vike won't scan, and import it here by path.
165
+ *
166
+ * ```ts
114
167
  * // pages/+onRenderClient.ts
115
- * import { createOnRenderClient } from '@llui/vike/client'
168
+ * import { createOnRenderClient, fromTransition } from '@llui/vike/client'
169
+ * import { routeTransition } from '@llui/transitions'
170
+ * import { AppLayout } from './Layout' // ← NOT './+Layout'
116
171
  *
117
172
  * export const onRenderClient = createOnRenderClient({
118
- * container: '#root',
119
- * onLeave: (el) => el.animate({ opacity: [1, 0] }, 200).finished,
120
- * onEnter: (el) => el.animate({ opacity: [0, 1] }, 200),
121
- * onMount: () => console.log('Page ready'),
173
+ * Layout: AppLayout,
174
+ * ...fromTransition(routeTransition({ duration: 200 })),
175
+ * onMount: () => console.log('page rendered'),
122
176
  * })
123
177
  * ```
124
178
  */
@@ -1 +1 @@
1
- {"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAa,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAE3E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,CAAC,EAAE,OAAO,CAAA;KACzB;CACF;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACtD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEnD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;IAEnC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,iBAAiB,GACnB,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,SAAS,CAAC,CAgBlD;AAMD;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAKjD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,GAC3B,CAAC,WAAW,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAEnD"}
1
+ {"version":3,"file":"on-render-client.d.ts","sourceRoot":"","sources":["../src/on-render-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAa,iBAAiB,EAAS,MAAM,WAAW,CAAA;AAKrF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,cAAc,CAAC,EAAE,OAAO,CAAA;KACzB;CACF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,cAAc,CAAC,EAAE,SAAS,OAAO,EAAE,CAAA;IACnC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,KAAK,WAAW,GAAG,aAAa,CAAC,eAAe,CAAC,CAAA;AAoBjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,EAAE,eAAe,GAAG,WAAW,GAAG,CAAC,CAAC,WAAW,EAAE,iBAAiB,KAAK,WAAW,CAAC,CAAA;IAE1F;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEnD;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAA;IAEnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,iBAAiB,GACnB,IAAI,CAAC,mBAAmB,EAAE,SAAS,GAAG,SAAS,CAAC,CAgBlD;AA2BD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAOzC;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,mBAAmB,GAC3B,CAAC,WAAW,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAEnD"}