caspian-utils 0.1.12 → 0.1.14
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/commands.md +26 -26
- package/dist/docs/components.md +86 -50
- package/dist/docs/database.md +62 -62
- package/dist/docs/file-conventions.md +3 -3
- package/dist/docs/project-structure.md +9 -6
- package/dist/docs/pulsepoint-runtime-map.md +1 -0
- package/dist/docs/pulsepoint.md +19 -11
- package/dist/docs/routing.md +28 -15
- package/package.json +1 -1
package/dist/docs/commands.md
CHANGED
|
@@ -103,17 +103,17 @@ Do not use `npm run build` as the default validation step for routine route, fea
|
|
|
103
103
|
```bash
|
|
104
104
|
npx prisma migrate dev
|
|
105
105
|
|
|
106
|
-
# If the change requires refreshed seed data:
|
|
107
|
-
npx prisma generate
|
|
108
|
-
# Destructive-data warning: ask the user for explicit approval before running this.
|
|
109
|
-
npx prisma db seed
|
|
110
|
-
|
|
111
|
-
npx ppy generate
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Use when `prisma/schema.prisma` changes and you need migrations, seed flow, and the generated Python ORM layer to stay aligned.
|
|
115
|
-
|
|
116
|
-
`npx prisma db seed` is a delicate operation because the configured seed script may clean tables or replace existing rows before inserting seed data. Before running it, an AI agent must say the exact command it plans to run, warn that it can delete or overwrite database data, confirm the active datasource when practical, and wait for explicit user approval.
|
|
106
|
+
# If the change requires refreshed seed data:
|
|
107
|
+
npx prisma generate
|
|
108
|
+
# Destructive-data warning: ask the user for explicit approval before running this.
|
|
109
|
+
npx prisma db seed
|
|
110
|
+
|
|
111
|
+
npx ppy generate
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use when `prisma/schema.prisma` changes and you need migrations, seed flow, and the generated Python ORM layer to stay aligned.
|
|
115
|
+
|
|
116
|
+
`npx prisma db seed` is a delicate operation because the configured seed script may clean tables or replace existing rows before inserting seed data. Before running it, an AI agent must say the exact command it plans to run, warn that it can delete or overwrite database data, confirm the active datasource when practical, and wait for explicit user approval.
|
|
117
117
|
|
|
118
118
|
## 2. Script Guardrails
|
|
119
119
|
|
|
@@ -182,12 +182,12 @@ In skip-prompt mode, the default feature values are:
|
|
|
182
182
|
|
|
183
183
|
- `backendOnly: false`
|
|
184
184
|
- `tailwindcss: false`
|
|
185
|
-
- `typescript: false`
|
|
186
|
-
- `mcp: false`
|
|
187
|
-
- `prisma: false`
|
|
188
|
-
- `websocket: false`
|
|
189
|
-
|
|
190
|
-
If a project needs WebSockets after creation, set `websocket: true` in `caspian.config.json`, then run the project update workflow so framework-managed files and any scaffolded socket surfaces stay aligned before assuming `@app.websocket(...)` routes or `src/lib/websocket/**` exist.
|
|
185
|
+
- `typescript: false`
|
|
186
|
+
- `mcp: false`
|
|
187
|
+
- `prisma: false`
|
|
188
|
+
- `websocket: false`
|
|
189
|
+
|
|
190
|
+
If a project needs WebSockets after creation, set `websocket: true` in `caspian.config.json`, then run the project update workflow so framework-managed files and any scaffolded socket surfaces stay aligned before assuming `@app.websocket(...)` routes or `src/lib/websocket/**` exist.
|
|
191
191
|
|
|
192
192
|
### 4.2 Backend-only combinations
|
|
193
193
|
|
|
@@ -598,18 +598,18 @@ The create and update commands above are not the whole maintenance story for a P
|
|
|
598
598
|
```bash
|
|
599
599
|
npx prisma migrate dev
|
|
600
600
|
|
|
601
|
-
# Only when seed flow or prisma/seed.ts depends on the new schema:
|
|
602
|
-
npx prisma generate
|
|
603
|
-
# Destructive-data warning: ask the user for explicit approval before running this.
|
|
604
|
-
npx prisma db seed
|
|
605
|
-
|
|
606
|
-
npx ppy generate
|
|
607
|
-
```
|
|
601
|
+
# Only when seed flow or prisma/seed.ts depends on the new schema:
|
|
602
|
+
npx prisma generate
|
|
603
|
+
# Destructive-data warning: ask the user for explicit approval before running this.
|
|
604
|
+
npx prisma db seed
|
|
605
|
+
|
|
606
|
+
npx ppy generate
|
|
607
|
+
```
|
|
608
608
|
|
|
609
609
|
Use this order:
|
|
610
610
|
|
|
611
611
|
1. Run `npx prisma migrate dev` first so migrations and the development database stay aligned.
|
|
612
|
-
2. If `prisma/seed.ts` or seed data depends on the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed` because the seed script may delete or overwrite database data.
|
|
612
|
+
2. If `prisma/seed.ts` or seed data depends on the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed` because the seed script may delete or overwrite database data.
|
|
613
613
|
3. Run `npx ppy generate` last so the generated Python ORM layer matches the updated Prisma schema.
|
|
614
614
|
|
|
615
615
|
Do not manually create or edit these generated files:
|
|
@@ -682,7 +682,7 @@ If an AI agent is deciding which command flow to use, apply these rules first.
|
|
|
682
682
|
- Treat `--tailwindcss` and `--typescript` as frontend-oriented flags. They are not meaningful in normal backend-only scaffolds.
|
|
683
683
|
- Treat starter kit defaults as a base layer that can be overridden by explicit flags.
|
|
684
684
|
- Read `caspian.config.json` before running update commands.
|
|
685
|
-
- When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first, then any required seed refresh, then `npx ppy generate`. Before running `npx prisma db seed`, warn the user that it can delete or overwrite data and wait for explicit approval.
|
|
685
|
+
- When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first, then any required seed refresh, then `npx ppy generate`. Before running `npx prisma db seed`, warn the user that it can delete or overwrite data and wait for explicit approval.
|
|
686
686
|
- Never hand-edit generated Python ORM files under `src/lib/prisma/` or `settings/prisma-schema.json`.
|
|
687
687
|
- Read [database.md](./database.md) before generating Prisma schema, migration, seed, or ORM guidance.
|
|
688
688
|
- Read [routing.md](./routing.md) before generating or modifying route folders under `src/app/`.
|
package/dist/docs/components.md
CHANGED
|
@@ -16,7 +16,7 @@ related:
|
|
|
16
16
|
|
|
17
17
|
Components in Caspian are implemented as Python functions decorated with `@component`.
|
|
18
18
|
|
|
19
|
-
In authored HTML, import them with
|
|
19
|
+
In authored route, layout, or component HTML files, import them with a top-of-file `@import` comment and render them with HTML-first kebab-cased `x-*` tags such as `<x-my-component />` or `<x-my-component>...</x-my-component>`.
|
|
20
20
|
|
|
21
21
|
Treat that `x-*` form as the current Caspian component contract for authored templates.
|
|
22
22
|
|
|
@@ -26,12 +26,13 @@ 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 `html(...)` to keep markup, server interpolation, and a PulsePoint `<script>` inline in one Python file (single-file component) for small
|
|
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
|
-
-
|
|
34
|
-
-
|
|
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 a small or medium UI responsibility. 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
|
+
- Split UI by responsibility the way you would split React components. Single-file component authoring is a file shape, not permission to put a full page, dashboard, or all tab panels into one Python file.
|
|
34
|
+
- 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.
|
|
35
|
+
- Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
|
|
35
36
|
|
|
36
37
|
## Framework Internals Note
|
|
37
38
|
|
|
@@ -107,7 +108,7 @@ Authored PulsePoint examples:
|
|
|
107
108
|
|
|
108
109
|
## Import And Use Components
|
|
109
110
|
|
|
110
|
-
Place component imports at the top of the HTML template, above the authored root element.
|
|
111
|
+
Place component imports at the top of the HTML template, above the authored root element.
|
|
111
112
|
|
|
112
113
|
```html
|
|
113
114
|
<!-- @import Container from "../components" -->
|
|
@@ -121,7 +122,7 @@ The import comment is the bridge between the Python component export and the `x-
|
|
|
121
122
|
|
|
122
123
|
In section-based apps, follow the same mental model as the Next.js App Router: import shared shell components such as sidebars, topbars, breadcrumbs, and section frames in the parent folder's `layout.html`, then import page-specific components in each child route's `index.html`.
|
|
123
124
|
|
|
124
|
-
Treat `<!-- @import ... -->` as a file-level directive, not as rendered markup. It does not count as the template root, and it
|
|
125
|
+
Treat `<!-- @import ... -->` as a file-level directive, not as rendered markup. It does not count as the template root, and it must not be nested inside the root wrapper element. In route and layout files, this means the import comments come before the first authored tag in `index.html` or `layout.html`.
|
|
125
126
|
|
|
126
127
|
- `<!-- @import Container from "../components" -->` resolves `Container.py` from that folder.
|
|
127
128
|
- The exported component name maps to the tag you render by kebab-casing it and prefixing `x-`, such as `Container` for `<x-container />` or `CommandDialog` for `<x-command-dialog />`.
|
|
@@ -141,14 +142,14 @@ Good:
|
|
|
141
142
|
</section>
|
|
142
143
|
```
|
|
143
144
|
|
|
144
|
-
Bad:
|
|
145
|
-
|
|
146
|
-
```html
|
|
147
|
-
<section class="auth-panel auth-panel-compact fade-up">
|
|
148
|
-
<!-- @import { Button, Input, Label } from "../../../lib/maddex" -->
|
|
149
|
-
<
|
|
150
|
-
</section>
|
|
151
|
-
```
|
|
145
|
+
Bad:
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<section class="auth-panel auth-panel-compact fade-up">
|
|
149
|
+
<!-- @import { Button, Input, Label } from "../../../lib/maddex" -->
|
|
150
|
+
<x-label>Email</x-label>
|
|
151
|
+
</section>
|
|
152
|
+
```
|
|
152
153
|
|
|
153
154
|
### Same-File Component Exports
|
|
154
155
|
|
|
@@ -221,11 +222,13 @@ Use this split when:
|
|
|
221
222
|
|
|
222
223
|
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.
|
|
223
224
|
|
|
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
|
-
`
|
|
225
|
+
## Single-File Components With `html(...)`
|
|
226
|
+
|
|
227
|
+
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)`.
|
|
228
|
+
|
|
229
|
+
Single-file does not mean one file per page. Treat each single-file component like a React component with one clear responsibility. If a screen has an overview tab, an activity tab, and a settings tab, those substantial panels should normally be separate components such as `OverviewTab.py`, `ActivityTab.py`, and `SettingsTab.py`, assembled by a small tab shell or the route template. If one component is growing because it owns unrelated forms, tables, charts, and action bars, split those responsibilities and pass the needed data as props.
|
|
230
|
+
|
|
231
|
+
`html(...)` renders the string through the same Jinja environment Caspian uses for template files, so three brace dialects coexist without colliding:
|
|
229
232
|
|
|
230
233
|
- `{{ value }}` is server render (Python to HTML).
|
|
231
234
|
- `{{ value | json }}` safely serializes a server value into a `<script>`.
|
|
@@ -256,11 +259,13 @@ def UserCard(user, **props):
|
|
|
256
259
|
|
|
257
260
|
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
261
|
|
|
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
|
-
-
|
|
263
|
-
-
|
|
262
|
+
Rules for inline `html(...)`:
|
|
263
|
+
|
|
264
|
+
- The single-root rule still applies: exactly one top-level element with any `<script>` nested inside it.
|
|
265
|
+
- Keep the component focused on one UI responsibility. Prefer composing several single-file components over one long Python file that contains multiple sections, tab panels, or workflows.
|
|
266
|
+
- Pass data, labels, variants, current selection, counts, permissions, or callbacks as props instead of making child components reach back into route markup or duplicate parent state.
|
|
267
|
+
- 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`.
|
|
268
|
+
- A `children` value is auto-marked safe (parity with `render_html(...)`), so `{{ children }}` renders nested component markup correctly without `| safe`.
|
|
264
269
|
- 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
270
|
- 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
271
|
|
|
@@ -314,14 +319,14 @@ def Counter(label: str = "Clicks", **props):
|
|
|
314
319
|
|
|
315
320
|
The fix is always the same: move the `<script>` above the root's closing tag so the whole return value is one element. Caspian has no fragment syntax — the single root must be a real native element — so if a component genuinely needs sibling-looking sections, wrap them all in one outer element (a `<div>` or `<section>`) and keep the script inside that wrapper. This rule is identical for the two-file `render_html(...)` form, so the same `.html` file must also end with `</script></root>` rather than `</root>` followed by a trailing `<script>`.
|
|
316
321
|
|
|
317
|
-
## Component Imports: Python Imports Or `@import`
|
|
318
|
-
|
|
319
|
-
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.
|
|
320
|
-
|
|
321
|
-
1. The `<!-- @import Name from "path" -->` HTML comment, resolved by path string.
|
|
322
|
-
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.
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
## Component Imports: Python Imports Or `@import`
|
|
323
|
+
|
|
324
|
+
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.
|
|
325
|
+
|
|
326
|
+
1. The `<!-- @import Name from "path" -->` HTML comment, resolved by path string. Use this as a top-of-file directive in authored `.html` templates, above the single root element.
|
|
327
|
+
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.
|
|
328
|
+
|
|
329
|
+
For single-file Python components that return `html("""...""")`, use real Python imports for child components. Do not put `<!-- @import ... -->` inside the returned HTML string. The compiler can parse local import comments, but that is a permissive fallback, not the authoring pattern for single-file components. Python imports are the unambiguous source of truth for which component a tag refers to, they give normal editor navigation, and they prevent 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.
|
|
325
330
|
|
|
326
331
|
```python
|
|
327
332
|
from casp.component_decorator import component, html
|
|
@@ -340,15 +345,15 @@ def Panel(children="", **props):
|
|
|
340
345
|
""", children=children)
|
|
341
346
|
```
|
|
342
347
|
|
|
343
|
-
|
|
348
|
+
Runtime resolution precedence for an `x-*` tag inside a component's own output, lowest to highest:
|
|
349
|
+
|
|
350
|
+
- inherited components from an ancestor template
|
|
351
|
+
- the component's own Python module imports
|
|
352
|
+
- a local `<!-- @import ... -->` in that same template
|
|
353
|
+
|
|
354
|
+
So a component's Python import wins over an inherited same-name component, and an explicit local `@import` wins over both. Authoring guidance is stricter than the runtime fallback: put `@import` comments only at the top of `.html` templates, and use Python imports inside single-file `html(...)` components.
|
|
344
355
|
|
|
345
|
-
-
|
|
346
|
-
- the component's own Python module imports
|
|
347
|
-
- a local `<!-- @import ... -->` in that same template
|
|
348
|
-
|
|
349
|
-
So a component's Python import wins over an inherited same-name component, and an explicit local `@import` wins over both.
|
|
350
|
-
|
|
351
|
-
### Direct-Call Composition (Calling A Component As A Function)
|
|
356
|
+
### Direct-Call Composition (Calling A Component As A Function)
|
|
352
357
|
|
|
353
358
|
A component can compose another component two ways: by writing its `<x-*>` tag, or by calling it as a plain Python function and interpolating the result with `{{ }}` (React-style). Both work, and both resolve nested `<x-*>` tags from the callee's own Python module imports.
|
|
354
359
|
|
|
@@ -377,7 +382,36 @@ Notes:
|
|
|
377
382
|
|
|
378
383
|
### Slot Content Resolves In The Parent Scope
|
|
379
384
|
|
|
380
|
-
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.
|
|
385
|
+
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.
|
|
386
|
+
|
|
387
|
+
## Component Granularity And Responsibility
|
|
388
|
+
|
|
389
|
+
Prefer small, named components that match the responsibility a developer would expect from a React-style component tree.
|
|
390
|
+
|
|
391
|
+
Use this split before writing markup:
|
|
392
|
+
|
|
393
|
+
- Page route: owns page assembly, first-render context, and route-level workflows.
|
|
394
|
+
- Shell component: owns shared layout around a page area, such as a tab list, sidebar, toolbar frame, or dashboard section wrapper.
|
|
395
|
+
- Section component: owns one visible content area, such as an overview panel, analytics section, settings form, activity list, billing table, or user card grid.
|
|
396
|
+
- Leaf component: owns repeated or reusable details, such as cards, rows, badges, buttons with behavior, empty states, and field groups.
|
|
397
|
+
|
|
398
|
+
For example, a tabbed account page should be assembled from focused parts:
|
|
399
|
+
|
|
400
|
+
```text
|
|
401
|
+
src/components/account/
|
|
402
|
+
AccountTabs.py
|
|
403
|
+
AccountOverviewTab.py
|
|
404
|
+
AccountSecurityTab.py
|
|
405
|
+
AccountBillingTab.py
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
`AccountTabs.py` can own the selected tab state and render the tab controls, while each tab panel owns its own markup and receives props such as `user`, `plan`, `sessions`, or `can_edit`. This keeps PulsePoint state close to the interaction that owns it and keeps each file readable.
|
|
409
|
+
|
|
410
|
+
Do not create a single `AccountPage.py` or `DashboardTabs.py` that contains every tab, form, table, metric card, and modal just because `html(...)` makes it possible. When the file starts needing headings in comments to separate unrelated areas, those headings are usually component boundaries.
|
|
411
|
+
|
|
412
|
+
Related subcomponents may live in one Python file only when they are tiny and tightly coupled, such as `Tabs`, `TabsList`, `TabsTrigger`, and `TabsContent` primitives, or a component plus two very small private helpers. For app-specific page chunks, prefer one exported component per file with a name that explains its role.
|
|
413
|
+
|
|
414
|
+
Props are the boundary between components. Pass parent-owned data and configuration down through attributes or direct Python calls, keep local interactive state inside the component that owns the behavior, and use slot content when the parent needs to provide authored child markup. If two sibling chunks need the same server data, load it in the route's `page()` or a shared helper and pass the relevant pieces into each component.
|
|
381
415
|
|
|
382
416
|
## Auto-Injected `pp-component` And PulsePoint Script Type
|
|
383
417
|
|
|
@@ -396,7 +430,7 @@ Every component HTML template must render exactly one authored top-level parent
|
|
|
396
430
|
|
|
397
431
|
The same rule applies to route and layout templates such as `src/app/**/index.html` and `src/app/**/layout.html`. See [routing.md](./routing.md) and [pulsepoint.md](./pulsepoint.md) for the non-component versions of the same authoring contract.
|
|
398
432
|
|
|
399
|
-
Top-of-file `<!-- @import ... -->` directives are allowed before that root element and do not violate the single-root rule.
|
|
433
|
+
Top-of-file `<!-- @import ... -->` directives are allowed before that root element and do not violate the single-root rule. They belong before the root, not inside it.
|
|
400
434
|
|
|
401
435
|
In source, that parent may be a native HTML element or a single imported `x-*` component tag. After component expansion, the template must still resolve to one final native HTML root so Caspian has exactly one place to inject `pp-component`.
|
|
402
436
|
|
|
@@ -508,11 +542,13 @@ async def ProfileCard(user_id: str) -> str:
|
|
|
508
542
|
|
|
509
543
|
Keep synchronous components as the default. Switch to `async def` only when the component itself needs awaited I/O.
|
|
510
544
|
|
|
511
|
-
## Best Practices
|
|
512
|
-
|
|
513
|
-
- Put reusable components in `src/components/` and keep route files in `src/app/`.
|
|
514
|
-
- 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`.
|
|
515
|
-
- 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
|
|
545
|
+
## Best Practices
|
|
546
|
+
|
|
547
|
+
- Put reusable components in `src/components/` and keep route files in `src/app/`.
|
|
548
|
+
- 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`.
|
|
549
|
+
- Default to a single Python file with inline `html(...)` for most focused components, including ones with PulsePoint behavior. Move to a thin Python wrapper plus a same-name `.html` template only when that focused component's markup is large or the `<script>` carries real logic. Both forms render identically, so this is a readability choice, not a capability one.
|
|
550
|
+
- Split by responsibility before switching file shapes. If a component is large because it contains several independent sections, tabs, tables, forms, or workflows, create smaller components first instead of turning it into one huge `.py` plus `.html` pair.
|
|
551
|
+
- In single-file `html(...)` components, resolve child `x-*` tags with real Python imports at the top of the `.py` module. Reserve `<!-- @import ... -->` comments for authored `.html` templates and keep those comments above the root.
|
|
516
552
|
- 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.
|
|
517
553
|
- Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
|
|
518
554
|
- Accept `children` or `**props` when the component should support nested content.
|
package/dist/docs/database.md
CHANGED
|
@@ -14,38 +14,38 @@ related:
|
|
|
14
14
|
|
|
15
15
|
This page documents the Prisma workflow for Caspian projects where `caspian.config.json` enables Prisma.
|
|
16
16
|
|
|
17
|
-
When a project enables Prisma, use `prisma/schema.prisma` for schema management, `prisma.config.ts` for Prisma config and seed wiring, and reuse an app-owned `src/lib/prisma/` package when the project includes one.
|
|
18
|
-
|
|
19
|
-
High-priority rule: when `caspian.config.json` has `prisma: true`, Python-side database access must use the generated Prisma Python ORM. Route `page()` functions, route-owned `@rpc()` actions, auth handlers, upload flows, and shared helpers should import the generated client from `src/lib/prisma/**` instead of inventing direct driver calls, hand-written SQL wrappers, JSON manifests as active stores, app-specific HTTP fetches, or browser-side database fetches. For normal reads and writes, default to Prisma methods such as `find_many`, `find_unique`, `create`, `update`, `delete`, `delete_many`, aggregates, includes, and transactions. Use raw SQL only through Prisma as a narrow fallback when the generated ORM cannot express a query clearly, and treat that fallback as provider-specific code that may break when a project moves between SQLite, MySQL, and PostgreSQL.
|
|
17
|
+
When a project enables Prisma, use `prisma/schema.prisma` for schema management, `prisma.config.ts` for Prisma config and seed wiring, and reuse an app-owned `src/lib/prisma/` package when the project includes one.
|
|
18
|
+
|
|
19
|
+
High-priority rule: when `caspian.config.json` has `prisma: true`, Python-side database access must use the generated Prisma Python ORM. Route `page()` functions, route-owned `@rpc()` actions, auth handlers, upload flows, and shared helpers should import the generated client from `src/lib/prisma/**` instead of inventing direct driver calls, hand-written SQL wrappers, JSON manifests as active stores, app-specific HTTP fetches, or browser-side database fetches. For normal reads and writes, default to Prisma methods such as `find_many`, `find_unique`, `create`, `update`, `delete`, `delete_many`, aggregates, includes, and transactions. Use raw SQL only through Prisma as a narrow fallback when the generated ORM cannot express a query clearly, and treat that fallback as provider-specific code that may break when a project moves between SQLite, MySQL, and PostgreSQL.
|
|
20
20
|
|
|
21
21
|
Treat `caspian.config.json` as the single source of truth for whether Prisma is enabled in a workspace. If `prisma` is false and the user wants Prisma, ask first, then update `caspian.config.json` and run `npx casp update project` before assuming Prisma-managed files exist.
|
|
22
22
|
|
|
23
23
|
## Overview
|
|
24
24
|
|
|
25
|
-
The standard Prisma flow in Caspian is:
|
|
26
|
-
|
|
27
|
-
1. Define models in `prisma/schema.prisma`.
|
|
28
|
-
2. Configure `DATABASE_URL` in `.env`.
|
|
29
|
-
3. Run `npx prisma migrate dev` after schema changes so the development database and migration history stay aligned.
|
|
30
|
-
4. If the change requires seed data, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
31
|
-
5. Run `npx ppy generate` so the Python ORM classes stay aligned with the updated schema.
|
|
32
|
-
6. Reuse the shared Python database layer in `src/lib/prisma/` when Python route or RPC code needs database access, and never hand-edit generated ORM classes.
|
|
33
|
-
|
|
34
|
-
Use this workflow instead of writing raw SQL first. For normal CRUD and relation work, stay on the generated Prisma API. Drop to raw SQL only through Prisma when a query cannot be expressed clearly with the generated client, and assume that raw SQL requires extra review for cross-database portability.
|
|
35
|
-
|
|
36
|
-
## Seed Command Safety
|
|
37
|
-
|
|
38
|
-
Treat `npx prisma db seed` as a destructive data operation, not as a routine regeneration command. A project's `prisma/seed.ts` may delete existing rows, reset lookup tables, replace users, or otherwise rewrite the current datasource before inserting seed data. If `DATABASE_URL` points at a shared, staging, or production database, running the seed command can delete production information.
|
|
39
|
-
|
|
40
|
-
Before any AI agent runs `npx prisma db seed`, it must:
|
|
41
|
-
|
|
42
|
-
1. Read `prisma.config.ts` and `prisma/seed.ts` to understand what the seed command will execute.
|
|
43
|
-
2. State the exact command it intends to run: `npx prisma db seed`.
|
|
44
|
-
3. Warn the user clearly that the command can delete or overwrite database data.
|
|
45
|
-
4. Confirm the active datasource or `DATABASE_URL` when practical, without exposing secrets.
|
|
46
|
-
5. Wait for explicit user approval before executing the command.
|
|
47
|
-
|
|
48
|
-
Do not run `npx prisma db seed` merely because it appears in the standard workflow. Ask first, and only continue after the user has approved that specific seed operation.
|
|
25
|
+
The standard Prisma flow in Caspian is:
|
|
26
|
+
|
|
27
|
+
1. Define models in `prisma/schema.prisma`.
|
|
28
|
+
2. Configure `DATABASE_URL` in `.env`.
|
|
29
|
+
3. Run `npx prisma migrate dev` after schema changes so the development database and migration history stay aligned.
|
|
30
|
+
4. If the change requires seed data, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
31
|
+
5. Run `npx ppy generate` so the Python ORM classes stay aligned with the updated schema.
|
|
32
|
+
6. Reuse the shared Python database layer in `src/lib/prisma/` when Python route or RPC code needs database access, and never hand-edit generated ORM classes.
|
|
33
|
+
|
|
34
|
+
Use this workflow instead of writing raw SQL first. For normal CRUD and relation work, stay on the generated Prisma API. Drop to raw SQL only through Prisma when a query cannot be expressed clearly with the generated client, and assume that raw SQL requires extra review for cross-database portability.
|
|
35
|
+
|
|
36
|
+
## Seed Command Safety
|
|
37
|
+
|
|
38
|
+
Treat `npx prisma db seed` as a destructive data operation, not as a routine regeneration command. A project's `prisma/seed.ts` may delete existing rows, reset lookup tables, replace users, or otherwise rewrite the current datasource before inserting seed data. If `DATABASE_URL` points at a shared, staging, or production database, running the seed command can delete production information.
|
|
39
|
+
|
|
40
|
+
Before any AI agent runs `npx prisma db seed`, it must:
|
|
41
|
+
|
|
42
|
+
1. Read `prisma.config.ts` and `prisma/seed.ts` to understand what the seed command will execute.
|
|
43
|
+
2. State the exact command it intends to run: `npx prisma db seed`.
|
|
44
|
+
3. Warn the user clearly that the command can delete or overwrite database data.
|
|
45
|
+
4. Confirm the active datasource or `DATABASE_URL` when practical, without exposing secrets.
|
|
46
|
+
5. Wait for explicit user approval before executing the command.
|
|
47
|
+
|
|
48
|
+
Do not run `npx prisma db seed` merely because it appears in the standard workflow. Ask first, and only continue after the user has approved that specific seed operation.
|
|
49
49
|
|
|
50
50
|
## Environment Setup
|
|
51
51
|
|
|
@@ -63,7 +63,7 @@ Example for PostgreSQL in async-friendly production environments:
|
|
|
63
63
|
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
For most local development, SQLite is the simplest starting point. For production or higher concurrency workloads, prefer PostgreSQL or MySQL. Regardless of the starting provider, keep application queries on the Prisma ORM whenever possible so schema and query behavior stay easier to migrate across providers.
|
|
66
|
+
For most local development, SQLite is the simplest starting point. For production or higher concurrency workloads, prefer PostgreSQL or MySQL. Regardless of the starting provider, keep application queries on the Prisma ORM whenever possible so schema and query behavior stay easier to migrate across providers.
|
|
67
67
|
|
|
68
68
|
## Global Prisma Configuration
|
|
69
69
|
|
|
@@ -121,7 +121,7 @@ model Post {
|
|
|
121
121
|
After changing the schema, use this order:
|
|
122
122
|
|
|
123
123
|
1. Run `npx prisma migrate dev`.
|
|
124
|
-
2. If seed data or the seed script needs the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
124
|
+
2. If seed data or the seed script needs the new schema, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
125
125
|
3. Run `npx ppy generate` to refresh the generated Python ORM classes.
|
|
126
126
|
|
|
127
127
|
Do not hand-edit generated Prisma or Python ORM output. Treat `prisma/schema.prisma` as the source of truth and regenerate from it.
|
|
@@ -131,8 +131,8 @@ Do not hand-edit generated Prisma or Python ORM output. Treat `prisma/schema.pri
|
|
|
131
131
|
Use these commands for the normal Prisma lifecycle in Caspian:
|
|
132
132
|
|
|
133
133
|
- `npx prisma migrate dev` creates and applies a development migration. This is the first command to run after changing `prisma/schema.prisma`.
|
|
134
|
-
- `npx prisma generate` compiles `schema.prisma` into the generated Node Prisma client. Run it before `npx prisma db seed` when the updated seed flow depends on the new schema.
|
|
135
|
-
- `npx prisma db seed` runs the configured seeding script. This may delete or overwrite existing database data; an AI agent must warn the user and receive explicit approval before executing it.
|
|
134
|
+
- `npx prisma generate` compiles `schema.prisma` into the generated Node Prisma client. Run it before `npx prisma db seed` when the updated seed flow depends on the new schema.
|
|
135
|
+
- `npx prisma db seed` runs the configured seeding script. This may delete or overwrite existing database data; an AI agent must warn the user and receive explicit approval before executing it.
|
|
136
136
|
- `npx ppy generate` regenerates the Python ORM classes used by the app. Use this after every schema change.
|
|
137
137
|
- `npx prisma migrate deploy` applies pending migrations in deployment environments.
|
|
138
138
|
- `npx prisma db push` syncs schema changes without creating a migration file, which is useful for prototyping.
|
|
@@ -140,7 +140,7 @@ Use these commands for the normal Prisma lifecycle in Caspian:
|
|
|
140
140
|
|
|
141
141
|
Default rule:
|
|
142
142
|
|
|
143
|
-
- When `prisma/schema.prisma` changes, use this order: `npx prisma migrate dev`; if seeding is involved, run `npx prisma generate`, ask for explicit approval before `npx prisma db seed`, then run `npx ppy generate`.
|
|
143
|
+
- When `prisma/schema.prisma` changes, use this order: `npx prisma migrate dev`; if seeding is involved, run `npx prisma generate`, ask for explicit approval before `npx prisma db seed`, then run `npx ppy generate`.
|
|
144
144
|
- Never hand-edit generated Prisma or Python ORM classes, and never replace `npx ppy generate` with a manual class update.
|
|
145
145
|
- Use migrations for tracked application changes.
|
|
146
146
|
- Use `db push` only when you intentionally want a faster, migration-free prototype loop.
|
|
@@ -173,7 +173,7 @@ If Python route or RPC code needs database access, import from `src.lib.prisma`
|
|
|
173
173
|
|
|
174
174
|
## Python Route Usage
|
|
175
175
|
|
|
176
|
-
If the project ships an app-owned Python Prisma-style layer under `src/lib/prisma/`, treat it as async-first and reuse it from route and RPC code. In a Prisma-enabled project, this is the required data-access path for Python code.
|
|
176
|
+
If the project ships an app-owned Python Prisma-style layer under `src/lib/prisma/`, treat it as async-first and reuse it from route and RPC code. In a Prisma-enabled project, this is the required data-access path for Python code.
|
|
177
177
|
|
|
178
178
|
Example:
|
|
179
179
|
|
|
@@ -194,7 +194,7 @@ Prisma calls fit naturally in:
|
|
|
194
194
|
- `async def page()` for first-render data
|
|
195
195
|
- `@rpc()` actions for browser-triggered reads and writes
|
|
196
196
|
|
|
197
|
-
Keep route-specific Prisma I/O in that route's `page()` or `@rpc()` actions in `src/app/**/index.py`. The installed layout engine supports synchronous and async `layout()` results, but layout work should stay focused on shared subtree props or metadata. Move Prisma helpers into `src/lib/**` only when the same behavior is reused by multiple routes, features, or integrations.
|
|
197
|
+
Keep route-specific Prisma I/O in that route's `page()` or `@rpc()` actions in `src/app/**/index.py`. The installed layout engine supports synchronous and async `layout()` results, but layout work should stay focused on shared subtree props or metadata. Move Prisma helpers into `src/lib/**` only when the same behavior is reused by multiple routes, features, or integrations.
|
|
198
198
|
|
|
199
199
|
See `fetch-data.md` for the recommended route-render versus RPC split.
|
|
200
200
|
|
|
@@ -332,39 +332,39 @@ async with prisma.transaction() as tx:
|
|
|
332
332
|
)
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
### Raw SQL Fallback
|
|
336
|
-
|
|
337
|
-
Use this escape hatch sparingly. Raw SQL is not the default query style in a Prisma-enabled Caspian project.
|
|
338
|
-
|
|
339
|
-
Before using `query_raw()` or `execute_raw()`, confirm that the generated Prisma API cannot express the query clearly with normal methods such as `find_many`, `find_unique`, `create`, `update`, `delete`, `delete_many`, `aggregate`, relation `include`, or transactions.
|
|
340
|
-
|
|
341
|
-
Important portability warning:
|
|
342
|
-
|
|
343
|
-
- Raw SQL is provider-specific. Placeholder style, identifier quoting, JSON operators, case-sensitivity rules, date functions, and aggregate behavior can differ across SQLite, MySQL, and PostgreSQL.
|
|
344
|
-
- A raw query that works in SQLite may fail after a move to PostgreSQL or MySQL even when the Prisma schema migrates cleanly.
|
|
345
|
-
- If a project may switch providers, prefer Prisma ORM methods first and treat raw SQL as an exception that needs provider-aware review.
|
|
346
|
-
|
|
347
|
-
```python
|
|
348
|
-
users = await prisma.user.find_many(
|
|
349
|
-
where={
|
|
350
|
-
"email": {"contains": "@gmail.com"},
|
|
351
|
-
},
|
|
352
|
-
)
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
If raw SQL is still unavoidable, keep it tightly scoped, document why the ORM was insufficient, and verify it against the current datasource provider in `prisma/schema.prisma`.
|
|
335
|
+
### Raw SQL Fallback
|
|
336
|
+
|
|
337
|
+
Use this escape hatch sparingly. Raw SQL is not the default query style in a Prisma-enabled Caspian project.
|
|
338
|
+
|
|
339
|
+
Before using `query_raw()` or `execute_raw()`, confirm that the generated Prisma API cannot express the query clearly with normal methods such as `find_many`, `find_unique`, `create`, `update`, `delete`, `delete_many`, `aggregate`, relation `include`, or transactions.
|
|
340
|
+
|
|
341
|
+
Important portability warning:
|
|
342
|
+
|
|
343
|
+
- Raw SQL is provider-specific. Placeholder style, identifier quoting, JSON operators, case-sensitivity rules, date functions, and aggregate behavior can differ across SQLite, MySQL, and PostgreSQL.
|
|
344
|
+
- A raw query that works in SQLite may fail after a move to PostgreSQL or MySQL even when the Prisma schema migrates cleanly.
|
|
345
|
+
- If a project may switch providers, prefer Prisma ORM methods first and treat raw SQL as an exception that needs provider-aware review.
|
|
346
|
+
|
|
347
|
+
```python
|
|
348
|
+
users = await prisma.user.find_many(
|
|
349
|
+
where={
|
|
350
|
+
"email": {"contains": "@gmail.com"},
|
|
351
|
+
},
|
|
352
|
+
)
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
If raw SQL is still unavoidable, keep it tightly scoped, document why the ORM was insufficient, and verify it against the current datasource provider in `prisma/schema.prisma`.
|
|
356
356
|
|
|
357
357
|
## Recommended Project Rules
|
|
358
358
|
|
|
359
359
|
- Keep the schema in `prisma/schema.prisma` and follow the required regeneration order after changes: `npx prisma migrate dev`, optional seed flow, then `npx ppy generate`.
|
|
360
|
-
- Reuse `src/lib/prisma/` for Python-side database access instead of creating a second bridge.
|
|
361
|
-
- When Prisma is enabled, do not bypass the Prisma Python ORM with direct database drivers, custom fetch functions, JSON files as active stores, or browser-side database access.
|
|
360
|
+
- Reuse `src/lib/prisma/` for Python-side database access instead of creating a second bridge.
|
|
361
|
+
- When Prisma is enabled, do not bypass the Prisma Python ORM with direct database drivers, custom fetch functions, JSON files as active stores, or browser-side database access.
|
|
362
362
|
- Never hand-edit generated Prisma or Python ORM classes.
|
|
363
363
|
- Keep reusable database helpers in `src/lib/`, and keep route-specific orchestration in `src/app/`.
|
|
364
364
|
- Use `await` with Prisma operations.
|
|
365
365
|
- Convert Prisma objects to template-safe dictionaries when rendering HTML.
|
|
366
366
|
- Validate incoming mutation data before calling `create`, `update`, or `delete` operations.
|
|
367
|
-
- Prefer Prisma queries over raw SQL. If raw SQL is unavoidable, keep it provider-aware, narrowly scoped, and documented so future database-provider migrations do not silently break application behavior.
|
|
367
|
+
- Prefer Prisma queries over raw SQL. If raw SQL is unavoidable, keep it provider-aware, narrowly scoped, and documented so future database-provider migrations do not silently break application behavior.
|
|
368
368
|
|
|
369
369
|
## AI Retrieval Notes
|
|
370
370
|
|
|
@@ -373,13 +373,13 @@ If an AI agent is working on a Caspian app with Prisma enabled, apply these rule
|
|
|
373
373
|
- Treat Prisma as the default ORM and persistence layer.
|
|
374
374
|
- Read `prisma/schema.prisma` before proposing model, relation, or field changes.
|
|
375
375
|
- When `prisma/schema.prisma` changes, run `npx prisma migrate dev` first.
|
|
376
|
-
- If the schema change requires seed data, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
376
|
+
- If the schema change requires seed data, run `npx prisma generate`, then request explicit user approval before running `npx prisma db seed`.
|
|
377
377
|
- Run `npx ppy generate` after schema changes to refresh the Python ORM classes.
|
|
378
378
|
- Never hand-edit generated Prisma or Python ORM classes.
|
|
379
379
|
- Read `prisma.config.ts` and `prisma/seed.ts` when you need the current project's Prisma tooling examples.
|
|
380
|
-
- Reuse the existing `src/lib/prisma/` package when the Python app needs database access.
|
|
381
|
-
- Treat use of the Prisma Python ORM as mandatory for Python-side database reads and writes in Prisma-enabled projects.
|
|
380
|
+
- Reuse the existing `src/lib/prisma/` package when the Python app needs database access.
|
|
381
|
+
- Treat use of the Prisma Python ORM as mandatory for Python-side database reads and writes in Prisma-enabled projects.
|
|
382
382
|
- For file managers and uploads, persist metadata in Prisma and keep blob storage separate. See [file-uploads.md](./file-uploads.md).
|
|
383
383
|
- Put reusable database helpers in `src/lib/`; keep route and RPC orchestration in `src/app/`.
|
|
384
|
-
- Use `async def page()` for route-specific first-render reads. Use `layout()` only for shared subtree props or metadata, and use `@rpc()` plus `pp.rpc()` for browser-triggered reads and writes.
|
|
384
|
+
- Use `async def page()` for route-specific first-render reads. Use `layout()` only for shared subtree props or metadata, and use `@rpc()` plus `pp.rpc()` for browser-triggered reads and writes.
|
|
385
385
|
- Check `fetch-data.md` for route versus RPC guidance and `validation.md` before writing public mutations.
|
|
@@ -40,9 +40,9 @@ Treat `caspian.config.json` and the actual project tree as the source of truth f
|
|
|
40
40
|
|
|
41
41
|
## Authored HTML Rule
|
|
42
42
|
|
|
43
|
-
For authored route, layout, loading, not-found, and error HTML files, keep exactly one top-level parent HTML element or one imported `x-*` component root.
|
|
44
|
-
|
|
45
|
-
- Keep any `<!-- @import ... -->` directives above that root.
|
|
43
|
+
For authored route, layout, loading, not-found, and error HTML files, keep exactly one top-level parent HTML element or one imported `x-*` component root.
|
|
44
|
+
|
|
45
|
+
- Keep any `<!-- @import ... -->` directives above that root. They are header directives for the file, not children of the root element.
|
|
46
46
|
- Keep any owned plain `<script>` inside that root, not after it.
|
|
47
47
|
- Do not handwrite `pp-component` or `type="text/pp"`.
|
|
48
48
|
|
|
@@ -125,9 +125,11 @@ See `routing.md` for the full App Router-style rules for dynamic segments, route
|
|
|
125
125
|
|
|
126
126
|
Use this folder for reusable UI components that should be imported into route templates or other component templates.
|
|
127
127
|
|
|
128
|
-
As the app grows, default to `src/components/` for application-level UI that will be shared across routes or features. Keep page-only markup close to the route in `src/app/`, but move shared cards, forms, shells, navigation, and other reusable visual building blocks into `src/components/`.
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
As the app grows, default to `src/components/` for application-level UI that will be shared across routes or features. Keep page-only markup close to the route in `src/app/`, but move shared cards, forms, shells, navigation, and other reusable visual building blocks into `src/components/`.
|
|
129
|
+
|
|
130
|
+
For page-specific chunks that are still substantial, a route-local component folder is also acceptable. Use this when a component is owned by one route but deserves its own focused file, such as one tab panel, one settings form, one analytics section, or one table toolbar. The route should still read as a short assembly of named `x-*` tags.
|
|
131
|
+
|
|
132
|
+
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. In authored HTML, that component is consumed as `<x-button />`.
|
|
131
133
|
|
|
132
134
|
One Python file can also export multiple related `@component` functions. When that happens, import those tags from that exact file path in HTML, for example `<!-- @import { Breadcrumb, BreadcrumbItem, BreadcrumbList } from "../components/Breadcrumb.py" -->`, and render them as `<x-breadcrumb />`, `<x-breadcrumb-item />`, and `<x-breadcrumb-list />` instead of assuming each tag has its own sibling `.py` file.
|
|
133
135
|
|
|
@@ -334,10 +336,11 @@ If an AI agent is deciding where to make changes, use these rules first.
|
|
|
334
336
|
- When the user asks for a dashboard, admin area, account area, or any grouped set of child routes, create a parent folder in `src/app/` with `layout.html` and place the child routes beneath it. Use `(group)/layout.html` only when that parent should not appear in the URL.
|
|
335
337
|
- Read [file-uploads.md](./file-uploads.md) when the task involves upload widgets, media libraries, or file-manager flows.
|
|
336
338
|
- Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
|
|
337
|
-
- Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, `x-*` component tags, import comments, and single-root template rules.
|
|
338
|
-
-
|
|
339
|
+
- Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, `x-*` component tags, import comments, and single-root template rules.
|
|
340
|
+
- Split UI by component responsibility before files become large. A tab panel, settings form, table section, toolbar, or repeated card/list item can be its own component even when it is only used by one route.
|
|
341
|
+
- When deciding between `src/components/` and `src/lib/`, use `src/components/` for anything rendered as reusable UI and `src/lib/` for helpers, services, validators, adapters, and shared business logic.
|
|
339
342
|
- Use [mcp.md](./mcp.md) only when `caspian.config.json` enables MCP and the task involves FastMCP tool definitions, nested config discovery, or local MCP commands.
|
|
340
|
-
- Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files.
|
|
343
|
+
- Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files. Do not nest them inside `<html>`, `<body>`, `<section>`, or any other parent wrapper.
|
|
341
344
|
- Follow the single-root authoring contract in [routing.md](./routing.md), [components.md](./components.md), and [pulsepoint.md](./pulsepoint.md): one authored root, any owned `<script>` inside that root, and no handwritten `pp-component` or `type="text/pp"` in source templates.
|
|
342
345
|
- Put shared helpers and reusable libraries in `src/lib/`.
|
|
343
346
|
- Use `settings/bs-config.ts` when uploaded public assets should not trigger BrowserSync reloads during the local stack.
|
|
@@ -47,6 +47,7 @@ If an inspected browser DOM disagrees with authored template source, remember th
|
|
|
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 |
|
|
49
49
|
| Tailwind merge | `{twMerge(...)}`, Python `merge_classes(...)` | `html_attrs.py`, `pp-reactive-v2.js` | Python emits frontend-ready expressions when Tailwind is enabled |
|
|
50
|
+
| Markup deferral | `<template pp-component>` / `<template pp-owner>` wrappers | render pipeline (`main.py`), `pp-reactive-v2.js` (`materializeTemplateComponentBoundaries`) | top-level roots ship inside an inert `<template>`; runtime materializes them on mount and SPA navigation before scanning. Browser never validates `<template>` contents, so `{...}` is safe in any attribute/position (SVG geometry, `src`/`href`, form `value`/date, table/select text) — no per-tag workarounds. See [pulsepoint.md](./pulsepoint.md) "Component markup is deferred inside an inert `<template>`" |
|
|
50
51
|
|
|
51
52
|
## AI Decision Rules
|
|
52
53
|
|
package/dist/docs/pulsepoint.md
CHANGED
|
@@ -346,12 +346,12 @@ Important:
|
|
|
346
346
|
- The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
|
|
347
347
|
- If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
|
|
348
348
|
- Authored route, layout, and component templates still need one top-level parent node so Caspian can inject the component boundary correctly after component expansion.
|
|
349
|
-
- A scriptless component root still mounts and can receive props, refs, events, and nested children, but it does not create local bindings from those props on its own.
|
|
349
|
+
- A scriptless component root still mounts and can receive props, refs, events, and nested children, but it does not create local bindings from those props on its own.
|
|
350
350
|
- Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
|
|
351
351
|
- The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
|
|
352
|
-
- `pp.props` contains the current prop bag for the component.
|
|
353
|
-
- `pp.props.children` contains the root's initial inner HTML before the owned script is removed from the render template.
|
|
354
|
-
- Props are not auto-injected as standalone top-level template or handler variables. Read them through `pp.props` or explicitly destructure them in the component script.
|
|
352
|
+
- `pp.props` contains the current prop bag for the component.
|
|
353
|
+
- `pp.props.children` contains the root's initial inner HTML before the owned script is removed from the render template.
|
|
354
|
+
- Props are not auto-injected as standalone top-level template or handler variables. Read them through `pp.props` or explicitly destructure them in the component script.
|
|
355
355
|
|
|
356
356
|
Bindings exported to the template:
|
|
357
357
|
|
|
@@ -570,13 +570,13 @@ When a child component needs the same token object, pass it from the provider sc
|
|
|
570
570
|
|
|
571
571
|
- Child component props are derived from DOM attributes.
|
|
572
572
|
- Attribute names are converted from kebab-case to camelCase for the prop bag.
|
|
573
|
-
- Native `on*` attributes and `pp-component` are not included in props.
|
|
574
|
-
- Empty attributes become boolean `true` props.
|
|
575
|
-
- Pure prop expressions such as `title="{pageTitle}"` are evaluated in the parent scope.
|
|
576
|
-
- Mixed strings such as `class="card {isActive ? 'active' : ''}"` are interpolated in the parent scope.
|
|
577
|
-
- Non-expression attribute values are passed through as strings.
|
|
578
|
-
- `children` is injected into props using the root's initial inner HTML.
|
|
579
|
-
- Inside the child component, those values should be accessed through `pp.props` or a top-level destructure such as `const { title } = pp.props`; they are not implicitly available as bare identifiers.
|
|
573
|
+
- Native `on*` attributes and `pp-component` are not included in props.
|
|
574
|
+
- Empty attributes become boolean `true` props.
|
|
575
|
+
- Pure prop expressions such as `title="{pageTitle}"` are evaluated in the parent scope.
|
|
576
|
+
- Mixed strings such as `class="card {isActive ? 'active' : ''}"` are interpolated in the parent scope.
|
|
577
|
+
- Non-expression attribute values are passed through as strings.
|
|
578
|
+
- `children` is injected into props using the root's initial inner HTML.
|
|
579
|
+
- Inside the child component, those values should be accessed through `pp.props` or a top-level destructure such as `const { title } = pp.props`; they are not implicitly available as bare identifiers.
|
|
580
580
|
|
|
581
581
|
Nested components:
|
|
582
582
|
|
|
@@ -621,6 +621,14 @@ Example:
|
|
|
621
621
|
</div>
|
|
622
622
|
```
|
|
623
623
|
|
|
624
|
+
### Component markup is deferred inside an inert `<template>`
|
|
625
|
+
|
|
626
|
+
The render pipeline wraps each top-level `pp-component` root in an inert `<template pp-component="…">`, and the runtime materializes it back into live DOM on `mount()` (and on every SPA navigation) before it scans for roots. The browser never parses, validates, or fetches the contents of a `<template>`, so raw `{...}` placeholders never reach live DOM at first paint.
|
|
627
|
+
|
|
628
|
+
- Because of this, `{...}` is safe in **any** attribute or position — including slots the browser would otherwise validate eagerly: SVG geometry (`d`, `viewBox`, `points`, `transform`), URL attributes (`src`, `srcset`, `href`, `poster`), form `value` on `date`/`number`/`color` inputs, and text placed directly inside `<table>` or `<select>`.
|
|
629
|
+
- Do not add per-tag workarounds to dodge first-paint validation (static-path `hidden` toggles instead of binding `d`, `data-*` URL holders, `hidden`-gated `<img src>`, or an SSR-resolved initial value). The deferral removes the whole class at once.
|
|
630
|
+
- `pp-style` and the `<input>`/`<select>`/`checked`/`defaultvalue`/`<textarea>` value rewrites still apply — but for different reasons that deferral does not replace: authoring-source tooling (`style="{...}"` breaks HTML/CSS linters) and attribute-vs-property correctness for controlled form fields.
|
|
631
|
+
|
|
624
632
|
## Refs
|
|
625
633
|
|
|
626
634
|
- Refs are for imperative element access. Do not use `pp-ref` as the default way to read normal form input values on submit; prefer the form's `onsubmit` event plus `Object.fromEntries(new FormData(event.currentTarget).entries())`.
|
package/dist/docs/routing.md
CHANGED
|
@@ -177,11 +177,13 @@ Examples:
|
|
|
177
177
|
|
|
178
178
|
Use `index.html` for the route template. This is the route's view layer.
|
|
179
179
|
|
|
180
|
-
If a route renders visible page content, that content belongs here even when the route also has an `index.py` companion.
|
|
181
|
-
|
|
182
|
-
Route templates can import reusable Python components with `<!-- @import ... -->` comments and render them with HTML-first `x-*` tags such as `<x-button />`. Use [components.md](./components.md) for the component authoring rules.
|
|
183
|
-
|
|
184
|
-
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.
|
|
180
|
+
If a route renders visible page content, that content belongs here even when the route also has an `index.py` companion.
|
|
181
|
+
|
|
182
|
+
Route templates can import reusable Python components with `<!-- @import ... -->` comments and render them with HTML-first `x-*` tags such as `<x-button />`. Use [components.md](./components.md) for the component authoring rules.
|
|
183
|
+
|
|
184
|
+
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.
|
|
185
|
+
|
|
186
|
+
Keep route templates as composition, not as the place where all section markup accumulates. A route with tabs, dashboard panels, forms, tables, or repeated cards should import focused components for those responsibilities and assemble them with `x-*` tags. For example, a dashboard route can import `<x-dashboard-header />`, `<x-metric-strip />`, `<x-activity-tab />`, and `<x-settings-tab />` rather than placing every tab panel's full markup in `index.html` or one giant Python component.
|
|
185
187
|
|
|
186
188
|
Route templates follow the same authored-vs-runtime contract documented in [pulsepoint.md](./pulsepoint.md) and the same single-root discipline documented in [components.md](./components.md): keep one authored parent node, keep any `<!-- @import ... -->` directives above that root, use a plain `<script>` inside that root when needed, and do not handwrite `pp-component` or `type="text/pp"`. That root may be a native HTML element or a single imported `x-*` component tag, but after expansion it must resolve to one final HTML root.
|
|
187
189
|
|
|
@@ -223,14 +225,24 @@ Also bad:
|
|
|
223
225
|
<aside class="dashboard-help">Tips</aside>
|
|
224
226
|
```
|
|
225
227
|
|
|
226
|
-
Also bad:
|
|
227
|
-
|
|
228
|
-
```html
|
|
229
|
-
<section class="dashboard-shell">
|
|
230
|
-
<!-- @import StatsCard from "../components" -->
|
|
231
|
-
<x-stats-card title="Users" value="42" />
|
|
232
|
-
</section>
|
|
233
|
-
```
|
|
228
|
+
Also bad:
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<section class="dashboard-shell">
|
|
232
|
+
<!-- @import StatsCard from "../components" -->
|
|
233
|
+
<x-stats-card title="Users" value="42" />
|
|
234
|
+
</section>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Correct:
|
|
238
|
+
|
|
239
|
+
```html
|
|
240
|
+
<!-- @import StatsCard from "../components" -->
|
|
241
|
+
|
|
242
|
+
<section class="dashboard-shell">
|
|
243
|
+
<x-stats-card title="Users" value="42" />
|
|
244
|
+
</section>
|
|
245
|
+
```
|
|
234
246
|
|
|
235
247
|
Use [pulsepoint.md](./pulsepoint.md) when you need the full authored-vs-rendered example instead of this routing-focused reminder. Use [pulsepoint-runtime-map.md](./pulsepoint-runtime-map.md) when a route task names a specific PulsePoint directive or SPA behavior and you need the owning runtime file quickly.
|
|
236
248
|
|
|
@@ -510,8 +522,9 @@ If an AI agent is choosing where to add or update route code, apply these rules
|
|
|
510
522
|
- If a route renders UI, create or update `index.html` for the markup.
|
|
511
523
|
- Add `index.py` only when the same route needs metadata or server behavior; do not place route HTML in `index.py`.
|
|
512
524
|
- Keep route-specific backend logic in the route's own `index.py`; extract to `src/lib/**` only for genuinely shared logic.
|
|
513
|
-
- Keep visible page markup in `index.html` and shared subtree shells in `layout.html`; do not place route HTML in `index.py` or layout HTML in `layout.py`.
|
|
514
|
-
-
|
|
525
|
+
- Keep visible page markup in `index.html` and shared subtree shells in `layout.html`; do not place route HTML in `index.py` or layout HTML in `layout.py`.
|
|
526
|
+
- Keep `index.html` as a short assembly of focused `x-*` components. When a route area has its own responsibility, such as a tab panel, settings form, data table, toolbar, or metric section, create a component for that area and pass route data into it as props.
|
|
527
|
+
- Use PulsePoint as the first-party interaction model for route and layout HTML. Avoid custom DOM wiring for normal events and reactivity.
|
|
515
528
|
- When the user asks for a dashboard, admin area, account section, or any grouped subtree of child routes, create a parent folder with `layout.html` and place the child routes beneath it. Follow the same mental model as the Next.js App Router.
|
|
516
529
|
- Use a normal folder such as `dashboard/` when the segment should appear in the URL. Use `(group)/` only when the folder should organize or wrap child routes without adding a path segment.
|
|
517
530
|
- Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
|