caspian-utils 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/components.md +249 -0
- package/dist/docs/index.md +5 -3
- package/dist/docs/project-structure.md +22 -4
- package/dist/docs/pulsepoint.md +9 -1
- package/dist/docs/routing.md +39 -2
- package/package.json +1 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Components
|
|
3
|
+
description: Create reusable Caspian components as `@component` Python functions, import them into templates as `<MyComponent />`, and split interactive UI into same-name HTML templates when PulsePoint logic is involved.
|
|
4
|
+
related:
|
|
5
|
+
title: Related docs
|
|
6
|
+
description: Use the structure guide for file placement, the routing guide for route templates, the PulsePoint guide for browser-side scripts, and the data guide for component-owned RPC flows.
|
|
7
|
+
links:
|
|
8
|
+
- /docs/project-structure
|
|
9
|
+
- /docs/routing
|
|
10
|
+
- /docs/pulsepoint
|
|
11
|
+
- /docs/fetch-data
|
|
12
|
+
- /docs/index
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Components in Caspian are Python functions decorated with `@component`.
|
|
16
|
+
|
|
17
|
+
Import them into a template with an `@import` comment, then render them with JSX-style tags such as `<MyComponent />` or `<MyComponent>...</MyComponent>`.
|
|
18
|
+
|
|
19
|
+
For the current workspace, component tooling scans Python files under the paths listed in `caspian.config.json`. Right now that means `src/`, so `src/components/` is the clean default location for reusable UI, even though any scanned path under `src/` can work.
|
|
20
|
+
|
|
21
|
+
## Mental Model
|
|
22
|
+
|
|
23
|
+
- Use a Python component when you want a reusable server-rendered UI building block.
|
|
24
|
+
- Return an HTML string directly for small presentational components.
|
|
25
|
+
- 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.
|
|
26
|
+
- Keep page-level workflows in `src/app/`, and move reusable UI into components.
|
|
27
|
+
|
|
28
|
+
## Basic Component
|
|
29
|
+
|
|
30
|
+
This is the simplest pattern: accept props, merge classes or attributes, and return HTML.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from casp.component_decorator import component
|
|
34
|
+
from casp.html_attrs import get_attributes, merge_classes
|
|
35
|
+
|
|
36
|
+
@component
|
|
37
|
+
def Container(children: str = "", **props) -> str:
|
|
38
|
+
incoming_class = props.pop("class", "")
|
|
39
|
+
final_class = merge_classes("mx-auto max-w-7xl px-4", incoming_class)
|
|
40
|
+
|
|
41
|
+
attributes = get_attributes({
|
|
42
|
+
"class": final_class,
|
|
43
|
+
}, props)
|
|
44
|
+
|
|
45
|
+
return f'<div {attributes}>{children}</div>'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Notes:
|
|
49
|
+
|
|
50
|
+
- `children` receives the inner content passed between opening and closing tags.
|
|
51
|
+
- `**props` lets the component accept additional HTML attributes such as `id`, `data-*`, and `aria-*`.
|
|
52
|
+
- The runtime normalizes `class`, `className`, and `class_name` into a merged `class` value before the component is called.
|
|
53
|
+
|
|
54
|
+
## Import And Use Components
|
|
55
|
+
|
|
56
|
+
Import components inside the HTML template where they are used.
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<!-- @import Container from "../components" -->
|
|
60
|
+
|
|
61
|
+
<Container class="py-10">
|
|
62
|
+
<h1>Dashboard</h1>
|
|
63
|
+
</Container>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The import comment is the bridge between the Python component file and the JSX-style tag.
|
|
67
|
+
|
|
68
|
+
- `<!-- @import Container from "../components" -->` resolves `Container.py` from that folder.
|
|
69
|
+
- The component function name should match the tag name you render, such as `Container` for `<Container />`.
|
|
70
|
+
- Grouped imports and aliases are also supported, for example `<!-- @import { Button, Card as UserCard } from "../components/ui" -->`.
|
|
71
|
+
|
|
72
|
+
## Template-Backed Components
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
`Counter.py`
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from casp.component_decorator import component, render_html
|
|
80
|
+
|
|
81
|
+
@component
|
|
82
|
+
def Counter(label: str = "Clicks") -> str:
|
|
83
|
+
return render_html(__file__, {
|
|
84
|
+
"label": label,
|
|
85
|
+
})
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`Counter.html`
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<div>
|
|
92
|
+
<h3>[[ label ]]</h3>
|
|
93
|
+
<button onclick="setCount(count + 1)">
|
|
94
|
+
{count}
|
|
95
|
+
</button>
|
|
96
|
+
|
|
97
|
+
<script type="text/pp">
|
|
98
|
+
const [count, setCount] = pp.state(0);
|
|
99
|
+
</script>
|
|
100
|
+
</div>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use this split when:
|
|
104
|
+
|
|
105
|
+
- Python is shaping props, loading data, or exposing server-side helpers.
|
|
106
|
+
- The template has enough markup that returning a raw f-string becomes noisy.
|
|
107
|
+
- PulsePoint state, effects, refs, or event handlers belong next to the component markup.
|
|
108
|
+
|
|
109
|
+
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.
|
|
110
|
+
|
|
111
|
+
## Auto-Injected `pp-component` And PulsePoint Scripts
|
|
112
|
+
|
|
113
|
+
Treat `pp-component="componentName"` as framework output, not authored source.
|
|
114
|
+
|
|
115
|
+
- Do not manually add `pp-component` to route templates or component HTML files.
|
|
116
|
+
- The Python render pipeline injects `pp-component` onto the single root element during render.
|
|
117
|
+
- Add `<script type="text/pp">` only when the route or component actually needs PulsePoint logic.
|
|
118
|
+
- When you do add a PulsePoint script, keep it inside that single root element.
|
|
119
|
+
|
|
120
|
+
Examples in runtime docs often show rendered HTML that already contains `pp-component`. That is the final output shape, not the normal authoring pattern.
|
|
121
|
+
|
|
122
|
+
### Authored Source vs Rendered Output
|
|
123
|
+
|
|
124
|
+
Author this:
|
|
125
|
+
|
|
126
|
+
`Counter.html`
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<div>
|
|
130
|
+
<h3>[[ label ]]</h3>
|
|
131
|
+
<button onclick="setCount(count + 1)">
|
|
132
|
+
{count}
|
|
133
|
+
</button>
|
|
134
|
+
|
|
135
|
+
<script type="text/pp">
|
|
136
|
+
const [count, setCount] = pp.state(0);
|
|
137
|
+
</script>
|
|
138
|
+
</div>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Do not author this by hand:
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<div pp-component="counter_ab12cd34">
|
|
145
|
+
<h3>Clicks</h3>
|
|
146
|
+
<button onclick="setCount(count + 1)">0</button>
|
|
147
|
+
|
|
148
|
+
<script type="text/pp">
|
|
149
|
+
const [count, setCount] = pp.state(0);
|
|
150
|
+
</script>
|
|
151
|
+
</div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The second example is the runtime shape after the Python side injects `pp-component` onto the component root.
|
|
155
|
+
|
|
156
|
+
## Single-Root Rule
|
|
157
|
+
|
|
158
|
+
Every component HTML template must render exactly one top-level lowercase HTML element.
|
|
159
|
+
|
|
160
|
+
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.
|
|
161
|
+
|
|
162
|
+
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.
|
|
163
|
+
|
|
164
|
+
Valid:
|
|
165
|
+
|
|
166
|
+
```html
|
|
167
|
+
<div class="card">
|
|
168
|
+
<h2>Title</h2>
|
|
169
|
+
</div>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Invalid:
|
|
173
|
+
|
|
174
|
+
```html
|
|
175
|
+
<h2>Title</h2>
|
|
176
|
+
<p>Body</p>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Also avoid making another component tag the root of the HTML file. The root must be a normal lowercase HTML element such as `<div>`, `<section>`, or `<article>`.
|
|
180
|
+
|
|
181
|
+
Think about this rule the same way you would in a React component: one parent element per template root. In Caspian, that requirement exists so the Python renderer has exactly one place to attach `pp-component`.
|
|
182
|
+
|
|
183
|
+
## Props, Types, And Children
|
|
184
|
+
|
|
185
|
+
Props come from the attributes on the component tag.
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from typing import Any, Literal
|
|
189
|
+
|
|
190
|
+
from casp.component_decorator import component
|
|
191
|
+
from casp.html_attrs import get_attributes, merge_classes
|
|
192
|
+
|
|
193
|
+
ButtonVariant = Literal["default", "outline", "destructive"]
|
|
194
|
+
|
|
195
|
+
@component
|
|
196
|
+
def Button(
|
|
197
|
+
children: Any = "",
|
|
198
|
+
variant: ButtonVariant = "default",
|
|
199
|
+
**props,
|
|
200
|
+
) -> str:
|
|
201
|
+
classes = {
|
|
202
|
+
"default": "btn btn-primary",
|
|
203
|
+
"outline": "btn btn-outline",
|
|
204
|
+
"destructive": "btn btn-danger",
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
incoming_class = props.pop("class", "")
|
|
208
|
+
attrs = get_attributes({
|
|
209
|
+
"class": merge_classes(classes[variant], incoming_class),
|
|
210
|
+
}, props)
|
|
211
|
+
|
|
212
|
+
return f'<button {attrs}>{children}</button>'
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Use typed parameters when you want better editor hints and stricter component APIs. `Literal[...]` is a good fit for variants, sizes, or other closed sets of values.
|
|
216
|
+
|
|
217
|
+
## Async Components
|
|
218
|
+
|
|
219
|
+
Components can also be async. Use this only when the component render contract really depends on awaited work such as a database query, service call, or file read.
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
from casp.component_decorator import component, render_html
|
|
223
|
+
|
|
224
|
+
@component
|
|
225
|
+
async def ProfileCard(user_id: str) -> str:
|
|
226
|
+
user = await get_user_by_id(user_id)
|
|
227
|
+
|
|
228
|
+
return render_html(__file__, {
|
|
229
|
+
"user": user,
|
|
230
|
+
})
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Keep synchronous components as the default. Switch to `async def` only when the component itself needs awaited I/O.
|
|
234
|
+
|
|
235
|
+
## Best Practices
|
|
236
|
+
|
|
237
|
+
- Put reusable components in `src/components/` and keep route files in `src/app/`.
|
|
238
|
+
- If the component includes PulsePoint behavior, prefer a thin Python wrapper plus a same-name `.html` template.
|
|
239
|
+
- Keep the component file name, exported function name, and tag name aligned, such as `Button.py`, `def Button(...)`, and `<Button />`.
|
|
240
|
+
- Accept `children` or `**props` when the component should support nested content.
|
|
241
|
+
- Keep page-level data loading in `page()` when the data is not intrinsic to the component itself.
|
|
242
|
+
- If you add `@rpc()` functions inside a component file, keep their names globally unique because component RPCs are not route-scoped.
|
|
243
|
+
|
|
244
|
+
## Related Reading
|
|
245
|
+
|
|
246
|
+
- Read [project-structure.md](./project-structure.md) for where reusable components should live.
|
|
247
|
+
- Read [routing.md](./routing.md) when importing components into route templates.
|
|
248
|
+
- Read [pulsepoint.md](./pulsepoint.md) for the runtime contract of `pp-component`, `script[type="text/pp"]`, props, and browser-side reactivity.
|
|
249
|
+
- Read [fetch-data.md](./fetch-data.md) when a component needs browser-triggered `pp.rpc()` calls or component-owned server actions.
|
package/dist/docs/index.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Caspian Docs
|
|
3
|
-
description: Caspian documentation with AI-aware routing to the right local docs before framework-specific code generation or project setup changes, with PulsePoint, RPC, and Validate as the default app stack.
|
|
3
|
+
description: Caspian documentation with AI-aware routing to the right local docs before framework-specific code generation or project setup changes, with components, PulsePoint, RPC, and Validate as the default app stack.
|
|
4
4
|
related:
|
|
5
5
|
title: Next Steps
|
|
6
|
-
description: Start with installation, review the CLI commands, then use the structure
|
|
6
|
+
description: Start with installation, review the CLI commands, then use the structure and component guides to place Caspian files correctly.
|
|
7
7
|
links:
|
|
8
8
|
- /docs/installation
|
|
9
9
|
- /docs/commands
|
|
10
10
|
- /docs/project-structure
|
|
11
|
+
- /docs/components
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
This directory contains the local Caspian documentation set for quick reference and AI-aware routing.
|
|
@@ -36,6 +37,7 @@ The packaged Caspian docs distributed by the current toolchain also live here:
|
|
|
36
37
|
- `commands.md` - Main Caspian CLI workflows for project creation, generation, updates, and config-aware maintenance
|
|
37
38
|
- `database.md` - Prisma schema, migration, seed, and client-generation workflow for the current workspace, plus Python-side helper caveats
|
|
38
39
|
- `auth.md` - Session-backed authentication with `casp.auth`, centralized `auth_config.py`, decorators, RBAC, and OAuth provider helpers
|
|
40
|
+
- `components.md` - Create reusable Python components, template-backed UI, JSX-style imports, and single-root component templates
|
|
39
41
|
- `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, and client-side behaviors
|
|
40
42
|
- `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
|
|
41
43
|
- `state.md` - Request-scoped server state with `StateManager`, session-backed JSON persistence, and listener callbacks for transient flows
|
|
@@ -52,7 +54,7 @@ If an AI tool needs Caspian project documentation, start with this directory and
|
|
|
52
54
|
Preferred lookup order:
|
|
53
55
|
|
|
54
56
|
1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
|
|
55
|
-
2. Read `database.md` for Prisma setup and ORM usage, `auth.md` for session auth and route protection, `pulsepoint.md` for reactive
|
|
57
|
+
2. 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.
|
|
56
58
|
3. Prefer local docs before generating code, commands, or migration guidance.
|
|
57
59
|
4. Check `node_modules/caspian-utils/dist/docs/` for packaged Caspian docs when local docs need more detail.
|
|
58
60
|
5. Only fall back to upstream documentation when local and packaged markdown do not cover the topic.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Project Structure
|
|
3
|
-
description: Understand the default Caspian project layout so AI agents place
|
|
3
|
+
description: Understand the default Caspian project layout so AI agents place routes, reusable components, PulsePoint templates, RPC actions, validation helpers, auth code, configuration, and database changes in the correct directories.
|
|
4
4
|
related:
|
|
5
5
|
title: Related docs
|
|
6
|
-
description: Start with installation for new apps, then use the auth guide for bootstrap and session wiring, the routing guide to map URLs correctly, and the cache guide when route HTML should be reused safely.
|
|
6
|
+
description: Start with installation for new apps, then use the component guide for reusable UI, the auth guide for bootstrap and session wiring, the routing guide to map URLs correctly, and the cache guide when route HTML should be reused safely.
|
|
7
7
|
links:
|
|
8
8
|
- /docs/installation
|
|
9
|
+
- /docs/components
|
|
9
10
|
- /docs/auth
|
|
10
11
|
- /docs/routing
|
|
11
12
|
- /docs/cache
|
|
@@ -19,13 +20,14 @@ This page explains the default layout of a Caspian application, where Caspian co
|
|
|
19
20
|
|
|
20
21
|
Caspian uses a lean project layout that keeps application code in `src`, database files in `prisma`, static assets in `public`, configuration in `caspian.config.json`, and framework internals in the installed package.
|
|
21
22
|
|
|
22
|
-
In that layout, the default stack is PulsePoint in
|
|
23
|
+
In that layout, the default stack is Python components for reusable UI, PulsePoint in templates for reactive browser behavior, RPC for browser-triggered server calls, and `casp.validate` for input validation at route and action boundaries.
|
|
23
24
|
|
|
24
25
|
For public pages that can safely reuse rendered HTML, Caspian also supports route-level page caching through `casp.cache_handler`.
|
|
25
26
|
|
|
26
27
|
## Top-Level Areas
|
|
27
28
|
|
|
28
29
|
- `src/` contains routes, page templates, styles, and shared libraries.
|
|
30
|
+
- `src/components/` contains reusable Python components and optional same-name HTML templates.
|
|
29
31
|
- `src/lib/auth/auth_config.py` contains auth-specific configuration for the app.
|
|
30
32
|
- `prisma/` contains the Prisma schema and seed scripts.
|
|
31
33
|
- `public/` contains static assets served directly.
|
|
@@ -50,6 +52,11 @@ my-app/
|
|
|
50
52
|
index.py
|
|
51
53
|
index.html
|
|
52
54
|
globals.css
|
|
55
|
+
components/
|
|
56
|
+
Container.py
|
|
57
|
+
ui/
|
|
58
|
+
Button.py
|
|
59
|
+
Button.html
|
|
53
60
|
lib/
|
|
54
61
|
auth/
|
|
55
62
|
auth_config.py
|
|
@@ -79,6 +86,14 @@ This directory handles file-based routing. Route templates and route-specific ba
|
|
|
79
86
|
|
|
80
87
|
See `routing.md` for the full App Router-style rules for dynamic segments, route groups, and nested layouts.
|
|
81
88
|
|
|
89
|
+
### `src/components/`
|
|
90
|
+
|
|
91
|
+
Use this folder for reusable UI components that should be imported into route templates or other component templates.
|
|
92
|
+
|
|
93
|
+
The common Caspian pattern is a Python file such as `Button.py` with `@component`, optionally paired with a same-name HTML file such as `Button.html` when the component has richer markup or PulsePoint behavior.
|
|
94
|
+
|
|
95
|
+
This workspace's component tooling scans `src/` based on `caspian.config.json`, so `src/components/` is a conventionally clean location, not a hard-coded runtime requirement.
|
|
96
|
+
|
|
82
97
|
### `src/lib/`
|
|
83
98
|
|
|
84
99
|
Use this folder for shared helpers, reusable validators, RPC-facing service wrappers, reusable UI utilities, and app-level support code.
|
|
@@ -138,7 +153,9 @@ The backend logic for the route. This is where route behavior can load first-ren
|
|
|
138
153
|
|
|
139
154
|
### `src/app/index.html`
|
|
140
155
|
|
|
141
|
-
The route template. It supports standard HTML,
|
|
156
|
+
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.
|
|
157
|
+
|
|
158
|
+
Use `components.md` when the task involves authoring reusable `<MyComponent />` tags backed by Python files.
|
|
142
159
|
|
|
143
160
|
### `src/app/globals.css`
|
|
144
161
|
|
|
@@ -165,6 +182,7 @@ If an AI agent is deciding where to make changes, use these rules first.
|
|
|
165
182
|
|
|
166
183
|
- Put route templates and route-specific backend logic in `src/app/`.
|
|
167
184
|
- Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
|
|
185
|
+
- 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.
|
|
168
186
|
- Put shared helpers and reusable libraries in `src/lib/`.
|
|
169
187
|
- Use PulsePoint conventions in route templates for reactive frontend behavior.
|
|
170
188
|
- Use `@rpc()` in route/backend code and `pp.rpc()` in client code for browser-triggered data flows.
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -3,8 +3,9 @@ title: PulsePoint Runtime Guide
|
|
|
3
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.
|
|
4
4
|
related:
|
|
5
5
|
title: Related docs
|
|
6
|
-
description: Read the routing, data-fetching, and project-structure docs alongside the PulsePoint runtime contract.
|
|
6
|
+
description: Read the components, routing, data-fetching, and project-structure docs alongside the PulsePoint runtime contract.
|
|
7
7
|
links:
|
|
8
|
+
- /docs/components
|
|
8
9
|
- /docs/routing
|
|
9
10
|
- /docs/fetch-data
|
|
10
11
|
- /docs/project-structure
|
|
@@ -17,6 +18,8 @@ This file documents the PulsePoint runtime that is currently shipped in `public/
|
|
|
17
18
|
|
|
18
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.
|
|
19
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.
|
|
22
|
+
|
|
20
23
|
PulsePoint is the default reactive frontend layer for Caspian.
|
|
21
24
|
|
|
22
25
|
Do not assume React, Vue, Svelte, JSX, Alpine, HTMX, or older PulsePoint docs unless the task explicitly asks for a different frontend contract.
|
|
@@ -34,6 +37,7 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
|
|
|
34
37
|
- Source authoring under `src/` may use convenience syntax that is transformed before it reaches the browser.
|
|
35
38
|
- This page describes the bundled browser runtime shipped in `public/js/pp-reactive-v2.js`.
|
|
36
39
|
- When source-layer examples conflict with the bundled runtime, follow the bundled runtime.
|
|
40
|
+
- In authored route and component templates, do not add `pp-component` manually. The Python side injects it onto the template root during render.
|
|
37
41
|
- In a `pp-component` root, component logic must live in `<script>`, not plain `<script>`.
|
|
38
42
|
|
|
39
43
|
## Runtime shape
|
|
@@ -45,6 +49,7 @@ A component root is any element with `pp-component`.
|
|
|
45
49
|
Important:
|
|
46
50
|
|
|
47
51
|
- `pp-component` is the instance id used for the component registry, saved state, cached template, parent tracking, and instance lookup.
|
|
52
|
+
- In normal Caspian authoring, `pp-component` arrives from the Python render pipeline on route, layout, and component roots rather than being handwritten in source templates.
|
|
48
53
|
- Reusing the same `pp-component` value for multiple live roots will destroy the previous registered instance and replace it with the new one.
|
|
49
54
|
- If a root is manually constructed with an empty `pp-component`, the runtime will assign an anonymous id. AI-generated markup should still use stable unique ids.
|
|
50
55
|
- `pp.mount()` bootstraps every `[pp-component]` in the document, so do not manually instantiate `Component` in authored app code.
|
|
@@ -55,6 +60,7 @@ Important:
|
|
|
55
60
|
- The runtime only treats `script` as component logic.
|
|
56
61
|
- The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
|
|
57
62
|
- If multiple matching scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
63
|
+
- Component HTML templates must have exactly one top-level lowercase HTML root because the compiler injects `pp-component` onto that root during render.
|
|
58
64
|
- 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.
|
|
59
65
|
- Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
|
|
60
66
|
- The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
|
|
@@ -93,6 +99,8 @@ Example:
|
|
|
93
99
|
</div>
|
|
94
100
|
```
|
|
95
101
|
|
|
102
|
+
That example shows runtime HTML after mountable roots already exist. In authored Caspian templates, you normally write the root without `pp-component` and let the Python renderer inject it before the browser runtime sees the HTML.
|
|
103
|
+
|
|
96
104
|
## Hooks and runtime API
|
|
97
105
|
|
|
98
106
|
Hooks exposed inside component scripts through `pp`:
|
package/dist/docs/routing.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Routing
|
|
3
|
-
description: Understand Caspian's Next.js App Router-style file-based routing, including src/app conventions, index files, dynamic segments, route groups, and
|
|
3
|
+
description: Understand Caspian's Next.js App Router-style file-based routing, including src/app conventions, index files, dynamic segments, route groups, nested layouts, and component-friendly route templates.
|
|
4
4
|
related:
|
|
5
5
|
title: Related docs
|
|
6
|
-
description: Read the structure guide first, then use the metadata guide for SEO fields, the cache guide for route-level HTML reuse, and the PulsePoint runtime guide for interactive route templates.
|
|
6
|
+
description: Read the structure guide first, then use the components guide for reusable UI, the metadata guide for SEO fields, the cache guide for route-level HTML reuse, and the PulsePoint runtime guide for interactive route templates.
|
|
7
7
|
links:
|
|
8
8
|
- /docs/project-structure
|
|
9
|
+
- /docs/components
|
|
9
10
|
- /docs/cache
|
|
10
11
|
- /docs/metadata
|
|
11
12
|
- /docs/pulsepoint
|
|
@@ -73,6 +74,42 @@ Examples:
|
|
|
73
74
|
|
|
74
75
|
Use `index.html` for the route template. This is the route's view layer.
|
|
75
76
|
|
|
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
|
+
|
|
79
|
+
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
|
+
|
|
81
|
+
That means route templates follow the same single-root discipline as component templates: one root element, no sibling roots, and PulsePoint scripts kept inside that root when needed.
|
|
82
|
+
|
|
83
|
+
Example authored route template:
|
|
84
|
+
|
|
85
|
+
```html
|
|
86
|
+
<!-- @import StatsCard from "../components" -->
|
|
87
|
+
|
|
88
|
+
<section class="dashboard-shell">
|
|
89
|
+
<StatsCard title="Users" value="42" />
|
|
90
|
+
|
|
91
|
+
<script type="text/pp">
|
|
92
|
+
const [filter, setFilter] = pp.state("all");
|
|
93
|
+
</script>
|
|
94
|
+
</section>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Rendered shape at runtime:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<section pp-component="page_a1b2c3d4" class="dashboard-shell">
|
|
101
|
+
<div pp-component="statscard_e5f6g7h8" title="Users" value="42">
|
|
102
|
+
...
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<script type="text/pp">
|
|
106
|
+
const [filter, setFilter] = pp.state("all");
|
|
107
|
+
</script>
|
|
108
|
+
</section>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Write the first form. Caspian produces the second form.
|
|
112
|
+
|
|
76
113
|
### `index.py`
|
|
77
114
|
|
|
78
115
|
Use `index.py` when the route needs metadata or async server-side logic. Because Caspian runs on FastAPI, the page entry should be async.
|