prisma-php 0.0.5 → 0.0.6

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.
@@ -69,13 +69,89 @@ This is the right mental model for:
69
69
  - componentized partials that PulsePoint should hydrate later
70
70
  - keeping PHP templating ergonomics while still producing component-aware markup
71
71
 
72
- For `ImportComponent`, think in terms of a React-style single-root component: one parent element defines the island boundary, Prisma PHP serializes props onto that root, and the framework injects a stable `pp-component` attribute there. Do not design imported partials as loose sibling markup.
72
+ For `ImportComponent`, think in terms of a single-root component boundary: one parent element defines the island boundary, Prisma PHP serializes props onto that root, and the framework injects a stable `pp-component` attribute there. Do not design imported partials as loose sibling markup.
73
73
 
74
74
  AI must not collapse these two models into one.
75
75
 
76
76
  - **PHPX** is for class-based component authoring.
77
77
  - **`ImportComponent`** is for importing PHP partial files as single-root component islands.
78
78
 
79
+ ## Important distinction: route files vs imported partials
80
+
81
+ This is the route/component distinction AI must keep straight.
82
+
83
+ ### Normal route files
84
+
85
+ For a normal route file such as `src/app/index.php` or nested `layout.php`, use this structure:
86
+
87
+ 1. PHP
88
+ 2. one parent HTML element for the route content
89
+ 3. one `<script>` block after that parent element
90
+
91
+ Example route-file pattern:
92
+
93
+ ```php filename="src/app/dashboard/index.php"
94
+ <?php
95
+
96
+ $title = 'Dashboard';
97
+ ?>
98
+
99
+ <section class="dashboard-page">
100
+ <h1><?= htmlspecialchars($title) ?></h1>
101
+ <p>Count: {count}</p>
102
+ </section>
103
+
104
+ <script>
105
+ const [count, setCount] = pp.state(0);
106
+ </script>
107
+ ```
108
+
109
+ That is the correct route-page pattern. It is documented in `layouts-and-pages.md`, not in `ImportComponent`.
110
+
111
+ ### Imported partials rendered with `ImportComponent`
112
+
113
+ For an imported partial, the file itself must emit exactly **one root element**. If the partial needs client script, place that script **inside** the parent root element.
114
+
115
+ Correct imported-partial pattern:
116
+
117
+ ```php filename="src/app/inc/SearchBox.php"
118
+ <?php
119
+
120
+ $title = 'Search';
121
+ ?>
122
+
123
+ <div class="rounded-lg border bg-card p-4" pp-component="search-box">
124
+ <h2 class="font-medium"><?= htmlspecialchars($title) ?></h2>
125
+ <input value="{query}" oninput="{(e) => setQuery(e.target.value)}" />
126
+ <script>
127
+ console.log('Search component ready');
128
+ </script>
129
+ </div>
130
+ ```
131
+
132
+ Incorrect imported-partial pattern:
133
+
134
+ ```php filename="src/app/inc/SearchBox.php"
135
+ <?php
136
+
137
+ $title = 'Search';
138
+ ?>
139
+
140
+ <section class="rounded-lg border bg-card p-4">
141
+ <h2 class="font-medium"><?= htmlspecialchars($title) ?></h2>
142
+ </section>
143
+
144
+ <script>
145
+ console.log('This script is a second root element');
146
+ </script>
147
+ ```
148
+
149
+ Why this is invalid:
150
+
151
+ - the `<section>` is one root element
152
+ - the sibling `<script>` is a second root element
153
+ - `ImportComponent` requires the file to resolve to **exactly one root element**
154
+
79
155
  ## Important AI rules for Prisma PHP components
80
156
 
81
157
  - treat the official Prisma PHP component docs as the source of truth
@@ -91,6 +167,8 @@ AI must not collapse these two models into one.
91
167
  - when using `ImportComponent`, always ensure the imported file renders **exactly one root element**
92
168
  - treat the `ImportComponent` root as the real component boundary because Prisma PHP serializes props onto it and injects `pp-component`
93
169
  - do not manually add `pp-component` inside imported partial source; let Prisma PHP inject it
170
+ - do not apply the imported-partial rule to normal route files
171
+ - do not apply the normal route-file pattern to imported partials
94
172
 
95
173
  ## Component file placement and naming
96
174
 
@@ -8,7 +8,7 @@ related:
8
8
  - /docs/get-started
