@jskit-ai/agent-docs 0.1.26 → 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.
Files changed (46) hide show
  1. package/DISTR_AGENT.md +1 -0
  2. package/guide/agent/app-extras/assistant.md +9 -20
  3. package/guide/agent/app-extras/mobile-capacitor.md +381 -0
  4. package/guide/agent/app-extras/realtime.md +13 -12
  5. package/guide/agent/app-setup/a-more-interesting-shell.md +102 -87
  6. package/guide/agent/app-setup/authentication.md +50 -31
  7. package/guide/agent/app-setup/console.md +8 -8
  8. package/guide/agent/app-setup/database-layer.md +15 -15
  9. package/guide/agent/app-setup/initial-scaffolding.md +37 -5
  10. package/guide/agent/app-setup/multi-homing.md +50 -47
  11. package/guide/agent/app-setup/quickstart.md +11 -14
  12. package/guide/agent/app-setup/users.md +24 -24
  13. package/guide/agent/app-setup/working-with-the-jskit-cli.md +18 -10
  14. package/guide/agent/generators/advanced-cruds.md +111 -68
  15. package/guide/agent/generators/crud-generators.md +5 -5
  16. package/guide/agent/generators/ui-generators.md +44 -36
  17. package/guide/agent/index.md +3 -2
  18. package/package.json +1 -1
  19. package/patterns/INDEX.md +4 -1
  20. package/patterns/client-requests.md +23 -6
  21. package/patterns/crud-repository-mapping.md +1 -1
  22. package/patterns/crud-scaffolding.md +10 -1
  23. package/patterns/filters.md +28 -10
  24. package/patterns/generated-ui-contract-tracking.md +62 -0
  25. package/patterns/page-scaffolding.md +20 -2
  26. package/patterns/placements.md +67 -1
  27. package/patterns/surfaces.md +5 -1
  28. package/patterns/ui-testing.md +3 -0
  29. package/reference/autogen/KERNEL_MAP.md +46 -1
  30. package/reference/autogen/README.md +3 -0
  31. package/reference/autogen/packages/assistant.md +2 -1
  32. package/reference/autogen/packages/auth-web.md +20 -1
  33. package/reference/autogen/packages/crud-ui-generator.md +20 -8
  34. package/reference/autogen/packages/google-rewarded-core.md +222 -0
  35. package/reference/autogen/packages/google-rewarded-web.md +71 -0
  36. package/reference/autogen/packages/kernel.md +94 -10
  37. package/reference/autogen/packages/mobile-capacitor.md +76 -0
  38. package/reference/autogen/packages/realtime.md +1 -0
  39. package/reference/autogen/packages/shell-web.md +48 -3
  40. package/reference/autogen/packages/ui-generator.md +34 -2
  41. package/reference/autogen/packages/users-core.md +2 -0
  42. package/reference/autogen/packages/users-web.md +92 -4
  43. package/reference/autogen/packages/workspaces-web.md +3 -2
  44. package/reference/autogen/tooling/jskit-cli.md +114 -9
  45. package/templates/app/AGENTS.md +1 -0
  46. package/workflow/bootstrap.md +1 -0
@@ -40,39 +40,48 @@ 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 still lands in the `home` surface, but that surface is no longer just a card in the middle of the page. It is wrapped in a real shell with an app bar, a navigation drawer, and a settings route at `/home/settings`.
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 important things have changed compared with the older starter shell.
45
+ Two shell pieces are worth noticing first.
46
46
 
47
- - Navigation now lives in the drawer itself. `Home` and `Settings` are real shell menu entries from the start.
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 new idea in `shell-web` is that the app now 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.
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
- Start by asking JSKIT what placement targets already exist:
56
+ Start by asking JSKIT what public placement targets already exist:
57
57
 
58
58
  ```bash
59
59
  npx jskit list-placements
60
60
  ```
61
61
 
62
- In a fresh `shell-web` app, the result looks like this:
62
+ In a fresh `shell-web` app, the result includes entries like these:
63
63
 
64
64
  ```text
65
65
  Available placements:
66
- - home-settings:primary-menu [src/pages/home/settings.vue]
67
- - shell-layout:primary-menu (default) [src/components/ShellLayout.vue]
68
- - shell-layout:secondary-menu [src/components/ShellLayout.vue]
69
- - shell-layout:top-left [src/components/ShellLayout.vue]
70
- - shell-layout:top-right [src/components/ShellLayout.vue]
66
+ - shell.primary-nav (default): Primary top-level navigation for the current surface.
67
+ - compact -> shell-layout:primary-bottom-nav
68
+ - medium -> shell-layout:primary-menu
69
+ - expanded -> shell-layout:primary-menu
70
+ - page.section-nav [owner:home-settings]: Navigation between child pages in the home settings section.
71
+ - compact -> home-settings:primary-menu
72
+ - medium -> home-settings:primary-menu
73
+ - expanded -> home-settings:primary-menu
71
74
  ```
72
75
 
73
- This command lists placement targets, not the content inside them. That is why the output is about target names and source files. Later, when you place things into the shell, this list stays the same unless you add or remove a `ShellOutlet` in source.
76
+ This command lists semantic placements, not the content inside them. Later, when you place things into the shell, this list stays stable unless the public placement topology changes.
74
77
 
75
- Those target names come from real `ShellOutlet` elements in the app. See `src/components/ShellLayout.vue`:
78
+ The concrete outlets still exist, but they are implementation details. If you need to inspect them directly, use:
79
+
80
+ ```bash
81
+ npx jskit list-placements --concrete
82
+ ```
83
+
84
+ Those concrete target names come from real `ShellOutlet` elements in the app. See `src/components/ShellLayout.vue`:
76
85
 
77
86
  ```html
78
87
  ...
@@ -83,27 +92,25 @@ Those target names come from real `ShellOutlet` elements in the app. See `src/co
83
92
  <ShellOutlet
84
93
  target="shell-layout:primary-menu"
85
94
  default
86
- default-link-component-token="local.main.ui.surface-aware-menu-link-item"
87
95
  />
88
96
  ...
89
- <ShellOutlet
90
- target="shell-layout:secondary-menu"
91
- default-link-component-token="local.main.ui.surface-aware-menu-link-item"
92
- />
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" />
93
103
  ```
94
104
 
95
105
  And the settings page introduces its own nested outlet in `src/pages/home/settings.vue`:
96
106
 
97
107
  ```html
98
- <ShellOutlet
99
- target="home-settings:primary-menu"
100
- default-link-component-token="local.main.ui.surface-aware-menu-link-item"
101
- />
108
+ <ShellOutlet target="home-settings:primary-menu" />
102
109
  ```
103
110
 
104
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.
105
112
 
106
- Just as importantly, `shell-web` already uses that placement system itself. The starter app is not only exposing placement targets; it is also seeding real placement entries into them. The drawer gets `Home` and `Settings`, and the nested settings menu gets `General`. That is why the shell already feels real before you generate anything of your own.
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.
107
114
 
108
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:
109
116
 
@@ -121,9 +128,9 @@ Showing link-item tokens only (token must end with "link-item"). Tip: use --all
121
128
  - local.main.ui.tab-link-item [app:packages/main/src/client/providers/MainClientProvider.js]
122
129
  ```
123
130
 
124
- So before we add anything of our own, the shell already knows about three local link-item tokens. They are app-owned components registered by the local package, and the shell uses them when an outlet needs to render links or tabs.
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.
125
132
 
126
- At this point the shell is already using placements itself. The next step is to add one of our own.
133
+ At this point the shell is using placements itself. The next step is to add one of our own.
127
134
 
128
135
  ### Adding generic elements directly
129
136
 
@@ -133,7 +140,7 @@ To add a small UI element to the shell itself:
133
140
  npx jskit generate ui-generator placed-element --name "Alerts Widget"
134
141
  ```
135
142
 
136
- That command creates a Vue component under `src/components/` (in this case `src/components/AlertsWidgetElement.vue`), registers a new local token for it, and adds a placement entry targeting `shell-layout:top-right`. After running it, refresh the home page in the browser. The shell now has a real app-owned widget living inside one of its named placement targets.
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.
137
144
 
138
145
  In this app, there is no need to pass `--surface`: since the app only has one enabled surface, JSKIT can infer it automatically.
139
146
 
@@ -141,13 +148,13 @@ In this app, there is no need to pass `--surface`: since the app only has one en
141
148
 
142
149
  The settings host uses the same placement machinery, but the normal way to grow it is not by dropping a free-standing widget there. The more interesting case is adding a child page and letting JSKIT wire the menu entry for you.
143
150
 
144
- The source path in the `list-placements` output helps you reason about where child pages belong. When you see:
151
+ The owner in the `list-placements` output helps you reason about where child pages belong. When you see:
145
152
 
146
153
  ```text
147
- - home-settings:primary-menu [src/pages/home/settings.vue]
154
+ - page.section-nav [owner:home-settings]
148
155
  ```
149
156
 
150
- you know that the outlet lives in the settings host page. So if you want a child page to appear in that menu, you should create it under that part of the route tree instead of treating the menu like a generic widget area. For example, `src/pages/home/settings/profile/index.vue` belongs to that settings section, so JSKIT can wire its preferred menu entry into `home-settings:primary-menu` automatically.
157
+ you know that the semantic placement is owned by the settings host page. So if you want a child page to appear in that menu, you should create it under that part of the route tree instead of treating the menu like a generic widget area. For example, `src/pages/home/settings/profile/index.vue` belongs to that settings section, so JSKIT can wire its preferred menu entry into `page.section-nav` with owner `home-settings` automatically.
151
158
 
152
159
  Now use the settings host the way it is normally meant to be used: add a real child page under it.
153
160
 
@@ -155,17 +162,17 @@ Now use the settings host the way it is normally meant to be used: add a real ch
155
162
  npx jskit generate ui-generator page home/settings/profile/index.vue --name "Profile"
156
163
  ```
157
164
 
158
- This is a more interesting example than the widget case. JSKIT creates the page file, notices that `src/pages/home/settings.vue` owns the `home-settings:primary-menu` outlet, and adds the preferred menu entry there automatically. You do not have to write that placement entry by hand.
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.
159
166
 
160
- Open `/home/settings/profile` in the browser. The settings shell now shows a second real child page and a second real menu entry created by the same page-generation command. `General` was already there 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.
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.
161
168
 
162
- Now add a second sibling page:
169
+ Add a second sibling page:
163
170
 
164
171
  ```bash
165
172
  npx jskit generate ui-generator page home/settings/notifications/index.vue --name "Notifications"
166
173
  ```
167
174
 
168
- Open `/home/settings/notifications` in the browser. You now 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 `home-settings:primary-menu` outlet, so the links simply stack in the menu for free.
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.
169
176
 
170
177
  The order is also easy to reason about:
171
178
 
@@ -177,7 +184,7 @@ The order is also easy to reason about:
177
184
  In JSKIT's file-based routing, a page file can act as a layout if it renders a `RouterView`.
178
185
 
179
186
  - `src/pages/home/settings.vue` owns the settings shell and wraps its child routes.
180
- - `src/pages/home/settings/index.vue` is now just a redirect, so `/home/settings` lands on `/home/settings/general`.
187
+ - `src/pages/home/settings/index.vue` is just a redirect, so `/home/settings` lands on `/home/settings/general`.
181
188
  - `src/pages/home/settings/general/index.vue` is the first real child page created by the starter shell.
182
189
  - `src/pages/home/settings/profile/index.vue` becomes `/home/settings/profile` and still renders inside the layout from `settings.vue`.
183
190
 
@@ -191,7 +198,7 @@ definePage({
191
198
  });
192
199
  ```
193
200
 
194
- This is why the `home-settings:primary-menu` outlet from `list-placements` is such a useful clue: it tells you which page is acting as the host.
201
+ This is why the `page.section-nav` owner from `list-placements` is such a useful clue: it tells you which page is acting as the host.
195
202
 
196
203
  Even an `index.vue` page can have children. If you want an index page to stay visible while child routes render underneath it, put those children under an `index/` directory such as `src/pages/home/settings/profile/index/details.vue`.
197
204
 
@@ -209,8 +216,10 @@ In placement metadata, raw `mdi-*` strings are acceptable because the shell menu
209
216
  ```js
210
217
  addPlacement({
211
218
  id: "home.settings.profile.link",
212
- target: "home-settings:primary-menu",
213
- componentToken: "local.main.ui.surface-aware-menu-link-item",
219
+ target: "page.section-nav",
220
+ owner: "home-settings",
221
+ kind: "link",
222
+ surfaces: ["home"],
214
223
  props: {
215
224
  label: "Profile",
216
225
  to: "./profile",
@@ -244,13 +253,13 @@ Later in the guide, `jskit doctor` will help catch the second mistake automatica
244
253
 
245
254
  ### Component tokens
246
255
 
247
- If you rerun the token listing now, you will see that only the widget command created a new app-owned component token:
256
+ If you rerun the token listing, the widget command is the only command in this chapter that created an app-owned component token:
248
257
 
249
258
  ```bash
250
259
  npx jskit list-component-tokens --all --prefix local.main.
251
260
  ```
252
261
 
253
- The output now includes:
262
+ The output includes:
254
263
 
255
264
  ```text
256
265
  - local.main.ui.element.alerts-widget
@@ -275,7 +284,7 @@ That is the first real example of JSKIT behaving like an extension system rather
275
284
 
276
285
  ## What `shell-web` changes in the app
277
286
 
278
- The most interesting files now look roughly like this:
287
+ The most interesting files look roughly like this:
279
288
 
280
289
  ```text
281
290
  src/
@@ -313,19 +322,18 @@ The first file worth reopening is still `package.json`. After `shell-web`, the m
313
322
  {
314
323
  "dependencies": {
315
324
  "@jskit-ai/shell-web": "0.x",
316
- "@tanstack/vue-query": "^5.90.5",
317
325
  "@mdi/js": "^7.4.47"
318
326
  }
319
327
  }
320
328
  ```
321
329
 
322
- The important part is not just that `@jskit-ai/shell-web` appears. The package also brings in Vue Query and icon data because the starter shell now includes a live health check and shell-specific UI elements.
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.
323
331
 
324
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.
325
333
 
326
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.
327
335
 
328
- That is worth noticing because this is the first chapter where JSKIT is no longer just scaffolding a base app. It is now applying a runtime package that owns concrete changes in your app tree.
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.
329
337
 
330
338
  ### The `home` surface gets a real wrapper
331
339
 
@@ -356,7 +364,7 @@ import { RouterView } from "vue-router";
356
364
  </template>
357
365
  ```
358
366
 
359
- That one change explains a lot. The `home` surface is no longer just a place where pages live. It is now a shell-wrapped surface. Every child page under `src/pages/home/` renders inside that app-owned `ShellLayout`.
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`.
360
368
 
361
369
  ### `src/placement.js` becomes the placement registry
362
370
 
@@ -377,15 +385,15 @@ export default function getPlacements() {
377
385
 
378
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.
379
387
 
380
- After the `shell-web` install plus the `placed-element` and `page` commands from this chapter, the bottom of the file now contains real placement entries:
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:
381
389
 
382
390
  ```js
383
391
  addPlacement({
384
392
  id: "shell-web.home.menu.home",
385
- target: "shell-layout:primary-menu",
386
- surfaces: ["*"],
393
+ target: "shell.primary-nav",
394
+ kind: "link",
395
+ surfaces: ["home"],
387
396
  order: 50,
388
- componentToken: "local.main.ui.surface-aware-menu-link-item",
389
397
  props: {
390
398
  label: "Home",
391
399
  surface: "home",
@@ -397,10 +405,10 @@ addPlacement({
397
405
 
398
406
  addPlacement({
399
407
  id: "shell-web.home.menu.settings",
400
- target: "shell-layout:primary-menu",
408
+ target: "shell.primary-nav",
409
+ kind: "link",
401
410
  surfaces: ["home"],
402
411
  order: 100,
403
- componentToken: "local.main.ui.surface-aware-menu-link-item",
404
412
  props: {
405
413
  label: "Settings",
406
414
  surface: "home",
@@ -411,10 +419,11 @@ addPlacement({
411
419
 
412
420
  addPlacement({
413
421
  id: "shell-web.home.settings.general",
414
- target: "home-settings:primary-menu",
422
+ target: "page.section-nav",
423
+ owner: "home-settings",
424
+ kind: "link",
415
425
  surfaces: ["home"],
416
426
  order: 100,
417
- componentToken: "local.main.ui.surface-aware-menu-link-item",
418
427
  props: {
419
428
  label: "General",
420
429
  surface: "home",
@@ -426,7 +435,8 @@ addPlacement({
426
435
 
427
436
  addPlacement({
428
437
  id: "ui-generator.element.alerts-widget",
429
- target: "shell-layout:top-right",
438
+ target: "shell.status",
439
+ kind: "component",
430
440
  surfaces: ["home"],
431
441
  order: 155,
432
442
  componentToken: "local.main.ui.element.alerts-widget"
@@ -434,10 +444,11 @@ addPlacement({
434
444
 
435
445
  addPlacement({
436
446
  id: "ui-generator.page.home.settings.profile.link",
437
- target: "home-settings:primary-menu",
447
+ target: "page.section-nav",
448
+ owner: "home-settings",
449
+ kind: "link",
438
450
  surfaces: ["home"],
439
451
  order: 155,
440
- componentToken: "local.main.ui.surface-aware-menu-link-item",
441
452
  props: {
442
453
  label: "Profile",
443
454
  surface: "home",
@@ -449,10 +460,11 @@ addPlacement({
449
460
 
450
461
  addPlacement({
451
462
  id: "ui-generator.page.home.settings.notifications.link",
452
- target: "home-settings:primary-menu",
463
+ target: "page.section-nav",
464
+ owner: "home-settings",
465
+ kind: "link",
453
466
  surfaces: ["home"],
454
467
  order: 155,
455
- componentToken: "local.main.ui.surface-aware-menu-link-item",
456
468
  props: {
457
469
  label: "Notifications",
458
470
  surface: "home",
@@ -465,15 +477,17 @@ addPlacement({
465
477
 
466
478
  That snippet shows the full placement contract clearly:
467
479
 
468
- - the target says where the UI should go
469
- - the component token says what should render that entry
480
+ - the target says which semantic placement should receive the entry
481
+ - the owner disambiguates page-owned semantic placements such as `page.section-nav`
482
+ - `kind: "link"` lets topology choose the concrete link renderer for the current layout
483
+ - component placements still provide their own `componentToken`
470
484
  - `props.to` tells the generated menu link which child route to open
471
485
  - `props.icon`, when you add one, belongs to menu metadata rather than direct Vuetify icon rendering
472
486
  - the surface list says where it is active
473
487
  - lower `order` values come first
474
- - when multiple entries target the same outlet with the same order, the shell keeps their source order
488
+ - when multiple entries target the same semantic placement with the same order, the shell keeps their source order
475
489
 
476
- That is why the settings menu now 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.
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.
477
491
 
478
492
  So the shell itself remains stable. What changes is the registry that feeds it.
479
493
 
@@ -493,7 +507,7 @@ registerMainClientComponent("local.main.ui.surface-aware-menu-link-item", () =>
493
507
  registerMainClientComponent("local.main.ui.tab-link-item", () => TabLinkItem);
494
508
  ```
495
509
 
496
- This is the same app-local provider seam from the previous chapter, but now you can see 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.
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.
497
511
 
498
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.
499
513
 
@@ -505,17 +519,9 @@ So the flow is:
505
519
 
506
520
  That is why the placement system feels dynamic even though the app still owns all of the concrete Vue files.
507
521
 
508
- ### `App.vue` and `error.js` now support shell-level errors
522
+ ### `App.vue` and `error.js` provide shell-level errors
509
523
 
510
- The top-level app root changes too. `src/App.vue` is no longer just:
511
-
512
- ```vue
513
- <v-app>
514
- <RouterView />
515
- </v-app>
516
- ```
517
-
518
- It now also includes a shell error host:
524
+ The top-level app root includes the normal route outlet and the shell error host:
519
525
 
520
526
  ```vue
521
527
  <script setup>
@@ -531,31 +537,43 @@ import ShellErrorHost from "@jskit-ai/shell-web/client/components/ShellErrorHost
531
537
  </template>
532
538
  ```
533
539
 
534
- That host is backed by the new app-owned `src/error.js` file:
540
+ That host is backed by the app-owned `src/error.js` file:
535
541
 
536
542
  ```js
537
543
  import { createDefaultErrorPolicy } from "@jskit-ai/shell-web/client/error";
538
544
 
539
545
  export default Object.freeze({
540
546
  defaultPresenterId: "material.snackbar",
541
- policy: createDefaultErrorPolicy(),
547
+ policy: createDefaultErrorPolicy({
548
+ resourceLoadChannel: "silent",
549
+ actionFeedbackChannel: "snackbar",
550
+ appRecoverableChannel: "banner",
551
+ blockingChannel: "dialog"
552
+ }),
542
553
  presenters: []
543
554
  });
544
555
  ```
545
556
 
546
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.
547
558
 
548
- ### The home page is no longer static
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
549
567
 
550
- `src/pages/home/index.vue` is also more interesting now. It is no longer just a welcome card. It uses Vue Query to fetch `/api/health` and display the result in the UI.
568
+ `src/pages/home/index.vue` uses Vue Query to fetch `/api/health` and display the result in the UI.
551
569
 
552
- That is why this chapter is the point where running both `npm run dev` and `npm run server` stops feeling optional. The page now expects the backend to be alive.
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.
553
571
 
554
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.
555
573
 
556
574
  ### The first client stores appear
557
575
 
558
- This is also the first chapter where an installed package starts exposing app-facing Pinia stores. `shell-web` exports these two:
576
+ `shell-web` also exposes app-facing Pinia stores:
559
577
 
560
578
  ```js
561
579
  import {
@@ -597,10 +615,10 @@ The same store also exposes the live drawer state through `drawerOpen`, plus `se
597
615
 
598
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.
599
617
 
600
- That distinction is worth noticing early:
618
+ That distinction is worth noticing:
601
619
 
602
620
  - runtime services still do the operational work
603
- - Pinia stores are now the normal Vue-facing shared-state surface
621
+ - Pinia stores are the normal Vue-facing shared-state surface
604
622
 
605
623
  ### The first settings route appears
606
624
 
@@ -616,10 +634,7 @@ The important host file is still `src/pages/home/settings.vue`:
616
634
 
617
635
  ```vue
618
636
  <v-list nav density="comfortable" rounded="lg" border>
619
- <ShellOutlet
620
- target="home-settings:primary-menu"
621
- default-link-component-token="local.main.ui.surface-aware-menu-link-item"
622
- />
637
+ <ShellOutlet target="home-settings:primary-menu" />
623
638
  </v-list>
624
639
 
625
640
  <RouterView />
@@ -627,11 +642,11 @@ The important host file is still `src/pages/home/settings.vue`:
627
642
 
628
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.
629
644
 
630
- The starter shell now uses a real child-page structure right away:
645
+ The starter shell uses a real child-page structure right away:
631
646
 
632
647
  - `src/pages/home/settings/index.vue` is only a redirect into the first child page
633
648
  - `src/pages/home/settings/general/index.vue` is the first real settings page
634
- - `src/placement.js` already seeds a `General` link into `home-settings:primary-menu`
649
+ - `src/placement.js` already seeds a `General` link into `page.section-nav` with owner `home-settings`
635
650
 
636
651
  When you need that landing redirect yourself, use the same helper pattern:
637
652
 
@@ -658,6 +673,6 @@ The app is still structurally simple. `shell-web` just makes that simple app beh
658
673
 
659
674
  ## Summary
660
675
 
661
- After this chapter, the app is still small, but it is no longer flat. `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`.
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`.
662
677
 
663
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.