caspian-utils 0.1.3 → 0.1.5

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.
@@ -7,10 +7,10 @@ related:
7
7
  links:
8
8
  - /docs/project-structure
9
9
  - /docs/core-runtime-map
10
- - /docs/routing
11
- - /docs/pulsepoint
12
- - /docs/pulsepoint-runtime-map
13
- - /docs/fetch-data
10
+ - /docs/routing
11
+ - /docs/pulsepoint
12
+ - /docs/pulsepoint-runtime-map
13
+ - /docs/fetch-data
14
14
  - /docs/index
15
15
  ---
16
16
 
@@ -26,20 +26,21 @@ As the app grows, treat `src/components/` as the default home for reusable appli
26
26
 
27
27
  ## Mental Model
28
28
 
29
- - Use a Python component when you want a reusable server-rendered UI building block.
30
- - Return an HTML string directly for small presentational components.
31
- - Use `render_html(...)` with a same-name `.html` file when the component has more markup, PulsePoint behavior, or clearer separation between Python logic and UI.
32
- - When a component needs first-party interactivity, bind events in the component template with PulsePoint-handled `on*` attributes and keep state in `pp.state(...)`; do not build id-driven `querySelector(...)` or `addEventListener(...)` wiring for normal component behavior.
33
- - Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
29
+ - Use a Python component when you want a reusable server-rendered UI building block.
30
+ - Return an HTML string directly for small presentational components.
31
+ - Use `html(...)` to keep markup, server interpolation, and a PulsePoint `<script>` inline in one Python file (single-file component) for small and medium UI. It renders through Caspian's Jinja env, so `{{ ... }}` is server-side and `{ ... }` stays for PulsePoint.
32
+ - Use `render_html(...)` with a same-name `.html` file when the component has large markup, a long script, or you want clearer separation between Python logic and UI.
33
+ - When a component needs first-party interactivity, bind events in the component template with PulsePoint-handled `on*` attributes and keep state in `pp.state(...)`; do not build id-driven `querySelector(...)` or `addEventListener(...)` wiring for normal component behavior.
34
+ - Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
34
35
 
35
36
  ## Framework Internals Note
36
37
 
37
38
  When the task is about component internals rather than normal app-owned components, these runtime files are the most relevant:
38
39
 
39
- - `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, and component loading.
40
- - `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution, root validation, and `pp-component` injection.
40
+ - `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, the inline `html(...)` helper, and component loading.
41
+ - `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution (including resolving a component's own tags from its Python module imports), parent-scope expansion of slot content, root validation, and `pp-component` injection.
41
42
  - `.venv/Lib/site-packages/casp/html_attrs.py` owns `get_attributes(...)` and the Python-side `merge_classes(...)` contract.
42
- Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader Python runtime-module map. Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when a component task is specifically about browser-side PulsePoint state, refs, context, portals, events, RPC, or SPA behavior.
43
+ Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader Python runtime-module map. Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when a component task is specifically about browser-side PulsePoint state, refs, context, portals, events, RPC, or SPA behavior.
43
44
 
44
45
  ## Basic Component
45
46
 
@@ -183,7 +184,7 @@ Do not invent folder-level imports for symbols that do not have their own file.
183
184
 
184
185
  ## Template-Backed Components
185
186
 
186
- 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.
187
+ When a component has large markup or a long script, keep the Python file focused on props or server-side preparation and move the markup into a same-name HTML file. For small and medium components, including ones with PulsePoint behavior, prefer the single-file `html(...)` form described in the next section instead.
187
188
 
188
189
  `Counter.py`
189
190
 
@@ -220,6 +221,89 @@ Use this split when:
220
221
 
221
222
  This keeps the component easy to read: Python owns the server-side logic and template context, while the HTML file owns the rendered UI and browser behavior.
222
223
 
224
+ ## Single-File Components With `html(...)`
225
+
226
+ When a component is small or medium, keep the markup, server-side interpolation, and the PulsePoint `<script>` inline in the Python file instead of a sibling `.html`. Import `html` from `casp.component_decorator` and return `html("""...""", **context)`.
227
+
228
+ `html(...)` renders the string through the same Jinja environment Caspian uses for template files, so three brace dialects coexist without colliding:
229
+
230
+ - `{{ value }}` is server render (Python to HTML).
231
+ - `{{ value | json }}` safely serializes a server value into a `<script>`.
232
+ - `{# comment #}` is a Jinja comment, stripped from output.
233
+ - `{ value }` is left untouched for PulsePoint client reactivity.
234
+
235
+ ```python
236
+ from casp.component_decorator import component, html
237
+ from casp.html_attrs import get_attributes, merge_classes
238
+
239
+ @component
240
+ def UserCard(user, **props):
241
+ attrs = get_attributes({
242
+ "class": merge_classes("card", props.pop("class", "")),
243
+ }, props)
244
+
245
+ # html
246
+ return html("""
247
+ <div {{ attrs }}>
248
+ <h3>{{ user.name }}</h3>
249
+ <button onclick="setLikes(likes + 1)">Likes: {likes}</button>
250
+ <script>
251
+ const [likes, setLikes] = pp.state({{ user.likes | json }});
252
+ </script>
253
+ </div>
254
+ """, attrs=attrs, user=user)
255
+ ```
256
+
257
+ The returned value is `Markup`, so the normal pipeline still injects `pp-component` on the single root and `transform_scripts(...)` rewrites the plain `<script>` to `type="text/pp"`. A single-file component renders identically to the two-file `render_html(...)` form; the choice is purely about readability.
258
+
259
+ Rules for inline `html(...)`:
260
+
261
+ - The single-root rule still applies: exactly one top-level element with any `<script>` nested inside it.
262
+ - Autoescaping is ON (Jinja default), so `{{ value }}` escapes HTML automatically and user text is safe without `| e`. The flip side: trusted HTML you want rendered as-is must be `Markup(...)` or piped through `| safe`.
263
+ - A `children` value is auto-marked safe (parity with `render_html(...)`), so `{{ children }}` renders nested component markup correctly without `| safe`.
264
+ - Do not use a Python f-string for the markup. PulsePoint single braces `{ ... }` would collide with f-string interpolation. Use a plain triple-quoted string passed to `html(...)`.
265
+ - Prefer `render_html(...)` with a same-name `.html` file when the markup is large or the `<script>` carries real logic. A long client script is a smell regardless of file shape; move heavy work into `@rpc()` actions or smaller composed components.
266
+
267
+ The leading `# html` comment above the string is an optional editor hint: some editors color the HTML inside a string tagged that way (JetBrains uses `# language=HTML`). It has no runtime effect.
268
+
269
+ ## Component Imports: Python Imports Or `@import`
270
+
271
+ A component can render other components with `x-*` tags in either the two-file `.html` or the inline `html(...)` form. There are two ways to tell Caspian which component a tag refers to.
272
+
273
+ 1. The `<!-- @import Name from "path" -->` HTML comment, resolved by path string. This works in both `.html` templates and inline strings.
274
+ 2. A real Python import at the top of the component module. A component's own `x-*` tags resolve from the components imported into that module, with no `@import` comment needed.
275
+
276
+ Prefer the Python import inside single-file components. It is the unambiguous source of truth for which component a tag refers to, it gives normal editor navigation, and it prevents same-name collisions across directories. If `variantA/Tag.py` and `variantB/Tag.py` both export `Tag`, the Python import in each consumer file decides which `Tag` its `<x-tag>` resolves to.
277
+
278
+ ```python
279
+ from casp.component_decorator import component, html
280
+ from src.components.variantA.Tag import Tag # resolves <x-tag> below
281
+
282
+ @component
283
+ def Panel(children="", **props):
284
+ # No @import comment. <x-tag> resolves from the Python import above,
285
+ # so it is unambiguously variantA's Tag.
286
+ # html
287
+ return html("""
288
+ <div class="panel">
289
+ <x-tag>inside-panel</x-tag>
290
+ {{ children }}
291
+ </div>
292
+ """, children=children)
293
+ ```
294
+
295
+ Resolution precedence for an `x-*` tag inside a component's own output, lowest to highest:
296
+
297
+ - inherited components from an ancestor template
298
+ - the component's own Python module imports
299
+ - a local `<!-- @import ... -->` in that same template
300
+
301
+ So a component's Python import wins over an inherited same-name component, and an explicit local `@import` wins over both.
302
+
303
+ ### Slot Content Resolves In The Parent Scope
304
+
305
+ Component tags written as slot content (children passed between a component's opening and closing tags) resolve in the scope where they were authored, not in the child component's scope. This matches slot semantics in other component systems: a `<x-tag>` written by the parent stays bound to the parent's `Tag` even when it is slotted into a child that has its own `Tag`. The component that authors a tag in markup must import that component, the same way it would for any tag it renders directly.
306
+
223
307
  ## Auto-Injected `pp-component` And PulsePoint Script Type
224
308
 
225
309
  Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source. The canonical authored-vs-runtime explanation lives in [pulsepoint.md](./pulsepoint.md).
@@ -353,9 +437,9 @@ Keep synchronous components as the default. Switch to `async def` only when the
353
437
 
354
438
  - Put reusable components in `src/components/` and keep route files in `src/app/`.
355
439
  - For dashboards, admin areas, account sections, and route groups with child routes, put the shared shell in the parent folder's `layout.html` and compose it from reusable components there instead of repeating the same shell in every child `index.html`.
356
- - If the component includes PulsePoint behavior, prefer a thin Python wrapper plus a same-name `.html` template.
357
- - For component clicks, inputs, menus, toggles, filters, and list updates, use PulsePoint events and directives inside that `.html` template. Avoid manual DOM selection, manual listener setup, and manual `innerHTML` rendering unless integrating a third-party imperative widget.
358
- - Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
440
+ - Default to a single Python file with inline `html(...)` for most components, including ones with PulsePoint behavior. Move to a thin Python wrapper plus a same-name `.html` template only when the markup is large or the `<script>` carries real logic. Both forms render identically, so this is a readability choice, not a capability one.
441
+ - For component clicks, inputs, menus, toggles, filters, and list updates, use PulsePoint events and directives inside the component template (inline `html(...)` or the `.html` file). Avoid manual DOM selection, manual listener setup, and manual `innerHTML` rendering unless integrating a third-party imperative widget.
442
+ - Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
359
443
  - Accept `children` or `**props` when the component should support nested content.
360
444
  - Keep page-level data loading in `page()` when the data is not intrinsic to the component itself.
361
445
  - If you add `@rpc()` functions inside a component file, keep their names globally unique because component RPCs are not route-scoped.
@@ -7,12 +7,12 @@ related:
7
7
  links:
8
8
  - /docs/project-structure
9
9
  - /docs/routing
10
- - /docs/auth
11
- - /docs/fetch-data
12
- - /docs/websockets
13
- - /docs/pulsepoint
14
- - /docs/pulsepoint-runtime-map
15
- - /docs/index
10
+ - /docs/auth
11
+ - /docs/fetch-data
12
+ - /docs/websockets
13
+ - /docs/pulsepoint
14
+ - /docs/pulsepoint-runtime-map
15
+ - /docs/index
16
16
  ---
17
17
 
18
18
  This page maps the app entry point and the installed Caspian runtime modules to the packaged docs that explain them.
@@ -26,85 +26,85 @@ Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib
26
26
  - the task is about framework internals rather than normal app-owned route or component code
27
27
  - AI needs to know which packaged doc should be kept aligned with a core-file change
28
28
 
29
- ## App Entry Point
29
+ ## App Entry Point
30
30
 
31
31
  | Concern | Core file | Read first | Why it matters |
32
32
  | --- | --- | --- | --- |
33
- | App bootstrap and request flow | [main.py](../../../../main.py) plus imported runtime helpers such as `casp.runtime_security` | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, middleware wiring, route registration, cache check and save, exception handlers, and final HTML transforms live here. Package-owned helpers imported by `main.py` may own safe public-file serving, baseline non-CSP response headers, production-safe error messages, or production session-secret enforcement. |
34
- | 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. |
35
-
36
- Important current `main.py` behaviors AI should keep in mind:
37
-
38
- - In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
39
- - `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
40
- - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
41
- - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
42
- - Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
43
- - Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
44
- - Baseline non-CSP response headers may be built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. Do not assume that helper is a third-party browser resource or domain registry; check the current helper before adding CSP behavior.
45
- - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `SecurityHeadersMiddleware`, so the effective request order is reversed at runtime.
46
- - `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.
47
-
48
- ## Caspian Core Feature Map
49
-
50
- Use this table when the task names a framework feature but the owning file is not obvious yet.
51
-
52
- | Core feature | App-owned entry points | Runtime owner | Packaged docs |
53
- | --- | --- | --- | --- |
54
- | Feature flags and generated surface area | `caspian.config.json` | `casp.caspian_config` | [commands.md](./commands.md), [project-structure.md](./project-structure.md) |
55
- | File-based routing | `src/app/**/index.html`, `src/app/**/index.py` | `main.py`, `casp.layout`, `casp.caspian_config` | [routing.md](./routing.md) |
56
- | Nested layouts | `src/app/**/layout.html`, `src/app/**/layout.py` | `casp.layout`, `main.py` | [routing.md](./routing.md), [metadata.md](./metadata.md) |
57
- | Metadata | route or layout `metadata`, runtime metadata helpers | `casp.layout`, `main.py` | [metadata.md](./metadata.md), [routing.md](./routing.md) |
58
- | Component imports and `x-*` tags | `src/components/**`, `src/app/**/*.html` | `casp.components_compiler`, `casp.component_decorator` | [components.md](./components.md), [routing.md](./routing.md) |
59
- | Auth and route protection | `src/lib/auth/auth_config.py`, `main.py`, route decorators | `casp.auth`, `main.py` middleware | [auth.md](./auth.md) |
60
- | Baseline response headers and safe public-file serving | `main.py` imports from `casp.runtime_security` | `.venv/Lib/site-packages/casp/runtime_security.py` | [auth.md](./auth.md), [project-structure.md](./project-structure.md) |
61
- | RPC and server actions | route or component Python modules with `@rpc()` | `casp.rpc`, `main.py` middleware | [fetch-data.md](./fetch-data.md), [file-uploads.md](./file-uploads.md) |
62
- | Streaming | route `page()` generators, RPC generators | `casp.streaming`, `casp.rpc`, `main.py` | [fetch-data.md](./fetch-data.md) |
63
- | WebSockets | `main.py` `@app.websocket(...)` endpoints when `caspian.config.json` has `websocket: true`, `src/lib/**` socket helpers, route-owned browser clients in `src/app/**` | FastAPI app-owned ASGI endpoints in `main.py` | [websockets.md](./websockets.md), [auth.md](./auth.md), [pulsepoint.md](./pulsepoint.md) |
64
- | Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
65
- | Page caching | route-level `Cache(...)` declarations | `casp.cache_handler`, `main.py` cache check/save | [cache.md](./cache.md) |
66
- | Validation | route and RPC input boundaries | `casp.validate` | [validation.md](./validation.md) |
67
- | Tailwind class merge | Python components and PulsePoint templates | `casp.html_attrs`, browser `twMerge(...)` | [components.md](./components.md), [pulsepoint.md](./pulsepoint.md) |
68
- | Prisma persistence | `prisma/**`, `src/lib/prisma/**`, route/RPC code | generated Prisma and Python clients | [database.md](./database.md), [fetch-data.md](./fetch-data.md) |
69
- | MCP tools | `src/lib/mcp/**` when enabled | app-owned FastMCP server | [mcp.md](./mcp.md), [commands.md](./commands.md) |
70
-
71
- For browser-side PulsePoint details, use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) instead of expanding this Python-runtime map.
72
-
73
- ## Task-To-File Examples
74
-
75
- Protected dashboard route:
76
-
77
- 1. Check `caspian.config.json` for enabled features such as Prisma and Tailwind.
78
- 2. Read [routing.md](./routing.md) for the section layout shape.
79
- 3. Read [auth.md](./auth.md) for route privacy and decorators.
80
- 4. Update `src/app/dashboard/layout.html` for the shared shell and child `index.html` or `index.py` files for page-specific behavior.
81
- 5. Verify auth bootstrap and middleware ownership in `main.py` only if route protection behaves unexpectedly.
82
-
83
- Interactive CRUD page:
84
-
85
- 1. Read [fetch-data.md](./fetch-data.md) for the `page()` plus `@rpc()` split.
86
- 2. Read [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) for browser-side `pp.rpc(...)` behavior.
87
- 3. Keep first-render data in `src/app/**/index.py`, visible markup in `src/app/**/index.html`, reusable helpers in `src/lib/**`, and database schema changes in `prisma/**`.
33
+ | App bootstrap and request flow | [main.py](../../../../main.py) plus imported runtime helpers such as `casp.runtime_security` | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, middleware wiring, route registration, cache check and save, exception handlers, and final HTML transforms live here. Package-owned helpers imported by `main.py` may own safe public-file serving, baseline non-CSP response headers, production-safe error messages, or production session-secret enforcement. |
34
+ | 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. |
35
+
36
+ Important current `main.py` behaviors AI should keep in mind:
37
+
38
+ - In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
39
+ - `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
40
+ - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
41
+ - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
42
+ - Safe public-file helpers live in `casp.runtime_security` and should reject `..` traversal before serving from `public/**`.
43
+ - Session middleware secrets may be resolved through `casp.runtime_security` so production can fail fast when `AUTH_SECRET` is missing or still on a default placeholder.
44
+ - Baseline non-CSP response headers may be built by `casp.runtime_security` and attached through `SecurityHeadersMiddleware`. Do not assume that helper is a third-party browser resource or domain registry; check the current helper before adding CSP behavior.
45
+ - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, `SecurityHeadersMiddleware`, so the effective request order is reversed at runtime.
46
+ - `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.
47
+
48
+ ## Caspian Core Feature Map
49
+
50
+ Use this table when the task names a framework feature but the owning file is not obvious yet.
51
+
52
+ | Core feature | App-owned entry points | Runtime owner | Packaged docs |
53
+ | --- | --- | --- | --- |
54
+ | Feature flags and generated surface area | `caspian.config.json` | `casp.caspian_config` | [commands.md](./commands.md), [project-structure.md](./project-structure.md) |
55
+ | File-based routing | `src/app/**/index.html`, `src/app/**/index.py` | `main.py`, `casp.layout`, `casp.caspian_config` | [routing.md](./routing.md) |
56
+ | Nested layouts | `src/app/**/layout.html`, `src/app/**/layout.py` | `casp.layout`, `main.py` | [routing.md](./routing.md), [metadata.md](./metadata.md) |
57
+ | Metadata | route or layout `metadata`, runtime metadata helpers | `casp.layout`, `main.py` | [metadata.md](./metadata.md), [routing.md](./routing.md) |
58
+ | Component imports and `x-*` tags | `src/components/**`, `src/app/**/*.html` | `casp.components_compiler`, `casp.component_decorator` | [components.md](./components.md), [routing.md](./routing.md) |
59
+ | Auth and route protection | `src/lib/auth/auth_config.py`, `main.py`, route decorators | `casp.auth`, `main.py` middleware | [auth.md](./auth.md) |
60
+ | Baseline response headers and safe public-file serving | `main.py` imports from `casp.runtime_security` | `.venv/Lib/site-packages/casp/runtime_security.py` | [auth.md](./auth.md), [project-structure.md](./project-structure.md) |
61
+ | RPC and server actions | route or component Python modules with `@rpc()` | `casp.rpc`, `main.py` middleware | [fetch-data.md](./fetch-data.md), [file-uploads.md](./file-uploads.md) |
62
+ | Streaming | route `page()` generators, RPC generators | `casp.streaming`, `casp.rpc`, `main.py` | [fetch-data.md](./fetch-data.md) |
63
+ | WebSockets | `main.py` `@app.websocket(...)` endpoints when `caspian.config.json` has `websocket: true`, `src/lib/**` socket helpers, route-owned browser clients in `src/app/**` | FastAPI app-owned ASGI endpoints in `main.py` | [websockets.md](./websockets.md), [auth.md](./auth.md), [pulsepoint.md](./pulsepoint.md) |
64
+ | Server state | request handlers and RPC actions | `casp.state_manager`, `main.py` middleware | [state.md](./state.md) |
65
+ | Page caching | route-level `Cache(...)` declarations | `casp.cache_handler`, `main.py` cache check/save | [cache.md](./cache.md) |
66
+ | Validation | route and RPC input boundaries | `casp.validate` | [validation.md](./validation.md) |
67
+ | Tailwind class merge | Python components and PulsePoint templates | `casp.html_attrs`, browser `twMerge(...)` | [components.md](./components.md), [pulsepoint.md](./pulsepoint.md) |
68
+ | Prisma persistence | `prisma/**`, `src/lib/prisma/**`, route/RPC code | generated Prisma and Python clients | [database.md](./database.md), [fetch-data.md](./fetch-data.md) |
69
+ | MCP tools | `src/lib/mcp/**` when enabled | app-owned FastMCP server | [mcp.md](./mcp.md), [commands.md](./commands.md) |
70
+
71
+ For browser-side PulsePoint details, use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) instead of expanding this Python-runtime map.
72
+
73
+ ## Task-To-File Examples
74
+
75
+ Protected dashboard route:
76
+
77
+ 1. Check `caspian.config.json` for enabled features such as Prisma and Tailwind.
78
+ 2. Read [routing.md](./routing.md) for the section layout shape.
79
+ 3. Read [auth.md](./auth.md) for route privacy and decorators.
80
+ 4. Update `src/app/dashboard/layout.html` for the shared shell and child `index.html` or `index.py` files for page-specific behavior.
81
+ 5. Verify auth bootstrap and middleware ownership in `main.py` only if route protection behaves unexpectedly.
82
+
83
+ Interactive CRUD page:
84
+
85
+ 1. Read [fetch-data.md](./fetch-data.md) for the `page()` plus `@rpc()` split.
86
+ 2. Read [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) for browser-side `pp.rpc(...)` behavior.
87
+ 3. Keep first-render data in `src/app/**/index.py`, visible markup in `src/app/**/index.html`, reusable helpers in `src/lib/**`, and database schema changes in `prisma/**`.
88
88
 
89
89
  ## Installed Runtime Map
90
90
 
91
91
  | Runtime file | Primary responsibility | Read these docs |
92
92
  | --- | --- | --- |
93
- | [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, sync or async `layout()` results, and parser-based `<slot />` replacement | [routing.md](./routing.md), [metadata.md](./metadata.md) |
94
- | [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
95
- | [.venv/Lib/site-packages/casp/runtime_security.py](../../../../.venv/Lib/site-packages/casp/runtime_security.py) | safe public-file serving, baseline non-CSP response headers, production-safe error messages, and production session-secret enforcement used by `main.py` | [project-structure.md](./project-structure.md), [auth.md](./auth.md) |
96
- | [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
93
+ | [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, sync or async `layout()` results, and parser-based `<slot />` replacement | [routing.md](./routing.md), [metadata.md](./metadata.md) |
94
+ | [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
95
+ | [.venv/Lib/site-packages/casp/runtime_security.py](../../../../.venv/Lib/site-packages/casp/runtime_security.py) | safe public-file serving, baseline non-CSP response headers, production-safe error messages, and production session-secret enforcement used by `main.py` | [project-structure.md](./project-structure.md), [auth.md](./auth.md) |
96
+ | [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
97
97
  | [.venv/Lib/site-packages/casp/streaming.py](../../../../.venv/Lib/site-packages/casp/streaming.py) | `SSE`, `ServerSentEvent`, and generator-to-event-stream wrapping | [fetch-data.md](./fetch-data.md) |
98
98
  | [.venv/Lib/site-packages/casp/state_manager.py](../../../../.venv/Lib/site-packages/casp/state_manager.py) | request-scoped state, session bucket persistence, listener lifecycle, and wire-request reset behavior | [state.md](./state.md) |
99
99
  | [.venv/Lib/site-packages/casp/cache_handler.py](../../../../.venv/Lib/site-packages/casp/cache_handler.py) | `Cache`, cache manifest handling, filename generation, disk-backed HTML storage, and invalidation | [cache.md](./cache.md) |
100
100
  | [.venv/Lib/site-packages/casp/validate.py](../../../../.venv/Lib/site-packages/casp/validate.py) | `Validate`, `Rule`, sanitization, and file-validation internals | [validation.md](./validation.md) |
101
- | [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, component loading, and prop normalization before component calls | [components.md](./components.md) |
102
- | [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
101
+ | [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, the inline `html(...)` single-file helper, component loading, and prop normalization before component calls | [components.md](./components.md) |
102
+ | [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution (including resolving a component's own tags from its Python module imports), parent-scope expansion of slot content, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
103
103
  | [.venv/Lib/site-packages/casp/scripts_type.py](../../../../.venv/Lib/site-packages/casp/scripts_type.py) | rewriting authored body `<script>` tags to `type="text/pp"` in rendered HTML | [pulsepoint.md](./pulsepoint.md), [routing.md](./routing.md), [components.md](./components.md) |
104
104
  | [.venv/Lib/site-packages/casp/html_attrs.py](../../../../.venv/Lib/site-packages/casp/html_attrs.py) | `get_attributes(...)`, alias normalization, and the Python-side `merge_classes(...)` contract | [components.md](./components.md) |
105
105
  | [.venv/Lib/site-packages/casp/caspian_config.py](../../../../.venv/Lib/site-packages/casp/caspian_config.py) | typed config loading, feature-flag reads, `settings/files-list.json` parsing, and route rule derivation | [project-structure.md](./project-structure.md), [commands.md](./commands.md), [routing.md](./routing.md) |
106
106
 
107
- Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. `html_native.py` owns BeautifulSoup-backed fragment parsing used by component/root transforms and layout slot replacement. Read these helpers only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
107
+ Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. `html_native.py` owns BeautifulSoup-backed fragment parsing used by component/root transforms and layout slot replacement. Read these helpers only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
108
108
 
109
109
  ## Verification Focus
110
110
 
@@ -112,13 +112,13 @@ Use these behavior checkpoints when AI needs the fastest verification path for a
112
112
 
113
113
  | Runtime area | Verify these behaviors |
114
114
  | --- | --- |
115
- | `main.py` routing and request flow | route registration, path and query injection, static asset handling, session-middleware wiring, response-header middleware, and exception rendering |
116
- | `main.py` WebSockets | `cfg.websocket` route-registration gate, `@app.websocket(...)` paths, origin checks before `accept()`, auth/session extraction, message-size and idle-timeout handling, close codes, and connection cleanup |
117
- | `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 |
115
+ | `main.py` routing and request flow | route registration, path and query injection, static asset handling, session-middleware wiring, response-header middleware, and exception rendering |
116
+ | `main.py` WebSockets | `cfg.websocket` route-registration gate, `@app.websocket(...)` paths, origin checks before `accept()`, auth/session extraction, message-size and idle-timeout handling, close codes, and connection cleanup |
117
+ | `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 |
118
118
  | `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
119
119
  | `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
120
120
  | `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
121
- | `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
121
+ | `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, Python-module-import tag resolution, parent-scope slot resolution, inline `html(...)` rendering and children-safe handling, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
122
122
  | `casp.state_manager` | wire vs non-wire reset behavior, request-state persistence assumptions, and AttributeDict access |
123
123
  | `casp.cache_handler` | filename generation, manifest writes, TTL handling, and invalidation behavior |
124
124
  | `casp.caspian_config` | config parsing, files index building, and Next.js-style route inventory behavior |
@@ -11,9 +11,9 @@ related:
11
11
  - /docs/core-runtime-map
12
12
  - /docs/pulsepoint-runtime-map
13
13
  - /docs/mcp
14
- - /docs/file-uploads
15
- - /docs/websockets
16
- - /docs/file-conventions
14
+ - /docs/file-uploads
15
+ - /docs/websockets
16
+ - /docs/file-conventions
17
17
  - /docs/project-structure
18
18
  - /docs/components
19
19
  ---
@@ -24,20 +24,20 @@ Treat these docs as reusable Caspian feature guidance. Treat `./caspian.config.j
24
24
 
25
25
  The docs can mention optional features even when those features are disabled in a project. Their job is to explain how a feature works, when a doc applies, and which files to inspect next once the feature is confirmed as relevant.
26
26
 
27
- Before making feature, tooling, or file-placement decisions in a Caspian project, read `./caspian.config.json` almost immediately. That file tells you which optional capabilities are enabled, such as Prisma, MCP, WebSockets, TypeScript, Tailwind, backend-only mode, and component scan directories.
27
+ Before making feature, tooling, or file-placement decisions in a Caspian project, read `./caspian.config.json` almost immediately. That file tells you which optional capabilities are enabled, such as Prisma, MCP, WebSockets, TypeScript, Tailwind, backend-only mode, and component scan directories.
28
28
 
29
29
  ## Default Stack
30
30
 
31
31
  When generating or editing a Caspian app, treat these as the default choices unless the task explicitly requires something else:
32
32
 
33
- - Use PulsePoint for reactive frontend behavior.
34
- - For first-party HTML events and reactivity, use PulsePoint `on*` attributes, state, refs, effects, directives, and `pp.rpc()` instead of ordinary DOM wiring with ids, `data-*` state, `querySelector`, `addEventListener`, or manual `innerHTML`.
35
- - 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.
33
+ - Use PulsePoint for reactive frontend behavior.
34
+ - For first-party HTML events and reactivity, use PulsePoint `on*` attributes, state, refs, effects, directives, and `pp.rpc()` instead of ordinary DOM wiring with ids, `data-*` state, `querySelector`, `addEventListener`, or manual `innerHTML`.
35
+ - 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.
36
36
  - When `caspian.config.json` has `tailwindcss: true`, use Python `merge_classes(...)` plus browser `twMerge(...)` as the only supported Tailwind class-merging path.
37
- - Use `@rpc()` plus `pp.rpc()` for browser-triggered reads, writes, streaming, and uploads.
38
- - When `caspian.config.json` has `websocket: true`, use app-owned FastAPI WebSocket endpoints only for long-lived bidirectional live channels; keep normal browser-triggered data work on RPC.
39
- - When `caspian.config.json` has `prisma: true`, use the generated Prisma Python ORM from `src/lib/prisma/**` for Python-side database reads and writes. Do not invent a second fetch layer, raw driver wrapper, JSON active store, or browser-side data path that bypasses Prisma.
40
- - Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
37
+ - Use `@rpc()` plus `pp.rpc()` for browser-triggered reads, writes, streaming, and uploads.
38
+ - When `caspian.config.json` has `websocket: true`, use app-owned FastAPI WebSocket endpoints only for long-lived bidirectional live channels; keep normal browser-triggered data work on RPC.
39
+ - When `caspian.config.json` has `prisma: true`, use the generated Prisma Python ORM from `src/lib/prisma/**` for Python-side database reads and writes. Do not invent a second fetch layer, raw driver wrapper, JSON active store, or browser-side data path that bypasses Prisma.
40
+ - Use `Validate` and `Rule` from `casp.validate` for server-side input validation and sanitization.
41
41
 
42
42
  ## AI Doc Shape
43
43
 
@@ -83,11 +83,11 @@ The packaged Caspian docs referenced by this index live here:
83
83
  - `database.md` - Prisma schema, migration, seed, and client-generation workflow for projects where `caspian.config.json` enables Prisma, plus Python-side helper caveats
84
84
  - `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
85
85
  - `file-conventions.md` - quick decision guide for `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, and `error.html`, plus the owning runtime files to verify
86
- - `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
87
- - `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, first-party `on*` events, state, effects, directives, SPA navigation scroll restoration, `pp-reset-scroll`, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
88
- - `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
89
- - `websockets.md` - WebSocket feature-gate guidance for projects where `caspian.config.json` has `websocket: true`, app-owned FastAPI endpoint placement, browser `WebSocket` clients inside PulsePoint templates, origin checks, auth/session handling, and when to choose sockets over RPC or SSE
90
- - `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
86
+ - `components.md` - Create reusable Python components, template-backed UI, single-file components with the inline `html(...)` helper, HTML-first `x-*` component tags, Python-import vs `@import` tag resolution and parent-scope slot resolution, the single-parent authored-root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
87
+ - `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, first-party `on*` events, state, effects, directives, SPA navigation scroll restoration, `pp-reset-scroll`, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
88
+ - `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
89
+ - `websockets.md` - WebSocket feature-gate guidance for projects where `caspian.config.json` has `websocket: true`, app-owned FastAPI endpoint placement, browser `WebSocket` clients inside PulsePoint templates, origin checks, auth/session handling, and when to choose sockets over RPC or SSE
90
+ - `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
91
91
  - `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
92
92
  - `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
93
93
  - `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
@@ -103,18 +103,18 @@ Preferred lookup order:
103
103
 
104
104
  1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
105
105
  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.
106
- 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"`.
107
- 4. Before inventing browser JavaScript, check whether the interaction is first-party UI behavior. If it is, use PulsePoint `on*` attributes, `pp.state`, refs, effects, directives, and `pp.rpc()` rather than id-driven DOM scripting.
108
- 5. When Prisma is enabled, route all Python database work through the generated Prisma Python ORM. Use `database.md` before schema, migration, seed, or ORM decisions, and use `fetch-data.md` before wiring route data or RPC flows.
109
- 6. If the task asks for WebSockets, live bidirectional channels, socket origin checks, or native browser `WebSocket` clients, confirm `caspian.config.json` has `websocket: true`, read `websockets.md`, then inspect `main.py`, `src/lib/**`, the owning `src/app/**` route, and `settings/bs-config.json`. If `websocket` is false and the user wants it, ask first, then enable the config flag and follow the update workflow.
110
- 7. 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`.
111
- 8. 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.
112
- 9. If the task names a PulsePoint feature or directive, read `pulsepoint-runtime-map.md` for the fastest feature-to-runtime lookup, then read `pulsepoint.md` for authoring rules.
113
- 10. 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.
114
- 11. Use `file-conventions.md` for quick decisions about `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, and `error.html`; 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`, `websockets.md`, and `file-uploads.md` for task-specific guidance.
115
- 12. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
116
- 13. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
117
- 14. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
106
+ 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"`.
107
+ 4. Before inventing browser JavaScript, check whether the interaction is first-party UI behavior. If it is, use PulsePoint `on*` attributes, `pp.state`, refs, effects, directives, and `pp.rpc()` rather than id-driven DOM scripting.
108
+ 5. When Prisma is enabled, route all Python database work through the generated Prisma Python ORM. Use `database.md` before schema, migration, seed, or ORM decisions, and use `fetch-data.md` before wiring route data or RPC flows.
109
+ 6. If the task asks for WebSockets, live bidirectional channels, socket origin checks, or native browser `WebSocket` clients, confirm `caspian.config.json` has `websocket: true`, read `websockets.md`, then inspect `main.py`, `src/lib/**`, the owning `src/app/**` route, and `settings/bs-config.json`. If `websocket` is false and the user wants it, ask first, then enable the config flag and follow the update workflow.
110
+ 7. 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`.
111
+ 8. 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.
112
+ 9. If the task names a PulsePoint feature or directive, read `pulsepoint-runtime-map.md` for the fastest feature-to-runtime lookup, then read `pulsepoint.md` for authoring rules.
113
+ 10. 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.
114
+ 11. Use `file-conventions.md` for quick decisions about `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, and `error.html`; 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`, `websockets.md`, and `file-uploads.md` for task-specific guidance.
115
+ 12. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
116
+ 13. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
117
+ 14. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
118
118
 
119
119
  ## Maintenance
120
120
 
@@ -37,12 +37,12 @@ If an inspected browser DOM disagrees with authored template source, remember th
37
37
  | State | `pp.state(initial)` | `pp-reactive-v2.js` | setters accept values or updater functions, state belongs to the component instance |
38
38
  | Effects | `pp.effect(...)`, `pp.layoutEffect(...)` | `pp-reactive-v2.js` | callbacks may return cleanup functions, promises are not awaited |
39
39
  | Refs | `pp.ref(...)`, `pp-ref` | `pp-reactive-v2.js` | generated ref internals are runtime-managed; do not author `data-pp-ref` |
40
- | Context | `pp.createContext(...)`, lowercase `<themecontext.provider>`, `pp.context(token)` | `TemplateCompiler.ts`, `NestedBoundaryManager.ts`, `pp-reactive-v2.js` | authored provider tags are HTML-first and lowercase; `TemplateCompiler.transformContextProviderTags(...)` rewrites `*.provider` to runtime-owned `pp-context-provider`; ancestry is logical component ancestry; do not invent `pp-context` or `pp.provideContext` |
40
+ | Context | `pp.createContext(...)`, lowercase `<themecontext.provider>`, `pp.context(token)` | `TemplateCompiler.ts`, `NestedBoundaryManager.ts`, `pp-reactive-v2.js` | authored provider tags are HTML-first and lowercase; `TemplateCompiler.transformContextProviderTags(...)` rewrites `*.provider` to runtime-owned `pp-context-provider`; ancestry is logical component ancestry; do not invent `pp-context` or `pp.provideContext` |
41
41
  | Portals | `pp.portal(ref, target?)` | `pp-reactive-v2.js` | context should preserve logical ancestry through the registry |
42
42
  | Lists | `<template pp-for="item in items">` | `pp-reactive-v2.js` | `pp-for` belongs on `<template>`, use plain `key`, not `pp-key` |
43
- | Events | native `onclick`, `oninput`, `onchange`, `onsubmit` | `pp-reactive-v2.js` | first-party events belong in `on*` attributes; event scope exposes `event`, `e`, `$event`, `target`, `currentTarget`, and `el`; normal form submits should use `Object.fromEntries(new FormData(event.currentTarget).entries())` instead of per-input refs; avoid id-driven `querySelector`/`addEventListener` for normal UI |
43
+ | Events | native `onclick`, `oninput`, `onchange`, `onsubmit` | `pp-reactive-v2.js` | first-party events belong in `on*` attributes; event scope exposes `event`, `e`, `$event`, `target`, `currentTarget`, and `el`; normal form submits should use `Object.fromEntries(new FormData(event.currentTarget).entries())` instead of per-input refs; avoid id-driven `querySelector`/`addEventListener` for normal UI |
44
44
  | RPC | `pp.rpc(...)` in scripts, `@rpc()` in Python | `pp-reactive-v2.js`, `casp/rpc.py` | use `pp.rpc`, not legacy `pp.fetchFunction`; protected actions use `@rpc(require_auth=True)` |
45
- | Upload progress | `pp.rpc(..., { onUploadProgress })` | `pp-reactive-v2.js`, `casp/rpc.py` | XHR path is used for progress callbacks; replace state from returned payload |
45
+ | Upload progress | `pp.rpc(..., { onUploadProgress })` | `pp-reactive-v2.js`, `casp/rpc.py` | XHR path is used for progress callbacks; the callback receives `{ loaded, total, percent }` and `percent` can be `null` when length is not computable; replace state from returned payload |
46
46
  | Streaming | `pp.rpc(..., { onStream })` | `pp-reactive-v2.js`, `casp/rpc.py`, `casp/streaming.py` | server generators become SSE responses |
47
47
  | SPA navigation | `body[pp-spa="true"]`, links | `pp-reactive-v2.js`, `main.py` | same-origin eligible links intercept; root-layout mismatches hard reload |
48
48
  | Scroll restoration | `pp-reset-scroll="true"` | `pp-reactive-v2.js` | push navigation resets window; mark only content panes that should reset |
@@ -63,32 +63,32 @@ If an inspected browser DOM disagrees with authored template source, remember th
63
63
  - Author one root element or one imported `x-*` root per route, layout, or component template.
64
64
  - Keep any owned plain `<script>` inside that same root.
65
65
  - Do not handwrite `pp-component`, `type="text/pp"`, `data-pp-ref`, `pp-owner`, `pp-event-owner`, or other runtime-managed attributes.
66
- - Use `pp.rpc(...)` for current browser-to-server calls.
67
- - Use native `on*` attributes for button clicks, form submits, input changes, filters, toggles, and menus instead of adding ids and manual listeners.
68
- - For ordinary form submits, bind `onsubmit` on the form and read named fields with `Object.fromEntries(new FormData(event.currentTarget).entries())`; reserve `pp-ref` for imperative access rather than routine payload collection.
69
- - Use lowercase provider tags such as `<themecontext.provider>` and `pp.context(...)` for context.
66
+ - Use `pp.rpc(...)` for current browser-to-server calls.
67
+ - Use native `on*` attributes for button clicks, form submits, input changes, filters, toggles, and menus instead of adding ids and manual listeners.
68
+ - For ordinary form submits, bind `onsubmit` on the form and read named fields with `Object.fromEntries(new FormData(event.currentTarget).entries())`; reserve `pp-ref` for imperative access rather than routine payload collection.
69
+ - Use lowercase provider tags such as `<themecontext.provider>` and `pp.context(...)` for context.
70
70
  - Use `pp-for` only on `<template>` and plain `key` for keyed lists.
71
71
  - Prefer PulsePoint state and directives over manual `innerHTML` repainting.
72
72
  - Keep direct DOM APIs inside `pp.ref(...)` plus `pp.effect(...)` only when a third-party or browser API integration actually requires them.
73
73
 
74
74
  ## Compact Examples
75
75
 
76
- Context provider:
77
-
78
- ```html
79
- <section>
80
- <script>
81
- const ThemeContext = pp.createContext("light");
76
+ Context provider:
77
+
78
+ ```html
79
+ <section>
80
+ <script>
81
+ const ThemeContext = pp.createContext("light");
82
82
  const [theme, setTheme] = pp.state("dark");
83
83
  </script>
84
84
 
85
- <themecontext.provider value="{theme}">
86
- <button onclick="setTheme(theme === 'dark' ? 'light' : 'dark')">
87
- Theme: {theme}
88
- </button>
89
- </themecontext.provider>
90
- </section>
91
- ```
85
+ <themecontext.provider value="{theme}">
86
+ <button onclick="setTheme(theme === 'dark' ? 'light' : 'dark')">
87
+ Theme: {theme}
88
+ </button>
89
+ </themecontext.provider>
90
+ </section>
91
+ ```
92
92
 
93
93
  Upload progress:
94
94
 
@@ -104,7 +104,7 @@ Upload progress:
104
104
  if (!file) return;
105
105
 
106
106
  await pp.rpc("upload_asset", { file }, {
107
- onUploadProgress: (event) => setProgress(event.percentage ?? 0),
107
+ onUploadProgress: (event) => setProgress(Math.round(event.percent ?? 0)),
108
108
  });
109
109
  }
110
110
  </script>
@@ -115,11 +115,11 @@ Grouped shell scroll reset:
115
115
 
116
116
  ```html
117
117
  <section class="dashboard-shell">
118
- <aside class="dashboard-sidebar">...</aside>
119
- <main class="dashboard-content" pp-reset-scroll="true">
120
- <slot />
121
- </main>
122
- </section>
118
+ <aside class="dashboard-sidebar">...</aside>
119
+ <main class="dashboard-content" pp-reset-scroll="true">
120
+ <slot />
121
+ </main>
122
+ </section>
123
123
  ```
124
124
 
125
125
  ## Verification Prompts