create-caspian-app 0.4.0-rc.5 → 0.4.0-rc.7

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.
@@ -35,7 +35,7 @@ This is the top architectural requirement for this workspace. Treat it as a hard
35
35
  - For current repo behavior, trust `main.py`, `src/lib/**`, `public/js/**`, `prisma/**`, and `src/app/**` over generic Caspian docs.
36
36
  - For framework internals, trust `.venv/Lib/site-packages/casp/**` over generic or older upstream guidance.
37
37
  - When packaged docs conflict with project code or installed runtime, the project code, `caspian.config.json`, and installed runtime win. Keep the packaged docs feature-oriented and point AI back to the project files that decide actual enablement and behavior.
38
- - When `prisma/schema.prisma` changes, follow this order: run `npx prisma migrate dev`; if the change affects seed flow or `prisma/seed.ts`, run `npx prisma generate` and then `npx prisma db seed`; then run `npx ppy generate` so the Python ORM stays aligned with the schema.
38
+ - When `prisma/schema.prisma` changes, follow this order: run `npx prisma migrate dev`; if the change affects seed flow or `prisma/seed.ts`, run `npx prisma generate` and then consider `npx prisma db seed`; then run `npx ppy generate` so the Python ORM stays aligned with the schema. Treat `npx prisma db seed` as a destructive data operation: it may clean tables and replace existing records, including production data if pointed at the wrong database. Before running it, tell the user exactly which command you intend to run, explain that it can delete or overwrite database data, confirm the current datasource when practical, and wait for the user's explicit approval.
39
39
  - Reuse the existing Python database layer in `src/lib/prisma/**`; do not create a second app-owned database abstraction unless the user explicitly asks for one.
40
40
  - When `caspian.config.json` has `prisma: true`, all Python-side database reads and writes must go through the generated Prisma Python ORM exposed from `src/lib/prisma/**`. Do not bypass it with ad hoc sqlite/postgres drivers, hand-written fetch helpers, JSON files as active stores, browser-side database fetches, or custom HTTP endpoints that reinvent the ORM. Use raw SQL only through Prisma as a narrow fallback when the generated ORM cannot express the query clearly.
41
41
  - Treat `src/lib/prisma/__init__.py`, `src/lib/prisma/db.py`, `src/lib/prisma/models.py`, and `settings/prisma-schema.json` as generated outputs owned by `npx ppy generate`; do not create or hand-edit them manually.
@@ -161,7 +161,7 @@ This is the top architectural requirement for this workspace. Treat it as a hard
161
161
  - Treat `prisma/schema.prisma` as the data-model source of truth.
162
162
  - Treat `prisma.config.ts` as the datasource and migration or seed configuration source of truth.
163
163
  - After changing `prisma/schema.prisma`, run `npx prisma migrate dev` first so migrations and the development database stay aligned.
164
- - If the schema change affects seed data or `prisma/seed.ts`, run `npx prisma generate` and then `npx prisma db seed`.
164
+ - If the schema change affects seed data or `prisma/seed.ts`, run `npx prisma generate`, then ask for explicit user approval before running `npx prisma db seed` because the seed script may delete or replace table data.
165
165
  - Run `npx ppy generate` after every schema change so the Python ORM files and `settings/prisma-schema.json` stay aligned with Prisma.
166
166
  - Keep Node-side generation and seeding aligned with `npx prisma generate` and `prisma/seed.ts`.
167
167
  - Keep Python-side database access aligned with `src/lib/prisma/**`, and treat that directory as generated output rather than a manual editing surface.
package/dist/AGENTS.md CHANGED
@@ -67,6 +67,7 @@ Use `.github/copilot-instructions.md` for the repo-wide implementation rules. Th
67
67
  - Use `node_modules/caspian-utils/dist/docs/websockets.md` when the task names WebSockets, live bidirectional channels, socket origin checks, socket auth/session behavior, broadcast managers, or native browser `WebSocket` clients.
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
+ - 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.
70
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
72
  - 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`.
72
73
  - For component-to-component composition, prefer real Python imports over `<!-- @import ... -->` comments inside single-file components. A component's own `x-*` tags resolve from the components imported into its Python module, which disambiguates same-name components across directories. 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. 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.
@@ -132,3 +133,145 @@ Before merging doc or runtime changes:
132
133
  2. Update the matching packaged doc in `node_modules/caspian-utils/dist/docs/` if the running behavior changed.
133
134
  3. Update `.github/copilot-instructions.md` if the repo-wide implementation rules changed.
134
135
  4. Update this file if the decision order, task routing, workspace clarifications, or packaged-doc maintenance rules changed.
136
+
137
+ <!-- maddex:start -->
138
+ # Maddex AI Context
139
+
140
+ This project uses `maddex` to generate reusable Python UI component modules.
141
+
142
+ - Config file: maddex.json
143
+ - Maddex instructions file: .github/instructions/maddex.instructions.md
144
+ - Components directory: src/lib/maddex
145
+ - Configured Python source path: src/lib/maddex
146
+ - Suggested module path: src.lib.maddex
147
+ - Tailwind CSS file: src/app/globals.css
148
+ - Icon library: ppicons
149
+ - Installed component count: 57
150
+ - Installed component inventory and project metadata: `maddex.json` (under `manifest`)
151
+
152
+ ## Installing Components
153
+
154
+ When a requested UI component does not exist yet, install it with `maddex` instead of hand-writing generated component modules.
155
+
156
+ - Add one component: `npx maddex add <component-name>`
157
+ - Add multiple components: `npx maddex add <component-a> <component-b>`
158
+ - Add the full catalogue: `npx maddex add --all`
159
+ - Refresh installed components: `npx maddex update`
160
+
161
+ ## Discovering Available Components
162
+
163
+ Use the Maddex catalogue API to find component names before installing them.
164
+
165
+ - Fetch all available components: `GET https://maddex.tsnc.tech/cli?component=all`
166
+ - Fetch one component by name: `GET https://maddex.tsnc.tech/cli?component=Button`
167
+ - The `component=all` endpoint returns a JSON array of component names.
168
+ - The single-component endpoint may return a `files` array for multi-file components or a legacy `content` string for a single Python module.
169
+
170
+ Single component response example:
171
+
172
+ ```json
173
+ {
174
+ "name": "Button",
175
+ "files": [
176
+ {
177
+ "name": "Button.py",
178
+ "content": "class Button:\n ...generated Python source..."
179
+ },
180
+ {
181
+ "name": "Button.html",
182
+ "content": "<button>...generated template...</button>"
183
+ }
184
+ ]
185
+ }
186
+ ```
187
+
188
+ ## Importing and Using Components
189
+
190
+ Use the installed Python module as the source for template `@import` comments, then copy the live docs example for the component you are rendering.
191
+
192
+ - Adjust the relative `@import` path to match the current file location.
193
+ - Maddex docs URLs use kebab-case component names such as `https://maddex.tsnc.tech/docs/button` and `https://maddex.tsnc.tech/docs/alert-dialog`.
194
+ - For any installed component, derive the docs page as `https://maddex.tsnc.tech/docs/<component-kebab-name>`.
195
+
196
+ Button example:
197
+
198
+ ```html
199
+ <!-- @import { Button } from ../../../../lib/maddex/Button.py -->
200
+
201
+ <div class="flex w-full flex-wrap items-center justify-center gap-3">
202
+ <x-button>Default</x-button>
203
+ <x-button variant="secondary">Secondary</x-button>
204
+ <x-button variant="destructive">Destructive</x-button>
205
+ <x-button variant="outline">Outline</x-button>
206
+ <x-button variant="ghost">Ghost</x-button>
207
+ <x-button variant="link">Link</x-button>
208
+ </div>
209
+ ```
210
+
211
+ ## Installed Components
212
+
213
+ - Accordion: `src/lib/maddex/Accordion.py` (docs: `https://maddex.tsnc.tech/docs/accordion`)
214
+ - Alert: `src/lib/maddex/Alert.py` (docs: `https://maddex.tsnc.tech/docs/alert`)
215
+ - AlertDialog: `src/lib/maddex/AlertDialog.py` (docs: `https://maddex.tsnc.tech/docs/alert-dialog`)
216
+ - AspectRatio: `src/lib/maddex/AspectRatio.py` (docs: `https://maddex.tsnc.tech/docs/aspect-ratio`)
217
+ - Avatar: `src/lib/maddex/Avatar.py` (docs: `https://maddex.tsnc.tech/docs/avatar`)
218
+ - Badge: `src/lib/maddex/Badge.py` (docs: `https://maddex.tsnc.tech/docs/badge`)
219
+ - Breadcrumb: `src/lib/maddex/Breadcrumb.py` (docs: `https://maddex.tsnc.tech/docs/breadcrumb`)
220
+ - Button: `src/lib/maddex/Button.py` (docs: `https://maddex.tsnc.tech/docs/button`)
221
+ - ButtonGroup: `src/lib/maddex/ButtonGroup.py` (docs: `https://maddex.tsnc.tech/docs/button-group`)
222
+ - Calendar: `src/lib/maddex/Calendar.py` (docs: `https://maddex.tsnc.tech/docs/calendar`)
223
+ - Card: `src/lib/maddex/Card.py` (docs: `https://maddex.tsnc.tech/docs/card`)
224
+ - Carousel: `src/lib/maddex/Carousel.py` (docs: `https://maddex.tsnc.tech/docs/carousel`)
225
+ - Chart: `src/lib/maddex/Chart.py` (docs: `https://maddex.tsnc.tech/docs/chart`)
226
+ - Checkbox: `src/lib/maddex/Checkbox.py` (docs: `https://maddex.tsnc.tech/docs/checkbox`)
227
+ - Collapsible: `src/lib/maddex/Collapsible.py` (docs: `https://maddex.tsnc.tech/docs/collapsible`)
228
+ - Combobox: `src/lib/maddex/Combobox.py` (docs: `https://maddex.tsnc.tech/docs/combobox`)
229
+ - Command: `src/lib/maddex/Command.py` (docs: `https://maddex.tsnc.tech/docs/command`)
230
+ - ContextMenu: `src/lib/maddex/ContextMenu.py` (docs: `https://maddex.tsnc.tech/docs/context-menu`)
231
+ - DataTable: `src/lib/maddex/DataTable.py` (docs: `https://maddex.tsnc.tech/docs/data-table`)
232
+ - DatePicker: `src/lib/maddex/DatePicker.py` (docs: `https://maddex.tsnc.tech/docs/date-picker`)
233
+ - Dialog: `src/lib/maddex/Dialog.py` (docs: `https://maddex.tsnc.tech/docs/dialog`)
234
+ - Drawer: `src/lib/maddex/Drawer.py` (docs: `https://maddex.tsnc.tech/docs/drawer`)
235
+ - DropdownMenu: `src/lib/maddex/DropdownMenu.py` (docs: `https://maddex.tsnc.tech/docs/dropdown-menu`)
236
+ - Editor: `src/lib/maddex/Editor.py` (docs: `https://maddex.tsnc.tech/docs/editor`)
237
+ - Empty: `src/lib/maddex/Empty.py` (docs: `https://maddex.tsnc.tech/docs/empty`)
238
+ - Field: `src/lib/maddex/Field.py` (docs: `https://maddex.tsnc.tech/docs/field`)
239
+ - HoverCard: `src/lib/maddex/HoverCard.py` (docs: `https://maddex.tsnc.tech/docs/hover-card`)
240
+ - Input: `src/lib/maddex/Input.py` (docs: `https://maddex.tsnc.tech/docs/input`)
241
+ - InputGroup: `src/lib/maddex/InputGroup.py` (docs: `https://maddex.tsnc.tech/docs/input-group`)
242
+ - InputOTP: `src/lib/maddex/InputOTP.py` (docs: `https://maddex.tsnc.tech/docs/input-otp`)
243
+ - Item: `src/lib/maddex/Item.py` (docs: `https://maddex.tsnc.tech/docs/item`)
244
+ - Kbd: `src/lib/maddex/Kbd.py` (docs: `https://maddex.tsnc.tech/docs/kbd`)
245
+ - Label: `src/lib/maddex/Label.py` (docs: `https://maddex.tsnc.tech/docs/label`)
246
+ - Menubar: `src/lib/maddex/Menubar.py` (docs: `https://maddex.tsnc.tech/docs/menubar`)
247
+ - NavigationMenu: `src/lib/maddex/NavigationMenu.py` (docs: `https://maddex.tsnc.tech/docs/navigation-menu`)
248
+ - Popover: `src/lib/maddex/Popover.py` (docs: `https://maddex.tsnc.tech/docs/popover`)
249
+ - Portal: `src/lib/maddex/Portal.py` (docs: `https://maddex.tsnc.tech/docs/portal`)
250
+ - Radio: `src/lib/maddex/Radio.py` (docs: `https://maddex.tsnc.tech/docs/radio`)
251
+ - RadioGroup: `src/lib/maddex/RadioGroup.py` (docs: `https://maddex.tsnc.tech/docs/radio-group`)
252
+ - Resizable: `src/lib/maddex/Resizable.py` (docs: `https://maddex.tsnc.tech/docs/resizable`)
253
+ - ScrollArea: `src/lib/maddex/ScrollArea.py` (docs: `https://maddex.tsnc.tech/docs/scroll-area`)
254
+ - Select: `src/lib/maddex/Select.py` (docs: `https://maddex.tsnc.tech/docs/select`)
255
+ - Separator: `src/lib/maddex/Separator.py` (docs: `https://maddex.tsnc.tech/docs/separator`)
256
+ - Sheet: `src/lib/maddex/Sheet.py` (docs: `https://maddex.tsnc.tech/docs/sheet`)
257
+ - Sidebar: `src/lib/maddex/Sidebar.py` (docs: `https://maddex.tsnc.tech/docs/sidebar`)
258
+ - Skeleton: `src/lib/maddex/Skeleton.py` (docs: `https://maddex.tsnc.tech/docs/skeleton`)
259
+ - Slider: `src/lib/maddex/Slider.py` (docs: `https://maddex.tsnc.tech/docs/slider`)
260
+ - Slot: `src/lib/maddex/Slot.py` (docs: `https://maddex.tsnc.tech/docs/slot`)
261
+ - Switch: `src/lib/maddex/Switch.py` (docs: `https://maddex.tsnc.tech/docs/switch`)
262
+ - Table: `src/lib/maddex/Table.py` (docs: `https://maddex.tsnc.tech/docs/table`)
263
+ - Tabs: `src/lib/maddex/Tabs.py` (docs: `https://maddex.tsnc.tech/docs/tabs`)
264
+ - Textarea: `src/lib/maddex/Textarea.py` (docs: `https://maddex.tsnc.tech/docs/textarea`)
265
+ - Toast: `src/lib/maddex/Toast.py` (docs: `https://maddex.tsnc.tech/docs/toast`)
266
+ - Toggle: `src/lib/maddex/Toggle.py` (docs: `https://maddex.tsnc.tech/docs/toggle`)
267
+ - ToggleGroup: `src/lib/maddex/ToggleGroup.py` (docs: `https://maddex.tsnc.tech/docs/toggle-group`)
268
+ - Tooltip: `src/lib/maddex/Tooltip.py` (docs: `https://maddex.tsnc.tech/docs/tooltip`)
269
+ - utils: `src/lib/maddex/utils.py` (docs: `https://maddex.tsnc.tech/docs/utils`)
270
+
271
+ ## Usage Notes
272
+
273
+ - Generated Python files follow this pattern: `src/lib/maddex/<ComponentName>.py`
274
+ - Prefer the configured module path `src.lib.maddex` when the host project exposes that package path for imports.
275
+ - Some components may include sidecar template or asset files next to the main Python module.
276
+ - Manual content outside this managed block is preserved.
277
+ <!-- maddex:end -->
@@ -154,6 +154,21 @@ function getExternalIP(): string | null {
154
154
  return null;
155
155
  }
156
156
 
157
+ // All reload triggers funnel through this single debounced worker so that a
158
+ // multi-file save (or several watchers firing for one edit) collapses into a
159
+ // single browser reload instead of a cascade of reloads.
160
+ const reloader = new DebouncedWorker(
161
+ () => {
162
+ if (bs.active) bs.reload();
163
+ },
164
+ 500,
165
+ "bs-reload",
166
+ );
167
+
168
+ function requestReload(reason?: string) {
169
+ reloader.schedule(reason);
170
+ }
171
+
157
172
  const pipeline = new DebouncedWorker(
158
173
  async () => {
159
174
  const changedFile = lastChangedFile;
@@ -172,8 +187,8 @@ const pipeline = new DebouncedWorker(
172
187
  updateRouteFilesCache();
173
188
 
174
189
  const isReady = await waitForPort(pythonPort);
175
- if (isReady && bs.active) {
176
- bs.reload();
190
+ if (isReady) {
191
+ requestReload("python restart");
177
192
  } else {
178
193
  console.error(
179
194
  chalk.red("Warning: Server failed to start or timed out."),
@@ -208,16 +223,16 @@ const pipeline = new DebouncedWorker(
208
223
  );
209
224
  await restartPythonServer(pythonPort, bsPort);
210
225
  const isReady = await waitForPort(pythonPort);
211
- if (isReady && bs.active) bs.reload();
212
- } else if (bs.active) {
213
- bs.reload();
226
+ if (isReady) requestReload("structure change");
227
+ } else {
228
+ requestReload("src change");
214
229
  }
215
230
  previousRouteFiles = routeFiles;
216
- } else if (bs.active) {
217
- bs.reload();
231
+ } else {
232
+ requestReload("src change");
218
233
  }
219
234
  },
220
- 350,
235
+ 1200,
221
236
  "bs-pipeline",
222
237
  );
223
238
 
@@ -245,9 +260,9 @@ const publicPipeline = new DebouncedWorker(
245
260
  console.log(
246
261
  chalk.cyan("-> Public directory changed, reloading browser..."),
247
262
  );
248
- if (bs.active) bs.reload();
263
+ requestReload("public change");
249
264
  },
250
- 350,
265
+ 1200,
251
266
  "bs-public-pipeline",
252
267
  );
253
268
 
@@ -327,8 +342,8 @@ async function shutdown(exitCode: number): Promise<void> {
327
342
  sourceWatchers.push(
328
343
  createSrcWatcher(viteFlagFile, {
329
344
  onEvent: (ev) => {
330
- if (ev === "change" && bs.active) {
331
- bs.reload();
345
+ if (ev === "change") {
346
+ requestReload("vite build complete");
332
347
  }
333
348
  },
334
349
  awaitWriteFinish: { stabilityThreshold: 100, pollInterval: 50 },
@@ -344,7 +359,7 @@ async function shutdown(exitCode: number): Promise<void> {
344
359
  if (_abs.includes("__pycache__")) return;
345
360
  await restartPythonServer(pythonPort, bsPort);
346
361
  const isReady = await waitForPort(pythonPort);
347
- if (isReady && bs.active) bs.reload();
362
+ if (isReady) requestReload("utils change");
348
363
  },
349
364
  awaitWriteFinish: DEFAULT_AWF,
350
365
  logPrefix: "watch-utils",
@@ -359,7 +374,7 @@ async function shutdown(exitCode: number): Promise<void> {
359
374
  if (_abs.includes("__pycache__")) return;
360
375
  await restartPythonServer(pythonPort, bsPort);
361
376
  const isReady = await waitForPort(pythonPort);
362
- if (isReady && bs.active) bs.reload();
377
+ if (isReady) requestReload("main.py change");
363
378
  },
364
379
  awaitWriteFinish: DEFAULT_AWF,
365
380
  logPrefix: "watch-main",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "0.4.0-rc.5",
3
+ "version": "0.4.0-rc.7",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",