@jskit-ai/agent-docs 0.1.277 → 0.1.279

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.
@@ -154,8 +154,9 @@ like the recovery sender below; with it, `APP_PUBLIC_URL` is required and the
154
154
  link is never returned or logged. In development without one, the link is in
155
155
  the registration's answer (`confirmationUrl`) and the log, and the sign-in page
156
156
  shows it with a button that confirms it. In production (`NODE_ENV=production`)
157
- without one, the server refuses to start, naming both ways out: install a
158
- sender, or set `AUTH_LOCAL_EMAIL_CONFIRMATION=false`. For direct library
157
+ without one, while anyone may register, the server refuses to start, naming
158
+ the ways out: install a sender, set `AUTH_LOCAL_EMAIL_CONFIRMATION=false`, or
159
+ set `AUTH_LOCAL_SELF_REGISTRATION=false`. For direct library
159
160
  composition, pass the sender as `confirmationSender` to
160
161
  `createLocalAuthService()`, with `emailConfirmationSender: true` in
161
162
  `resolveLocalAuthConfig()`; without one, pass
@@ -164,6 +165,16 @@ composition, pass the sender as `confirmationSender` to
164
165
  | Env | Default | Meaning |
165
166
  | --- | --- | --- |
166
167
  | `AUTH_LOCAL_EMAIL_CONFIRMATION` | on | `false` registers and signs in at once, without confirming the email. |
168
+ | `AUTH_LOCAL_SELF_REGISTRATION` | on | `false`: only invited accounts and the first account register; anyone else is refused with 403 `auth.local.register.closed`. |
169
+
170
+ Confirmation guards self-registration. An app whose accounts are only invited,
171
+ or created through the first account, sets `AUTH_LOCAL_SELF_REGISTRATION=false`
172
+ and needs no confirmation sender: an invitation counts when a feature's
173
+ invitation context resolver (such as workspace invites) resolves it for that
174
+ email, and the first account is admitted by its setup code. In production
175
+ without a sender, those registrations sign in at once; with one, they confirm
176
+ as usual. An app that refused the register routes itself, with a 404 hook,
177
+ can drop the hook for this setting.
167
178
 
168
179
  Accounts that exist already are confirmed: only registrations made after the
169
180
  upgrade wait. Waiting registrations are kept in the file store
@@ -71,7 +71,11 @@ Delete, rather than port:
71
71
  page needs no frame at all;
72
72
  - `!important`, Sass variable overrides and component size or radius tweaks.
73
73
  `material.css` sets 40dp buttons with a 48dp touch target, 12dp cards, 28dp
74
- dialogs and sheets, 8dp chips and 16dp FABs, and an app's own props still win.
74
+ dialogs and sheets, 8dp chips and 16dp FABs, and an app's own props still win;
75
+ - every `height` and `min-height` on a button, as a prop, a style or CSS. A
76
+ button that should look smaller becomes `size="small"` or `size="x-small"`:
77
+ it keeps the 48dp touch target. A long label ends in an ellipsis instead of
78
+ spilling out; give a label that can run long its `title`.
75
79
 
76
80
  Use colour roles (`color="secondary-container"`, `bg-color="surface-container-high"`,
77
81
  the `on-surface-variant` class) instead of colours.
@@ -110,8 +114,13 @@ definePage({ meta: { jskit: { title: "Edit book", parent: "/books/:bookId" } } }
110
114
  ```
111
115
 
112
116
  `parent` is a path on the page's own surface, or `{ surface, path }` for
113
- another one. A main navigation destination declares no parent and shows no
114
- arrow. The shell's Up arrow returns to the page the person came from, or else
117
+ another one; its `:param` segments take the current route's values. A main
118
+ navigation destination declares no parent and shows no arrow. A page with a
119
+ parent renders in its surface's shell layout (`ShellLayout` around
120
+ `<RouterView />`), which draws the bar with its title, Up and actions:
121
+ `jskit check` reports `nav/missing-shell-layout` until it does. The account
122
+ settings page needs `src/pages/account.vue` from the users/account-settings
123
+ pattern. The shell's Up arrow returns to the page the person came from, or else
115
124
  to the parent, so:
116
125
 
117
126
  - delete "Back to X" buttons, `router.back()` and `history.back()` calls;
@@ -203,6 +212,11 @@ opens the drawer over the page. A modal drawer opens over the app bar, as
203
212
  Material 3 draws it, so a browser test that clicked the menu control again to
204
213
  close it taps the scrim or presses Escape instead.
205
214
 
215
+ Each main-navigation placement is literal data, one `addPlacement({ ... })`
216
+ per destination with plain string `surface` and suffixes; one a helper builds is
217
+ a `nav/placement-data` finding, and no page is judged for a missing parent until
218
+ it is declared literally.
219
+
206
220
  ## 9. Check it
207
221
 
208
222
  Look at every screen at 390, 768 and 1280 pixels wide, in light and dark. A
@@ -87,7 +87,10 @@ Exactly this, and nothing more:
87
87
  2. **Each top-level destination is a main-navigation item** (a `kind: "link"`
88
88
  placement with `target: "shell.primary-nav"` or `"shell.secondary-nav"`) and
89
89
  declares no parent; nor do its tabs. A shell page with no parent is
90
- top-level: no arrow, a fresh trail.
90
+ top-level: no arrow, a fresh trail. Placements are literal declarative data,
91
+ one `addPlacement({ ... })` per destination with plain string `surface`,
92
+ `scopedSuffix` and `unscopedSuffix`, never built by a helper, a spread or a
93
+ template, so a reader and `jskit check` can map every destination.
91
94
  3. **Every in-app link and navigation goes through JSKIT navigation:**
92
95
  `:to="paths.page('/suppliers')"` and `router.push(paths.page(...))` with
93
96
  `usePaths()`, the CRUD runtime's `resolveViewUrl()`, `resolveEditUrl()` and
@@ -103,7 +106,10 @@ Exactly this, and nothing more:
103
106
  5. **A full page load goes through the shell too:** `reloadAppPage(path)`
104
107
  (after sign-in or a user switch, to rebuild client state) and
105
108
  `leaveApp(url)` (an external site in the same tab, such as a payment page),
106
- from `@jskit-ai/shell-web/client/navigation/documentNavigation`.
109
+ from `@jskit-ai/shell-web/client/navigation/documentNavigation`. `leaveApp`
110
+ opens the site with a push, so Back returns to the page; a hand-off page
111
+ that only forwards (to an OAuth consent screen) uses
112
+ `leaveApp(url, { replace: true })`, so Back from the site doesn't rerun it.
107
113
 
108
114
  ### Forbidden in app code
109
115
 
@@ -112,9 +118,11 @@ Exactly this, and nothing more:
112
118
  | `nav/history` | `history.back()`, `history.go()`, `history.pushState()`/`replaceState()`, `router.back()`, `router.go()` |
113
119
  | `nav/window-location` | `window.location` assignments, `.href =`, `location.assign()`, `location.replace()` |
114
120
  | `nav/raw-link` | a hand-built in-app path in `to`, `href`, `router.push()`, `router.replace()` or a `to:`/`href:` object key outside a placement |
115
- | `nav/back-control` | any back control but the shell's Up navigating the app: "Back to X", "← Suppliers", back arrows and navigating chevrons, breadcrumbs, a custom app-bar back button, a back component, a `goBack()`-style handler |
121
+ | `nav/back-control` | any back control but the shell's Up that navigates the app: "Back to X", "← Suppliers", back arrows and navigating chevrons, breadcrumbs, a custom app-bar back button, a back component, a `goBack()`-style handler |
116
122
  | `nav/missing-parent` | an inner page of a shell surface without a declared parent |
117
123
  | `nav/top-level-parent` | a main-navigation destination that declares a parent |
124
+ | `nav/placement-data` | a main-navigation placement that isn't literal data (no page is judged for a missing parent until it is) |
125
+ | `nav/missing-shell-layout` | a page that declares a parent with no shell layout rendering it, so its title, Up and actions never show |
118
126
  | `nav/allow` | any `m3-check: allow nav/...` comment: nothing excuses a navigation rule |
119
127
 
120
128
  ### Each rule, right and wrong
@@ -145,9 +153,15 @@ Exactly this, and nothing more:
145
153
  `finish(recordUrl)`; the record shows, and Up goes where the record's Up went
146
154
  before. Wrong: `router.push(recordUrl)` after Save, so Up or Back reopens the
147
155
  form.
148
- - **Steps inside a page are named actions:** "Previous step" or the
149
- destination's name, not "Back" or a back arrow. A frame's own Back
150
- (`contentWindow.history`) is fine.
156
+ - **Steps and drill-downs inside a page are named actions.** A wizard step or
157
+ a panel the page opens in place is not a page, so it has no Up. Right:
158
+ "Previous step", or the destination's name ("Dashboard"), with no back
159
+ arrow. Wrong: "Back" or a back arrow. `nav/back-control` flags only controls
160
+ that navigate the app: a link into it, or a handler that reaches the router,
161
+ JSKIT paths, `up()`, `finish()` or the browser's history or location. Prose
162
+ ("back from version 2"), a variable named `back` and an embedded frame's own
163
+ Back and Forward (`frame.contentWindow.history.back()`, as in a preview
164
+ browser) are not back controls.
151
165
 
152
166
  ## Foundation
153
167
 
@@ -181,10 +195,16 @@ JSKIT ships these exact sizes; an app never re-implements or overrides them,
181
195
  and needs no Sass and no `!important` (Vuetify 4 keeps its CSS in cascade
182
196
  layers):
183
197
 
184
- - 40dp buttons with a 48dp touch target, icon buttons included;
185
- - 12dp cards;
198
+ - 40dp buttons with a 48dp touch target, icon buttons included; a smaller
199
+ button is `size="small"` or `size="x-small"`, which keeps its smaller look
200
+ and the 48dp target;
201
+ - one-line button labels: a label longer than its button ends in an ellipsis.
202
+ Keep labels short, and give a button whose label can run long, such as a
203
+ name, the full label as its `title`;
204
+ - 12dp cards: elevated (the default) at level 1, flat and tonal at level 0;
186
205
  - 28dp dialogs and bottom-sheet top corners, square full-screen dialogs;
187
- - 8dp chips and 16dp FABs;
206
+ - 8dp chips, and FABs of 56dp (small 40dp, large 96dp; extended 56dp tall)
207
+ with 16dp corners;
188
208
  - emphasized easing, with reduced motion honoured.
189
209
 
190
210
  So never set `height` or `min-height` on a button, or a radius on a card,
@@ -196,12 +216,22 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
196
216
  `useShellPageTitle(title)` (`@jskit-ai/shell-web/client/composables/useShellPageTitle`)
197
217
  for a runtime title such as the record's name. The shell renders it as the
198
218
  app bar's only `h1`; pages add no `h1`, page header, title block or
199
- standalone heading. A section inside a page uses a title role.
219
+ standalone heading. A section inside a page uses a title role. A page with
220
+ no top app bar (a hero front page, an intro, an invite or hand-off page)
221
+ has its own one `h1`.
200
222
  - **Up** sits in the bar on every inner page (see Navigation and Up). On phones
201
223
  it replaces the menu icon.
202
- - **A layout with its own top app bar** calls `useShellAppBar()` and renders
203
- `ShellUpButton`, `ShellPageTitle` and `ShellPageActionsTarget` in it;
204
- `jskit check` treats it as a shell layout.
224
+ - **A layout that draws its own top app bar** (a workspace bar with the
225
+ app's own controls, instead of `ShellLayout`) builds it from `ShellLayout`'s
226
+ own pieces, from `@jskit-ai/shell-web/client`. It calls `useShellAppBar()`
227
+ once, so its pages enter the trail and their `ShellPageActions` show in its
228
+ bar, and renders `ShellUpButton` first, then `ShellPageTitle` (the page's one
229
+ `h1`: `useShellPageTitle()`, else the route's `jskit.title`, else its `title`
230
+ prop) and `ShellPageActionsTarget`. A title that is also a switcher, such as a
231
+ project menu, goes in `ShellPageTitle`'s default slot (given `{ title }`) as
232
+ the menu's activator, so it stays the one `h1`. `jskit check` counts it as a shell
233
+ layout, so its inner pages need parents; an Up the app draws itself is a
234
+ `nav/back-control` finding.
205
235
  - **Actions:** a screen's actions go in the bar inside `ShellPageActions`
206
236
  (`@jskit-ai/shell-web/client/components/ShellPageActions`), each a
207
237
  `ShellPageAction` with `icon`, `label` (also its accessible name and compact
@@ -209,7 +239,9 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
209
239
  On compact widths every action shows only its icon except the single
210
240
  `primary` one.
211
241
  - **Window size classes:** compact under 600 (navigation bar), medium 600–839
212
- (navigation rail), expanded 840–1199 and large from 1200 (drawer). Read them
242
+ (navigation rail), expanded 840–1199 and large from 1200 (drawer). A surface
243
+ with no navigation destinations, such as an account surface, draws no
244
+ navigation bar, rail or drawer; its pages keep the app bar. Read them
213
245
  from `useShellWindowSizeClass()`
214
246
  (`@jskit-ai/shell-web/client/composables/useShellWindowSizeClass`), never from
215
247
  copied breakpoint numbers.
@@ -309,8 +341,8 @@ and so every app's verify, fail on them too:
309
341
 
310
342
  | Rule | Finds |
311
343
  |---|---|
312
- | `m3/duplicate-h1` | a second `h1`, or an in-page `h1` in an app that uses the JSKIT shell |
313
- | `m3/raw-hex` | a hex colour outside `src/theme/` |
344
+ | `m3/duplicate-h1` | a second `h1`, or an in-page `h1` under the shell's page title |
345
+ | `m3/raw-hex` | a hex colour outside `src/theme/` (`palettes.js` holds content colours) |
314
346
  | `m3/dark-scheme` | no dark scheme, or the theme pinned to light |
315
347
  | `m3/outlined-card` | `variant="outlined"` or `border` on a card or sheet |
316
348
  | `m3/mdi-icon` | `mdi-*` icon strings and `@mdi/js` imports |
@@ -328,16 +360,39 @@ Take the approved route first; none needs an exception:
328
360
 
329
361
  - **Brand or partner colours** are theme custom colours, used by name:
330
362
  `colors: { partner: "#0A7D3E" }` in `src/theme/index.js`, then
331
- `<v-chip color="partner">`. `{ value: "#F5C518", blend: false }` keeps
332
- its hue.
333
- - **Chart and data colours** come from the theme: `const theme = useTheme()`,
334
- then `["primary", "tertiary"].map((role) => theme.current.value.colors[role])`.
363
+ `<v-chip color="partner">`. A hex string is harmonized toward the seed; a
364
+ colour that must keep its hue is `{ value: "#F5C518", blend: false }`
365
+ (Material Theme Builder's Blend switched off), with the same four roles.
366
+ - **Content colours**, colours that are content rather than UI, keep one
367
+ value in both schemes: an identity palette a person picks and the server
368
+ stores, avatar and illustration artwork, brand marks (a language's
369
+ file-type tint), fixed chart series, QR codes, colours written into
370
+ exported files. They are named exports of plain hex values in
371
+ `src/theme/palettes.js`, used as they are, never for UI chrome (a theme
372
+ colour is toned per scheme, so it can't keep an identity the same).
373
+ Components read them through bindings:
374
+
375
+ ```vue
376
+ <script setup>
377
+ // src/theme/palettes.js: export const botColours = ["#6750A4", "#B3261E"];
378
+ import { botColours } from "@/theme/palettes.js";
379
+ const props = defineProps({ bot: Object });
380
+ const ring = botColours[props.bot.colour];
381
+ </script>
382
+ <template>
383
+ <v-avatar :style="{ backgroundColor: ring }"><svg><path :fill="ring" /></svg></v-avatar>
384
+ </template>
385
+ <style scoped>.v-avatar { outline: 2px solid v-bind(ring); }</style>
386
+ ```
387
+ - **Chart and data colours** that follow the scheme come from the theme:
388
+ `const theme = useTheme()`, then
389
+ `["primary", "tertiary"].map((role) => theme.current.value.colors[role])`.
335
390
  - **A third-party widget** lives in one wrapper component, such as
336
391
  `src/components/RouteMap.vue`, that hands it theme colours; pages use only
337
392
  the wrapper.
338
393
 
339
- What is left may be excused one line at a time, on its line or the line
340
- before: `<!-- m3-check: allow m3/raw-hex - the partner's contractual logo colour -->`
394
+ What is left, such as a colour inside static SVG markup that can't import,
395
+ may be excused one line at a time, on its line or the line before: `<!-- m3-check: allow m3/raw-hex - the partner's contractual logo colour -->`
341
396
  (`// m3-check: ...` in scripts, `/* ... */` in CSS). **Never add one on your
342
397
  own:** ask the person first, naming the rule and the reason. A comment with no
343
398
  reason (`m3/allow-reason`) or an unknown rule (`m3/allow-rule`), and any
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.277",
3
+ "version": "0.1.279",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -35,7 +35,7 @@
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
37
  "@jskit-ai/kernel": "0.2.10",
38
- "@jskit-ai/material-web": "0.1.1",
38
+ "@jskit-ai/material-web": "0.1.2",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@jskit-ai/config-eslint": "0.1.245",
53
- "@jskit-ai/jskit-catalog": "0.2.29",
53
+ "@jskit-ai/jskit-catalog": "0.2.31",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -35,7 +35,7 @@
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
37
  "@jskit-ai/kernel": "0.2.10",
38
- "@jskit-ai/material-web": "0.1.1",
38
+ "@jskit-ai/material-web": "0.1.2",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
@@ -45,13 +45,13 @@
45
45
  "vue-router": "^5.3.1",
46
46
  "vuetify": "^4.3.0",
47
47
  "@jskit-ai/http-runtime": "0.2.10",
48
- "@jskit-ai/shell-web": "0.2.12",
48
+ "@jskit-ai/shell-web": "0.2.13",
49
49
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
50
50
  "@vue/devtools-api": "^8.2.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jskit-ai/config-eslint": "0.1.245",
54
- "@jskit-ai/jskit-catalog": "0.2.29",
54
+ "@jskit-ai/jskit-catalog": "0.2.31",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "eslint": "^10.8.0",
@@ -15,7 +15,7 @@ A concrete Fastify, Vue, and JSKIT application foundation for products that do n
15
15
 
16
16
  - Id: `app/minimal-foundation`
17
17
  - Keywords: `app`, `fastify`, `foundation`, `minimal`, `server`, `vite`, `vue`
18
- - Owner: `@jskit-ai/agent-docs@0.1.277`
18
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
19
19
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md)
20
20
  - Examples: [example/](../../skills/jskit/references/patterns/app/minimal-foundation/example/)
21
21
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`
@@ -26,7 +26,7 @@ A concrete JSKIT web application foundation with responsive shell navigation, se
26
26
 
27
27
  - Id: `app/shell-foundation`
28
28
  - Keywords: `app`, `foundation`, `material`, `navigation`, `placements`, `shell`, `vite`, `vue`
29
- - Owner: `@jskit-ai/agent-docs@0.1.277`
29
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
30
30
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/app/shell-foundation/PATTERN.md)
31
31
  - Examples: [example/](../../skills/jskit/references/patterns/app/shell-foundation/example/)
32
32
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -37,7 +37,7 @@ Configure an assistant runtime for one application surface and expose its chat a
37
37
 
38
38
  - Id: `assistant/assistant-surface`
39
39
  - Keywords: `ai`, `assistant`, `chat`, `config`, `environment`, `page`, `placement`, `settings`, `surface`
40
- - Owner: `@jskit-ai/assistant-runtime@0.2.25`
40
+ - Owner: `@jskit-ai/assistant-runtime@0.2.26`
41
41
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/assistant/assistant-surface/PATTERN.md)
42
42
  - Examples: [example/](../../skills/jskit/references/patterns/assistant/assistant-surface/example/)
43
43
  - Requires: `@jskit-ai/assistant-runtime`, `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`, `@jskit-ai/shell-web`
@@ -48,7 +48,7 @@ Run one OpenID Connect sign-in for all of an owner's apps as its own JSKIT proje
48
48
 
49
49
  - Id: `auth/accounts-hub`
50
50
  - Keywords: `accounts hub`, `auth`, `authentication`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
51
- - Owner: `@jskit-ai/oidc-issuer-core@0.1.17`
51
+ - Owner: `@jskit-ai/oidc-issuer-core@0.1.18`
52
52
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/auth/accounts-hub/PATTERN.md)
53
53
  - Examples: [example/](../../skills/jskit/references/patterns/auth/accounts-hub/example/)
54
54
  - Requires: `@jskit-ai/oidc-issuer-core`
@@ -59,7 +59,7 @@ Compose JSKIT authentication routes, views, profile controls, and public surface
59
59
 
60
60
  - Id: `auth/auth-surface`
61
61
  - Keywords: `account`, `auth`, `login`, `logout`, `password`, `placement`, `profile`, `reset`, `surface`
62
- - Owner: `@jskit-ai/auth-web@0.2.14`
62
+ - Owner: `@jskit-ai/auth-web@0.2.15`
63
63
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/auth/auth-surface/PATTERN.md)
64
64
  - Examples: [example/](../../skills/jskit/references/patterns/auth/auth-surface/example/)
65
65
  - Requires: `@jskit-ai/auth-core`, `@jskit-ai/auth-web`, `@jskit-ai/shell-web`
@@ -70,7 +70,7 @@ Let people sign in with the accounts they already have in an accounts hub or ano
70
70
 
71
71
  - Id: `auth/oidc-sign-in`
72
72
  - Keywords: `accounts hub`, `auth`, `authentication`, `back-channel logout`, `dynamic client registration`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
73
- - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.16`
73
+ - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.17`
74
74
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/auth/oidc-sign-in/PATTERN.md)
75
75
  - Examples: [example/](../../skills/jskit/references/patterns/auth/oidc-sign-in/example/)
76
76
  - Requires: `@jskit-ai/auth-provider-oidc-core`, `@jskit-ai/auth-web`
@@ -246,7 +246,7 @@ Add a protected administration surface, settings shell, and semantic navigation
246
246
 
247
247
  - Id: `console/console-surface`
248
248
  - Keywords: `admin`, `console`, `navigation`, `owner`, `placement`, `settings`, `surface`
249
- - Owner: `@jskit-ai/console-web@0.1.236`
249
+ - Owner: `@jskit-ai/console-web@0.1.237`
250
250
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/console/console-surface/PATTERN.md)
251
251
  - Examples: [example/](../../skills/jskit/references/patterns/console/console-surface/example/)
252
252
  - Requires: `@jskit-ai/console-core`, `@jskit-ai/console-web`, `@jskit-ai/shell-web`
@@ -257,7 +257,7 @@ Build a routed CRUD user interface as thin application pages over JSKIT's shared
257
257
 
258
258
  - Id: `crud/crud-screen-set`
259
259
  - Keywords: `actions`, `add`, `crud`, `delete`, `edit`, `filters`, `list`, `material`, `routes`, `view`, `vue`
260
- - Owner: `@jskit-ai/http-web@0.2.17`
260
+ - Owner: `@jskit-ai/http-web@0.2.18`
261
261
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md)
262
262
  - Examples: [example/](../../skills/jskit/references/patterns/crud/crud-screen-set/example/)
263
263
  - Requires: `@jskit-ai/http-web`, `@jskit-ai/resource-crud-core`
@@ -312,7 +312,7 @@ Wrap a JSKIT web application in a Capacitor Android shell using native Capacitor
312
312
 
313
313
  - Id: `mobile/android-application`
314
314
  - Keywords: `android`, `capacitor`, `device`, `mobile`, `native`, `shell`, `webview`
315
- - Owner: `@jskit-ai/mobile-capacitor@0.2.12`
315
+ - Owner: `@jskit-ai/mobile-capacitor@0.2.13`
316
316
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/mobile/android-application/PATTERN.md)
317
317
  - Examples: [example/](../../skills/jskit/references/patterns/mobile/android-application/example/)
318
318
  - Requires: `@capacitor/android`, `@capacitor/app`, `@capacitor/cli`, `@jskit-ai/mobile-capacitor`
@@ -323,7 +323,7 @@ Add JSKIT realtime events with an optional Redis backplane and an explicit shell
323
323
 
324
324
  - Id: `realtime/realtime-application`
325
325
  - Keywords: `realtime`, `redis`, `socket.io`, `sockets`, `status`, `websocket`
326
- - Owner: `@jskit-ai/realtime@0.2.15`
326
+ - Owner: `@jskit-ai/realtime@0.2.16`
327
327
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/realtime/realtime-application/PATTERN.md)
328
328
  - Examples: [example/](../../skills/jskit/references/patterns/realtime/realtime-application/example/)
329
329
  - Requires: `@jskit-ai/realtime`, `@jskit-ai/shell-web`
@@ -334,7 +334,7 @@ Compose application-owned Google Publisher Tag delivery with the generic rewarde
334
334
 
335
335
  - Id: `rewards/google-rewarded`
336
336
  - Keywords: `ads`, `google`, `publisher tag`, `rewarded`, `unlock`
337
- - Owner: `@jskit-ai/rewarded-web@0.2.14`
337
+ - Owner: `@jskit-ai/rewarded-web@0.2.15`
338
338
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/rewards/google-rewarded/PATTERN.md)
339
339
  - Examples: [example/](../../skills/jskit/references/patterns/rewards/google-rewarded/example/)
340
340
  - Requires: `@jskit-ai/rewarded-web`
@@ -345,7 +345,7 @@ Define a server feature through explicit capabilities and first-class actions, a
345
345
 
346
346
  - Id: `server/feature-package`
347
347
  - Keywords: `actions`, `feature`, `json-rest`, `knex`, `orchestration`, `package`, `provider`, `repository`, `routes`, `server`
348
- - Owner: `@jskit-ai/agent-docs@0.1.277`
348
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
349
349
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/server/feature-package/PATTERN.md)
350
350
  - Examples: [example/](../../skills/jskit/references/patterns/server/feature-package/example/)
351
351
  - Requires: `@jskit-ai/kernel`
@@ -356,7 +356,7 @@ Compose the JSKIT responsive shell, semantic placements, settings navigation, an
356
356
 
357
357
  - Id: `shell/application-shell`
358
358
  - Keywords: `adaptive`, `app`, `layout`, `navigation`, `placement`, `responsive`, `settings`, `shell`
359
- - Owner: `@jskit-ai/shell-web@0.2.12`
359
+ - Owner: `@jskit-ai/shell-web@0.2.13`
360
360
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/shell/application-shell/PATTERN.md)
361
361
  - Examples: [example/](../../skills/jskit/references/patterns/shell/application-shell/example/)
362
362
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -367,7 +367,7 @@ Add product routes and shell extensions through file routing, semantic placement
367
367
 
368
368
  - Id: `ui/page-and-placement`
369
369
  - Keywords: `component`, `navigation`, `outlet`, `page`, `placement`, `routes`, `section`, `shell`, `subpages`, `vue`
370
- - Owner: `@jskit-ai/shell-web@0.2.12`
370
+ - Owner: `@jskit-ai/shell-web@0.2.13`
371
371
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/ui/page-and-placement/PATTERN.md)
372
372
  - Examples: [example/](../../skills/jskit/references/patterns/ui/page-and-placement/example/)
373
373
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -378,7 +378,7 @@ Compose an account settings route and profile, preference, notification, and sig
378
378
 
379
379
  - Id: `users/account-settings`
380
380
  - Keywords: `account`, `link provider`, `notifications`, `oauth`, `preferences`, `profile`, `settings`, `sign-in methods`, `user`, `vue`
381
- - Owner: `@jskit-ai/users-web@0.2.19`
381
+ - Owner: `@jskit-ai/users-web@0.2.20`
382
382
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/users/account-settings/PATTERN.md)
383
383
  - Examples: [example/](../../skills/jskit/references/patterns/users/account-settings/example/)
384
384
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/users-core`, `@jskit-ai/users-web`
@@ -411,7 +411,7 @@ Compose workspace selection, invitation, member administration, settings, and re
411
411
 
412
412
  - Id: `workspaces/workspace-surfaces`
413
413
  - Keywords: `admin`, `invite`, `member`, `navigation`, `settings`, `surface`, `switcher`, `workspace`
414
- - Owner: `@jskit-ai/workspaces-web@0.2.19`
414
+ - Owner: `@jskit-ai/workspaces-web@0.2.20`
415
415
  - Read: [PATTERN.md](../../skills/jskit/references/patterns/workspaces/workspace-surfaces/PATTERN.md)
416
416
  - Examples: [example/](../../skills/jskit/references/patterns/workspaces/workspace-surfaces/example/)
417
417
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/workspaces-core`, `@jskit-ai/workspaces-web`
@@ -52,7 +52,7 @@ Local functions
52
52
  - `resolveSetupCode(env, { isProduction })`
53
53
  - `resolveAppPublicUrl(env, { emailRecovery, emailedConfirmation, oauthEnabled })`
54
54
  - `resolveRecoveryDelivery(env, { emailRecovery, isProduction })`
55
- - `resolveConfirmationDelivery(env, { emailConfirmationSender, isProduction })`
55
+ - `resolveConfirmationDelivery(env, { emailConfirmationSender, isProduction, selfRegistration })`
56
56
  - `resolvePasswordMinLength(env)`
57
57
 
58
58
  ### `src/server/lib/emailConfirmation.js`
@@ -724,8 +724,6 @@ Exports
724
724
  ### `fixtures/crud-screens/main.js`
725
725
  Exports
726
726
  - None
727
- Local functions
728
- - `destination(order, label, suffix, icon)`
729
727
 
730
728
  ### `fixtures/crud-screens/vite.config.mjs`
731
729
  Exports
@@ -182,6 +182,10 @@ Exports
182
182
  - `shellNavigationLinkProps`
183
183
  - `useShellNavigationLink(props)`
184
184
 
185
+ ### `src/client/composables/useShellOutletPlacements.js`
186
+ Exports
187
+ - `useShellOutletPlacements(target, context = {})`
188
+
185
189
  ### `src/client/composables/useShellPageTitle.js`
186
190
  Exports
187
191
  - `useShellPageTitle(title)`
@@ -327,7 +331,7 @@ Local functions
327
331
 
328
332
  ### `src/client/navigation/documentNavigation.js`
329
333
  Exports
330
- - `leaveApp(url)`
334
+ - `leaveApp(url, { replace = false } = {})`
331
335
  - `reloadAppPage(target)`
332
336
 
333
337
  ### `src/client/navigation/linkResolver.js`
@@ -335,7 +339,7 @@ Exports
335
339
  - `resolveShellLinkPath({ context = null, surface = "", explicitTo = "", relativePath = "/", surfaceRelativePath = "", params = {}, strictParams = true } = {})`
336
340
  - `useShellLinkResolver({ surface = "" } = {})`
337
341
  Local functions
338
- - `materializeSurfaceRouteBase(routeBaseTemplate = "/", { params = {}, strictParams = true, surface = "" } = {})`
342
+ - `fillPathParams(pathTemplate = "/", { params = {}, strictParams = true, label = "route params" } = {})`
339
343
  - `resolveSurfaceBasePath(context = null, surface = "", { params = {}, strictParams = true } = {})`
340
344
 
341
345
  ### `src/client/navigation/shellNavigationRuntime.js`
@@ -547,6 +551,7 @@ Exports
547
551
  - `normalizeDesktopDrawerClosedMode(value = "rail")`
548
552
  - `resolveShellDrawerPresentation({ modal = false, open = false, desktopClosedMode = "rail" } = {})`
549
553
  - `resolveShellDrawerToggleLabel({ modal = false, open = false } = {})`
554
+ - `shellLayoutHasDrawer({ menuSlot = false, menuCount = 0, identityCount = 0, bottomNavigationCount = 0 } = {})`
550
555
 
551
556
  ### `src/client/support/drawerWidth.js`
552
557
  Exports
@@ -653,12 +658,14 @@ Exports
653
658
  Exports
654
659
  - None
655
660
 
661
+ ### `fixtures/adaptive-shell/src/ButtonsPage.vue`
662
+ Exports
663
+ - None
664
+
656
665
  ### `fixtures/adaptive-shell/src/main.js`
657
666
  Exports
658
667
  - None
659
668
  Local functions
660
- - `createSurfacePlacement(surface, order, label, suffix)`
661
- - `createSurfacePlacements(surface)`
662
669
  - `refuseBlockedRecord(to)`
663
670
 
664
671
  ### `fixtures/adaptive-shell/src/OwnBarLayout.vue`
@@ -209,6 +209,10 @@ Exports
209
209
  Exports
210
210
  - None
211
211
 
212
+ ### `patterns/account-settings/example/src/pages/account.vue`
213
+ Exports
214
+ - None
215
+
212
216
  ### `patterns/account-settings/example/src/pages/account/index.vue`
213
217
  Exports
214
218
  - None
@@ -131,6 +131,7 @@ Exports
131
131
  - `checkScriptBackNames(code, offset, report)`
132
132
  - `isInAppPath(value)`
133
133
  - `isPageFile(file)`
134
+ - `readTitledFiles({ root, appFiles })`
134
135
  Local functions
135
136
  - `compact(text)`
136
137
  - `isPlacementLink(code, offset)`
@@ -146,6 +147,7 @@ Local functions
146
147
  - `enclosingObject(text, offset)`
147
148
  - `stringValue(text, key)`
148
149
  - `readMainNavigation(sources)`
150
+ - `computedPlacementPart(placement)`
149
151
  - `normalisePath(value)`
150
152
  - `routeOf(file)`
151
153
  - `routePattern(route)`
@@ -153,7 +155,7 @@ Local functions
153
155
  - `surfaceOf(route, surfaces)`
154
156
  - `routeDeclarations(source)`
155
157
  - `readPage(file, source)`
156
- - `createShellBarReader(appFiles, read)`
158
+ - `createShellBarReader(appFiles, read, tag = SHELL_UP_TAG)`
157
159
  - `localComponent(file, specifier, known)`
158
160
  - `pageKey(file)`
159
161
 
@@ -82,7 +82,10 @@ Exactly this, and nothing more:
82
82
  2. **Each top-level destination is a main-navigation item** (a `kind: "link"`
83
83
  placement with `target: "shell.primary-nav"` or `"shell.secondary-nav"`) and
84
84
  declares no parent; nor do its tabs. A shell page with no parent is
85
- top-level: no arrow, a fresh trail.
85
+ top-level: no arrow, a fresh trail. Placements are literal declarative data,
86
+ one `addPlacement({ ... })` per destination with plain string `surface`,
87
+ `scopedSuffix` and `unscopedSuffix`, never built by a helper, a spread or a
88
+ template, so a reader and `jskit check` can map every destination.
86
89
  3. **Every in-app link and navigation goes through JSKIT navigation:**
87
90
  `:to="paths.page('/suppliers')"` and `router.push(paths.page(...))` with
88
91
  `usePaths()`, the CRUD runtime's `resolveViewUrl()`, `resolveEditUrl()` and
@@ -98,7 +101,10 @@ Exactly this, and nothing more:
98
101
  5. **A full page load goes through the shell too:** `reloadAppPage(path)`
99
102
  (after sign-in or a user switch, to rebuild client state) and
100
103
  `leaveApp(url)` (an external site in the same tab, such as a payment page),
101
- from `@jskit-ai/shell-web/client/navigation/documentNavigation`.
104
+ from `@jskit-ai/shell-web/client/navigation/documentNavigation`. `leaveApp`
105
+ opens the site with a push, so Back returns to the page; a hand-off page
106
+ that only forwards (to an OAuth consent screen) uses
107
+ `leaveApp(url, { replace: true })`, so Back from the site doesn't rerun it.
102
108
 
103
109
  ### Forbidden in app code
104
110
 
@@ -107,9 +113,11 @@ Exactly this, and nothing more:
107
113
  | `nav/history` | `history.back()`, `history.go()`, `history.pushState()`/`replaceState()`, `router.back()`, `router.go()` |
108
114
  | `nav/window-location` | `window.location` assignments, `.href =`, `location.assign()`, `location.replace()` |
109
115
  | `nav/raw-link` | a hand-built in-app path in `to`, `href`, `router.push()`, `router.replace()` or a `to:`/`href:` object key outside a placement |
110
- | `nav/back-control` | any back control but the shell's Up navigating the app: "Back to X", "← Suppliers", back arrows and navigating chevrons, breadcrumbs, a custom app-bar back button, a back component, a `goBack()`-style handler |
116
+ | `nav/back-control` | any back control but the shell's Up that navigates the app: "Back to X", "← Suppliers", back arrows and navigating chevrons, breadcrumbs, a custom app-bar back button, a back component, a `goBack()`-style handler |
111
117
  | `nav/missing-parent` | an inner page of a shell surface without a declared parent |
112
118
  | `nav/top-level-parent` | a main-navigation destination that declares a parent |
119
+ | `nav/placement-data` | a main-navigation placement that isn't literal data (no page is judged for a missing parent until it is) |
120
+ | `nav/missing-shell-layout` | a page that declares a parent with no shell layout rendering it, so its title, Up and actions never show |
113
121
  | `nav/allow` | any `m3-check: allow nav/...` comment: nothing excuses a navigation rule |
114
122
 
115
123
  ### Each rule, right and wrong
@@ -140,9 +148,15 @@ Exactly this, and nothing more:
140
148
  `finish(recordUrl)`; the record shows, and Up goes where the record's Up went
141
149
  before. Wrong: `router.push(recordUrl)` after Save, so Up or Back reopens the
142
150
  form.
143
- - **Steps inside a page are named actions:** "Previous step" or the
144
- destination's name, not "Back" or a back arrow. A frame's own Back
145
- (`contentWindow.history`) is fine.
151
+ - **Steps and drill-downs inside a page are named actions.** A wizard step or
152
+ a panel the page opens in place is not a page, so it has no Up. Right:
153
+ "Previous step", or the destination's name ("Dashboard"), with no back
154
+ arrow. Wrong: "Back" or a back arrow. `nav/back-control` flags only controls
155
+ that navigate the app: a link into it, or a handler that reaches the router,
156
+ JSKIT paths, `up()`, `finish()` or the browser's history or location. Prose
157
+ ("back from version 2"), a variable named `back` and an embedded frame's own
158
+ Back and Forward (`frame.contentWindow.history.back()`, as in a preview
159
+ browser) are not back controls.
146
160
 
147
161
  ## Foundation
148
162
 
@@ -176,10 +190,16 @@ JSKIT ships these exact sizes; an app never re-implements or overrides them,
176
190
  and needs no Sass and no `!important` (Vuetify 4 keeps its CSS in cascade
177
191
  layers):
178
192
 
179
- - 40dp buttons with a 48dp touch target, icon buttons included;
180
- - 12dp cards;
193
+ - 40dp buttons with a 48dp touch target, icon buttons included; a smaller
194
+ button is `size="small"` or `size="x-small"`, which keeps its smaller look
195
+ and the 48dp target;
196
+ - one-line button labels: a label longer than its button ends in an ellipsis.
197
+ Keep labels short, and give a button whose label can run long, such as a
198
+ name, the full label as its `title`;
199
+ - 12dp cards: elevated (the default) at level 1, flat and tonal at level 0;
181
200
  - 28dp dialogs and bottom-sheet top corners, square full-screen dialogs;
182
- - 8dp chips and 16dp FABs;
201
+ - 8dp chips, and FABs of 56dp (small 40dp, large 96dp; extended 56dp tall)
202
+ with 16dp corners;
183
203
  - emphasized easing, with reduced motion honoured.
184
204
 
185
205
  So never set `height` or `min-height` on a button, or a radius on a card,
@@ -191,12 +211,22 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
191
211
  `useShellPageTitle(title)` (`@jskit-ai/shell-web/client/composables/useShellPageTitle`)
192
212
  for a runtime title such as the record's name. The shell renders it as the
193
213
  app bar's only `h1`; pages add no `h1`, page header, title block or
194
- standalone heading. A section inside a page uses a title role.
214
+ standalone heading. A section inside a page uses a title role. A page with
215
+ no top app bar (a hero front page, an intro, an invite or hand-off page)
216
+ has its own one `h1`.
195
217
  - **Up** sits in the bar on every inner page (see Navigation and Up). On phones
196
218
  it replaces the menu icon.
197
- - **A layout with its own top app bar** calls `useShellAppBar()` and renders
198
- `ShellUpButton`, `ShellPageTitle` and `ShellPageActionsTarget` in it;
199
- `jskit check` treats it as a shell layout.
219
+ - **A layout that draws its own top app bar** (a workspace bar with the
220
+ app's own controls, instead of `ShellLayout`) builds it from `ShellLayout`'s
221
+ own pieces, from `@jskit-ai/shell-web/client`. It calls `useShellAppBar()`
222
+ once, so its pages enter the trail and their `ShellPageActions` show in its
223
+ bar, and renders `ShellUpButton` first, then `ShellPageTitle` (the page's one
224
+ `h1`: `useShellPageTitle()`, else the route's `jskit.title`, else its `title`
225
+ prop) and `ShellPageActionsTarget`. A title that is also a switcher, such as a
226
+ project menu, goes in `ShellPageTitle`'s default slot (given `{ title }`) as
227
+ the menu's activator, so it stays the one `h1`. `jskit check` counts it as a shell
228
+ layout, so its inner pages need parents; an Up the app draws itself is a
229
+ `nav/back-control` finding.
200
230
  - **Actions:** a screen's actions go in the bar inside `ShellPageActions`
201
231
  (`@jskit-ai/shell-web/client/components/ShellPageActions`), each a
202
232
  `ShellPageAction` with `icon`, `label` (also its accessible name and compact
@@ -204,7 +234,9 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
204
234
  On compact widths every action shows only its icon except the single
205
235
  `primary` one.
206
236
  - **Window size classes:** compact under 600 (navigation bar), medium 600–839
207
- (navigation rail), expanded 840–1199 and large from 1200 (drawer). Read them
237
+ (navigation rail), expanded 840–1199 and large from 1200 (drawer). A surface
238
+ with no navigation destinations, such as an account surface, draws no
239
+ navigation bar, rail or drawer; its pages keep the app bar. Read them
208
240
  from `useShellWindowSizeClass()`
209
241
  (`@jskit-ai/shell-web/client/composables/useShellWindowSizeClass`), never from
210
242
  copied breakpoint numbers.
@@ -304,8 +336,8 @@ and so every app's verify, fail on them too:
304
336
 
305
337
  | Rule | Finds |
306
338
  |---|---|
307
- | `m3/duplicate-h1` | a second `h1`, or an in-page `h1` in an app that uses the JSKIT shell |
308
- | `m3/raw-hex` | a hex colour outside `src/theme/` |
339
+ | `m3/duplicate-h1` | a second `h1`, or an in-page `h1` under the shell's page title |
340
+ | `m3/raw-hex` | a hex colour outside `src/theme/` (`palettes.js` holds content colours) |
309
341
  | `m3/dark-scheme` | no dark scheme, or the theme pinned to light |
310
342
  | `m3/outlined-card` | `variant="outlined"` or `border` on a card or sheet |
311
343
  | `m3/mdi-icon` | `mdi-*` icon strings and `@mdi/js` imports |
@@ -323,16 +355,39 @@ Take the approved route first; none needs an exception:
323
355
 
324
356
  - **Brand or partner colours** are theme custom colours, used by name:
325
357
  `colors: { partner: "#0A7D3E" }` in `src/theme/index.js`, then
326
- `<v-chip color="partner">`. `{ value: "#F5C518", blend: false }` keeps
327
- its hue.
328
- - **Chart and data colours** come from the theme: `const theme = useTheme()`,
329
- then `["primary", "tertiary"].map((role) => theme.current.value.colors[role])`.
358
+ `<v-chip color="partner">`. A hex string is harmonized toward the seed; a
359
+ colour that must keep its hue is `{ value: "#F5C518", blend: false }`
360
+ (Material Theme Builder's Blend switched off), with the same four roles.
361
+ - **Content colours**, colours that are content rather than UI, keep one
362
+ value in both schemes: an identity palette a person picks and the server
363
+ stores, avatar and illustration artwork, brand marks (a language's
364
+ file-type tint), fixed chart series, QR codes, colours written into
365
+ exported files. They are named exports of plain hex values in
366
+ `src/theme/palettes.js`, used as they are, never for UI chrome (a theme
367
+ colour is toned per scheme, so it can't keep an identity the same).
368
+ Components read them through bindings:
369
+
370
+ ```vue
371
+ <script setup>
372
+ // src/theme/palettes.js: export const botColours = ["#6750A4", "#B3261E"];
373
+ import { botColours } from "@/theme/palettes.js";
374
+ const props = defineProps({ bot: Object });
375
+ const ring = botColours[props.bot.colour];
376
+ </script>
377
+ <template>
378
+ <v-avatar :style="{ backgroundColor: ring }"><svg><path :fill="ring" /></svg></v-avatar>
379
+ </template>
380
+ <style scoped>.v-avatar { outline: 2px solid v-bind(ring); }</style>
381
+ ```
382
+ - **Chart and data colours** that follow the scheme come from the theme:
383
+ `const theme = useTheme()`, then
384
+ `["primary", "tertiary"].map((role) => theme.current.value.colors[role])`.
330
385
  - **A third-party widget** lives in one wrapper component, such as
331
386
  `src/components/RouteMap.vue`, that hands it theme colours; pages use only
332
387
  the wrapper.
333
388
 
334
- What is left may be excused one line at a time, on its line or the line
335
- before: `<!-- m3-check: allow m3/raw-hex - the partner's contractual logo colour -->`
389
+ What is left, such as a colour inside static SVG markup that can't import,
390
+ may be excused one line at a time, on its line or the line before: `<!-- m3-check: allow m3/raw-hex - the partner's contractual logo colour -->`
336
391
  (`// m3-check: ...` in scripts, `/* ... */` in CSS). **Never add one on your
337
392
  own:** ask the person first, naming the rule and the reason. A comment with no
338
393
  reason (`m3/allow-reason`) or an unknown rule (`m3/allow-rule`), and any
@@ -15,7 +15,7 @@ A concrete Fastify, Vue, and JSKIT application foundation for products that do n
15
15
 
16
16
  - Id: `app/minimal-foundation`
17
17
  - Keywords: `app`, `fastify`, `foundation`, `minimal`, `server`, `vite`, `vue`
18
- - Owner: `@jskit-ai/agent-docs@0.1.277`
18
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
19
19
  - Read: [PATTERN.md](patterns/app/minimal-foundation/PATTERN.md)
20
20
  - Examples: [example/](patterns/app/minimal-foundation/example/)
21
21
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`
@@ -26,7 +26,7 @@ A concrete JSKIT web application foundation with responsive shell navigation, se
26
26
 
27
27
  - Id: `app/shell-foundation`
28
28
  - Keywords: `app`, `foundation`, `material`, `navigation`, `placements`, `shell`, `vite`, `vue`
29
- - Owner: `@jskit-ai/agent-docs@0.1.277`
29
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
30
30
  - Read: [PATTERN.md](patterns/app/shell-foundation/PATTERN.md)
31
31
  - Examples: [example/](patterns/app/shell-foundation/example/)
32
32
  - Requires: `@jskit-ai/http-runtime`, `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -37,7 +37,7 @@ Configure an assistant runtime for one application surface and expose its chat a
37
37
 
38
38
  - Id: `assistant/assistant-surface`
39
39
  - Keywords: `ai`, `assistant`, `chat`, `config`, `environment`, `page`, `placement`, `settings`, `surface`
40
- - Owner: `@jskit-ai/assistant-runtime@0.2.25`
40
+ - Owner: `@jskit-ai/assistant-runtime@0.2.26`
41
41
  - Read: [PATTERN.md](patterns/assistant/assistant-surface/PATTERN.md)
42
42
  - Examples: [example/](patterns/assistant/assistant-surface/example/)
43
43
  - Requires: `@jskit-ai/assistant-runtime`, `@jskit-ai/connectors-catalog`, `@jskit-ai/connectors-core`, `@jskit-ai/shell-web`
@@ -48,7 +48,7 @@ Run one OpenID Connect sign-in for all of an owner's apps as its own JSKIT proje
48
48
 
49
49
  - Id: `auth/accounts-hub`
50
50
  - Keywords: `accounts hub`, `auth`, `authentication`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
51
- - Owner: `@jskit-ai/oidc-issuer-core@0.1.17`
51
+ - Owner: `@jskit-ai/oidc-issuer-core@0.1.18`
52
52
  - Read: [PATTERN.md](patterns/auth/accounts-hub/PATTERN.md)
53
53
  - Examples: [example/](patterns/auth/accounts-hub/example/)
54
54
  - Requires: `@jskit-ai/oidc-issuer-core`
@@ -59,7 +59,7 @@ Compose JSKIT authentication routes, views, profile controls, and public surface
59
59
 
60
60
  - Id: `auth/auth-surface`
61
61
  - Keywords: `account`, `auth`, `login`, `logout`, `password`, `placement`, `profile`, `reset`, `surface`
62
- - Owner: `@jskit-ai/auth-web@0.2.14`
62
+ - Owner: `@jskit-ai/auth-web@0.2.15`
63
63
  - Read: [PATTERN.md](patterns/auth/auth-surface/PATTERN.md)
64
64
  - Examples: [example/](patterns/auth/auth-surface/example/)
65
65
  - Requires: `@jskit-ai/auth-core`, `@jskit-ai/auth-web`, `@jskit-ai/shell-web`
@@ -70,7 +70,7 @@ Let people sign in with the accounts they already have in an accounts hub or ano
70
70
 
71
71
  - Id: `auth/oidc-sign-in`
72
72
  - Keywords: `accounts hub`, `auth`, `authentication`, `back-channel logout`, `dynamic client registration`, `issuer`, `master login`, `oidc`, `openid connect`, `single sign-on`, `sso`
73
- - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.16`
73
+ - Owner: `@jskit-ai/auth-provider-oidc-core@0.2.17`
74
74
  - Read: [PATTERN.md](patterns/auth/oidc-sign-in/PATTERN.md)
75
75
  - Examples: [example/](patterns/auth/oidc-sign-in/example/)
76
76
  - Requires: `@jskit-ai/auth-provider-oidc-core`, `@jskit-ai/auth-web`
@@ -246,7 +246,7 @@ Add a protected administration surface, settings shell, and semantic navigation
246
246
 
247
247
  - Id: `console/console-surface`
248
248
  - Keywords: `admin`, `console`, `navigation`, `owner`, `placement`, `settings`, `surface`
249
- - Owner: `@jskit-ai/console-web@0.1.236`
249
+ - Owner: `@jskit-ai/console-web@0.1.237`
250
250
  - Read: [PATTERN.md](patterns/console/console-surface/PATTERN.md)
251
251
  - Examples: [example/](patterns/console/console-surface/example/)
252
252
  - Requires: `@jskit-ai/console-core`, `@jskit-ai/console-web`, `@jskit-ai/shell-web`
@@ -257,7 +257,7 @@ Build a routed CRUD user interface as thin application pages over JSKIT's shared
257
257
 
258
258
  - Id: `crud/crud-screen-set`
259
259
  - Keywords: `actions`, `add`, `crud`, `delete`, `edit`, `filters`, `list`, `material`, `routes`, `view`, `vue`
260
- - Owner: `@jskit-ai/http-web@0.2.17`
260
+ - Owner: `@jskit-ai/http-web@0.2.18`
261
261
  - Read: [PATTERN.md](patterns/crud/crud-screen-set/PATTERN.md)
262
262
  - Examples: [example/](patterns/crud/crud-screen-set/example/)
263
263
  - Requires: `@jskit-ai/http-web`, `@jskit-ai/resource-crud-core`
@@ -312,7 +312,7 @@ Wrap a JSKIT web application in a Capacitor Android shell using native Capacitor
312
312
 
313
313
  - Id: `mobile/android-application`
314
314
  - Keywords: `android`, `capacitor`, `device`, `mobile`, `native`, `shell`, `webview`
315
- - Owner: `@jskit-ai/mobile-capacitor@0.2.12`
315
+ - Owner: `@jskit-ai/mobile-capacitor@0.2.13`
316
316
  - Read: [PATTERN.md](patterns/mobile/android-application/PATTERN.md)
317
317
  - Examples: [example/](patterns/mobile/android-application/example/)
318
318
  - Requires: `@capacitor/android`, `@capacitor/app`, `@capacitor/cli`, `@jskit-ai/mobile-capacitor`
@@ -323,7 +323,7 @@ Add JSKIT realtime events with an optional Redis backplane and an explicit shell
323
323
 
324
324
  - Id: `realtime/realtime-application`
325
325
  - Keywords: `realtime`, `redis`, `socket.io`, `sockets`, `status`, `websocket`
326
- - Owner: `@jskit-ai/realtime@0.2.15`
326
+ - Owner: `@jskit-ai/realtime@0.2.16`
327
327
  - Read: [PATTERN.md](patterns/realtime/realtime-application/PATTERN.md)
328
328
  - Examples: [example/](patterns/realtime/realtime-application/example/)
329
329
  - Requires: `@jskit-ai/realtime`, `@jskit-ai/shell-web`
@@ -334,7 +334,7 @@ Compose application-owned Google Publisher Tag delivery with the generic rewarde
334
334
 
335
335
  - Id: `rewards/google-rewarded`
336
336
  - Keywords: `ads`, `google`, `publisher tag`, `rewarded`, `unlock`
337
- - Owner: `@jskit-ai/rewarded-web@0.2.14`
337
+ - Owner: `@jskit-ai/rewarded-web@0.2.15`
338
338
  - Read: [PATTERN.md](patterns/rewards/google-rewarded/PATTERN.md)
339
339
  - Examples: [example/](patterns/rewards/google-rewarded/example/)
340
340
  - Requires: `@jskit-ai/rewarded-web`
@@ -345,7 +345,7 @@ Define a server feature through explicit capabilities and first-class actions, a
345
345
 
346
346
  - Id: `server/feature-package`
347
347
  - Keywords: `actions`, `feature`, `json-rest`, `knex`, `orchestration`, `package`, `provider`, `repository`, `routes`, `server`
348
- - Owner: `@jskit-ai/agent-docs@0.1.277`
348
+ - Owner: `@jskit-ai/agent-docs@0.1.279`
349
349
  - Read: [PATTERN.md](patterns/server/feature-package/PATTERN.md)
350
350
  - Examples: [example/](patterns/server/feature-package/example/)
351
351
  - Requires: `@jskit-ai/kernel`
@@ -356,7 +356,7 @@ Compose the JSKIT responsive shell, semantic placements, settings navigation, an
356
356
 
357
357
  - Id: `shell/application-shell`
358
358
  - Keywords: `adaptive`, `app`, `layout`, `navigation`, `placement`, `responsive`, `settings`, `shell`
359
- - Owner: `@jskit-ai/shell-web@0.2.12`
359
+ - Owner: `@jskit-ai/shell-web@0.2.13`
360
360
  - Read: [PATTERN.md](patterns/shell/application-shell/PATTERN.md)
361
361
  - Examples: [example/](patterns/shell/application-shell/example/)
362
362
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -367,7 +367,7 @@ Add product routes and shell extensions through file routing, semantic placement
367
367
 
368
368
  - Id: `ui/page-and-placement`
369
369
  - Keywords: `component`, `navigation`, `outlet`, `page`, `placement`, `routes`, `section`, `shell`, `subpages`, `vue`
370
- - Owner: `@jskit-ai/shell-web@0.2.12`
370
+ - Owner: `@jskit-ai/shell-web@0.2.13`
371
371
  - Read: [PATTERN.md](patterns/ui/page-and-placement/PATTERN.md)
372
372
  - Examples: [example/](patterns/ui/page-and-placement/example/)
373
373
  - Requires: `@jskit-ai/kernel`, `@jskit-ai/shell-web`
@@ -378,7 +378,7 @@ Compose an account settings route and profile, preference, notification, and sig
378
378
 
379
379
  - Id: `users/account-settings`
380
380
  - Keywords: `account`, `link provider`, `notifications`, `oauth`, `preferences`, `profile`, `settings`, `sign-in methods`, `user`, `vue`
381
- - Owner: `@jskit-ai/users-web@0.2.19`
381
+ - Owner: `@jskit-ai/users-web@0.2.20`
382
382
  - Read: [PATTERN.md](patterns/users/account-settings/PATTERN.md)
383
383
  - Examples: [example/](patterns/users/account-settings/example/)
384
384
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/users-core`, `@jskit-ai/users-web`
@@ -411,7 +411,7 @@ Compose workspace selection, invitation, member administration, settings, and re
411
411
 
412
412
  - Id: `workspaces/workspace-surfaces`
413
413
  - Keywords: `admin`, `invite`, `member`, `navigation`, `settings`, `surface`, `switcher`, `workspace`
414
- - Owner: `@jskit-ai/workspaces-web@0.2.19`
414
+ - Owner: `@jskit-ai/workspaces-web@0.2.20`
415
415
  - Read: [PATTERN.md](patterns/workspaces/workspace-surfaces/PATTERN.md)
416
416
  - Examples: [example/](patterns/workspaces/workspace-surfaces/example/)
417
417
  - Requires: `@jskit-ai/shell-web`, `@jskit-ai/workspaces-core`, `@jskit-ai/workspaces-web`
@@ -35,7 +35,7 @@
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
37
  "@jskit-ai/kernel": "0.2.10",
38
- "@jskit-ai/material-web": "0.1.1",
38
+ "@jskit-ai/material-web": "0.1.2",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@jskit-ai/config-eslint": "0.1.245",
53
- "@jskit-ai/jskit-catalog": "0.2.29",
53
+ "@jskit-ai/jskit-catalog": "0.2.31",
54
54
  "@playwright/test": "1.61.1",
55
55
  "@vitejs/plugin-vue": "^6.0.7",
56
56
  "eslint": "^10.8.0",
@@ -35,7 +35,7 @@
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
37
  "@jskit-ai/kernel": "0.2.10",
38
- "@jskit-ai/material-web": "0.1.1",
38
+ "@jskit-ai/material-web": "0.1.2",
39
39
  "@tanstack/vue-query": "^5.101.0",
40
40
  "fastify": "^5.12.7",
41
41
  "pino": "^10.3.1",
@@ -45,13 +45,13 @@
45
45
  "vue-router": "^5.3.1",
46
46
  "vuetify": "^4.3.0",
47
47
  "@jskit-ai/http-runtime": "0.2.10",
48
- "@jskit-ai/shell-web": "0.2.12",
48
+ "@jskit-ai/shell-web": "0.2.13",
49
49
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
50
50
  "@vue/devtools-api": "^8.2.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@jskit-ai/config-eslint": "0.1.245",
54
- "@jskit-ai/jskit-catalog": "0.2.29",
54
+ "@jskit-ai/jskit-catalog": "0.2.31",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "eslint": "^10.8.0",
@@ -3,6 +3,6 @@
3
3
  "type": "module",
4
4
  "dependencies": {
5
5
  "@jskit-ai/database-runtime-mysql": "0.1.257",
6
- "@jskit-ai/oidc-issuer-core": "0.1.17"
6
+ "@jskit-ai/oidc-issuer-core": "0.1.18"
7
7
  }
8
8
  }
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@jskit-ai/auth-provider-oidc-core": "0.2.16",
6
- "@jskit-ai/auth-web": "0.2.14"
5
+ "@jskit-ai/auth-provider-oidc-core": "0.2.17",
6
+ "@jskit-ai/auth-web": "0.2.15"
7
7
  }
8
8
  }
@@ -2,7 +2,7 @@
2
2
  "private": true,
3
3
  "type": "module",
4
4
  "dependencies": {
5
- "@jskit-ai/realtime": "0.2.15",
6
- "@jskit-ai/shell-web": "0.2.12"
5
+ "@jskit-ai/realtime": "0.2.16",
6
+ "@jskit-ai/shell-web": "0.2.13"
7
7
  }
8
8
  }
@@ -57,10 +57,15 @@ provider offers provider linking (`providerLinking` in its capabilities).
57
57
  - Loading uses layout-stable skeletons, never an indeterminate spinner.
58
58
  - Pending controls keep stable labels and prevent duplicate submission.
59
59
  - The route and placement owner agree.
60
+ - The account surface renders in its own layout, `src/pages/account.vue`
61
+ (`ShellLayout` around `<RouterView />`, `meta.jskit.surface: "account"`), so
62
+ the settings page's title, Up arrow and Done action are in the top app bar.
63
+ With no navigation items, the surface shows no navigation bar, rail or drawer.
60
64
 
61
65
  ## Example files
62
66
 
63
- `example/` contains an account route and four concrete settings sections.
67
+ `example/` contains the account surface's layout, an account route and four
68
+ concrete settings sections.
64
69
  Adapt their fields and copy while retaining the shared account runtime.
65
70
 
66
71
  ## Variation points
@@ -0,0 +1,22 @@
1
+ <route lang="json">
2
+ {
3
+ "meta": {
4
+ "jskit": {
5
+ "surface": "account"
6
+ }
7
+ }
8
+ }
9
+ </route>
10
+
11
+ <script setup>
12
+ // The account surface's layout: the shell's top app bar carries the settings page's title, its Up arrow back to home
13
+ // and its Done action. The surface has no navigation items, so the shell draws no navigation bar, rail or drawer.
14
+ import ShellLayout from "@/components/ShellLayout.vue";
15
+ import { RouterView } from "vue-router";
16
+ </script>
17
+
18
+ <template>
19
+ <ShellLayout title="" subtitle="">
20
+ <RouterView />
21
+ </ShellLayout>
22
+ </template>