9
9
  ---
10
10
 
11
- {/_AI agent hint: Prisma PHP uses local installed docs as the source of truth for the active project version. Before generating framework-specific code, read `prisma-php.json` first, then read the relevant docs from `node_modules/prisma-php/dist/docs`. For authentication, read `authentication.md` first, then verify the official auth docs: `auth-get-started`, `credentials`, and `state-manager-auth`. For file uploads, `multipart/form-data`, `$_FILES`, `PP\FileManager\UploadFile`, rename/delete flows, or file manager screens, read `file-manager.md` first, then verify the official File Manager docs: `get-started-file`. For ORM work, read `prisma-php-orm.md` before choosing schema, migration, or generation commands. For PulsePoint runtime behavior specifically, read `pulsepoint.md` before generating reactive UI code. For normal interactive full-stack page work, default to PulsePoint plus `pp.fetchFunction(...)` unless the user explicitly asks for a PHP-only interaction style. When building PulsePoint-aware `index.php` or `layout.php`, keep PHP first, HTML second, and one `<script>` block last, with the rendered route content inside a single parent HTML element that acts as the route-view boundary. For imported partial components, keep exactly one root element because Prisma PHP uses that root as the component boundary where serialized props and `pp-component` attach. For backend validation, sanitization, `Validator`, `Rule`, `pp.fetchFunction(...)` validation flows, or request validation in `route.php`, read `validator.md` first, then use `fetching-data.md`, `error-handling.md`, and `route-handlers.md` for context-specific patterns, then consult the official Validator docs. `PP\Validator` is a Prisma PHP core class located in `vendor/tsnc/prisma-php/src/Validator.php`. Route creation is file-based from `src/app`; Prisma PHP auto-generates route listings such as `files-list.json`, so AI agents must not create, edit, or maintain that file manually._/}
11
+ {/_AI agent hint: Prisma PHP uses local installed docs as the source of truth for the active project version. Before generating framework-specific code, read `prisma-php.json` first, then read the relevant docs from `node_modules/prisma-php/dist/docs`. For authentication, read `authentication.md` first, then verify the official auth docs: `auth-get-started`, `credentials`, and `state-manager-auth`. For file uploads, `multipart/form-data`, `$_FILES`, `PP\FileManager\UploadFile`, rename/delete flows, or file manager screens, read `file-manager.md` first, then verify the official File Manager docs: `get-started-file`. For ORM work, read `prisma-php-orm.md` before choosing schema, migration, or generation commands. For PulsePoint runtime behavior specifically, read `pulsepoint.md` before generating reactive UI code. For normal interactive full-stack page work, default to PulsePoint plus `pp.fetchFunction(...)` unless the user explicitly asks for a PHP-only interaction style. For normal PulsePoint-aware route files such as `index.php` and nested `layout.php`, keep PHP first, HTML second, and one `<script>` block last, with the rendered route content inside a single parent HTML element that acts as the route-view boundary. For imported partial components rendered with `ImportComponent::render(...)`, keep exactly one root element and place any component-local `<script>` inside that root because Prisma PHP uses that root as the component boundary where serialized props and `pp-component` attach. Do not confuse the route-file pattern with the imported-partial pattern. For backend validation, sanitization, `Validator`, `Rule`, `pp.fetchFunction(...)` validation flows, or request validation in `route.php`, read `validator.md` first, then use `fetching-data.md`, `error-handling.md`, and `route-handlers.md` for context-specific patterns, then consult the official Validator docs. `PP\Validator` is a Prisma PHP core class located in `vendor/tsnc/prisma-php/src/Validator.php`. Route creation is file-based from `src/app`; Prisma PHP auto-generates route listings such as `files-list.json`, so AI agents must not create, edit, or maintain that file manually._/}
12
12
 
13
13
  Welcome to the Prisma PHP documentation!
14
14
 
@@ -45,6 +45,69 @@ For typical full-stack Prisma PHP work, AI should assume this default:
45
45
 
46
46
  Do **not** default to PHP-only interaction flows for normal reactive UI work unless the user explicitly asks for that style.
47
47
 
48
+ ### Important structure rule
49
+
50
+ There are **two different valid structures** that AI must not mix up.
51
+
52
+ #### 1. Normal route files
53
+
54
+ For a normal route file such as `src/app/index.php` or nested `layout.php`, use:
55
+
56
+ 1. PHP
57
+ 2. one parent HTML element
58
+ 3. one `<script>` block after that parent element
59
+
60
+ Example:
61
+
62
+ ```php filename="src/app/index.php"
63
+ <?php
64
+
65
+ use PP\MainLayout;
66
+
67
+ MainLayout::$title = 'Todos';
68
+ MainLayout::$description = 'Track tasks and view the current item count.';
69
+ ?>
70
+
71
+ <section>
72
+ <h1>Todos</h1>
73
+ <p>Count: {count}</p>
74
+ </section>
75
+
76
+ <script>
77
+ const [count, setCount] = pp.state(0);
78
+ </script>
79
+ ```
80
+
81
+ For SEO metadata, prefer `MainLayout::$title` and `MainLayout::$description` instead of relying on a local heading variable alone.
82
+
83
+ #### 2. Imported partials rendered with `ImportComponent`
84
+
85
+ For an imported partial component, use:
86
+
87
+ 1. PHP
88
+ 2. exactly one parent root element containing the component markup
89
+ 3. any component-local `<script>` **inside** that root element
90
+
91
+ Example:
92
+
93
+ ```php filename="src/app/inc/SearchBox.php"
94
+ <?php
95
+
96
+ // PHP Code
97
+
98
+ ?>
99
+
100
+ <div class="rounded-lg border p-4" pp-component="search-box">
101
+ <h2>Search</h2>
102
+ <input value="{query}" />
103
+ <script>
104
+ console.log('Search component ready');
105
+ </script>
106
+ </div>
107
+ ```
108
+
109
+ Do not confuse the route-file pattern with the `ImportComponent` partial pattern.
110
+
48
111
  ### Read this doc when you need
49
112
 
50
113
  - **project setup or file placement** → `project-structure.md`
@@ -72,7 +135,8 @@ Do **not** default to PHP-only interaction flows for normal reactive UI work unl
72
135
  - do not create, edit, or maintain `files-list.json`; Prisma PHP generates route listings automatically
73
136
  - when building interactive frontend behavior, prefer the documented PulsePoint pattern with browser-side reactive state and `pp.fetchFunction(...)` for server calls; treat this as the default unless the user explicitly asks for a PHP-only interaction pattern
74
137
  - when building PulsePoint inside `index.php` or `layout.php`, read `layouts-and-pages.md` together with `pulsepoint.md`, use PHP then HTML then one `<script>` block, keep route content inside a single parent HTML element, and treat that root as the route-view boundary
75
- - when importing a PHP partial with `ImportComponent`, require exactly one root element because Prisma PHP attaches serialized props and a stable `pp-component` attribute to that root; do not manually sprinkle `pp-component` across normal route markup
138
+ - when importing a PHP partial with `ImportComponent`, require exactly one root element and keep any partial-local `<script>` inside that root because Prisma PHP attaches serialized props and a stable `pp-component` attribute to that root
139
+ - do not manually sprinkle `pp-component` across normal route markup
76
140
  - when building backend validation logic, read `validator.md` first, then use the route-specific docs for the request entry point
77
141
  - when using `pp.fetchFunction(...)`, expose the PHP function explicitly with `#[Exposed]`
78
142
  - when changing feature flags, update `prisma-php.json` first, then follow the documented update workflow
@@ -15,7 +15,7 @@ related:
15
15
  - /docs/route-php
16
16
  ---
17
17
 
18
- Prisma PHP uses **file-system based routing**, meaning you can use folders and files to define routes. This page will guide you through how to create layouts and pages, and organize them using Prisma PHP conventions.
18
+ Prisma PHP uses **file-system based routing**, meaning you can use folders and files to define routes. This page explains how to create pages and layouts, how to choose the correct route file, and how to keep PulsePoint-aware route files structurally consistent.
19
19
 
20
20
  ## Important route generation note
21
21
 
@@ -30,7 +30,7 @@ When adding a page or nested route, create the correct folder and add the proper
30
30
  Prisma PHP has two important route entry files, and they serve different purposes.
31
31
 
32
32
  - `index.php` is the **UI entry point** for a route. Use it when the route should render HTML or a normal full-stack page. In full-stack projects, this is also the default home for route-local PulsePoint-driven interactivity.
33
- - `route.php` is the **direct handler entry point** for a route. Use it when the route should behave like an API endpoint, JSON endpoint, AJAX handler, form-processing endpoint, or other no-view server handler.
33
+ - `route.php` is the **direct handler entry point** for a route. Use it when the route should behave like an API endpoint, JSON endpoint, AJAX handler, form-processing endpoint, webhook, or other no-view server handler.
34
34
 
35
35
  A helpful project-level rule comes from `prisma-php.json`:
36
36
 
@@ -70,24 +70,38 @@ In practice, that means:
70
70
  - render the route markup after the PHP block
71
71
  - place one PulsePoint `<script>` block at the bottom of the file
72
72
 
73
- For the HTML portion, keep a **single parent HTML tag** around the route content, similar to React's single-root component rule. Treat that root as the page or layout boundary instead of as a throwaway wrapper.
73
+ For the HTML portion, keep a **single parent HTML tag** around the route content. Treat that root as the page or layout boundary instead of as a throwaway wrapper.
74
74
 
75
- - In `index.php`, wrap the page UI in one parent element such as `<main>`, `<section>`, or `<article>`, and treat that root as the route view's component-style boundary.
75
+ - In `index.php`, wrap the page UI in one parent element such as `<main>`, `<section>`, or `<article>`, and treat that root as the route-view boundary.
76
76
  - In nested `layout.php`, wrap the shared layout UI and `<?= MainLayout::$children; ?>` in one parent element so the layout still behaves like one boundary.
77
- - If that markup is later extracted into an `ImportComponent` partial, preserve the same single-root structure so Prisma PHP can attach the stable `pp-component` attribute to that root element.
78
77
  - The root `layout.php` is the document shell and is the only layout that should contain `<html>` and `<body>`. Inside `<body>`, keep one clear wrapper around `<?= MainLayout::$children; ?>` when PulsePoint behavior is present.
79
78
 
80
- Example PulsePoint page structure:
79
+ ### Important distinction: route files vs imported partials
81
80
 
82
- ```php filename="src/app/dashboard/index.php"
81
+ Do **not** confuse the structure of a normal route file with the structure of an imported partial component.
82
+
83
+ For **normal route files** such as `src/app/index.php` and nested `layout.php`, use this shape:
84
+
85
+ 1. PHP
86
+ 2. one parent HTML element for the rendered route UI
87
+ 3. one `<script>` block after that parent element
88
+
89
+ Correct route-file pattern:
90
+
91
+ ```php filename="src/app/index.php"
83
92
  <?php
84
93
 
85
- $title = 'Dashboard';
94
+ use PP\Attributes\Exposed;
95
+ use PP\MainLayout;
96
+ use PP\Validator;
97
+
98
+ MainLayout::$title = 'Todos';
99
+ MainLayout::$description = 'Track tasks and view the current item count.';
86
100
  ?>
87
- <section class="dashboard-page">
88
- <h1><?= htmlspecialchars($title); ?></h1>
89
- <p>Count: {count}</p>
90
- <button onclick="setCount(count + 1)">Increment</button>
101
+
102
+ <section class="space-y-4">
103
+ <h1>Todos</h1>
104
+ <p>Count: {count}</p>
91
105
  </section>
92
106
 
93
107
  <script>
@@ -95,7 +109,10 @@ $title = 'Dashboard';
95
109
  </script>
96
110
  ```
97
111
 
98
- This order keeps server logic, rendered markup, and client reactivity easy to scan for both humans and AI tools.
112
+ That is the default structure for a PulsePoint-aware route page.
113
+ Use `MainLayout::$title` and `MainLayout::$description` for page metadata, and keep a separate heading variable only when the visible heading text needs to differ.
114
+
115
+ By contrast, **imported partials rendered with `ImportComponent::render(...)` follow a different rule**: the imported file must emit exactly **one root element**, and if it contains a `<script>`, that script must live **inside** that root element. Read `components.md` for that pattern.
99
116
 
100
117
  ## Creating a page
101
118
 
@@ -106,7 +123,15 @@ For example, to create an index page (`/`):
106
123
  ```php filename="src/app/index.php"
107
124
  <?php
108
125
 
109
- echo "<h1>Hello Prisma PHP!</h1>";
126
+ use PP\MainLayout;
127
+
128
+ MainLayout::$title = 'Hello Prisma PHP!';
129
+ MainLayout::$description = 'Welcome to your Prisma PHP application.';
130
+ ?>
131
+
132
+ <section pp-component="home-page">
133
+ <h1>Hello Prisma PHP!</h1>
134
+ </section>
110
135
  ```
111
136
 
112
137
  A route is created from the folder structure, and `index.php` is the default file used for route UI.
@@ -120,15 +145,18 @@ You can define a layout by creating a `layout.php` file. The root layout is requ
120
145
  For example, to create a root layout:
121
146
 
122
147
  ```php filename="src/app/layout.php"
148
+ <?php
123
149
 
124
- <?php use PP\MainLayout; ?>
150
+ use PP\MainLayout;
151
+ ?>
125
152
 
126
153
  <!DOCTYPE html>
127
154
  <html lang="en">
128
155
  <head>
129
156
  <meta charset="UTF-8">
130
157
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
131
- <title>My Prisma PHP App</title>
158
+ <title><?= htmlspecialchars(MainLayout::$title ?: 'My Prisma PHP App') ?></title>
159
+ <meta name="description" content="<?= htmlspecialchars(MainLayout::$description ?: 'Default application description') ?>">
132
160
  </head>
133
161
  <body>
134
162
  <main>
@@ -157,7 +185,15 @@ src/app/
157
185
  ```php filename="src/app/blog/index.php"
158
186
  <?php
159
187
 
160
- echo "<h1>Blog</h1>";
188
+ use PP\MainLayout;
189
+
190
+ MainLayout::$title = 'Blog';
191
+ MainLayout::$description = 'Read the latest articles and updates.';
192
+ ?>
193
+
194
+ <section pp-component="blog-page">
195
+ <h1>Blog</h1>
196
+ </section>
161
197
  ```
162
198
 
163
199
  You can continue nesting folders to create deeper routes. For example, to create a route for a specific blog post:
@@ -172,11 +208,18 @@ src/app/
172
208
  ```php filename="src/app/blog/[slug]/index.php"
173
209
  <?php
174
210
 
211
+ use PP\MainLayout;
175
212
  use PP\Request;
176
213
 
177
214
  $slug = Request::$dynamicParams['slug'] ?? null;
178
215
 
179
- echo "<h1>Blog post: " . htmlspecialchars((string) $slug) . "</h1>";
216
+ MainLayout::$title = 'Blog post: ' . ucfirst(str_replace('-', ' ', (string) $slug));
217
+ MainLayout::$description = 'Read the full article for ' . ucfirst(str_replace('-', ' ', (string) $slug)) . '.';
218
+ ?>
219
+
220
+ <section pp-component="blog-post-page">
221
+ <h1>Blog post: <?= htmlspecialchars((string) $slug) ?></h1>
222
+ </section>
180
223
  ```
181
224
 
182
225
  Wrapping a folder name in square brackets creates a **dynamic route segment** that can be filled from the incoming URL.
@@ -198,10 +241,12 @@ src/app/
198
241
  ```
199
242
 
200
243
  ```php filename="src/app/blog/layout.php"
244
+ <?php
201
245
 
202
- <?php use PP\MainLayout; ?>
246
+ use PP\MainLayout;
247
+ ?>
203
248
 
204
- <section>
249
+ <section pp-component="blog-layout">
205
250
  <header>
206
251
  <h1>Blog Section</h1>
207
252
  </header>
@@ -211,7 +256,8 @@ src/app/
211
256
  ```
212
257
 
213
258
  If you combine both layouts, the root layout wraps the blog layout, and the blog layout wraps both `src/app/blog/index.php` and `src/app/blog/[slug]/index.php`.
214
- Nested layouts should behave like single-root route views: one parent element should wrap the shared layout UI and `MainLayout::$children;`. Keep thinking in terms of one component-style boundary, not multiple sibling roots.
259
+ Nested layouts should behave like single-root route views: one parent element should wrap the shared layout UI and `MainLayout::$children;`.
260
+ Keep thinking in terms of one component-style boundary, not multiple sibling roots.
215
261
 
216
262
  ## Creating a dynamic segment
217
263
 
@@ -320,9 +366,12 @@ Example:
320
366
  ```php filename="src/app/users/route.php"
321
367
  <?php
322
368
 
323
- echo json_encode([
324
- 'success' => true,
325
- ]);
369
+ use Lib\Prisma\Classes\Prisma;
370
+
371
+ $prisma = Prisma::getInstance();
372
+ $users = $prisma->user->findMany();
373
+
374
+ echo json_encode($users);
326
375
  ```
327
376
 
328
377
  ## Good to know
@@ -342,3 +391,4 @@ echo json_encode([
342
391
  - Dynamic route parameters are available through `Request::$dynamicParams`.
343
392
  - Route groups let you organize routes and assign different layouts without affecting the URL.
344
393
  - Private folders help you colocate implementation files without making them routable.
394
+ - Imported partials rendered through `ImportComponent` follow a different structural rule: one root element containing any component-local `<script>`.
@@ -13,6 +13,8 @@ related:
13
13
 
14
14
  This page explains how to manage metadata in Prisma PHP and how icon file conventions such as `favicon`, `icon`, and `apple-icon` work.
15
15
 
16
+ For better SEO, treat `MainLayout::$title` and `MainLayout::$description` as the primary way to set page metadata. A local `$title` variable only affects rendered page content unless you also assign the document metadata through `MainLayout`.
17
+
16
18
  Prisma PHP’s metadata system is centered on the `MainLayout` class rather than Next.js exports like `metadata`, `generateMetadata`, or generated `opengraph-image.tsx`. The provided Prisma PHP docs describe dynamic page metadata and icon file conventions, but they do **not** document a dedicated Next.js-style generated Open Graph image pipeline on these pages. citeturn0view0turn1view0
17
19
 
18
20
  ## Metadata in Prisma PHP
@@ -40,6 +42,28 @@ In this model:
40
42
  - `MainLayout::$description` sets the page description
41
43
  - `MainLayout::addCustomMetaData(...)` adds additional metadata such as `author` or other `<meta>` values citeturn0view0
42
44
 
45
+ ## Heading text vs document metadata
46
+
47
+ If your page has a visible heading, keep it separate from the document metadata when needed.
48
+
49
+ This is a better pattern than relying on a local `$title` variable alone:
50
+
51
+ ```php filename="src/app/dashboard/index.php"
52
+ <?php
53
+
54
+ use Lib\MainLayout;
55
+
56
+ MainLayout::$title = "Dashboard";
57
+ MainLayout::$description = "View account stats, recent activity, and important actions.";
58
+ ?>
59
+
60
+ <section>
61
+ <h1>Dashboard</h1>
62
+ </section>
63
+ ```
64
+
65
+ Use this pattern when the browser title, search-engine title, and page heading should stay aligned. If the visible heading needs to differ from the SEO title, keep the heading in its own variable and continue setting metadata through `MainLayout`.
66
+
43
67
  ## Dynamic metadata
44
68
 
45
69
  Prisma PHP also supports dynamic metadata based on request data or other runtime conditions.
@@ -83,6 +107,34 @@ MainLayout::$description = "Learn more about our company.";
83
107
  </section>
84
108
  ```
85
109
 
110
+ ## Rendering metadata in the root layout
111
+
112
+ Your root `layout.php` can read from `MainLayout` so page-level metadata set in `index.php` or nested `layout.php` is reflected in the document head.
113
+
114
+ ```php filename="src/app/layout.php"
115
+ <?php
116
+
117
+ use Lib\MainLayout;
118
+ ?>
119
+
120
+ <!DOCTYPE html>
121
+ <html lang="en">
122
+ <head>
123
+ <meta charset="UTF-8">
124
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
125
+ <title><?= htmlspecialchars(MainLayout::$title ?: "My Prisma PHP App") ?></title>
126
+ <meta name="description" content="<?= htmlspecialchars(MainLayout::$description ?: "Default application description") ?>">
127
+ </head>
128
+ <body>
129
+ <main>
130
+ <?= MainLayout::$children; ?>
131
+ </main>
132
+ </body>
133
+ </html>
134
+ ```
135
+
136
+ This lets route files control SEO metadata while the root layout stays responsible for the actual `<head>` output.
137
+
86
138
  ## Adding custom metadata
87
139
 
88
140
  You can attach additional custom metadata through `MainLayout::addCustomMetaData(...)`.
@@ -219,7 +271,7 @@ Because those pages do not document a dedicated Prisma PHP Open Graph image gene
219
271
  ## Good to know
220
272
 
221
273
  - Prisma PHP uses `MainLayout` for dynamic metadata management. citeturn0view0
222
- - `MainLayout::$title` and `MainLayout::$description` are the main documented page metadata properties. citeturn0view0
274
+ - `MainLayout::$title` and `MainLayout::$description` are the main documented page metadata properties.
223
275
  - `MainLayout::addCustomMetaData(...)` adds custom metadata such as `author`. citeturn0view0
224
276
  - Metadata should be set before any HTML output. citeturn0view0
225
277
  - `MainLayout::addHeadScript(...)` and `MainLayout::addFooterScript(...)` can inject scripts into the page shell. citeturn0view0
@@ -75,15 +75,15 @@ For AI-assisted generation, the default full-stack route pattern should be:
75
75
 
76
76
  Only prefer a more PHP-only interaction pattern when the user explicitly asks for it.
77
77
 
78
- ### Framework-managed generated files
78
+ ## Framework-managed generated files
79
79
 
80
80
  Some files in a Prisma PHP project are framework-managed and should not be manually maintained as part of normal route work.
81
81
 
82
- #### `files-list.json`
82
+ ### `files-list.json`
83
83
 
84
84
  Prisma PHP automatically generates the route list in `files-list.json`.
85
85
 
86
- Do **not** create routes by editing this file. Do **not** manually add, remove, reorder, or sync entries in it.
86
+ Do **not** create routes by editing this file. Do **not** manually add, remove, reorder, or sync entries in it.
87
87
 
88
88
  Instead, create or update the correct folders and route files in `src/app`, then let Prisma PHP regenerate `files-list.json` automatically.
89
89
 
@@ -104,6 +104,67 @@ Routes are defined by the route tree under `src/app`. Prisma PHP then derives ro
104
104
  | `not-found.php` | Not found UI |
105
105
  | `error.php` | Error handling UI |
106
106
 
107
+ ## Route file shape rules
108
+
109
+ The route tree tells Prisma PHP **where** a route lives. The file shape tells AI **how** the route file should be structured.
110
+
111
+ ### `index.php` and nested `layout.php`
112
+
113
+ For normal PulsePoint-aware route files, use:
114
+
115
+ 1. PHP
116
+ 2. one parent HTML element
117
+ 3. one `<script>` block after that parent element
118
+
119
+ Example:
120
+
121
+ ```php filename="src/app/dashboard/index.php"
122
+ <?php
123
+
124
+ use PP\MainLayout;
125
+
126
+ MainLayout::$title = 'Dashboard';
127
+ MainLayout::$description = 'View dashboard metrics and current activity.';
128
+ ?>
129
+
130
+ <section class="dashboard-page">
131
+ <h1>Dashboard</h1>
132
+ <p>Count: {count}</p>
133
+ </section>
134
+
135
+ <script>
136
+ const [count, setCount] = pp.state(0);
137
+ </script>
138
+ ```
139
+
140
+ That is the default route-file structure for a full-stack page.
141
+ For document metadata, set `MainLayout::$title` and `MainLayout::$description` instead of assuming a local `$title` variable will populate the `<head>`.
142
+
143
+ ### `ImportComponent` partials are different
144
+
145
+ An imported partial rendered through `ImportComponent::render(...)` is **not** a normal route file.
146
+
147
+ For imported partials, the file must emit exactly **one root element**, and if the partial contains a `<script>`, that script must live **inside** that root element.
148
+
149
+ Example:
150
+
151
+ ```php filename="src/app/inc/SearchBox.php"
152
+ <?php
153
+
154
+ $title = 'Search';
155
+ ?>
156
+
157
+ <div class="rounded-lg border p-4" pp-component="search-box">
158
+ <h2><?= htmlspecialchars($title) ?></h2>
159
+ <input value="{query}" />
160
+ <script>
161
+ console.log('Search component ready');
162
+ </script>
163
+ </div>
164
+ ```
165
+
166
+ Do not confuse the normal route-file pattern with the imported-partial pattern.
167
+
107
168
  ## Nested routes
108
169
 
109
170
  Folders define URL segments. Nesting folders nests segments. A route becomes publicly accessible when an `index.php` file exists inside that route folder.
@@ -198,6 +259,8 @@ prisma-php-project/
198
259
  │ │ │ │ └── route.php
199
260
  │ │ │ └── _components/
200
261
  │ │ │ └── StatsCard.php
262
+ │ │ ├── inc/
263
+ │ │ │ └── SearchBox.php
201
264
  │ │ └── blog/
202
265
  │ │ ├── index.php
203
266
  │ │ └── [slug]/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-php",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Prisma PHP tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {