@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.
- package/DISTR_AGENT.md +1 -0
- package/guide/agent/app-extras/assistant.md +9 -20
- package/guide/agent/app-extras/mobile-capacitor.md +381 -0
- package/guide/agent/app-extras/realtime.md +13 -12
- package/guide/agent/app-setup/a-more-interesting-shell.md +102 -87
- package/guide/agent/app-setup/authentication.md +50 -31
- package/guide/agent/app-setup/console.md +8 -8
- 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 +50 -47
- package/guide/agent/app-setup/quickstart.md +11 -14
- package/guide/agent/app-setup/users.md +24 -24
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +18 -10
- 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 +44 -36
- package/guide/agent/index.md +3 -2
- 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 +20 -2
- package/patterns/placements.md +67 -1
- package/patterns/surfaces.md +5 -1
- package/patterns/ui-testing.md +3 -0
- package/reference/autogen/KERNEL_MAP.md +46 -1
- package/reference/autogen/README.md +3 -0
- package/reference/autogen/packages/assistant.md +2 -1
- package/reference/autogen/packages/auth-web.md +20 -1
- package/reference/autogen/packages/crud-ui-generator.md +20 -8
- package/reference/autogen/packages/google-rewarded-core.md +222 -0
- package/reference/autogen/packages/google-rewarded-web.md +71 -0
- package/reference/autogen/packages/kernel.md +94 -10
- package/reference/autogen/packages/mobile-capacitor.md +76 -0
- package/reference/autogen/packages/realtime.md +1 -0
- package/reference/autogen/packages/shell-web.md +48 -3
- package/reference/autogen/packages/ui-generator.md +34 -2
- package/reference/autogen/packages/users-core.md +2 -0
- package/reference/autogen/packages/users-web.md +92 -4
- package/reference/autogen/packages/workspaces-web.md +3 -2
- package/reference/autogen/tooling/jskit-cli.md +114 -9
- package/templates/app/AGENTS.md +1 -0
- 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
|
|
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
|
-
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
|
|
62
|
+
In a fresh `shell-web` app, the result includes entries like these:
|
|
63
63
|
|
|
64
64
|
```text
|
|
65
65
|
Available placements:
|
|
66
|
-
-
|
|
67
|
-
- shell-layout:primary-
|
|
68
|
-
- shell-layout:
|
|
69
|
-
- shell-layout:
|
|
70
|
-
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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`
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
-
|
|
154
|
+
- page.section-nav [owner:home-settings]
|
|
148
155
|
```
|
|
149
156
|
|
|
150
|
-
you know that the
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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 `
|
|
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: "
|
|
213
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
386
|
-
|
|
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
|
|
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: "
|
|
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
|
|
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: "
|
|
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: "
|
|
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
|
|
469
|
-
- the
|
|
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
|
|
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
|
|
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,
|
|
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`
|
|
522
|
+
### `App.vue` and `error.js` provide shell-level errors
|
|
509
523
|
|
|
510
|
-
The top-level app root
|
|
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
|
|
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
|
-
|
|
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`
|
|
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`
|
|
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
|
-
|
|
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
|
|
618
|
+
That distinction is worth noticing:
|
|
601
619
|
|
|
602
620
|
- runtime services still do the operational work
|
|
603
|
-
- Pinia stores are
|
|
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
|
|
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
|
|
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
|
|
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.
|