create-caspian-app 0.5.0-rc.1 → 0.5.0-rc.2
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.
|
@@ -16,10 +16,11 @@ This is the top architectural requirement for this workspace. Treat it as a hard
|
|
|
16
16
|
|
|
17
17
|
- Build pages from components, not from one large block of HTML. A route's `src/app/**/index.html` should read like a short composition of `x-*` component tags, not a wall of markup. When a page would otherwise carry a long stretch of HTML, that markup must move into a component instead of living in the page.
|
|
18
18
|
- Separate every page into meaningful chunks and give each chunk its own component. Typical chunks are a top menu / topbar, header, sidebar / nav rail, hero, toolbar, content sections, cards, lists, forms, footer, and any repeated block. Each chunk owns its own long markup inside its component file, so the page content stays small and readable.
|
|
19
|
-
- Default to single-file Python components authored with inline `html(...)` (import `html` from `casp.component_decorator`, return `html("""...""", **context)`).
|
|
20
|
-
-
|
|
19
|
+
- Default to single-file Python components authored with inline `html(...)` (import `html` from `casp.component_decorator`, return `html("""...""", **context)`) for each focused chunk. Single-file means the component's Python, markup, and small PulsePoint script live together; it does not mean the whole page, full dashboard, or every tab panel should be collapsed into one Python file.
|
|
20
|
+
- Split component files by responsibility the way you would split React components. If a page has tabs, create focused components such as `OverviewTab.py`, `ActivityTab.py`, and `SettingsTab.py` instead of one oversized `DashboardTabs.py` that contains every panel. If a section has its own form, table, toolbar, or card list, make that section a component and pass data, flags, callbacks, or labels as props.
|
|
21
|
+
- Put these page-chunk components in `src/components/` (or a route-local component folder when they are truly single-route), import them into the route with top-of-file `<!-- @import ... -->` directives above the route root, and render them as kebab-cased `x-*` tags. Keep the single-root contract in both the page and each component.
|
|
21
22
|
- When the user asks to build or extend a page, plan the chunk breakdown first (for example: top menu component, sidebar component, content section component), create those components, then assemble them in the route. Do not start by pasting a full HTML page into `index.html` and only later consider extraction; component-first is the starting point, not a cleanup step.
|
|
22
|
-
- If you find an existing page
|
|
23
|
+
- If you find an existing page or single-file component with multiple unrelated responsibilities, prefer splitting it into focused chunk components as part of the work rather than adding more markup to it.
|
|
23
24
|
|
|
24
25
|
## Global Rules
|
|
25
26
|
|
|
@@ -58,8 +59,8 @@ This is the top architectural requirement for this workspace. Treat it as a hard
|
|
|
58
59
|
- Treat imperative DOM APIs and `pp-ref` element reads as narrow escape hatches for third-party widgets, browser APIs that require direct DOM access, focus/measurement/media/canvas behavior, or one-off integration code. When they are needed, keep them inside the owning PulsePoint component script, usually behind `pp.ref(...)` and `pp.effect(...)`, so PulsePoint still owns the component state and event flow.
|
|
59
60
|
- When `caspian.config.json` has `tailwindcss: true`, treat Python `merge_classes(...)` plus browser `twMerge(...)` as the only Tailwind class-merging contract: `merge_classes(...)` emits frontend-ready `{twMerge(...)}` expressions, and authored PulsePoint attribute expressions or scripts may call global `twMerge(...)` directly.
|
|
60
61
|
- Treat Caspian component usage as HTML-first in the current runtime: import Python components with `<!-- @import ... -->` and render them as kebab-cased `x-*` tags such as `<x-button />` or `<x-command-dialog />`.
|
|
61
|
-
- Components may be authored single-file. Return `html("""...""", **context)` (import `html` from `casp.component_decorator`) to keep markup, server interpolation, and a PulsePoint `<script>` inline instead of a same-name `.html` via `render_html(...)`. Inside `html(...)`, `{{ ... }}` is server-side Jinja and `{ ... }` is left for PulsePoint; never use a Python f-string for the markup. Autoescaping is on, so `{{ value }}` is safe for user text and trusted HTML needs `Markup(...)` or `| safe`; a `children` value is auto-safe. Prefer single-file `html(...)` for small and medium components
|
|
62
|
-
- Inside single-file components,
|
|
62
|
+
- Components may be authored single-file. Return `html("""...""", **context)` (import `html` from `casp.component_decorator`) to keep markup, server interpolation, and a PulsePoint `<script>` inline instead of a same-name `.html` via `render_html(...)`. Inside `html(...)`, `{{ ... }}` is server-side Jinja and `{ ... }` is left for PulsePoint; never use a Python f-string for the markup. Autoescaping is on, so `{{ value }}` is safe for user text and trusted HTML needs `Markup(...)` or `| safe`; a `children` value is auto-safe. Prefer single-file `html(...)` for small and medium components, but keep each file focused on one responsibility. Split multiple panels, tabs, forms, tables, cards, and toolbars into separate components instead of making one giant single-file component.
|
|
63
|
+
- Inside single-file components, use real Python imports instead of `<!-- @import ... -->` comments for child components: a component's own `x-*` tags resolve from the components imported into its Python module, which disambiguates same-name components across directories. Do not put an import comment inside the `html("""...""")` string. Runtime resolution precedence is inherited ancestor components, then the component's own Python imports, then a local `@import`, but the authoring pattern for single-file components is Python imports. Slot content resolves in the scope where it was authored, so the template that writes an `x-*` tag must import that component.
|
|
63
64
|
- For CRUD operations and any browser-initiated reads from the backend, use route or backend `@rpc()` actions on the server and `pp.rpc(...)` from PulsePoint code on the client unless the user explicitly asks for another integration pattern.
|
|
64
65
|
- Google and GitHub OAuth ship pre-registered in this starter: `main.py` already calls `Auth.set_providers(GithubProvider(), GoogleProvider())`, and `AuthMiddleware` already handles the `signin/{google,github}` and `callback/{google,github}` paths under `api_auth_prefix` (default `/api/auth`). To add social sign-in, point a link or button at `/api/auth/signin/google` or `/api/auth/signin/github` and set the provider credentials in `.env` (`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI`, `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`). Do not hand-roll an OAuth flow, manual `httpx`/`authlib` token exchange, or custom callback routes; reuse the shipped providers and let `auth.auth_providers(...)` own redirect, callback, and sign-in.
|
|
65
66
|
- For one-way streaming output, including AI/LLM/chat token streams, use Caspian's shipped RPC streaming: write a generator `@rpc()` action that `yield`s chunks (the runtime wraps generators as SSE via `casp.streaming.SSE`) and consume it with `pp.rpc(name, args, { onStream, onStreamComplete, onStreamError })`. When bridging a Python LLM/SDK stream, `async for` over the provider's stream inside the `@rpc()` action and `yield` each token. Do not reinvent one-way streaming with raw `fetch`/`ReadableStream`, `EventSource`, or a WebSocket; reserve WebSockets for genuinely bidirectional channels per the WebSocket rules above.
|
|
@@ -121,7 +122,7 @@ This is the top architectural requirement for this workspace. Treat it as a hard
|
|
|
121
122
|
- Keep `src/components/` as the default home for reusable application UI components and for the page chunks produced by component-first composition (top menus, sidebars, headers, content sections, cards, lists, forms, footers).
|
|
122
123
|
- Move shared cards, forms, shells, navigation, and other reusable rendered building blocks here once they are used across routes or features.
|
|
123
124
|
- Keep route-owned markup in `src/app/**`, and keep non-UI helpers or services in `src/lib/**`.
|
|
124
|
-
- Author components as a single Python file with inline `html(...)` by default for small and medium UI, or as a `.py` plus same-name `.html` via `render_html(...)` for large markup or long scripts. Keep the single-root rule in both forms. Resolve child `x-*` tags from real Python imports in single-file components rather than `<!-- @import ... -->`. See `node_modules/caspian-utils/dist/docs/components.md`.
|
|
125
|
+
- Author components as a single Python file with inline `html(...)` by default for small and medium UI, or as a `.py` plus same-name `.html` via `render_html(...)` for large markup or long scripts. Keep the single-root rule in both forms. Resolve child `x-*` tags from real Python imports in single-file components rather than `<!-- @import ... -->`. Prefer one focused component per file unless a file intentionally exports tiny, tightly coupled subcomponents. See `node_modules/caspian-utils/dist/docs/components.md`.
|
|
125
126
|
|
|
126
127
|
### `public/js/main.js`
|
|
127
128
|
|
|
@@ -142,7 +143,7 @@ This is the top architectural requirement for this workspace. Treat it as a hard
|
|
|
142
143
|
- Keep route templates and layouts server-rendered first, with PulsePoint enhancement as the default interactive layer.
|
|
143
144
|
- Keep visible page and layout markup in `index.html` and `layout.html`. Treat `index.py` and `layout.py` as backend companions for metadata, `page()` or `layout()`, `@rpc()` actions, auth checks, caching, redirects, and other server-side preparation, not as places to author visible HTML.
|
|
144
145
|
- When a route renders UI, author that markup in the route's `index.html` even if the route also has an `index.py` companion.
|
|
145
|
-
- When route templates import reusable Python components, render them as kebab-cased `x-*` tags such as `<x-button />` after
|
|
146
|
+
- When route templates import reusable Python components, render them as kebab-cased `x-*` tags such as `<x-button />` after top-of-file `<!-- @import Button from "..." -->` directives. The import comments belong above the single route root, not inside it.
|
|
146
147
|
- For route-level reactivity, prefer PulsePoint state, effects, refs, and template directives together with `pp.rpc(...)` instead of manual DOM mutation or ad hoc browser fetch code.
|
|
147
148
|
- For route-level buttons, forms, inputs, toggles, menus, filters, uploads, and list updates, bind events directly in the authored HTML with native PulsePoint-handled `on*` attributes such as `onclick`, `oninput`, `onchange`, and `onsubmit`. Avoid id-driven `querySelector`/`addEventListener` setup for first-party UI because it duplicates the PulsePoint event and rerender model.
|
|
148
149
|
- For simple route-level form submissions, collect the submitted fields with `Object.fromEntries(new FormData(event.currentTarget).entries())` inside the `onsubmit` handler and pass that object directly to `pp.rpc(...)`. Use `pp.state(...)` for pending/error/success UI and controlled non-native widgets; use `pp-ref` only when the handler needs imperative element access such as focus, measurement, file input reset, or third-party integration.
|
package/dist/AGENTS.md
CHANGED
|
@@ -68,9 +68,10 @@ Use `.github/copilot-instructions.md` for the repo-wide implementation rules. Th
|
|
|
68
68
|
- Use `node_modules/caspian-utils/dist/docs/file-conventions.md` when the task asks what belongs in `index.html`, `index.py`, `layout.html`, `layout.py`, `loading.html`, `not-found.html`, or `error.html`.
|
|
69
69
|
- When `caspian.config.json` has `prisma: true`, database reads and writes from Python routes, layouts, RPC actions, upload flows, auth flows, and helpers must use the generated Prisma Python ORM in `src/lib/prisma/**`. Do not create a separate database fetch layer with raw drivers, hand-written SQL helpers, JSON manifests, app-specific HTTP fetches, or browser-side data fetches to replace the ORM. Use raw SQL only as a narrow Prisma ORM fallback when the generated client cannot express a query clearly.
|
|
70
70
|
- Treat `npx prisma db seed` as a delicate, potentially destructive operation. In this workspace, seed scripts may clear tables before inserting fresh records. Before running that command, an AI agent must propose the exact command, warn that it can delete or overwrite database data including production data if the datasource is wrong, confirm the datasource when practical, and wait for explicit user approval.
|
|
71
|
-
- Component-first page composition is the highest-priority authoring rule for this workspace (see `.github/copilot-instructions.md`). Build pages as a short assembly of `x-*` chunk components (top menu, sidebar, header, content sections, cards, forms, footer) and keep each chunk's long markup inside its own single-file `html(...)` component, so `src/app/**/index.html` stays small instead of holding a wall of HTML. Plan the chunk breakdown before writing the route, not as a later cleanup pass.
|
|
71
|
+
- Component-first page composition is the highest-priority authoring rule for this workspace (see `.github/copilot-instructions.md`). Build pages as a short assembly of `x-*` chunk components (top menu, sidebar, header, content sections, cards, forms, footer) and keep each chunk's long markup inside its own focused single-file `html(...)` component, so `src/app/**/index.html` stays small instead of holding a wall of HTML. Plan the chunk breakdown before writing the route, not as a later cleanup pass.
|
|
72
|
+
- Split single-file Python components by responsibility, using the same mental model as React components. A page with tabs should usually have one component for the tab shell and separate components for each substantial tab panel. A section with its own form, table, toolbar, or list should usually be its own component with data and options passed by props, not an unrelated block inside a giant Python file.
|
|
72
73
|
- Components may be authored as a single Python file. Import `html` from `casp.component_decorator` and return `html("""...""", **context)` to keep markup, server interpolation, and a PulsePoint `<script>` inline, instead of pairing the `.py` with a same-name `.html` through `render_html(...)`. Inside `html(...)`, `{{ ... }}` is server-side Jinja and `{ ... }` stays for PulsePoint; do not use a Python f-string for the markup. Prefer single-file `html(...)` for small and medium components and keep `render_html(...)` plus a `.html` file for large markup or long scripts. Both forms render identically through `transform_components(...)` then `transform_scripts(...)`. See `node_modules/caspian-utils/dist/docs/components.md`.
|
|
73
|
-
- For component-to-component composition,
|
|
74
|
+
- For component-to-component composition, use real Python imports inside single-file `html(...)` components instead of placing `<!-- @import ... -->` inside the returned HTML string. A component's own `x-*` tags resolve from the components imported into its Python module, which disambiguates same-name components across directories. Runtime resolution precedence inside a component's output is inherited ancestor components, then the component's own Python imports, then a local `@import` in that same template, but the authoring pattern for single-file components is Python imports. Slot content (children) resolves in the scope where it was authored, so the component that writes an `x-*` tag in markup must import that component.
|
|
74
75
|
- For first-party HTML interactivity in this workspace, PulsePoint is the required default. Use PulsePoint `on*` event attributes, `pp.state`, refs, effects, directives, and `pp.rpc()` instead of inventing id/data-attribute driven JavaScript with `querySelector`, `getElementById`, `addEventListener`, manual `innerHTML`, or parallel client state. For simple forms, bind `onsubmit` in the HTML, convert named fields with `Object.fromEntries(new FormData(event.currentTarget).entries())`, and validate/normalize that payload in Python; do not add `pp-ref` to each input, create a form ref, and attach an effect-managed submit listener just to collect submitted values.
|
|
75
76
|
- When `caspian.config.json` has `websocket: true`, WebSocket behavior is app-owned in `main.py`. Do not assume fixed route names or endpoint paths across Caspian projects; define project-specific socket paths in the app, keep shared socket helpers in `src/lib/websocket/**` when needed, and let each route that needs a socket client pass its own `websocket_path` or `websocket_url` into its template.
|
|
76
77
|
- WebSocket pages may be public, authenticated, role-gated, or mixed depending on the app. Keep route-specific browser UI in that route's `src/app/**/index.html`, keep first-render socket URL/path values in the matching `index.py`, and keep reusable session/auth/connection/broadcast helpers out of route files only when they are shared.
|