caspian-utils 0.0.23 → 0.0.25

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.
@@ -53,6 +53,8 @@ Use prompts like these to check whether AI lands on the correct docs and files.
53
53
  | Prompt | First docs AI should read | Controlling files AI should reach | Verification focus |
54
54
  | --- | --- | --- | --- |
55
55
  | Create a protected dashboard section with child routes and a shared shell. | [index.md](./index.md), [routing.md](./routing.md), [auth.md](./auth.md), [project-structure.md](./project-structure.md) | `src/app/**`, `src/lib/auth/auth_config.py`, `main.py`, `.venv/Lib/site-packages/casp/layout.py` | section layout ownership, route privacy mode, and child-route wrapping |
56
+ | Make a grouped shell keep sidebar scroll while resetting page content on child-route navigation. | [index.md](./index.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md), [core-runtime-map.md](./core-runtime-map.md) | `src/app/**/layout.html`, `public/js/pp-reactive-v2.js`, `main.py` | `pp-reset-scroll` placement, push-vs-history scroll behavior, and shared-shell ownership |
57
+ | Create a new contact page with interactive form behavior. | [index.md](./index.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md) | `src/app/**/index.html`, `main.py`, `.venv/Lib/site-packages/casp/components_compiler.py`, `.venv/Lib/site-packages/casp/scripts_type.py` | single-root template shape, script-inside-root authoring, and authored-vs-runtime boundaries |
56
58
  | Add a file manager page with upload progress and persisted metadata. | [index.md](./index.md), [file-uploads.md](./file-uploads.md), [fetch-data.md](./fetch-data.md), [database.md](./database.md) | `src/app/**/index.html`, `src/app/**/index.py`, `src/lib/**`, `prisma/schema.prisma` when Prisma applies | route-owned upload actions, persisted metadata flow, and Prisma-backed storage boundaries |
57
59
  | Explain why authored Caspian templates use a plain `<script>` instead of `type="text/pp"`. | [index.md](./index.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md), [routing.md](./routing.md) | `main.py`, `.venv/Lib/site-packages/casp/scripts_type.py`, `.venv/Lib/site-packages/casp/components_compiler.py` | script rewriting, `pp-component` injection, and authored-vs-runtime boundaries |
58
60
  | Debug why `StateManager` does not persist across a full redirect. | [index.md](./index.md), [state.md](./state.md), [auth.md](./auth.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/state_manager.py` | wire vs non-wire reset behavior and `request.state.session` dependency |
@@ -68,6 +70,8 @@ Treat the table as a prompt pack for spot checks, not as a full validation matri
68
70
  - AI reads only the packaged feature doc and never checks `main.py` or the installed runtime.
69
71
  - AI edits framework internals when the task only requires app-owned route or helper changes.
70
72
  - AI treats runtime HTML examples as authored template examples.
73
+ - AI generates a route or component template with a valid-looking root element but leaves a sibling top-level `<script>` or second top-level element after it.
74
+ - AI puts `pp-reset-scroll="true"` on the whole shell or `body` when only the page-content pane should reset, causing persistent sidebars or rails to lose their scroll position.
71
75
  - AI decides behavior from memory without checking the owning implementation details.
72
76
 
73
77
  ## Decision Rule
@@ -241,10 +241,17 @@ Top-of-file `<!-- @import ... -->` directives are allowed before that root eleme
241
241
 
242
242
  In source, that parent may be a native HTML element or a single imported `x-*` component tag. After component expansion, the template must still resolve to one final native HTML root so Caspian has exactly one place to inject `pp-component`.
243
243
 
244
- This is not just style guidance. The installed compiler injects `pp-component` onto the final root element, and it raises an error when the template has no root, multiple sibling roots, or stray top-level text.
244
+ This is not just style guidance. The installed compiler injects `pp-component` onto the final root element, and it raises an error when the template has no root, multiple sibling roots, a sibling script after the root, or stray top-level text.
245
245
 
246
246
  For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent node. If the template needs a PulsePoint script, keep that script inside the same parent root.
247
247
 
248
+ Failure shape to avoid:
249
+
250
+ - one root element followed by a sibling `<script>`
251
+ - two top-level sibling elements
252
+ - top-level text outside the root
253
+ - an imported `x-*` root that expands to multiple native siblings
254
+
248
255
  Good:
249
256
 
250
257
  ```html
@@ -276,6 +283,18 @@ Also bad:
276
283
  <p>Body</p>
277
284
  ```
278
285
 
286
+ Also bad:
287
+
288
+ ```html
289
+ <x-card>
290
+ <h2>Title</h2>
291
+ </x-card>
292
+
293
+ <script>
294
+ const [open, setOpen] = pp.state(false);
295
+ </script>
296
+ ```
297
+
279
298
  If you choose an imported `x-*` component as the authored root of the file, make sure it resolves to one native HTML root when compiled. Do not leave an unresolved component tag as the root, and do not emit multiple top-level component siblings.
280
299
 
281
300
  ## Props, Types, And Children
@@ -29,6 +29,7 @@ Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib
29
29
  | Concern | Core file | Read first | Why it matters |
30
30
  | --- | --- | --- | --- |
31
31
  | App bootstrap and request flow | [main.py](../../../../main.py) | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, static asset routes, middleware order, route registration, cache check and save, exception handlers, and final HTML transforms all live here. |
32
+ | Browser runtime, SPA navigation, and scroll restoration | [public/js/pp-reactive-v2.js](../../../../public/js/pp-reactive-v2.js) | [pulsepoint.md](./pulsepoint.md), [fetch-data.md](./fetch-data.md) | The shipped `pp` runtime, same-origin SPA interception, per-history-entry scroll state, `pp-reset-scroll` behavior, and browser RPC helpers live here. |
32
33
 
33
34
  Important current `main.py` behaviors AI should keep in mind:
34
35
 
@@ -37,6 +38,7 @@ Important current `main.py` behaviors AI should keep in mind:
37
38
  - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
38
39
  - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
39
40
  - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, so the effective request order is reversed at runtime.
41
+ - `public/js/pp-reactive-v2.js` saves scroll positions per history entry, resets window scroll on push navigation, and uses `pp-reset-scroll="true"` to opt specific containers or the whole body into reset behavior.
40
42
 
41
43
  ## Installed Runtime Map
42
44
 
@@ -65,6 +67,7 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
65
67
  | Runtime area | Verify these behaviors |
66
68
  | --- | --- |
67
69
  | `main.py` routing and request flow | route registration, path and query injection, static asset handling, and exception rendering |
70
+ | `public/js/pp-reactive-v2.js` browser runtime | SPA interception, history-vs-push scroll behavior, `pp-reset-scroll` semantics, and browser-side `pp.rpc()` behavior |
68
71
  | `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
69
72
  | `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
70
73
  | `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
@@ -28,6 +28,7 @@ Before making feature, tooling, or file-placement decisions in a Caspian project
28
28
  When generating or editing a Caspian app, treat these as the default choices unless the task explicitly requires something else:
29
29
 
30
30
  - Use PulsePoint for reactive frontend behavior.
31
+ - Treat every authored route, layout, and component HTML file like a React component return value: exactly one top-level parent HTML element or one imported `x-*` root, with any owned plain `<script>` kept inside that same root.
31
32
  - When `caspian.config.json` has `tailwindcss: true`, use Python `merge_classes(...)` plus browser `twMerge(...)` as the only supported Tailwind class-merging path.
32
33
  - Use `@rpc()` plus `pp.rpc()` for browser-triggered reads, writes, streaming, and uploads.
33
34
  - Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
@@ -49,14 +50,14 @@ The packaged Caspian docs referenced by this index live here:
49
50
  - `database.md` - Prisma schema, migration, seed, and client-generation workflow for projects where `caspian.config.json` enables Prisma, plus Python-side helper caveats
50
51
  - `auth.md` - Session-backed authentication with `casp.auth`, centralized `auth_config.py`, public-vs-private route mode guidance, RPC-first signout guidance, RBAC, and OAuth provider helpers
51
52
  - `components.md` - Create reusable Python components, template-backed UI, HTML-first `x-*` component tags, the single-parent authored-root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
52
- - `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, client-side behaviors, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
53
+ - `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, SPA navigation scroll restoration, `pp-reset-scroll`, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
53
54
  - `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
54
55
  - `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
55
56
  - `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
56
57
  - `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
57
58
  - `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
58
59
  - `metadata.md` - Static and dynamic metadata, SEO inheritance, and Open Graph or Twitter card tags
59
- - `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, dashboard and section layouts, route groups, nested layouts, single-parent authored templates, and backend Python companions that do not own visible markup
60
+ - `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, dashboard and section layouts, route groups, nested layouts, shared-shell scroll-reset ownership, single-parent authored templates, and backend Python companions that do not own visible markup
60
61
  - `project-structure.md` - Default Caspian layout and where route files, reusable UI in `src/components/`, reusable non-UI code in `src/lib/`, and database files belong
61
62
 
62
63
  ## AI Retrieval Notes
@@ -67,13 +68,14 @@ Preferred lookup order:
67
68
 
68
69
  1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
69
70
  2. Read `./caspian.config.json` before making any feature assumption. A doc existing in the package does not mean that feature is enabled in the current project.
70
- 3. Treat `caspian.config.json` as the single source of truth for optional features. Use feature-specific docs only when the matching flag is enabled. If a feature is disabled and the user wants it, ask first, then update `caspian.config.json` and follow the update workflow in `commands.md`.
71
- 4. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read `core-runtime-map.md` to jump to the controlling runtime file and the matching feature doc.
72
- 5. After the feature is confirmed, inspect the actual project files that decide behavior, such as `package.json`, `main.py`, `src/app/**`, `src/lib/**`, `settings/**`, `prisma/**`, and the installed `casp` runtime.
73
- 6. Use `commands.md` for scaffold and update workflows, `project-structure.md` for placement decisions, and the feature docs such as `mcp.md`, `database.md`, `auth.md`, `fetch-data.md`, and `file-uploads.md` for task-specific guidance.
74
- 7. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
75
- 8. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
76
- 9. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
71
+ 3. Before authoring or editing any `src/app/**` or component HTML template, apply the single-root invariant: one authored root only, any owned `<script>` inside that root, and no handwritten `pp-component` or `type="text/pp"`.
72
+ 4. Treat `caspian.config.json` as the single source of truth for optional features. Use feature-specific docs only when the matching flag is enabled. If a feature is disabled and the user wants it, ask first, then update `caspian.config.json` and follow the update workflow in `commands.md`.
73
+ 5. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read `core-runtime-map.md` to jump to the controlling runtime file and the matching feature doc.
74
+ 6. After the feature is confirmed, inspect the actual project files that decide behavior, such as `package.json`, `main.py`, `src/app/**`, `src/lib/**`, `settings/**`, `prisma/**`, and the installed `casp` runtime.
75
+ 7. Use `commands.md` for scaffold and update workflows, `project-structure.md` for placement decisions, and the feature docs such as `mcp.md`, `database.md`, `auth.md`, `fetch-data.md`, and `file-uploads.md` for task-specific guidance.
76
+ 8. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
77
+ 9. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
78
+ 10. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
77
79
 
78
80
  ## Maintenance
79
81
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: PulsePoint Runtime Guide
3
- description: Use this page when the task mentions PulsePoint, `pp.state`, `pp.effect`, `pp-ref`, `pp-style`, `pp-for`, portals, SPA navigation, or `public/js/pp-reactive-v2.js`.
3
+ description: Use this page when the task mentions PulsePoint, `pp.state`, `pp.effect`, `pp-ref`, `pp-style`, `pp-for`, portals, `pp-reset-scroll`, SPA navigation, or `public/js/pp-reactive-v2.js`.
4
4
  related:
5
5
  title: Related docs
6
6
  description: Read the components, routing, data-fetching, and project-structure docs alongside the PulsePoint runtime contract.
@@ -17,7 +17,7 @@ related:
17
17
 
18
18
  This file documents the PulsePoint contract for the shipped Caspian browser runtime. Treat it as the AI-facing source of truth when generating or reviewing interactive Caspian UI.
19
19
 
20
- If a task involves `pp.state`, `pp.effect`, `pp.layoutEffect`, `pp-ref`, `pp-style`, `pp-spread`, `pp-for`, context, portals, SPA navigation, or component boundary behavior, read this page first and keep generated code aligned with the current Caspian runtime.
20
+ If a task involves `pp.state`, `pp.effect`, `pp.layoutEffect`, `pp-ref`, `pp-style`, `pp-spread`, `pp-for`, context, portals, `pp-reset-scroll`, SPA navigation, or component boundary behavior, read this page first and keep generated code aligned with the current Caspian runtime.
21
21
 
22
22
  Use `components.md` for authoring Python `@component` files, same-name HTML templates, and HTML-first `x-*` component tags. Use this page for the browser-side PulsePoint contract, the authoring rules that feed it, and the React-style mental model used by the shipped runtime.
23
23
 
@@ -73,6 +73,8 @@ For authored Caspian templates:
73
73
  - Do not handwrite `pp-component="..."`.
74
74
  - Do not handwrite `type="text/pp"`.
75
75
 
76
+ Treat that single-root rule as a hard invariant for AI-generated templates. A sibling `<script>` after the root or any second top-level element will break Caspian's `pp-component` injection and fail the render.
77
+
76
78
  Keep visible route, layout, and component markup in the HTML templates. Treat `index.py` and `layout.py` as backend companions for data, metadata, props, RPC actions, auth, caching, redirects, and other server-side preparation, not as template bodies.
77
79
 
78
80
  Caspian already handles those details for you during render.
@@ -102,6 +104,18 @@ Authored example:
102
104
 
103
105
  When that template reaches the browser, Caspian will already have injected the component id and rewritten the owned script to `type="text/pp"`. Those runtime attributes are for the rendered output, not for authored source examples.
104
106
 
107
+ Invalid authored shape:
108
+
109
+ ```html
110
+ <section>
111
+ <h2>{title}</h2>
112
+ </section>
113
+
114
+ <script>
115
+ const { title = "Counter" } = pp.props;
116
+ </script>
117
+ ```
118
+
105
119
  ## Runtime shape
106
120
 
107
121
  PulsePoint is a browser-side component runtime. It executes one component script per root, renders HTML from the component scope, morphs the DOM in place, binds native event handlers, restores focus, updates refs, applies portals, and then runs layout/effect hooks.
@@ -387,7 +401,12 @@ Example:
387
401
  - `pp-loading-content="true"` marks the page region that gets swapped or faded during navigation.
388
402
  - Route-specific loading states are looked up with `pp-loading-url`.
389
403
  - `pp-loading-transition` accepts JSON with `fadeIn` and `fadeOut` timing values.
390
- - `pp-reset-scroll="true"` resets scroll positions during navigation.
404
+ - The runtime saves scroll positions per history entry while SPA navigation is enabled.
405
+ - Push navigation resets window scroll to the top.
406
+ - Saved history-entry scroll state is used during history traversal instead of relying only on the live DOM scroll at the time the user clicks Back or Forward.
407
+ - Unmarked scrollable containers may keep their outgoing scroll on push navigation so shared shells such as sidebars, rails, and docs nav panes stay stable across child-route changes.
408
+ - `pp-reset-scroll="true"` on a scroll container opts that container into reset-on-navigation behavior. Use it on the main content pane of a grouped shell when page content should start at the top on each child-route navigation.
409
+ - `body[pp-reset-scroll="true"]` is the global override for routes that should reset window scroll and every scrollable element.
391
410
  - Navigation dispatches `pp:navigation:start`, `pp:navigation:complete`, and `pp:navigation:error` events on `document`.
392
411
 
393
412
  RPC notes:
@@ -433,6 +452,7 @@ Use these rules when generating or editing PulsePoint runtime code:
433
452
  - If a development-only source tree exists behind the shipped runtime, treat it as optional implementation detail rather than something generated apps are guaranteed to contain.
434
453
  - In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
435
454
  - For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md), keep the shared interactive shell in the parent folder's `layout.html`, and keep route-specific PulsePoint code in each child `index.html`.
455
+ - For grouped shells with independent shell and content scrolling, put `pp-reset-scroll="true"` on the content pane rather than the whole shell when only the page content should reset between child-route navigations.
436
456
  - Prefer PulsePoint state and template directives over manual DOM mutation for reactive updates.
437
457
  - If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
438
458
  - In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
@@ -31,8 +31,21 @@ Start with these rules:
31
31
  - Add `index.py` only when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side logic.
32
32
  - Use a standalone `index.py` only for non-visual routes such as redirects or action-only handlers.
33
33
  - When a folder owns child routes, use `layout.html` to wrap them. This is the default pattern for dashboards, admin sections, account areas, settings trees, and route groups.
34
+ - In grouped shells with separate shell and content scrolling, put `pp-reset-scroll="true"` on the content pane in `layout.html` when that pane should reset on child-route navigation while the shell sidebar or rail keeps its own scroll.
34
35
  - Use `layout.py` when a layout needs shared synchronous props or metadata before rendering.
35
36
  - Keep visible route and layout markup in `index.html` and `layout.html`. Treat `index.py` and `layout.py` as backend companions, not as places to author visible HTML.
37
+ - Treat every authored route and layout template like a React component body: it must have exactly one top-level parent HTML element or one imported `x-*` root, and any owned plain `<script>` must live inside that same root.
38
+
39
+ ## Hard Template Invariant
40
+
41
+ For authored route and layout templates, the single-root rule is a hard runtime requirement.
42
+
43
+ - Use exactly one authored parent node.
44
+ - Keep any `<!-- @import ... -->` directives above that root.
45
+ - Keep any owned plain `<script>` inside that root, not after it.
46
+ - Do not leave sibling top-level HTML tags, sibling component tags, or stray top-level text.
47
+
48
+ If AI generates this incorrectly, Caspian fails render with errors like `must have exactly one top-level HTML element so Caspian can inject pp-component`.
36
49
 
37
50
  Use [cache.md](./cache.md) when an `index.py` route also needs declarative page caching via `Cache(...)`.
38
51
 
@@ -133,6 +146,8 @@ src/
133
146
 
134
147
  Use this pattern when the app has a public grouped section that should stay out of the URL and a private dashboard section that should appear in the URL. The root layout owns app-wide chrome, `(marketing)/layout.html` owns the shared public marketing shell, and `dashboard/layout.html` owns the shared dashboard shell for `/dashboard/*` child routes.
135
148
 
149
+ When a shared shell has its own scrollable sidebar or rail plus a separate page-content scroller, keep the persistent shell scrollers unmarked and put `pp-reset-scroll="true"` on the page-content scroller in `layout.html`. That gives grouped sections the common app-router behavior where the main pane resets on child-route navigation while the sidebar keeps its scroll position. Use `body[pp-reset-scroll="true"]` only when the target route should reset every scrollable surface.
150
+
136
151
  ## Core Concepts
137
152
 
138
153
  Every route lives inside `src/app`. For routes that render UI, `index.html` owns the markup and `index.py` is only an optional companion for server logic or metadata.
@@ -167,7 +182,7 @@ Place those import comments at the top of the file, above the authored root elem
167
182
 
168
183
  Route templates follow the same authored-vs-runtime contract documented in [pulsepoint.md](./pulsepoint.md) and the same single-root discipline documented in [components.md](./components.md): keep one authored parent node, keep any `<!-- @import ... -->` directives above that root, use a plain `<script>` inside that root when needed, and do not handwrite `pp-component` or `type="text/pp"`. That root may be a native HTML element or a single imported `x-*` component tag, but after expansion it must resolve to one final HTML root.
169
184
 
170
- For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent node that contains the entire route markup.
185
+ For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent node that contains the entire route markup. This includes any owned script.
171
186
 
172
187
  Good:
173
188
 
@@ -195,6 +210,16 @@ Bad:
195
210
 
196
211
  Also bad:
197
212
 
213
+ ```html
214
+ <section class="dashboard-shell">
215
+ <h1>Dashboard</h1>
216
+ </section>
217
+
218
+ <aside class="dashboard-help">Tips</aside>
219
+ ```
220
+
221
+ Also bad:
222
+
198
223
  ```html
199
224
  <section class="dashboard-shell">
200
225
  <!-- @import StatsCard from "../components" -->
@@ -476,6 +501,7 @@ If an AI agent is choosing where to add or update route code, apply these rules
476
501
  - Use a normal folder such as `dashboard/` when the segment should appear in the URL. Use `(group)/` only when the folder should organize or wrap child routes without adding a path segment.
477
502
  - Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
478
503
  - Use `layout.html` for shared wrappers and `layout.py` for layout-level synchronous props or metadata.
504
+ - For grouped shells with separate shell and content scrolling, put `pp-reset-scroll="true"` on the content pane instead of the whole shell when only the page content should reset between child routes.
479
505
  - When one route needs to change a parent layout, return `(render_page(__file__, ...), {"dashboard_body_class": ...})` from `page()` and read that value as `[[ layout.dashboard_body_class ]]` in the wrapping `layout.html`.
480
506
  - Use `layout.py` for layout props that should apply across an entire subtree. Use `render_layout(__file__, {...})` only when the layout should consume direct local variables such as `[[ my_class ]]` instead of the standard `[[ layout.* ]]` namespace.
481
507
  - Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored parent node.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {