@jskit-ai/agent-docs 0.1.27 → 0.1.28
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/DISTR_AGENT.md +1 -0
- package/guide/agent/app-extras/assistant.md +2 -2
- package/guide/agent/app-extras/mobile-capacitor.md +10 -3
- package/guide/agent/app-extras/realtime.md +11 -11
- package/guide/agent/app-setup/a-more-interesting-shell.md +52 -44
- package/guide/agent/app-setup/authentication.md +40 -22
- package/guide/agent/app-setup/console.md +5 -5
- package/guide/agent/app-setup/database-layer.md +15 -15
- package/guide/agent/app-setup/initial-scaffolding.md +37 -5
- package/guide/agent/app-setup/multi-homing.md +28 -28
- package/guide/agent/app-setup/users.md +22 -22
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +13 -6
- package/guide/agent/generators/advanced-cruds.md +111 -68
- package/guide/agent/generators/crud-generators.md +5 -5
- package/guide/agent/generators/ui-generators.md +15 -13
- package/guide/agent/index.md +1 -1
- package/package.json +1 -1
- package/patterns/INDEX.md +4 -1
- package/patterns/client-requests.md +23 -6
- package/patterns/crud-repository-mapping.md +1 -1
- package/patterns/crud-scaffolding.md +10 -1
- package/patterns/filters.md +28 -10
- package/patterns/generated-ui-contract-tracking.md +62 -0
- package/patterns/page-scaffolding.md +17 -0
- package/patterns/placements.md +60 -0
- package/patterns/surfaces.md +5 -1
- package/patterns/ui-testing.md +3 -0
- package/reference/autogen/KERNEL_MAP.md +30 -1
- package/reference/autogen/packages/assistant.md +1 -1
- package/reference/autogen/packages/crud-ui-generator.md +19 -8
- package/reference/autogen/packages/kernel.md +38 -6
- package/reference/autogen/packages/realtime.md +1 -0
- package/reference/autogen/packages/shell-web.md +35 -3
- package/reference/autogen/packages/ui-generator.md +26 -5
- package/reference/autogen/packages/users-core.md +2 -0
- package/reference/autogen/packages/users-web.md +92 -3
- package/reference/autogen/packages/workspaces-web.md +3 -1
- package/reference/autogen/tooling/jskit-cli.md +24 -8
- package/templates/app/AGENTS.md +1 -0
- package/workflow/bootstrap.md +1 -0
package/DISTR_AGENT.md
CHANGED
|
@@ -31,6 +31,7 @@ Core rules:
|
|
|
31
31
|
- Keep that checkpoint compact. Do not expand it into a long preamble unless the developer asks for detail.
|
|
32
32
|
- When a request involves JSKIT UI, routing, surfaces, CRUDs, filters, placements, live actions, or similar implementation details, scan `patterns/INDEX.md` for matching keywords and read only the relevant pattern files.
|
|
33
33
|
- Reuse existing JSKIT helpers and runtime seams before adding new local helpers.
|
|
34
|
+
- If `npm install` is run while the app is using local JSKIT checkout links, run `npm run devlinks` immediately afterward.
|
|
34
35
|
- A freshly scaffolded JSKIT app can still be in Stage 1. If platform decisions are not settled yet, continue with the initialize workflow before adding runtime packages.
|
|
35
36
|
- Do not treat a missing `config.tenancyMode` line or an untouched minimal scaffold as a final tenancy decision.
|
|
36
37
|
- Do not install tenancy-sensitive packages until Stage 1 decisions are complete and the chosen tenancy is written into `config/public.js`.
|
|
@@ -455,7 +455,7 @@ npm run dev
|
|
|
455
455
|
npm run server
|
|
456
456
|
```
|
|
457
457
|
|
|
458
|
-
After sign-in, the app should
|
|
458
|
+
After sign-in, the app should expose these assistant routes:
|
|
459
459
|
|
|
460
460
|
- `/console/assistant`
|
|
461
461
|
- `/w/your-personal-slug/admin/assistant`
|
|
@@ -534,7 +534,7 @@ That is what lets one app host several assistants at once without their AI setti
|
|
|
534
534
|
|
|
535
535
|
### `.env` gains one AI block per assistant surface
|
|
536
536
|
|
|
537
|
-
The app
|
|
537
|
+
The app gets env keys such as:
|
|
538
538
|
|
|
539
539
|
```dotenv
|
|
540
540
|
CONSOLE_ASSISTANT_AI_PROVIDER=openai
|
|
@@ -36,6 +36,9 @@ npx jskit mobile android doctor
|
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
If doctor passes, the app is ready for the normal mobile workflow.
|
|
39
|
+
The `jskit mobile android ...` commands expect the mobile runtime package to be
|
|
40
|
+
installed in `package.json` and recorded in `.jskit/lock.json`; they do not
|
|
41
|
+
install `@jskit-ai/mobile-capacitor` for you.
|
|
39
42
|
|
|
40
43
|
## What you need on your machine
|
|
41
44
|
|
|
@@ -126,6 +129,10 @@ This:
|
|
|
126
129
|
- builds the JSKIT web app into `dist/`
|
|
127
130
|
- runs `cap sync android`
|
|
128
131
|
|
|
132
|
+
The refresh step requires the managed files from the package install to exist.
|
|
133
|
+
If `capacitor.config.json` or `.jskit/mobile-capacitor.md` is missing, install
|
|
134
|
+
the package again with `npx jskit add package @jskit-ai/mobile-capacitor`.
|
|
135
|
+
|
|
129
136
|
### Create the local tunnel
|
|
130
137
|
|
|
131
138
|
```bash
|
|
@@ -177,7 +184,7 @@ Use it when you want a clean signed-out state.
|
|
|
177
184
|
|
|
178
185
|
## The command set
|
|
179
186
|
|
|
180
|
-
These are the mobile commands the package
|
|
187
|
+
These are the CLI mobile commands used by the package workflow:
|
|
181
188
|
|
|
182
189
|
- `jskit mobile android devices`
|
|
183
190
|
- list visible Android devices
|
|
@@ -288,8 +295,8 @@ The package manages:
|
|
|
288
295
|
- `MainActivity.java` or `MainActivity.kt`
|
|
289
296
|
- the managed deep-link block in `AndroidManifest.xml`
|
|
290
297
|
|
|
291
|
-
`jskit mobile android sync`
|
|
292
|
-
`config.mobile`.
|
|
298
|
+
After the package is installed, `jskit mobile android sync` refreshes those
|
|
299
|
+
managed files from `config.mobile`.
|
|
293
300
|
|
|
294
301
|
### Why the tunnel exists
|
|
295
302
|
|
|
@@ -21,13 +21,13 @@ The first command records the runtime package in the app and updates the existin
|
|
|
21
21
|
|
|
22
22
|
Unlike the database, users, console, and workspace chapters, this one does **not** need `npm run db:migrate`. `realtime` does not add schema files. It is transport infrastructure, not persistence.
|
|
23
23
|
|
|
24
|
-
## What
|
|
24
|
+
## What the package adds
|
|
25
25
|
|
|
26
|
-
Installing `realtime`
|
|
26
|
+
Installing `realtime` adds three app capabilities.
|
|
27
27
|
|
|
28
28
|
### The app gets a realtime transport
|
|
29
29
|
|
|
30
|
-
The server
|
|
30
|
+
The server mounts a socket.io runtime on the same Fastify server that already serves your JSKIT surfaces. The browser gets a matching socket.io client runtime through the normal client boot process.
|
|
31
31
|
|
|
32
32
|
That means later modules, or your own app code, can stop thinking only in terms of request/response HTTP flows. They can start publishing live events and listening for them in Vue.
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ That means later modules, or your own app code, can stop thinking only in terms
|
|
|
35
35
|
|
|
36
36
|
The package also uses the shell scaffolding that already exists.
|
|
37
37
|
|
|
38
|
-
`realtime` appends a placement entry into `src/placement.js` that targets `shell
|
|
38
|
+
`realtime` appends a placement entry into `src/placement.js` that targets the semantic `shell.status` placement, so the shell shows a small status dot without you having to create a new page for it. In the default shell topology, `shell.status` renders through the concrete `shell-layout:top-right` outlet.
|
|
39
39
|
|
|
40
40
|
That dot is:
|
|
41
41
|
|
|
@@ -52,7 +52,7 @@ The app already had a browser dev server on `5173` and a backend runtime on `300
|
|
|
52
52
|
|
|
53
53
|
So one of the main values of this package is that you do **not** have to hand-edit Vite config just to make socket.io work in local development.
|
|
54
54
|
|
|
55
|
-
### There are
|
|
55
|
+
### There are no realtime pages
|
|
56
56
|
|
|
57
57
|
This is worth saying clearly because it can otherwise feel surprising.
|
|
58
58
|
|
|
@@ -75,7 +75,7 @@ npm run server
|
|
|
75
75
|
|
|
76
76
|
Then open `http://localhost:5173/home`.
|
|
77
77
|
|
|
78
|
-
The
|
|
78
|
+
The visible check is the shell status area. You should see the realtime status dot alongside the other shell controls.
|
|
79
79
|
|
|
80
80
|
If the websocket connects successfully, the dot is green. If the backend is unavailable or the socket is reconnecting, the dot is red. Hovering it shows the current status text.
|
|
81
81
|
|
|
@@ -159,7 +159,7 @@ After the install, the app has:
|
|
|
159
159
|
|
|
160
160
|
That one entry is what lets the browser dev server proxy websocket traffic correctly during local development.
|
|
161
161
|
|
|
162
|
-
### `src/placement.js`
|
|
162
|
+
### `src/placement.js` includes the shell status placement
|
|
163
163
|
|
|
164
164
|
The package appends this placement:
|
|
165
165
|
|
|
@@ -216,9 +216,9 @@ That is the right level of abstraction for this package:
|
|
|
216
216
|
|
|
217
217
|
## Summary
|
|
218
218
|
|
|
219
|
-
This chapter does not make the app look radically different, but it adds an important
|
|
219
|
+
This chapter does not make the app look radically different, but it adds an important capability.
|
|
220
220
|
|
|
221
|
-
- the backend
|
|
222
|
-
- the frontend
|
|
223
|
-
- the shell
|
|
221
|
+
- the backend hosts a realtime socket server
|
|
222
|
+
- the frontend keeps one shared websocket connection alive
|
|
223
|
+
- the shell exposes a live connection indicator
|
|
224
224
|
- later modules can build live behavior on top of that transport without inventing their own websocket setup
|
|
@@ -40,18 +40,18 @@ That is why the intended flow is:
|
|
|
40
40
|
|
|
41
41
|
If you build directly on top of the plain scaffold first and only try to add `shell-web` later, the install may fail because those app-owned files no longer match the untouched scaffold baseline.
|
|
42
42
|
|
|
43
|
-
Open `http://localhost:5173/` in the browser. The app
|
|
43
|
+
Open `http://localhost:5173/` in the browser. The app lands in the `home` surface inside a real shell with an app bar, a navigation drawer, and a settings route at `/home/settings`.
|
|
44
44
|
|
|
45
|
-
Two
|
|
45
|
+
Two shell pieces are worth noticing first.
|
|
46
46
|
|
|
47
|
-
- Navigation
|
|
47
|
+
- Navigation lives in the drawer itself. `Home` and `Settings` are real shell menu entries from the start.
|
|
48
48
|
- `Settings` is already a real nested section. Opening `/home/settings` redirects to `/home/settings/general`, and the left-side menu already contains a starter `General` entry.
|
|
49
49
|
|
|
50
50
|
Open `http://localhost:5173/home/settings` in the browser to see that nested settings shell immediately:
|
|
51
51
|
|
|
52
52
|
## Module features
|
|
53
53
|
|
|
54
|
-
The most important
|
|
54
|
+
The most important idea in `shell-web` is that the app has _specific, named places_ where UI can be inserted later. JSKIT calls those places _placements_. In practice, this means later packages or generators do not have to rewrite the whole shell every time they want to add a menu entry, a widget, or a settings section.
|
|
55
55
|
|
|
56
56
|
Start by asking JSKIT what public placement targets already exist:
|
|
57
57
|
|
|
@@ -59,12 +59,12 @@ Start by asking JSKIT what public placement targets already exist:
|
|
|
59
59
|
npx jskit list-placements
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
In a fresh `shell-web` app, the result
|
|
62
|
+
In a fresh `shell-web` app, the result includes entries like these:
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
Available placements:
|
|
66
66
|
- shell.primary-nav (default): Primary top-level navigation for the current surface.
|
|
67
|
-
- compact -> shell-layout:primary-
|
|
67
|
+
- compact -> shell-layout:primary-bottom-nav
|
|
68
68
|
- medium -> shell-layout:primary-menu
|
|
69
69
|
- expanded -> shell-layout:primary-menu
|
|
70
70
|
- page.section-nav [owner:home-settings]: Navigation between child pages in the home settings section.
|
|
@@ -95,6 +95,11 @@ Those concrete target names come from real `ShellOutlet` elements in the app. Se
|
|
|
95
95
|
/>
|
|
96
96
|
...
|
|
97
97
|
<ShellOutlet target="shell-layout:secondary-menu" />
|
|
98
|
+
...
|
|
99
|
+
<ShellOutlet target="shell-layout:primary-bottom-nav" />
|
|
100
|
+
...
|
|
101
|
+
<ShellOutlet target="shell-layout:supporting-bottom-sheet" />
|
|
102
|
+
<ShellOutlet target="shell-layout:supporting-side-panel" />
|
|
98
103
|
```
|
|
99
104
|
|
|
100
105
|
And the settings page introduces its own nested outlet in `src/pages/home/settings.vue`:
|
|
@@ -105,7 +110,7 @@ And the settings page introduces its own nested outlet in `src/pages/home/settin
|
|
|
105
110
|
|
|
106
111
|
That nested example matters. It shows that the shell is not the only place that can host placements. A page inside the shell can define its own insertion point too. That is how JSKIT can later build menus inside sections such as settings without rewriting the whole shell.
|
|
107
112
|
|
|
108
|
-
Just as importantly, `shell-web`
|
|
113
|
+
Just as importantly, `shell-web` uses that placement system itself. The starter app exposes semantic placement targets and seeds real placement entries into them. `Home` and `Settings` land in primary navigation, and `General` lands in the nested settings menu. The shell is exercising the same extension contract that later package and generator output uses.
|
|
109
114
|
|
|
110
115
|
The shell also ships with a few app-owned component tokens that it can use as default link renderers. You can inspect those too:
|
|
111
116
|
|
|
@@ -123,9 +128,9 @@ Showing link-item tokens only (token must end with "link-item"). Tip: use --all
|
|
|
123
128
|
- local.main.ui.tab-link-item [app:packages/main/src/client/providers/MainClientProvider.js]
|
|
124
129
|
```
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
The shell knows about three local link-item tokens. They are app-owned components registered by the local package, and topology uses them when an outlet needs to render links or tabs.
|
|
127
132
|
|
|
128
|
-
At this point the shell is
|
|
133
|
+
At this point the shell is using placements itself. The next step is to add one of our own.
|
|
129
134
|
|
|
130
135
|
### Adding generic elements directly
|
|
131
136
|
|
|
@@ -135,7 +140,7 @@ To add a small UI element to the shell itself:
|
|
|
135
140
|
npx jskit generate ui-generator placed-element --name "Alerts Widget"
|
|
136
141
|
```
|
|
137
142
|
|
|
138
|
-
That command creates a Vue component under `src/components/` (in this case `src/components/AlertsWidgetElement.vue`), registers a
|
|
143
|
+
That command creates a Vue component under `src/components/` (in this case `src/components/AlertsWidgetElement.vue`), registers a local token for it, and adds a placement entry targeting `shell.status`. After running it, refresh the home page in the browser. The shell renders the app-owned widget inside one of its named placement targets.
|
|
139
144
|
|
|
140
145
|
In this app, there is no need to pass `--surface`: since the app only has one enabled surface, JSKIT can infer it automatically.
|
|
141
146
|
|
|
@@ -159,15 +164,15 @@ npx jskit generate ui-generator page home/settings/profile/index.vue --name "Pro
|
|
|
159
164
|
|
|
160
165
|
This is a more interesting example than the widget case. JSKIT creates the page file, notices that `src/pages/home/settings.vue` owns the settings section navigation, and adds the preferred semantic menu entry there automatically. You do not have to write that placement entry by hand.
|
|
161
166
|
|
|
162
|
-
Open `/home/settings/profile` in the browser. The settings shell
|
|
167
|
+
Open `/home/settings/profile` in the browser. The settings shell shows a second real child page and a second real menu entry created by the same page-generation command. `General` comes from `shell-web`; `Profile` is the first additional settings page you add yourself. This is the important part of the chapter: the exact same placement system works both at the top shell level and inside a page-owned nested outlet.
|
|
163
168
|
|
|
164
|
-
|
|
169
|
+
Add a second sibling page:
|
|
165
170
|
|
|
166
171
|
```bash
|
|
167
172
|
npx jskit generate ui-generator page home/settings/notifications/index.vue --name "Notifications"
|
|
168
173
|
```
|
|
169
174
|
|
|
170
|
-
Open `/home/settings/notifications` in the browser. You
|
|
175
|
+
Open `/home/settings/notifications` in the browser. You get a third settings menu entry without touching `settings.vue`, without writing a second menu component, and without hand-editing `src/placement.js`. JSKIT appends another placement entry targeting the same `page.section-nav` owner, so the links stack in the menu for free.
|
|
171
176
|
|
|
172
177
|
The order is also easy to reason about:
|
|
173
178
|
|
|
@@ -179,7 +184,7 @@ The order is also easy to reason about:
|
|
|
179
184
|
In JSKIT's file-based routing, a page file can act as a layout if it renders a `RouterView`.
|
|
180
185
|
|
|
181
186
|
- `src/pages/home/settings.vue` owns the settings shell and wraps its child routes.
|
|
182
|
-
- `src/pages/home/settings/index.vue` is
|
|
187
|
+
- `src/pages/home/settings/index.vue` is just a redirect, so `/home/settings` lands on `/home/settings/general`.
|
|
183
188
|
- `src/pages/home/settings/general/index.vue` is the first real child page created by the starter shell.
|
|
184
189
|
- `src/pages/home/settings/profile/index.vue` becomes `/home/settings/profile` and still renders inside the layout from `settings.vue`.
|
|
185
190
|
|
|
@@ -248,13 +253,13 @@ Later in the guide, `jskit doctor` will help catch the second mistake automatica
|
|
|
248
253
|
|
|
249
254
|
### Component tokens
|
|
250
255
|
|
|
251
|
-
If you rerun the token listing
|
|
256
|
+
If you rerun the token listing, the widget command is the only command in this chapter that created an app-owned component token:
|
|
252
257
|
|
|
253
258
|
```bash
|
|
254
259
|
npx jskit list-component-tokens --all --prefix local.main.
|
|
255
260
|
```
|
|
256
261
|
|
|
257
|
-
The output
|
|
262
|
+
The output includes:
|
|
258
263
|
|
|
259
264
|
```text
|
|
260
265
|
- local.main.ui.element.alerts-widget
|
|
@@ -279,7 +284,7 @@ That is the first real example of JSKIT behaving like an extension system rather
|
|
|
279
284
|
|
|
280
285
|
## What `shell-web` changes in the app
|
|
281
286
|
|
|
282
|
-
The most interesting files
|
|
287
|
+
The most interesting files look roughly like this:
|
|
283
288
|
|
|
284
289
|
```text
|
|
285
290
|
src/
|
|
@@ -317,19 +322,18 @@ The first file worth reopening is still `package.json`. After `shell-web`, the m
|
|
|
317
322
|
{
|
|
318
323
|
"dependencies": {
|
|
319
324
|
"@jskit-ai/shell-web": "0.x",
|
|
320
|
-
"@tanstack/vue-query": "^5.90.5",
|
|
321
325
|
"@mdi/js": "^7.4.47"
|
|
322
326
|
}
|
|
323
327
|
}
|
|
324
328
|
```
|
|
325
329
|
|
|
326
|
-
The important part is not just that `@jskit-ai/shell-web` appears. The package
|
|
330
|
+
The important part is not just that `@jskit-ai/shell-web` appears. The package brings in shell runtime code and icon data; Vue Query is already owned by the base app bootstrap so every client package uses the same query client.
|
|
327
331
|
|
|
328
332
|
It is also worth noticing what does **not** happen here. The `placed-element` and `page` commands from this chapter mutate app-owned files, but they do not add a permanent runtime dependency to `package.json`. They are tooling actions, not runtime package installs.
|
|
329
333
|
|
|
330
334
|
The lock file becomes more interesting too. In the first chapter, `.jskit/lock.json` only knew about `@local/main`. Now it also records `@jskit-ai/shell-web` and the exact files and text mutations that package introduced.
|
|
331
335
|
|
|
332
|
-
That is worth noticing because this is the first chapter where JSKIT
|
|
336
|
+
That is worth noticing because this is the first chapter where JSKIT applies a runtime package that owns concrete changes in your app tree.
|
|
333
337
|
|
|
334
338
|
### The `home` surface gets a real wrapper
|
|
335
339
|
|
|
@@ -360,7 +364,7 @@ import { RouterView } from "vue-router";
|
|
|
360
364
|
</template>
|
|
361
365
|
```
|
|
362
366
|
|
|
363
|
-
That one change explains a lot. The `home` surface is
|
|
367
|
+
That one change explains a lot. The `home` surface is a shell-wrapped surface. Every child page under `src/pages/home/` renders inside that app-owned `ShellLayout`.
|
|
364
368
|
|
|
365
369
|
### `src/placement.js` becomes the placement registry
|
|
366
370
|
|
|
@@ -381,7 +385,7 @@ export default function getPlacements() {
|
|
|
381
385
|
|
|
382
386
|
That file is the app-owned seam for placements. `shell-web` owns the runtime that can render placements, but the app owns the registry source that lists what should appear in those targets.
|
|
383
387
|
|
|
384
|
-
After the `shell-web` install plus the `placed-element` and `page` commands from this chapter, the bottom of the file
|
|
388
|
+
After the `shell-web` install plus the `placed-element` and `page` commands from this chapter, the bottom of the file contains real placement entries:
|
|
385
389
|
|
|
386
390
|
```js
|
|
387
391
|
addPlacement({
|
|
@@ -483,7 +487,7 @@ That snippet shows the full placement contract clearly:
|
|
|
483
487
|
- lower `order` values come first
|
|
484
488
|
- when multiple entries target the same semantic placement with the same order, the shell keeps their source order
|
|
485
489
|
|
|
486
|
-
That is why the settings menu
|
|
490
|
+
That is why the settings menu shows `General` first, followed by `Profile` and `Notifications`: `General` is seeded by `shell-web` with a lower order, while the two generated pages share the same later order and keep their source order.
|
|
487
491
|
|
|
488
492
|
So the shell itself remains stable. What changes is the registry that feeds it.
|
|
489
493
|
|
|
@@ -503,7 +507,7 @@ registerMainClientComponent("local.main.ui.surface-aware-menu-link-item", () =>
|
|
|
503
507
|
registerMainClientComponent("local.main.ui.tab-link-item", () => TabLinkItem);
|
|
504
508
|
```
|
|
505
509
|
|
|
506
|
-
This is the same app-local provider seam from the previous chapter,
|
|
510
|
+
This is the same app-local provider seam from the previous chapter, and this chapter shows why it matters. The provider is what lets the placement runtime resolve app-owned components by token instead of hard-coding imports inside the shell runtime.
|
|
507
511
|
|
|
508
512
|
The `Profile` and `Notifications` pages did not need to add another provider registration because they reuse the existing `local.main.ui.surface-aware-menu-link-item` token for their menu entries.
|
|
509
513
|
|
|
@@ -515,17 +519,9 @@ So the flow is:
|
|
|
515
519
|
|
|
516
520
|
That is why the placement system feels dynamic even though the app still owns all of the concrete Vue files.
|
|
517
521
|
|
|
518
|
-
### `App.vue` and `error.js`
|
|
519
|
-
|
|
520
|
-
The top-level app root changes too. `src/App.vue` is no longer just:
|
|
521
|
-
|
|
522
|
-
```vue
|
|
523
|
-
<v-app>
|
|
524
|
-
<RouterView />
|
|
525
|
-
</v-app>
|
|
526
|
-
```
|
|
522
|
+
### `App.vue` and `error.js` provide shell-level errors
|
|
527
523
|
|
|
528
|
-
|
|
524
|
+
The top-level app root includes the normal route outlet and the shell error host:
|
|
529
525
|
|
|
530
526
|
```vue
|
|
531
527
|
<script setup>
|
|
@@ -541,31 +537,43 @@ import ShellErrorHost from "@jskit-ai/shell-web/client/components/ShellErrorHost
|
|
|
541
537
|
</template>
|
|
542
538
|
```
|
|
543
539
|
|
|
544
|
-
That host is backed by the
|
|
540
|
+
That host is backed by the app-owned `src/error.js` file:
|
|
545
541
|
|
|
546
542
|
```js
|
|
547
543
|
import { createDefaultErrorPolicy } from "@jskit-ai/shell-web/client/error";
|
|
548
544
|
|
|
549
545
|
export default Object.freeze({
|
|
550
546
|
defaultPresenterId: "material.snackbar",
|
|
551
|
-
policy: createDefaultErrorPolicy(
|
|
547
|
+
policy: createDefaultErrorPolicy({
|
|
548
|
+
resourceLoadChannel: "silent",
|
|
549
|
+
actionFeedbackChannel: "snackbar",
|
|
550
|
+
appRecoverableChannel: "banner",
|
|
551
|
+
blockingChannel: "dialog"
|
|
552
|
+
}),
|
|
552
553
|
presenters: []
|
|
553
554
|
});
|
|
554
555
|
```
|
|
555
556
|
|
|
556
557
|
The idea is the same as with placements: `shell-web` provides the runtime, but the app owns the configuration file that the runtime reads.
|
|
557
558
|
|
|
558
|
-
|
|
559
|
+
The default error policy is intent-based. Runtime code reports what kind of error happened and the app-owned policy decides the presentation:
|
|
560
|
+
|
|
561
|
+
- `resource-load` uses `silent`; the screen keeps the load message and retry action local.
|
|
562
|
+
- `action-feedback` uses `snackbar`; saves, commands, and other user-triggered actions report through one lightweight feedback channel.
|
|
563
|
+
- `app-recoverable` uses `banner`; shell refresh and recoverable navigation failures stay visible without blocking the app.
|
|
564
|
+
- `blocking` uses `dialog`; unexpected UI failures and other blocking errors require explicit attention.
|
|
565
|
+
|
|
566
|
+
### The home page talks to the backend
|
|
559
567
|
|
|
560
|
-
`src/pages/home/index.vue`
|
|
568
|
+
`src/pages/home/index.vue` uses Vue Query to fetch `/api/health` and display the result in the UI.
|
|
561
569
|
|
|
562
|
-
That is why this chapter is the point where running both `npm run dev` and `npm run server`
|
|
570
|
+
That is why this chapter is the point where running both `npm run dev` and `npm run server` matters. The page expects the backend to be alive.
|
|
563
571
|
|
|
564
572
|
This matters because it is the first tiny example of the frontend and backend participating in the same shell. The request itself is simple, but the behavior is more realistic than the empty starter card from the previous chapter.
|
|
565
573
|
|
|
566
574
|
### The first client stores appear
|
|
567
575
|
|
|
568
|
-
|
|
576
|
+
`shell-web` also exposes app-facing Pinia stores:
|
|
569
577
|
|
|
570
578
|
```js
|
|
571
579
|
import {
|
|
@@ -607,10 +615,10 @@ The same store also exposes the live drawer state through `drawerOpen`, plus `se
|
|
|
607
615
|
|
|
608
616
|
You do not need either store very often in this chapter, because `shell-web` already mounts the shell and error host for you. The starter `General` settings page uses the higher-level `useShellLayoutState()` helper instead of talking to `useShellLayoutStore()` directly, because that helper combines the store-backed drawer state with the current route and surface context that `ShellLayout` also needs.
|
|
609
617
|
|
|
610
|
-
That distinction is worth noticing
|
|
618
|
+
That distinction is worth noticing:
|
|
611
619
|
|
|
612
620
|
- runtime services still do the operational work
|
|
613
|
-
- Pinia stores are
|
|
621
|
+
- Pinia stores are the normal Vue-facing shared-state surface
|
|
614
622
|
|
|
615
623
|
### The first settings route appears
|
|
616
624
|
|
|
@@ -634,7 +642,7 @@ The important host file is still `src/pages/home/settings.vue`:
|
|
|
634
642
|
|
|
635
643
|
This file matters for the same reason as `ShellLayout.vue`: it creates another named extension point instead of hard-coding a finished settings UI. The difference is that this one lives inside a page, not at the top shell level.
|
|
636
644
|
|
|
637
|
-
The starter shell
|
|
645
|
+
The starter shell uses a real child-page structure right away:
|
|
638
646
|
|
|
639
647
|
- `src/pages/home/settings/index.vue` is only a redirect into the first child page
|
|
640
648
|
- `src/pages/home/settings/general/index.vue` is the first real settings page
|
|
@@ -665,6 +673,6 @@ The app is still structurally simple. `shell-web` just makes that simple app beh
|
|
|
665
673
|
|
|
666
674
|
## Summary
|
|
667
675
|
|
|
668
|
-
After this chapter, the app is still small, but it
|
|
676
|
+
After this chapter, the app is still small, but it has a shell shape. `shell-web` adds an app-owned shell layout, a placement registry, a shell error host, menu-link tokens in the local client provider, real drawer navigation, and the first nested settings section under `home`.
|
|
669
677
|
|
|
670
678
|
More importantly, this chapter is where placements stop being theory. The shell already uses them for `Home`, `Settings`, and the starter `General` settings page. Then you inspect the available targets, place real UI into the outer shell, and add more child settings pages that automatically land in the nested settings menu. That is the first real example of JSKIT working as an extension system rather than just a scaffold generator.
|
|
@@ -83,7 +83,7 @@ npm run server
|
|
|
83
83
|
|
|
84
84
|
Then open `http://localhost:5173/auth/login` in the browser.
|
|
85
85
|
|
|
86
|
-
The login page is
|
|
86
|
+
The login page is real, and it already contains several different auth modes behind the same card.
|
|
87
87
|
|
|
88
88
|
- **Sign in** is the normal email-and-password flow.
|
|
89
89
|
- **Register** creates a new Supabase auth user.
|
|
@@ -91,7 +91,7 @@ The login page is now real, and it already contains several different auth modes
|
|
|
91
91
|
- **Use one-time code** switches to an email OTP login flow.
|
|
92
92
|
- **Remember this account on this device** stores a small local hint in browser storage so the next visit can greet the last-used account and let the user keep that email preselected.
|
|
93
93
|
|
|
94
|
-
If you go back to `http://localhost:5173/home`, the shell also has a small auth widget in the
|
|
94
|
+
If you go back to `http://localhost:5173/home`, the shell also has a small auth widget in the status area. When you are signed out it shows a guest state and a menu entry that leads to `/auth/login`. After you sign in, the same placement changes to a sign-out menu.
|
|
95
95
|
|
|
96
96
|
## Reading the screen carefully
|
|
97
97
|
|
|
@@ -165,7 +165,7 @@ JSKIT asks Supabase to send OTP login emails only for existing users. In other w
|
|
|
165
165
|
|
|
166
166
|
That endpoint asks Supabase to send a password reset email. In this chapter's scaffold, this mode is only the **request** step.
|
|
167
167
|
|
|
168
|
-
The backend already supports the later recovery endpoints too, but the chapter's simple app does **not** yet scaffold a dedicated app-owned `reset-password` page. So at this stage, the guide should be read as: the screen can request recovery emails
|
|
168
|
+
The backend already supports the later recovery endpoints too, but the chapter's simple app does **not** yet scaffold a dedicated app-owned `reset-password` page. So at this stage, the guide should be read as: the screen can request recovery emails, while the full browser-side password-reset completion UI is still something you would add explicitly in the app.
|
|
169
169
|
|
|
170
170
|
### Remembered account behavior
|
|
171
171
|
|
|
@@ -184,7 +184,7 @@ On the next visit, the card can show a `Welcome back, ...` panel and a `Use anot
|
|
|
184
184
|
|
|
185
185
|
The screen is also ready to show OAuth provider buttons, but only if providers are configured.
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
For this chapter, `config.server.js` keeps this empty:
|
|
188
188
|
|
|
189
189
|
```js
|
|
190
190
|
config.auth = {
|
|
@@ -332,13 +332,22 @@ addPlacement({
|
|
|
332
332
|
});
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
-
And the page file itself is a normal
|
|
335
|
+
And the page file itself is a normal generated empty-state screen:
|
|
336
336
|
|
|
337
337
|
```vue
|
|
338
338
|
<template>
|
|
339
|
-
<section class="
|
|
340
|
-
<
|
|
341
|
-
|
|
339
|
+
<section class="generated-page-screen d-flex flex-column ga-4">
|
|
340
|
+
<header>
|
|
341
|
+
<p class="text-overline text-medium-emphasis mb-1">Screen</p>
|
|
342
|
+
<h1 class="generated-page-screen__title">Reports</h1>
|
|
343
|
+
</header>
|
|
344
|
+
|
|
345
|
+
<v-sheet rounded="lg" border class="generated-page-screen__empty-state">
|
|
346
|
+
<h2 class="text-h6 mb-2">No Reports activity yet</h2>
|
|
347
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
348
|
+
Activity and actions for this screen will appear here.
|
|
349
|
+
</p>
|
|
350
|
+
</v-sheet>
|
|
342
351
|
</section>
|
|
343
352
|
</template>
|
|
344
353
|
```
|
|
@@ -364,9 +373,18 @@ To make the route require login, add a route guard block to `src/pages/home/repo
|
|
|
364
373
|
</route>
|
|
365
374
|
|
|
366
375
|
<template>
|
|
367
|
-
<section class="
|
|
368
|
-
<
|
|
369
|
-
|
|
376
|
+
<section class="generated-page-screen d-flex flex-column ga-4">
|
|
377
|
+
<header>
|
|
378
|
+
<p class="text-overline text-medium-emphasis mb-1">Screen</p>
|
|
379
|
+
<h1 class="generated-page-screen__title">Reports</h1>
|
|
380
|
+
</header>
|
|
381
|
+
|
|
382
|
+
<v-sheet rounded="lg" border class="generated-page-screen__empty-state">
|
|
383
|
+
<h2 class="text-h6 mb-2">No Reports activity yet</h2>
|
|
384
|
+
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
385
|
+
Activity and actions for this screen will appear here.
|
|
386
|
+
</p>
|
|
387
|
+
</v-sheet>
|
|
370
388
|
</section>
|
|
371
389
|
</template>
|
|
372
390
|
```
|
|
@@ -383,7 +401,7 @@ So after login, JSKIT can send the user back to the page they originally asked f
|
|
|
383
401
|
|
|
384
402
|
### Hide the menu entry when signed out
|
|
385
403
|
|
|
386
|
-
The route is
|
|
404
|
+
The route is protected, but the drawer link is still visible. That is expected. Route protection and shell visibility are separate concerns.
|
|
387
405
|
|
|
388
406
|
To hide the `Reports` menu entry until the user is logged in, update the placement entry in `src/placement.js`:
|
|
389
407
|
|
|
@@ -407,7 +425,7 @@ addPlacement({
|
|
|
407
425
|
|
|
408
426
|
The only new part is the `when(...)` line. That predicate is evaluated by the shell placement runtime using the auth context that `auth-web` injects from `/api/session`.
|
|
409
427
|
|
|
410
|
-
So the behavior
|
|
428
|
+
So the behavior becomes:
|
|
411
429
|
|
|
412
430
|
- signed out:
|
|
413
431
|
- the `Reports` drawer entry disappears
|
|
@@ -440,7 +458,7 @@ const auth = useAuthStore();
|
|
|
440
458
|
</v-alert>
|
|
441
459
|
|
|
442
460
|
<h1 class="text-h5 mb-2">Home</h1>
|
|
443
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
461
|
+
<p class="text-body-2 text-medium-emphasis">Your account session is active on this device.</p>
|
|
444
462
|
</section>
|
|
445
463
|
</template>
|
|
446
464
|
```
|
|
@@ -501,7 +519,7 @@ At this point the guide has shown three distinct layers of client state:
|
|
|
501
519
|
- `shell-web` adds shell-facing stores such as `useShellLayoutStore()`
|
|
502
520
|
- `auth-web` adds `useAuthStore()` for authentication state
|
|
503
521
|
|
|
504
|
-
That progression is intentional. Packages keep their operational runtimes internally, but the app-facing shared state they surface to Vue code is
|
|
522
|
+
That progression is intentional. Packages keep their operational runtimes internally, but the app-facing shared state they surface to Vue code is store-based.
|
|
505
523
|
|
|
506
524
|
## What `auth-base` adds to the app
|
|
507
525
|
|
|
@@ -528,7 +546,7 @@ Three things are worth noticing immediately.
|
|
|
528
546
|
|
|
529
547
|
- `auth-provider-supabase-core` is the provider-specific runtime.
|
|
530
548
|
- `auth-web` is the part that adds the web routes and the default auth UI.
|
|
531
|
-
- there is
|
|
549
|
+
- there is an `auth` surface-specific dev/build script family, just as `home` already had.
|
|
532
550
|
|
|
533
551
|
The provider command also writes a new `.env` file:
|
|
534
552
|
|
|
@@ -541,7 +559,7 @@ APP_PUBLIC_URL=http://localhost:5173
|
|
|
541
559
|
|
|
542
560
|
This is the bridge between the scaffold and your real Supabase project. `APP_PUBLIC_URL` matters because auth emails and callback flows need to know which browser URL they should return to.
|
|
543
561
|
|
|
544
|
-
Public routing config changes too. `config/public.js`
|
|
562
|
+
Public routing config changes too. `config/public.js` has a second surface:
|
|
545
563
|
|
|
546
564
|
```js
|
|
547
565
|
config.surfaceDefinitions.auth = {
|
|
@@ -568,7 +586,7 @@ config.auth = {
|
|
|
568
586
|
};
|
|
569
587
|
```
|
|
570
588
|
|
|
571
|
-
That small block explains a lot of the default login screen. The stock UI is ready for OAuth providers such as Google, but
|
|
589
|
+
That small block explains a lot of the default login screen. The stock UI is ready for OAuth providers such as Google, but this chapter keeps the provider list empty, so the page only shows the email/password and one-time-code flows. Later, if you enable a provider in Supabase and list it here, the same login screen can expose that button too.
|
|
572
590
|
|
|
573
591
|
The auth routes themselves are app-owned wrappers around the module-supplied default views. `src/pages/auth/login.vue` looks like this:
|
|
574
592
|
|
|
@@ -640,7 +658,7 @@ addPlacement({
|
|
|
640
658
|
});
|
|
641
659
|
```
|
|
642
660
|
|
|
643
|
-
This is the shell placement system from the previous chapter doing real work again. `auth-web` does not hard-code a permanent
|
|
661
|
+
This is the shell placement system from the previous chapter doing real work again. `auth-web` does not hard-code a permanent auth button into `ShellLayout.vue`. Instead, it contributes the profile widget into the semantic `shell.status` placement and contributes sign-in/sign-out links into the semantic `auth.profile-menu` placement. Topology maps those semantic targets to concrete outlets and renderers.
|
|
644
662
|
|
|
645
663
|
So the auth story in this chapter is spread across clear responsibilities:
|
|
646
664
|
|
|
@@ -702,7 +720,7 @@ onBeforeUnmount(() => {
|
|
|
702
720
|
</v-alert>
|
|
703
721
|
|
|
704
722
|
<h1 class="text-h5 mb-2">Home</h1>
|
|
705
|
-
<p class="text-body-2 text-medium-emphasis">
|
|
723
|
+
<p class="text-body-2 text-medium-emphasis">Your account session is active on this device.</p>
|
|
706
724
|
</section>
|
|
707
725
|
</template>
|
|
708
726
|
```
|
|
@@ -917,7 +935,7 @@ It is also why the shell widget can react cleanly to auth state without storing
|
|
|
917
935
|
|
|
918
936
|
### Authenticated Playwright testing with the dev auth bypass
|
|
919
937
|
|
|
920
|
-
JSKIT
|
|
938
|
+
JSKIT ships a development-only auth bootstrap path specifically so authenticated UI can be verified in Playwright without depending on a real live login flow through Supabase.
|
|
921
939
|
|
|
922
940
|
This is the standard path the agent should use for authenticated browser tests:
|
|
923
941
|
|
|
@@ -1083,6 +1101,6 @@ That is why the confirmation screen in the app should be understood as a **sessi
|
|
|
1083
1101
|
|
|
1084
1102
|
## Summary
|
|
1085
1103
|
|
|
1086
|
-
After this chapter, the app can really authenticate against Supabase. It has a public `auth` surface, a stock login page, a sign-out route, and a shell widget that reflects auth state. The provider-specific values
|
|
1104
|
+
After this chapter, the app can really authenticate against Supabase. It has a public `auth` surface, a stock login page, a sign-out route, and a shell widget that reflects auth state. The provider-specific values live in `.env`, and the web auth layer is wired into the same placement and surface system introduced earlier in the guide.
|
|
1087
1105
|
|
|
1088
1106
|
Just as importantly, the app is still deliberately incomplete. Authentication exists, but the database-backed user model does not. That separation is useful, because the next layer of the guide can explain users and persistence without having to also explain the first auth setup at the same time.
|