caspian-utils 0.0.4 → 0.0.6

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,7 +53,7 @@ Notes:
53
53
 
54
54
  ## Import And Use Components
55
55
 
56
- Import components inside the HTML template where they are used.
56
+ Place component imports at the top of the HTML template, above the authored root element.
57
57
 
58
58
  ```html
59
59
  <!-- @import Container from "../components" -->
@@ -65,10 +65,33 @@ Import components inside the HTML template where they are used.
65
65
 
66
66
  The import comment is the bridge between the Python component file and the JSX-style tag.
67
67
 
68
+ Treat `<!-- @import ... -->` as a file-level directive, not as rendered markup. It does not count as the template root, and it should not be nested inside the root wrapper element.
69
+
68
70
  - `<!-- @import Container from "../components" -->` resolves `Container.py` from that folder.
69
71
  - The component function name should match the tag name you render, such as `Container` for `<Container />`.
70
72
  - Grouped imports and aliases are also supported, for example `<!-- @import { Button, Card as UserCard } from "../components/ui" -->`.
71
73
 
74
+ Good:
75
+
76
+ ```html
77
+ <!-- @import { Button, Input, Label } from "../../../lib/maddex" -->
78
+
79
+ <section class="auth-panel auth-panel-compact fade-up">
80
+ <Label>Email</Label>
81
+ <Input type="email" />
82
+ <Button>Continue</Button>
83
+ </section>
84
+ ```
85
+
86
+ Bad:
87
+
88
+ ```html
89
+ <section class="auth-panel auth-panel-compact fade-up">
90
+ <!-- @import { Button, Input, Label } from "../../../lib/maddex" -->
91
+ <Label>Email</Label>
92
+ </section>
93
+ ```
94
+
72
95
  ## Template-Backed Components
73
96
 
74
97
  When a component includes richer UI or PulsePoint behavior, keep the Python file focused on props or server-side preparation and move the markup into a same-name HTML file.
@@ -161,6 +184,8 @@ Every component HTML template must render exactly one top-level lowercase HTML e
161
184
 
162
185
  The same rule applies to route templates such as `src/app/**/index.html`: one root element, no sibling roots, and no manual `pp-component` authoring.
163
186
 
187
+ Top-of-file `<!-- @import ... -->` directives are allowed before that root element and do not violate the single-root rule.
188
+
164
189
  This is not just style guidance. The installed compiler injects `pp-component` onto the root element, and it raises an error when the template has no root, multiple sibling roots, stray top-level text, or a component tag as the root.
165
190
 
166
191
  For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent element. If the template needs a PulsePoint script, keep that script inside the same parent root.
@@ -58,7 +58,7 @@ Preferred lookup order:
58
58
  1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
59
59
  2. Read `./caspian.config.json` before making any feature assumption. Use it to confirm which project capabilities are enabled and which directories or tooling rules apply.
60
60
  3. Read `database.md` for Prisma setup and ORM usage, `auth.md` for session auth and route protection, `components.md` for reusable UI authoring, `pulsepoint.md` for browser-side reactive runtime behavior, `fetch-data.md` for data flows, `state.md` for transient request-scoped state, `cache.md` for page caching, and `validation.md` for input boundaries.
61
- 4. For authored component, route, and layout HTML files, generate exactly one top-level lowercase HTML element. Think React-style single parent wrapper: keep any owned PulsePoint logic in a plain `<script>` inside that root, and do not handwrite `pp-component="..."` or `type="text/pp"` because Caspian injects those runtime attributes automatically.
61
+ 4. For authored component, route, and layout HTML files, generate exactly one top-level lowercase HTML element. Think React-style single parent wrapper: keep any owned PulsePoint logic in a plain `<script>` inside that root, do not handwrite `pp-component="..."` or `type="text/pp"` because Caspian injects those runtime attributes automatically, and treat `<!-- @import ... -->` comments as file-level directives that belong above the authored root element instead of inside `<div>`, `<section>`, or `<html>`.
62
62
  5. Prefer local docs before generating code, commands, or migration guidance.
63
63
  6. Check `node_modules/caspian-utils/dist/docs/` for packaged Caspian docs when local docs need more detail.
64
64
  7. Only fall back to upstream documentation when local and packaged markdown do not cover the topic.
@@ -161,6 +161,8 @@ The project auth configuration file. Use this path when changing authentication
161
161
 
162
162
  The root layout shared across pages.
163
163
 
164
+ If this layout imports reusable components, place each `<!-- @import ... -->` comment above the authored root `<html>` element instead of nesting the comment inside `<html>` or `<body>`.
165
+
164
166
  ### `src/app/index.py`
165
167
 
166
168
  The backend logic for the route. This is where route behavior can load first-render data, expose `@rpc()` actions, and import framework features such as auth helpers, `casp.validate`, and route-level `Cache(...)` declarations.
@@ -169,6 +171,8 @@ The backend logic for the route. This is where route behavior can load first-ren
169
171
 
170
172
  The route template. It supports standard HTML, `<!-- @import ... -->` component imports, and PulsePoint directives, and it should be the default place for reactive frontend behavior in Caspian.
171
173
 
174
+ Treat import comments as top-of-file directives that belong above the route's single authored root element.
175
+
172
176
  Use `components.md` when the task involves authoring reusable `<MyComponent />` tags backed by Python files.
173
177
 
174
178
  ### `src/app/globals.css`
@@ -198,6 +202,7 @@ If an AI agent is deciding where to make changes, use these rules first.
198
202
  - Put route templates and route-specific backend logic in `src/app/`.
199
203
  - Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
200
204
  - Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, import comments, and single-root template rules.
205
+ - Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files.
201
206
  - For route and component HTML files, always emit one top-level lowercase HTML element. Good: one wrapper containing the content and a plain `<script>` when needed. Bad: a wrapper element followed by a sibling top-level `<script>`, or handwritten `pp-component="..."` and `type="text/pp"` attributes in source.
202
207
  - Put shared helpers and reusable libraries in `src/lib/`.
203
208
  - Use PulsePoint conventions in route templates for reactive frontend behavior.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: PulsePoint Runtime Guide
3
- description: Learn how AI agents should use PulsePoint as the default reactive frontend contract in Caspian, including the bundled runtime loaded from `public/js/main.js` and implemented in `public/js/pp-reactive-v2.js`, component script rules, and supported directives.
3
+ description: Learn how AI agents should author PulsePoint against the shipped browser runtime in `public/js/pp-reactive-v2.js` and the Caspian render pipeline that injects runtime attributes automatically.
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.
@@ -14,16 +14,31 @@ related:
14
14
 
15
15
  ## Purpose
16
16
 
17
- This file documents the PulsePoint runtime that is currently shipped in `public/js/pp-reactive-v2.js` and loaded by `public/js/main.js`. Treat it as the working contract for AI-generated code.
17
+ This file documents the current PulsePoint contract for this workspace. Treat it as the AI-facing source of truth when generating or reviewing interactive Caspian UI.
18
18
 
19
19
  If a task involves `pp.state`, `pp.effect`, `pp.layoutEffect`, `pp-ref`, `pp-spread`, `pp-for`, context, portals, SPA navigation, or component boundary behavior, read this page first and keep generated code aligned with the runtime implemented in this repo.
20
20
 
21
- Use `components.md` for authoring Python `@component` files and same-name HTML templates. Use this page for the browser-side `pp-component` contract and `script[type="text/pp"]` behavior after those templates are rendered.
21
+ Use `components.md` for authoring Python `@component` files and same-name HTML templates. Use this page for the browser-side PulsePoint contract, the authoring rules that feed it, and the React-style mental model used by the current runtime.
22
22
 
23
- PulsePoint is the default reactive frontend layer for Caspian.
23
+ PulsePoint is the default reactive frontend layer for Caspian. In this workspace it follows a React-like component pattern, but it is HTML-first rather than JSX-first.
24
24
 
25
25
  Do not assume React, Vue, Svelte, JSX, Alpine, HTMX, or older PulsePoint docs unless the task explicitly asks for a different frontend contract.
26
26
 
27
+ ## Source Of Truth
28
+
29
+ For the current workspace, follow this order when documenting or generating PulsePoint code:
30
+
31
+ - `public/js/pp-reactive-v2.js` is the shipped browser runtime contract AI should follow.
32
+ - `main.py` is the render-pipeline source of truth for how Caspian injects runtime attributes and rewrites scripts before the browser sees the HTML.
33
+ - If you are working inside PulsePoint or Caspian runtime development code and there is an authoring source tree behind the shipped files, use it only as an implementation detail. Do not assume that source tree exists in generated apps or shipped framework output.
34
+
35
+ Important current facts:
36
+
37
+ - `public/js/pp-reactive-v2.js` exposes the global `pp` runtime and auto-mounts on DOM ready.
38
+ - `main.py` renders the final HTML, runs `transform_components(...)`, then runs `transform_scripts(...)` before returning the response.
39
+
40
+ If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
41
+
27
42
  ## Default Frontend Rule
28
43
 
29
44
  When a Caspian page needs reactive browser behavior, use PulsePoint.
@@ -32,14 +47,46 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
32
47
  - Keep server-rendered HTML plus PulsePoint enhancement as the baseline architecture.
33
48
  - Only introduce another frontend runtime when the user explicitly asks for it or the project already depends on one.
34
49
 
35
- ## Source layer vs raw runtime
50
+ ## Authoring Model
51
+
52
+ PulsePoint authoring in this repo is split into two layers:
53
+
54
+ - The authored layer: route, layout, and component templates under `src/` with plain HTML plus a plain `<script>`.
55
+ - The runtime layer: the browser sees `pp-component` roots and `script[type="text/pp"]` after Caspian transforms the HTML.
56
+
57
+ For authored Caspian templates:
58
+
59
+ - Keep exactly one top-level lowercase HTML root element.
60
+ - Put the component logic inside a plain `<script>` inside that same root.
61
+ - Do not handwrite `pp-component="..."`.
62
+ - Do not handwrite `type="text/pp"`.
63
+
64
+ Caspian already handles those details for you during render.
65
+
66
+ That means AI-generated examples should default to authored template source, not raw runtime HTML.
67
+
68
+ Authored example:
36
69
 
37
- - Source authoring under `src/` may use convenience syntax that is transformed before it reaches the browser.
38
- - This page describes the bundled browser runtime shipped in `public/js/pp-reactive-v2.js`.
39
- - When source-layer examples conflict with the bundled runtime, follow the bundled runtime.
40
- - In authored route, layout, and component templates, do not add `pp-component` manually. The Python side injects it onto the template root during render.
41
- - In authored templates, write PulsePoint logic in a plain `<script>` inside that root. `main.py` runs `transform_scripts(...)`, so the runtime receives `script[type="text/pp"]`.
42
- - When reading or debugging runtime HTML, look for `pp-component` roots and `script[type="text/pp"]`.
70
+ ```html
71
+ <section>
72
+ <h2>{title}</h2>
73
+ <p>Count: {count}</p>
74
+
75
+ <button onclick="setCount(count + 1)">Increment</button>
76
+ <button onclick="reset()">Reset</button>
77
+
78
+ <script>
79
+ const { title = "Counter" } = pp.props;
80
+ const [count, setCount] = pp.state(0);
81
+
82
+ function reset() {
83
+ setCount(0);
84
+ }
85
+ </script>
86
+ </section>
87
+ ```
88
+
89
+ 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.
43
90
 
44
91
  ## Runtime shape
45
92
 
@@ -57,17 +104,16 @@ Important:
57
104
 
58
105
  ## Component roots and scripts
59
106
 
60
- - Each runtime component root should have at most one `script[type="text/pp"]` block.
61
- - The runtime only treats `script[type="text/pp"]` as component logic.
107
+ - Each runtime component root should have at most one owned script.
108
+ - At runtime, the owned script is `script[type="text/pp"]`.
62
109
  - The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
63
- - If multiple matching scripts exist in the same root, the first matching owned script wins. Generate one script per root.
64
- - Authored component and route HTML templates must have exactly one top-level lowercase HTML root because the compiler injects `pp-component` onto that root during render. Think React-style single parent wrapper, not sibling top-level tags.
65
- - In authored Caspian templates, write plain `<script>` inside the single root and let the render pipeline rewrite it before mount.
110
+ - If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
111
+ - Authored route, layout, and component templates still need one top-level lowercase HTML root so Caspian can inject the component boundary correctly.
66
112
  - A scriptless component root still mounts and can receive props, refs, events, and nested children, but it has no local runtime scope beyond its props.
67
113
  - Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
68
114
  - The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
69
115
  - `pp.props` contains the current prop bag for the component.
70
- - `pp.props.children` contains the root's initial inner HTML before the owning `script` block is removed from the render template.
116
+ - `pp.props.children` contains the root's initial inner HTML before the owned script is removed from the render template.
71
117
 
72
118
  Bindings exported to the template:
73
119
 
@@ -85,12 +131,12 @@ Bindings that should not be assumed:
85
131
  Example:
86
132
 
87
133
  ```html
88
- <div pp-component="counter-card-1">
134
+ <div>
89
135
  <h2>{title}</h2>
90
136
  <p>Count: {count}</p>
91
137
  <button onclick="setCount(count + 1)">Increment</button>
92
138
 
93
- <script type="text/pp">
139
+ <script>
94
140
  const { title } = pp.props;
95
141
  const [count, setCount] = pp.state(0);
96
142
 
@@ -101,10 +147,12 @@ Example:
101
147
  </div>
102
148
  ```
103
149
 
104
- That example shows runtime HTML after mountable roots already exist. In authored Caspian templates, you normally write the root without `pp-component` and keep the logic in a plain `<script>` so the Python render path can inject both runtime attributes before the browser runtime sees the HTML.
150
+ That is the authored form. In browser-inspected runtime HTML, Caspian will already have added the component id and the owned script type.
105
151
 
106
152
  ## Hooks and runtime API
107
153
 
154
+ PulsePoint uses a React-style mental model inside each component script: stateful render scope, dependency-based effects, refs, reducer-style updates, context consumption, and portals.
155
+
108
156
  Hooks exposed inside component scripts through `pp`:
109
157
 
110
158
  - `pp.state(initial)` returns `[value, setValue]`.
@@ -115,11 +163,10 @@ Hooks exposed inside component scripts through `pp`:
115
163
  - `pp.callback(callback, deps)` memoizes a function.
116
164
  - `pp.reducer(reducer, initialState)` returns `[state, dispatch]`.
117
165
  - `pp.context(token)` resolves a provided context value from ancestor components.
118
- - `pp.provideContext(token, value)` provides a context value to descendant components.
119
166
  - `pp.portal(ref, target?)` registers a ref-managed element for portal rendering and returns an object that includes `sourceParent`.
120
167
  - `pp.props` exposes the current props.
121
168
 
122
- Global helpers exposed on the `pp` singleton:
169
+ Global helpers exposed through the `pp` singleton and also merged into the component runtime:
123
170
 
124
171
  - `pp.createContext(defaultValue)` creates a context token.
125
172
  - `pp.mount()` bootstraps the runtime. It is idempotent.
@@ -132,21 +179,22 @@ Global helpers exposed on the `pp` singleton:
132
179
 
133
180
  Notes:
134
181
 
135
- - The global `pp` singleton auto-mounts once `public/js/main.js` loads the bundled runtime and the DOM is ready. Manual `pp.mount()` is still safe because it short-circuits after the first mount.
182
+ - The global `pp` singleton auto-mounts once the runtime is loaded and the DOM is ready. Manual `pp.mount()` is still safe because it short-circuits after the first mount.
136
183
  - `pp.state` setters accept either a value or an updater function.
137
184
  - `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Returned promises are not awaited.
138
185
  - `pp.portal(ref)` defaults to `document.body` when no target is provided.
139
186
  - Older docs may call the RPC helper `pp.fetchFunction()`. In the current bundled runtime the implemented global API is `pp.rpc()`.
140
187
  - Keep template-facing bindings at the top level so the AST-based exporter can see them.
188
+ - For predictable code generation, prefer passing an explicit dependency array to `pp.effect`, `pp.layoutEffect`, `pp.memo`, and `pp.callback`.
141
189
 
142
190
  ## Context
143
191
 
144
- Context is implemented in the current runtime.
192
+ Context is implemented in the current runtime, but the current API follows a React-style `Context.Provider` pattern rather than a legacy `pp.provideContext(...)` helper.
145
193
 
146
194
  How it works:
147
195
 
148
196
  - Create a token with `pp.createContext(defaultValue)`.
149
- - Provide a value from a component with `pp.provideContext(token, value)`.
197
+ - Provide a value with `Context.Provider`.
150
198
  - Read it in a descendant component with `pp.context(token)`.
151
199
  - Resolution walks the logical component parent chain stored in the registry, not the live DOM.
152
200
  - Portaled descendants still resolve providers through component ancestry.
@@ -159,30 +207,39 @@ Important:
159
207
  - The same token object must be shared between provider and consumer.
160
208
  - Context is component-level, not directive-based. There is no `pp-context` attribute.
161
209
  - `pp.context(token)` resolves from ancestors. A component does not consume the value it provides in the same render.
162
- - If provider and consumer live in different component script scopes, pass the token through props or store it in shared global/outer state.
210
+ - If provider and consumer live in different component script scopes, pass the token through props or store it in shared outer or global state.
211
+ - The preferred authoring style is a provider tag in the template. The runtime also supports imperative `ThemeContext.Provider({ value })` calls during render, but the tag form is clearer for AI-generated authored templates.
212
+ - Do not invent or document `pp.provideContext`. The current runtime explicitly does not expose it.
163
213
 
164
- Example:
214
+ Provider example:
165
215
 
166
216
  ```html
167
- <section pp-component="theme-provider-1">
168
- <div pp-component="theme-label-1" theme-token="{ThemeContext}">
169
- <p>{theme}</p>
170
-
171
- <script>
172
- const { themeToken } = pp.props;
173
- const theme = pp.context(themeToken);
174
- </script>
175
- </div>
176
-
217
+ <section>
177
218
  <script>
178
219
  const ThemeContext = pp.createContext("light");
179
220
  const [theme] = pp.state("dark");
180
-
181
- pp.provideContext(ThemeContext, theme);
182
221
  </script>
222
+
223
+ <ThemeContext.Provider value="{theme}">
224
+ <p>This subtree receives the provided theme.</p>
225
+ </ThemeContext.Provider>
183
226
  </section>
184
227
  ```
185
228
 
229
+ Consumer example for a child component that receives the token through props:
230
+
231
+ ```html
232
+ <div>
233
+ <p>{theme}</p>
234
+
235
+ <script>
236
+ const theme = pp.context(pp.props.themeToken);
237
+ </script>
238
+ </div>
239
+ ```
240
+
241
+ When a child component needs the same token object, pass it from the provider scope as a prop such as `theme-token="{ThemeContext}"`.
242
+
186
243
  ## Props and nested components
187
244
 
188
245
  - Child component props are derived from DOM attributes.
@@ -217,7 +274,7 @@ Nested components:
217
274
  Example:
218
275
 
219
276
  ```html
220
- <div pp-component="profile-card-1">
277
+ <div>
221
278
  <button pp-spread="{...buttonAttrs}" hidden="{isLoading}">Save</button>
222
279
 
223
280
  <script>
@@ -246,7 +303,7 @@ Example:
246
303
  Example:
247
304
 
248
305
  ```html
249
- <div pp-component="focus-box-1">
306
+ <div>
250
307
  <input pp-ref="nameInput" />
251
308
  <button onclick="nameInput.current?.focus()">Focus</button>
252
309
 
@@ -270,7 +327,7 @@ Example:
270
327
  Example:
271
328
 
272
329
  ```html
273
- <div pp-component="todo-list-1">
330
+ <div>
274
331
  <ul>
275
332
  <template pp-for="(todo, index) in todos">
276
333
  <li key="{todo.id}">
@@ -328,23 +385,43 @@ Notes:
328
385
  - Root-layout mismatches during SPA navigation trigger a hard reload.
329
386
  - `pp.mount()` bootstraps every `[pp-component]` it finds, so generated code should call it only through the global runtime if you are manually mounting at all.
330
387
 
388
+ ## Runtime Output And Debugging
389
+
390
+ When you inspect rendered HTML in the browser, you should expect to see runtime-managed attributes and elements that are not part of authored source.
391
+
392
+ Normal runtime output includes:
393
+
394
+ - `pp-component="..."` on mounted roots.
395
+ - `script[type="text/pp"]` for owned component scripts.
396
+ - internal attributes such as captured ref tokens and event-owner bookkeeping.
397
+
398
+ These are runtime details.
399
+
400
+ - Mention them in docs when explaining how the browser runtime works.
401
+ - Do not use them as the default form in authored examples.
402
+ - Do not tell AI to handwrite them in route, layout, or component templates unless the task is explicitly about raw runtime HTML or runtime internals.
403
+
331
404
  ## AI rules
332
405
 
333
406
  Use these rules when generating or editing PulsePoint runtime code:
334
407
 
335
408
  - Treat PulsePoint as the default reactive frontend for Caspian app code.
336
- - Use the bundled runtime contract in `public/js/pp-reactive-v2.js`.
409
+ - Use `public/js/pp-reactive-v2.js` as the shipped runtime contract AI should follow.
410
+ - Keep `main.py` in view because it injects the runtime-facing attributes and rewrites authored scripts before the browser sees them.
411
+ - 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.
337
412
  - In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
338
413
  - If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
339
414
  - In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
340
415
  - Keep template-facing variables at top level.
341
- - Use `pp.createContext`, `pp.context`, and `pp.provideContext` for context. Do not invent `pp-context`.
416
+ - Follow the React-style context pattern: `pp.createContext(...)`, `<Context.Provider value="{...}">`, and `pp.context(token)`.
417
+ - Do not invent `pp.provideContext`, `pp-context`, or other legacy context helpers.
342
418
  - Keep `pp-for` on `<template>` and use plain `key`.
343
419
  - Use native `on*` attributes, not framework-specific event syntax.
344
420
  - Use refs and portals only through the implemented `pp` APIs.
345
421
  - Use `pp.rpc()` for the bundled runtime API instead of older `pp.fetchFunction()` wording.
346
422
  - Avoid generating internal runtime attributes.
347
423
  - Avoid scriptless nested components when the child template contains its own bindings.
424
+ - Prefer authored-template examples over runtime-inspected HTML examples unless the doc is specifically explaining runtime internals.
348
425
 
349
426
  ## What to avoid
350
427
 
@@ -354,6 +431,7 @@ Do not generate these unless the current source explicitly adds support:
354
431
  - `pp-context`
355
432
  - `pp-key`
356
433
  - `data-pp-ref`
434
+ - `pp-context-provider`
357
435
  - `pp-owner`
358
436
  - `pp-event-owner`
359
437
  - `pp-dynamic-script`
@@ -368,10 +446,13 @@ Do not generate these unless the current source explicitly adds support:
368
446
  These are current runtime caveats that matter for authors and AI tools:
369
447
 
370
448
  - `pp-component` is the registry key for instances, state, parent tracking, and templates. Treat it as unique per mounted root.
449
+ - `public/js/pp-reactive-v2.js` is the runtime surface that ships and should be assumed to exist.
450
+ - Caspian already injects `pp-component` and rewrites owned scripts to `type="text/pp"` during render.
371
451
  - Nested roots without their own `script[type="text/pp"]` block are not fully isolated during parent template compilation.
372
452
  - The global `pp` singleton auto-mounts on DOM ready, and `pp.mount()` is idempotent.
373
453
  - `pp.effect` and `pp.layoutEffect` are cleanup-style hooks. Their callbacks are not promise-aware.
374
454
  - `pp.context()` resolves through ancestor components, not the current component's own pending providers.
455
+ - `pp.provideContext` is not part of the current runtime API. Use `Context.Provider`.
375
456
  - `pp.portal()` preserves logical ancestry through the registry, so context and prop refresh behavior continue to work through portaled descendants.
376
457
 
377
458
  ## Final reminder
@@ -76,6 +76,8 @@ Use `index.html` for the route template. This is the route's view layer.
76
76
 
77
77
  Route templates can import reusable Python components with `<!-- @import ... -->` comments and render them with JSX-style tags such as `<Button />`. Use [components.md](./components.md) for the component authoring rules.
78
78
 
79
+ Place those import comments at the top of the file, above the authored root element. They are file-level directives, not children of the route root.
80
+
79
81
  Do not manually add `pp-component="..."` to the route root. The Python render pipeline injects that attribute onto the route's single top-level lowercase HTML element.
80
82
 
81
83
  Do not manually add `type="text/pp"` to a route-owned script either. In source templates, write a plain `<script>` inside the root and let `main.py` call `transform_scripts(...)` before the browser runtime sees the HTML.
@@ -108,6 +110,15 @@ Bad:
108
110
  </script>
109
111
  ```
110
112
 
113
+ Also bad:
114
+
115
+ ```html
116
+ <section class="dashboard-shell">
117
+ <!-- @import StatsCard from "../components" -->
118
+ <StatsCard title="Users" value="42" />
119
+ </section>
120
+ ```
121
+
111
122
  Example authored route template:
112
123
 
113
124
  ```html
@@ -219,6 +230,8 @@ Route groups are useful when you want to:
219
230
 
220
231
  Layouts work like the Next.js App Router layout system. A `layout.html` file wraps the routes beneath its folder, and nested layouts compose automatically.
221
232
 
233
+ When a layout imports components, keep each `<!-- @import ... -->` comment above the layout's authored wrapper element, such as the root `<section>` in a nested layout or the root `<html>` in the app layout.
234
+
222
235
  Resolved SEO fields are exposed to layouts as `[[ metadata.* ]]`, while values returned from `layout.py` are exposed separately as `[[ layout.* ]]`.
223
236
 
224
237
  For example, a page inside `/dashboard/settings` is wrapped by the root layout first and then by the dashboard layout.
@@ -300,6 +313,7 @@ If an AI agent is choosing where to add or update route code, apply these rules
300
313
  - Use `index.html` for route templates and `index.py` for route-level async logic.
301
314
  - Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
302
315
  - Use `layout.html` for shared wrappers and `layout.py` for layout-level synchronous props or metadata.
316
+ - Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored root element.
303
317
  - Use [metadata.md](./metadata.md) when a route or layout needs SEO fields.
304
318
  - Use `[segment]` for single dynamic parameters.
305
319
  - Use `[...segment]` for catch-all route matching.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {