@jskit-ai/agent-docs 0.1.279 → 0.1.280
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/guide/agent/app-extras/assistant.md +123 -0
- package/guide/agent/app-setup/authentication.md +34 -3
- package/guide/agent/app-setup/database-layer.md +35 -6
- package/guide/agent/app-setup/material-3-migration.md +38 -9
- package/guide/agent/app-setup/multi-homing.md +8 -2
- package/guide/agent/app-setup/upgrading-jskit.md +30 -0
- package/guide/agent/app-setup/users.md +9 -1
- package/guide/agent/framework/material-3.md +111 -71
- package/guide/agent/framework/sanity-checks.md +180 -0
- package/guide/agent/framework/translation.md +4 -0
- package/guide/agent/framework/typed-features.md +158 -105
- package/guide/agent/framework/ui-operations.md +3 -1
- package/guide/agent/index.md +3 -0
- package/package.json +2 -2
- package/patterns/feature-package/example/booking-engine/package.json +1 -1
- package/patterns/minimal-foundation/PATTERN.md +6 -2
- package/patterns/minimal-foundation/example/AGENTS.md +10 -0
- package/patterns/minimal-foundation/example/package.json +6 -6
- package/patterns/minimal-foundation/example/playwright.config.mjs +3 -0
- package/patterns/minimal-foundation/example/src/main.js +3 -2
- package/patterns/minimal-foundation/example/vite.config.mjs +3 -1
- package/patterns/shell-foundation/PATTERN.md +4 -1
- package/patterns/shell-foundation/example/AGENTS.md +10 -0
- package/patterns/shell-foundation/example/package.json +7 -7
- package/patterns/shell-foundation/example/packages/main/package.json +1 -1
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +3 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +3 -0
- package/patterns/shell-foundation/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/main.js +3 -2
- package/patterns/shell-foundation/example/src/pages/home.vue +1 -1
- package/patterns/shell-foundation/example/src/views/NotFound.vue +1 -1
- package/patterns/shell-foundation/example/vite.config.mjs +3 -1
- package/reference/autogen/PATTERN_INDEX.md +37 -37
- package/reference/autogen/packages/agent-docs.md +4 -0
- package/reference/autogen/packages/assistant-runtime.md +27 -2
- package/reference/autogen/packages/auth-web.md +10 -0
- package/reference/autogen/packages/crud-core.md +3 -0
- package/reference/autogen/packages/http-web.md +0 -4
- package/reference/autogen/packages/json-rest-api-core.md +17 -4
- package/reference/autogen/packages/kernel.md +10 -1
- package/reference/autogen/packages/material-web.md +2 -1
- package/reference/autogen/packages/shell-web.md +84 -0
- package/reference/autogen/packages/users-web.md +5 -2
- package/reference/autogen/packages/workspaces-core.md +2 -0
- package/reference/autogen/tooling/jskit-catalog.md +52 -5
- package/reference/autogen/tooling/testUtils.md +15 -1
- package/skills/jskit/SKILL.md +15 -5
- package/skills/jskit/references/heuristics/INDEX.md +3 -1
- package/skills/jskit/references/heuristics/row-policies.md +3 -3
- package/skills/jskit/references/heuristics/typed-features.md +7 -7
- package/skills/jskit/references/heuristics/ui-testing.md +8 -7
- package/skills/jskit/references/material-3.md +111 -71
- package/skills/jskit/references/multi-homing.md +8 -2
- package/skills/jskit/references/pattern-index.md +37 -37
- package/skills/jskit/references/patterns/app/minimal-foundation/PATTERN.md +6 -2
- package/skills/jskit/references/patterns/app/minimal-foundation/example/AGENTS.md +10 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +6 -6
- package/skills/jskit/references/patterns/app/minimal-foundation/example/playwright.config.mjs +3 -0
- package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +3 -2
- package/skills/jskit/references/patterns/app/minimal-foundation/example/vite.config.mjs +3 -1
- package/skills/jskit/references/patterns/app/shell-foundation/PATTERN.md +4 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/AGENTS.md +10 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +7 -7
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +3 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/playwright.config.mjs +3 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +3 -2
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home.vue +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +1 -1
- package/skills/jskit/references/patterns/app/shell-foundation/example/vite.config.mjs +3 -1
- package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/PATTERN.md +18 -5
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/components/AppBrand.vue +17 -0
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/login.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/reset-password.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/signout.vue +2 -2
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/LoginView.vue +6 -1
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/ResetPasswordView.vue +6 -1
- package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/SignOutView.vue +6 -1
- package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
- package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/aws-storage-queries/example/formats/package.json +1 -1
- package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
- package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console.vue +1 -1
- package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
- package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
- package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
- package/skills/jskit/references/patterns/rewards/google-rewarded/example/googlePublisherTag.js +5 -2
- package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
- package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +11 -6
- package/skills/jskit/references/patterns/shell/application-shell/example/src/components/menus/SectionTabLinkItem.vue +42 -0
- package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home.vue +1 -1
- package/skills/jskit/references/patterns/shell/application-shell/example/src/views/NotFound.vue +1 -1
- package/skills/jskit/references/patterns/ui/page-and-placement/PATTERN.md +6 -0
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +9 -4
- package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placementTopology.js +5 -3
- package/skills/jskit/references/patterns/users/account-settings/PATTERN.md +24 -3
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +1 -1
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +2 -1
- package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +3 -2
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/[section].vue +23 -0
- package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account.vue +4 -3
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
- package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +3 -3
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/root.vue +1 -1
- package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/root.vue +1 -1
- package/skills/jskit/references/sanity-checks.md +180 -0
- package/skills/jskit/references/translation.md +4 -0
- package/skills/jskit/references/typed-features.md +158 -105
- package/skills/jskit/references/ui-operations.md +3 -1
|
@@ -47,7 +47,8 @@ app and never leaves it.
|
|
|
47
47
|
- **What the person sees:** one Up arrow on every inner page, also when opened
|
|
48
48
|
directly: icon only (`$arrowBack`), accessible name "Back" (localized), a
|
|
49
49
|
tooltip on pointer devices. None on top-level destinations, and no other
|
|
50
|
-
back control anywhere; the bar title says where the person is. A
|
|
50
|
+
back control anywhere; the bar title says where the person is. A new or edit
|
|
51
|
+
form shows a close (X) in the arrow's place, which cancels. A list-detail
|
|
51
52
|
layout showing the parent beside the page needs no arrow for that step;
|
|
52
53
|
collapsed to one pane, the record pane gets it back.
|
|
53
54
|
- **Where Up goes:** to the previous trail entry while it is valid, so inside
|
|
@@ -79,11 +80,17 @@ Exactly this, and nothing more:
|
|
|
79
80
|
</script>
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
The same key works in a `<route lang="json">` block.
|
|
83
|
-
list, an edit page its record, a sub-page its page. Tabs under a layout
|
|
83
|
+
The same key works in a `<route lang="json">` block. Tabs under a layout
|
|
84
84
|
(`settings.vue` rendering `<RouterView>` for `settings/`) declare nothing:
|
|
85
85
|
they inherit the nearest layout's declaration, and moving between them keeps
|
|
86
|
-
the trail.
|
|
86
|
+
the trail and the scroll. A folder without a layout (`bookings/` with no
|
|
87
|
+
`bookings.vue`) only groups routes: its pages are different pages, each
|
|
88
|
+
opening at the top. The parent is read from the page up to its surface root, the
|
|
89
|
+
record that declares `jskit.surface`, and stops there. So the surface goes
|
|
90
|
+
only on the surface's layout page (`account.vue`: `ShellLayout` around
|
|
91
|
+
`<RouterView />`) and the parent on the page it renders
|
|
92
|
+
(`account/index.vue`). A package route declares its parent in its
|
|
93
|
+
`package.json` `meta`; under no layout, its component draws `ShellLayout`.
|
|
87
94
|
2. **Each top-level destination is a main-navigation item** (a `kind: "link"`
|
|
88
95
|
placement with `target: "shell.primary-nav"` or `"shell.secondary-nav"`) and
|
|
89
96
|
declares no parent; nor do its tabs. A shell page with no parent is
|
|
@@ -95,14 +102,20 @@ Exactly this, and nothing more:
|
|
|
95
102
|
`:to="paths.page('/suppliers')"` and `router.push(paths.page(...))` with
|
|
96
103
|
`usePaths()`, the CRUD runtime's `resolveViewUrl()`, `resolveEditUrl()` and
|
|
97
104
|
`resolveParams()`, and link placements (`surface` with `scopedSuffix` and
|
|
98
|
-
`unscopedSuffix`; `to` only for a global route
|
|
99
|
-
|
|
105
|
+
`unscopedSuffix`; `to` only for a global route). A global route
|
|
106
|
+
(`scope: "global"`, on no surface) is
|
|
107
|
+
`paths.page("/auth/login", { scope: "global" })`; `explicitTo` only passes
|
|
108
|
+
through a finished target that arrives as data, never a hand-written path.
|
|
109
|
+
The trail follows the router, so there is no special link component.
|
|
100
110
|
4. **A custom new or edit page finishes through the shell:**
|
|
101
111
|
`useShellNavigation()` (`@jskit-ai/shell-web/client/navigation/useShellNavigation`)
|
|
102
112
|
gives `finish(target)` for Save (the target replaces the form in the trail)
|
|
103
113
|
and `finish()` for Cancel (back to where the form was opened, else its
|
|
104
|
-
parent)
|
|
105
|
-
`
|
|
114
|
+
parent); `useShellPageClose({ label, close })`
|
|
115
|
+
(`@jskit-ai/shell-web/client/composables/useShellPageClose`) makes the
|
|
116
|
+
arrow its Cancel, never an X beside it. A page whose
|
|
117
|
+
record turns out deleted or forbidden calls `pageUnavailable()`. JSKIT's
|
|
118
|
+
CRUD screens already do all three.
|
|
106
119
|
5. **A full page load goes through the shell too:** `reloadAppPage(path)`
|
|
107
120
|
(after sign-in or a user switch, to rebuild client state) and
|
|
108
121
|
`leaveApp(url)` (an external site in the same tab, such as a payment page),
|
|
@@ -118,11 +131,12 @@ Exactly this, and nothing more:
|
|
|
118
131
|
| `nav/history` | `history.back()`, `history.go()`, `history.pushState()`/`replaceState()`, `router.back()`, `router.go()` |
|
|
119
132
|
| `nav/window-location` | `window.location` assignments, `.href =`, `location.assign()`, `location.replace()` |
|
|
120
133
|
| `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 |
|
|
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 |
|
|
122
|
-
| `nav/missing-parent` | an inner page of a shell surface without a declared parent |
|
|
134
|
+
| `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 (`<ContextBackButton />` or `<page-back-link>`, and its import), a `goBack()`-style handler |
|
|
135
|
+
| `nav/missing-parent` | an inner page of a shell surface without a declared parent, when it always renders under the shell's top app bar (a layout that also renders its pages without `ShellLayout`, such as a shop device's sign-in in a `v-if`, leaves its pages unjudged) |
|
|
123
136
|
| `nav/top-level-parent` | a main-navigation destination that declares a parent |
|
|
124
137
|
| `nav/placement-data` | a main-navigation placement that isn't literal data (no page is judged for a missing parent until it is) |
|
|
125
138
|
| `nav/missing-shell-layout` | a page that declares a parent with no shell layout rendering it, so its title, Up and actions never show |
|
|
139
|
+
| `nav/surface-parent` | a parent declared beside `jskit.surface`, which the shell never reads |
|
|
126
140
|
| `nav/allow` | any `m3-check: allow nav/...` comment: nothing excuses a navigation rule |
|
|
127
141
|
|
|
128
142
|
### Each rule, right and wrong
|
|
@@ -180,35 +194,43 @@ Exactly this, and nothing more:
|
|
|
180
194
|
- **Type:** Vuetify 4 Material 3 roles (`text-headline-small`,
|
|
181
195
|
`text-title-large`, `text-body-medium`, `text-label-large`). Never the
|
|
182
196
|
Material 2 scale (`text-h5`, `text-body-2`) or a page-local type system.
|
|
183
|
-
Roboto Flex
|
|
197
|
+
Material 3 has two typefaces, both Roboto Flex unless the app names its own
|
|
198
|
+
in `src/theme/index.js`:
|
|
199
|
+
`typography: { brand: '"Fraunces", serif', plain: '"Inter", sans-serif' }`.
|
|
200
|
+
`brand` sets display, headline and title-large, `plain` the rest (title-medium
|
|
201
|
+
and -small, body, label); each is a CSS font-family value, and the app loads
|
|
202
|
+
the font. Never set `font-family` or `--v-font-body` in app CSS.
|
|
184
203
|
- **Icons:** Material Symbols Rounded through Vuetify `$` aliases: Vuetify's
|
|
185
204
|
standard ones (`$close`, `$menu`, `$delete`, `$prev`, `$next`) and JSKIT's
|
|
186
205
|
camelCase names (`$add`, `$edit`, `$search`, `$moreVert`, `$filterList`,
|
|
187
206
|
`$arrowBack`); `$homeFilled`-style `Filled` variants mark active navigation.
|
|
188
207
|
An app adds an icon under `theme.icons` in `src/theme/index.js` as
|
|
189
208
|
`{ name: "<24x24 path data>" }` from Material Symbols Rounded, then uses
|
|
190
|
-
`$name`. No `mdi-*` strings and no `@mdi/js` imports in app code.
|
|
209
|
+
`$name`. No `mdi-*` strings and no `@mdi/js` imports in app code. Material
|
|
210
|
+
Symbols has no brand marks (Google, GitHub, a payment network): a brand's
|
|
211
|
+
logo is the brand's own artwork, a content image or SVG used as its owner
|
|
212
|
+
publishes it, not an icon alias.
|
|
191
213
|
|
|
192
214
|
## Exact sizes
|
|
193
215
|
|
|
194
|
-
JSKIT ships these exact sizes; an app never re-implements or overrides them
|
|
195
|
-
and needs no Sass and no `!important` (Vuetify 4 keeps its CSS in cascade
|
|
196
|
-
layers):
|
|
216
|
+
JSKIT ships these exact sizes; an app never re-implements or overrides them:
|
|
197
217
|
|
|
198
218
|
- 40dp buttons with a 48dp touch target, icon buttons included; a smaller
|
|
199
219
|
button is `size="small"` or `size="x-small"`, which keeps its smaller look
|
|
200
|
-
and the 48dp target
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
and the 48dp target. No icon button is under 32dp, and a target stops at
|
|
221
|
+
the edge of a button beside it, so each of a row's touching icon buttons
|
|
222
|
+
answers its own press;
|
|
223
|
+
- one-line button labels, ending in an ellipsis: keep labels short, and give
|
|
224
|
+
one that can run long, such as a name, its full label as `title`;
|
|
225
|
+
- 12dp cards: flat (the default) and tonal at level 0, `variant="elevated"`
|
|
226
|
+
at level 1; flat expansion panels;
|
|
205
227
|
- 28dp dialogs and bottom-sheet top corners, square full-screen dialogs;
|
|
206
228
|
- 8dp chips, and FABs of 56dp (small 40dp, large 96dp; extended 56dp tall)
|
|
207
229
|
with 16dp corners;
|
|
208
230
|
- emphasized easing, with reduced motion honoured.
|
|
209
231
|
|
|
210
232
|
So never set `height` or `min-height` on a button, or a radius on a card,
|
|
211
|
-
dialog, chip or FAB.
|
|
233
|
+
dialog, chip or FAB.
|
|
212
234
|
|
|
213
235
|
## The top app bar carries the page
|
|
214
236
|
|
|
@@ -216,35 +238,46 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
|
|
|
216
238
|
`useShellPageTitle(title)` (`@jskit-ai/shell-web/client/composables/useShellPageTitle`)
|
|
217
239
|
for a runtime title such as the record's name. The shell renders it as the
|
|
218
240
|
app bar's only `h1`; pages add no `h1`, page header, title block or
|
|
219
|
-
standalone heading.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
-
|
|
223
|
-
|
|
241
|
+
standalone heading. Route pages own the title: a section or a nested
|
|
242
|
+
component uses a title role (a component's claim gives the title back when
|
|
243
|
+
it goes). A page with no top app bar (a hero front page, an intro, an invite
|
|
244
|
+
or hand-off page) has its own one `h1`.
|
|
245
|
+
- **Up** leads the bar on every inner page; on phones it replaces the menu
|
|
246
|
+
icon.
|
|
224
247
|
- **A layout that draws its own top app bar** (a workspace bar with the
|
|
225
248
|
app's own controls, instead of `ShellLayout`) builds it from `ShellLayout`'s
|
|
226
249
|
own pieces, from `@jskit-ai/shell-web/client`. It calls `useShellAppBar()`
|
|
227
250
|
once, so its pages enter the trail and their `ShellPageActions` show in its
|
|
228
251
|
bar, and renders `ShellUpButton` first, then `ShellPageTitle` (the page's one
|
|
229
|
-
`h1
|
|
230
|
-
prop) and `ShellPageActionsTarget`. A title that is also a switcher, such as a
|
|
252
|
+
`h1`) and `ShellPageActionsTarget`. A title that is also a switcher, such as a
|
|
231
253
|
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
|
|
233
|
-
layout, so its inner pages need parents
|
|
234
|
-
`nav/back-control` finding.
|
|
254
|
+
the menu's activator, so it stays the one `h1`. `jskit check` counts it as a
|
|
255
|
+
shell layout, so its inner pages need parents.
|
|
235
256
|
- **Actions:** a screen's actions go in the bar inside `ShellPageActions`
|
|
236
257
|
(`@jskit-ai/shell-web/client/components/ShellPageActions`), each a
|
|
237
258
|
`ShellPageAction` with `icon`, `label` (also its accessible name and compact
|
|
238
|
-
tooltip), and `primary`, `to`, `replace`, `
|
|
239
|
-
|
|
240
|
-
`
|
|
259
|
+
tooltip), and `primary`, `to`, `replace`, `href` (with `download` for a
|
|
260
|
+
file, such as a CSV export), `disabled`, `loading` or `color` (`error` for a
|
|
261
|
+
destructive one); attributes such as `target` reach its button. On compact
|
|
262
|
+
widths the bar keeps one trailing item after the title: the single
|
|
263
|
+
`primary` action when the page has one (a form's Save), otherwise its own
|
|
264
|
+
item nearest the corner (a top-right placement such as the account avatar).
|
|
265
|
+
Every other action and the bar's other items (a notification bell, tools)
|
|
266
|
+
go to its "More actions" menu, where `overflow` actions always are. Medium
|
|
267
|
+
and wider bars show them all.
|
|
241
268
|
- **Window size classes:** compact under 600 (navigation bar), medium 600–839
|
|
242
|
-
(navigation rail), expanded 840–1199 and large from 1200 (drawer)
|
|
243
|
-
|
|
244
|
-
navigation bar, rail or drawer; its pages keep the app bar. Read them
|
|
245
|
-
from `useShellWindowSizeClass()`
|
|
269
|
+
(navigation rail), expanded 840–1199 and large from 1200 (drawer), read from
|
|
270
|
+
`useShellWindowSizeClass()`
|
|
246
271
|
(`@jskit-ai/shell-web/client/composables/useShellWindowSizeClass`), never from
|
|
247
|
-
copied breakpoint numbers.
|
|
272
|
+
copied breakpoint numbers. A surface with fewer than two navigation
|
|
273
|
+
destinations (none, as on an account surface, or Home alone) draws no
|
|
274
|
+
navigation bar, rail or drawer; its pages keep the app bar.
|
|
275
|
+
- **Section tabs:** a page's own sections (`page.section-nav`) are Material 3
|
|
276
|
+
primary tabs: the topology names `ShellSectionTabLinkItem`'s token (the
|
|
277
|
+
starter registers it as `local.main.ui.section-tab-link-item`), and the page
|
|
278
|
+
draws one tab row, `display: flex; overflow-x: auto` over a 1dp
|
|
279
|
+
`outline-variant` bottom border. `ShellTabLinkItem` is the navigation bar's
|
|
280
|
+
destination; placed in a page it draws a section tab.
|
|
248
281
|
|
|
249
282
|
## Colour
|
|
250
283
|
|
|
@@ -267,35 +300,41 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
|
|
|
267
300
|
control; never a permanent panel of outlined fields. A card opens its record
|
|
268
301
|
when tapped (a stretched title link); a table row opens on click. Row actions
|
|
269
302
|
are trailing icon buttons (edit, a `$moreVert` overflow menu, a chevron to
|
|
270
|
-
open), never "View" or "Edit" text buttons on every row. "New" is
|
|
271
|
-
|
|
272
|
-
and
|
|
303
|
+
open), never "View" or "Edit" text buttons on every row. "New" is
|
|
304
|
+
`ShellPrimaryAction` (`label`, and `to` or a click handler): an extended FAB
|
|
305
|
+
on compact and medium widths, a filled bar button on expanded. On/off filters
|
|
306
|
+
are `aria-pressed` chips, or chips in a `v-chip-group`; JSKIT draws them as
|
|
307
|
+
filter chips. Empty and error states carry an icon.
|
|
273
308
|
- **A list never scrolls sideways, at any width.** The list's own width (a
|
|
274
309
|
container query, not the window) chooses cards or table. Cards flow into a
|
|
275
310
|
grid; tables use a fixed layout with an ellipsis on long values, and fold
|
|
276
311
|
low-priority columns into a stronger column's second line. Lists show figures
|
|
277
312
|
("120 kg", tabular and right-aligned) and names, never ids.
|
|
278
313
|
- **Record:** a header (avatar, title, status chips) and value-first detail
|
|
279
|
-
lists, never a grid of label/value text. Edit sits in the bar
|
|
314
|
+
lists, never a grid of label/value text. Edit sits in the bar, its secondary
|
|
315
|
+
actions in the bar's overflow menu, never in the content; the record's
|
|
280
316
|
next workflow step is the one filled button, shown in context rather than a
|
|
281
317
|
row of mostly disabled actions.
|
|
282
|
-
- **Form:**
|
|
283
|
-
Nothing moves under a finger: a multi-select chip
|
|
284
|
-
when it loses focus. A pick-one-of-N choice is a
|
|
285
|
-
|
|
318
|
+
- **Form:** the bar's close (X) cancels and Save is its filled action. Fields
|
|
319
|
+
use `hideDetails: "auto"`. Nothing moves under a finger: a multi-select chip
|
|
320
|
+
field keeps its input width when it loses focus. A pick-one-of-N choice is a
|
|
321
|
+
segmented button (`v-btn-toggle`, drawn by JSKIT, with 12dp segment sides as
|
|
322
|
+
in any `v-btn-group`) with `role="radiogroup"` and
|
|
323
|
+
`role="radio"`/`aria-checked` buttons.
|
|
286
324
|
- **Dialogs:** plain `v-dialog` and `v-bottom-sheet`; JSKIT gives them their
|
|
287
325
|
Material 3 shapes. A full-screen dialog (compact and medium) puts close, title
|
|
288
326
|
and its confirm action in a top bar, never at the bottom where a snackbar
|
|
289
327
|
covers them.
|
|
290
328
|
- **Menus and badges:** a menu's list has `role="menu"` and its items
|
|
291
329
|
`role="menuitem"`, with `aria-disabled` on unavailable ones; a badge has a
|
|
292
|
-
`label` such as "3 batches".
|
|
330
|
+
`label` such as "3 batches". JSKIT draws a menu's list and a dialog's card;
|
|
331
|
+
an app sets no `VMenu` or `VDialog` defaults, under which
|
|
332
|
+
Vuetify 4.2.1 to 4.3.2 loses a `v-select`'s typeahead match, or a dialog's
|
|
333
|
+
virtual list, when an ancestor re-renders.
|
|
293
334
|
|
|
294
335
|
## Components, state, and feedback
|
|
295
336
|
|
|
296
|
-
- Choose components by purpose
|
|
297
|
-
tables, sheets, dialogs, menus, navigation, progress, alerts, snackbars, and
|
|
298
|
-
tooltips over custom imitations.
|
|
337
|
+
- Choose standard components by purpose over custom imitations.
|
|
299
338
|
- Give one primary action emphasis. Distinguish destructive, secondary, and
|
|
300
339
|
icon-only actions without relying on color; give icon controls accessible
|
|
301
340
|
names and fields meaningful labels, errors, and autocomplete behavior.
|
|
@@ -312,6 +351,11 @@ dialog, chip or FAB. A missing size is a JSKIT defect.
|
|
|
312
351
|
field. A user-triggered command uses JSKIT's shared action
|
|
313
352
|
feedback/snackbar path. Never insert a transient command-error alert above
|
|
314
353
|
page content where it shifts the working layout.
|
|
354
|
+
- A snackbar never covers the FAB or the actions it reports on. `ShellErrorHost`
|
|
355
|
+
centres it above the navigation bar on compact windows and starts it at the
|
|
356
|
+
content's start edge, at most half its width, on wider ones. A FAB takes
|
|
357
|
+
layout room (`<v-fab app layout>`, as `ShellPrimaryAction`'s does), so the
|
|
358
|
+
snackbar shows above it.
|
|
315
359
|
|
|
316
360
|
## Adaptive layout
|
|
317
361
|
|
|
@@ -333,16 +377,15 @@ npx jskit check m3 src/pages/home/books # a section
|
|
|
333
377
|
npx jskit check m3 $(git diff --name-only --diff-filter=d main) # changed files
|
|
334
378
|
```
|
|
335
379
|
|
|
336
|
-
Plain `jskit check` fails on the [navigation rules](#forbidden-in-app-code)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
and so every app's verify, fail on them too:
|
|
380
|
+
Plain `jskit check` fails on the [navigation rules](#forbidden-in-app-code).
|
|
381
|
+
`jskit check m3` lists those and the visual rules below. The visual rules are a
|
|
382
|
+
report until every JSKIT app has been converted to them; the JSKIT release after
|
|
383
|
+
that makes plain `jskit check`, and so every app's verify, fail on them too:
|
|
341
384
|
|
|
342
385
|
| Rule | Finds |
|
|
343
386
|
|---|---|
|
|
344
|
-
| `m3/duplicate-h1` | a second `h1
|
|
345
|
-
| `m3/raw-hex` | a hex colour outside `src/theme/` (`palettes.js` holds content colours) |
|
|
387
|
+
| `m3/duplicate-h1` | a second `h1` that can show beside the first (one per `v-if` branch is one), or an in-page `h1` under a layout that always shows the shell's page title |
|
|
388
|
+
| `m3/raw-hex` | a hex colour, or one written as channels (`rgba(0, 0, 0, 0.15)`, as in a hand-made shadow), outside `src/theme/` (`palettes.js` holds content colours); `rgba(var(--v-theme-shadow), 0.2)` reads a role and passes |
|
|
346
389
|
| `m3/dark-scheme` | no dark scheme, or the theme pinned to light |
|
|
347
390
|
| `m3/outlined-card` | `variant="outlined"` or `border` on a card or sheet |
|
|
348
391
|
| `m3/mdi-icon` | `mdi-*` icon strings and `@mdi/js` imports |
|
|
@@ -395,9 +438,9 @@ What is left, such as a colour inside static SVG markup that can't import,
|
|
|
395
438
|
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 -->`
|
|
396
439
|
(`// m3-check: ...` in scripts, `/* ... */` in CSS). **Never add one on your
|
|
397
440
|
own:** ask the person first, naming the rule and the reason. A comment with no
|
|
398
|
-
reason (`m3/allow-reason`) or an unknown rule (`m3/allow-rule`)
|
|
399
|
-
|
|
400
|
-
|
|
441
|
+
reason (`m3/allow-reason`) or an unknown rule (`m3/allow-rule`) is itself a
|
|
442
|
+
finding, as any `nav/allow` is; nothing turns the check off for a file, a
|
|
443
|
+
folder or the app. `jskit check m3` lists every
|
|
401
444
|
exception with its file, line and reason, and their total.
|
|
402
445
|
|
|
403
446
|
## Material 3 audit
|
|
@@ -406,19 +449,16 @@ Run this behavior-preserving audit over every affected screen:
|
|
|
406
449
|
|
|
407
450
|
1. Identify the owning JSKIT surface, shared screen, shell, placement, theme,
|
|
408
451
|
and Vuetify components before judging local markup.
|
|
409
|
-
2. Check navigation and Up first, as must-fix: plain `jskit check` passes
|
|
410
|
-
every
|
|
411
|
-
destinations have none; no inner link is hand-built; nothing writes back
|
|
412
|
-
logic.
|
|
452
|
+
2. Check navigation and Up first, as must-fix: plain `jskit check` passes and
|
|
453
|
+
every rule in Navigation and Up holds.
|
|
413
454
|
3. Find parallel components, duplicated state chrome, page-local themes, raw
|
|
414
455
|
visual constants, CSS against Vuetify internals, nested generic cards,
|
|
415
|
-
borders instead of tones, Material 2 typography, and desktop-only layout
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
5. Correct issues at the narrowest established owner. Consolidate only truly
|
|
456
|
+
borders instead of tones, Material 2 typography, and desktop-only layout;
|
|
457
|
+
check states, focus, labels, targets, contrast and reduced motion against
|
|
458
|
+
the rules above.
|
|
459
|
+
4. Correct issues at the narrowest established owner. Consolidate only truly
|
|
420
460
|
repeated policy; avoid pass-through wrappers and speculative abstractions.
|
|
421
|
-
|
|
461
|
+
5. Run `jskit check` and `jskit check m3` on the affected files, then focused
|
|
422
462
|
tests and Playwright at compact, medium, and expanded widths in light and
|
|
423
463
|
dark (`shellBackButton(page)` and `pressShellBack(page)` from
|
|
424
464
|
`@jskit-ai/shell-web/test/shellNavigation` drive Up).
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/framework/sanity-checks.md`. Do not edit manually. -->
|
|
2
|
+
|
|
3
|
+
# Sanity checks
|
|
4
|
+
|
|
5
|
+
Before calling a list, a save, a test, a refactor, a screen or a deployment
|
|
6
|
+
done, measure what it actually costs and does, judge whether that is sane, and
|
|
7
|
+
fix the cause when it is not. This page is the JSKIT part of that: the
|
|
8
|
+
pitfalls JSKIT's own packages cause, then which JSKIT call, helper or setting
|
|
9
|
+
applies each check, with one line on what the check catches and why.
|
|
10
|
+
|
|
11
|
+
## JSKIT's own pitfalls
|
|
12
|
+
|
|
13
|
+
### Theme and component defaults
|
|
14
|
+
|
|
15
|
+
**Check:** after changing a theme, a Vuetify default, a material-web default
|
|
16
|
+
or a shared component, use what it affects in a production build: open a
|
|
17
|
+
select and type, press a segmented button at desktop width, tap neighbouring
|
|
18
|
+
icon buttons, and leave a field to see whether the layout shifts under the
|
|
19
|
+
next tap. A default reaches every component that renders the one it names, so
|
|
20
|
+
a change made for one screen can break others that never import it
|
|
21
|
+
([Material 3](/guide/framework/material-3)).
|
|
22
|
+
|
|
23
|
+
**Examples:**
|
|
24
|
+
|
|
25
|
+
- A `VMenu: { VSheet: {} }` default broke a select's typeahead.
|
|
26
|
+
- A common button padding squeezed segmented buttons into slivers.
|
|
27
|
+
- A field that grew 32 px on blur moved the button under a person's tap.
|
|
28
|
+
|
|
29
|
+
### Navigation metadata
|
|
30
|
+
|
|
31
|
+
**Check:** open each inner page directly and by link, and see its Up arrow
|
|
32
|
+
and title. Some navigation declarations look right but switch Up off, and
|
|
33
|
+
some shapes leave pages the navigation rules cannot judge
|
|
34
|
+
([Material 3](/guide/framework/material-3)):
|
|
35
|
+
|
|
36
|
+
- a parent declared beside `jskit.surface` is never read; the surface goes on
|
|
37
|
+
the layout page and the parent on the page it renders (`nav/surface-parent`
|
|
38
|
+
reports the two on one record);
|
|
39
|
+
- an inner page repeating its surface becomes a root of its own, with no Up;
|
|
40
|
+
- until a main-navigation placement is literal data, no page is judged for a
|
|
41
|
+
missing parent (`nav/placement-data`);
|
|
42
|
+
- a layout that also renders its pages without `ShellLayout` leaves those
|
|
43
|
+
pages unjudged by `nav/missing-parent`, so a missing parent passes;
|
|
44
|
+
- a package route is judged only when its client entry lists
|
|
45
|
+
`routeComponents` literally; a built map passes unjudged.
|
|
46
|
+
|
|
47
|
+
**Example:** a design-system update moved a page title into a top app bar
|
|
48
|
+
that one page did not have, so its title vanished; only the browser suite
|
|
49
|
+
caught it.
|
|
50
|
+
|
|
51
|
+
### Generated action and tool contracts
|
|
52
|
+
|
|
53
|
+
**Check:** call each assistant tool the way the model will, with sparse
|
|
54
|
+
`fields` and an `include`, and compare the result with the output contract it
|
|
55
|
+
advertises. An action is a tool only when it has an input contract and a
|
|
56
|
+
truthful model-facing output; the assistant validates the result against
|
|
57
|
+
`extensions.assistant.output` and rejects unexpected fields, so a contract
|
|
58
|
+
that promises what execution cannot return fails at run time, not at review
|
|
59
|
+
([Assistant](/guide/app-extras/assistant#database-surface-action-tools)).
|
|
60
|
+
|
|
61
|
+
**Example:** sparse primary fields stay sparse, and an included record is read
|
|
62
|
+
from its lookup container (`items[0].lookups.pet.name`); a contract that
|
|
63
|
+
expects the relationship id or an unselected field never gets it.
|
|
64
|
+
|
|
65
|
+
## Applying each check
|
|
66
|
+
|
|
67
|
+
### Every row
|
|
68
|
+
|
|
69
|
+
- An unpaged JSON:API query returns the first 20 rows, and a page above the
|
|
70
|
+
resource's `queryMaxLimit` (100 unless it sets one) is cut to that size,
|
|
71
|
+
both without an error; CRUD `queryDocuments()` refuses such a `limit`. A
|
|
72
|
+
read its callers treat as complete uses `queryAllJsonRestCollectionRows()`
|
|
73
|
+
or the repository's `queryEveryDocument()`, because the missing rows
|
|
74
|
+
otherwise vanish from every picker and delivery built on it
|
|
75
|
+
([Every row](/guide/framework/typed-features#every-row)).
|
|
76
|
+
- A lookup by a unique key matches at most one row, so it may read `rows[0]`
|
|
77
|
+
of a plain query.
|
|
78
|
+
|
|
79
|
+
### Query counts and batches
|
|
80
|
+
|
|
81
|
+
- Count a path's queries with the test Knex instance's `query` event at a few
|
|
82
|
+
rows and at a realistic number; the count should follow tables and pages,
|
|
83
|
+
not rows ([Query counts at realistic
|
|
84
|
+
sizes](/guide/framework/typed-features#query-counts-at-realistic-sizes)).
|
|
85
|
+
- A JSON:API read adds a few lookup and visibility queries per page and per
|
|
86
|
+
relationship field. That follows pages, so it is expected.
|
|
87
|
+
- `getDocumentById()`, `queryDocuments()` or a service `get` inside a loop is
|
|
88
|
+
the usual per-row read; batch it with an id-list `searchSchema` filter or
|
|
89
|
+
`include`, grouped with `Map.groupBy()` ([Lists read related rows in
|
|
90
|
+
batches](/guide/framework/typed-features#lists-read-related-rows-in-batches)).
|
|
91
|
+
- A `rowPolicy` adds one correlated predicate to the query it is given, so
|
|
92
|
+
its cost follows the rows read, not the subject's history. It runs on every
|
|
93
|
+
single-record read, including a write's default `returning: "full"` read
|
|
94
|
+
back, so a write that discards its result passes `returning: "minimal"` and
|
|
95
|
+
a batch of creates uses `createDocuments()`, in the owner's transaction
|
|
96
|
+
([Bounded access
|
|
97
|
+
checks](/guide/framework/typed-features#bounded-access-checks)).
|
|
98
|
+
|
|
99
|
+
### Tests
|
|
100
|
+
|
|
101
|
+
- `jskit test` runs test files in parallel within the machine's test budget
|
|
102
|
+
(`V64_TEST_CONCURRENCY`, else half the CPUs), and Playwright's `workers` and
|
|
103
|
+
Vitest's `maxWorkers` come from `testConcurrency()`. `jskit check` reports a
|
|
104
|
+
script or config that runs files one at a time; a file that must run alone
|
|
105
|
+
says why on its first line, `// jskit-test: alone - <reason>` ([Test files
|
|
106
|
+
run in parallel](/guide/framework/typed-features#test-files-run-in-parallel)).
|
|
107
|
+
- Tests use `TEST_DB_NAME` and `BROWSER_TEST_DB_NAME`, both different from
|
|
108
|
+
`DB_NAME`, because tests write and delete rows and would destroy the
|
|
109
|
+
developer's data. A skip for want of a test database is a failure.
|
|
110
|
+
- Test databases restore a stored snapshot checked by a digest of the
|
|
111
|
+
migration and seed sources, and the harness reports each preparation phase's
|
|
112
|
+
duration, so slow preparation shows ([Test databases are prepared
|
|
113
|
+
once](/guide/framework/typed-features#test-databases-are-prepared-once),
|
|
114
|
+
[integration test
|
|
115
|
+
databases](/guide/app-setup/database-layer#integration-test-databases)).
|
|
116
|
+
|
|
117
|
+
### Inputs and access
|
|
118
|
+
|
|
119
|
+
- An action input declared `mode: "patch"` makes every field optional. Use it
|
|
120
|
+
only for an action that updates part of a record, and send that action
|
|
121
|
+
nothing to see what it does, because a missing field is then never refused.
|
|
122
|
+
- A route that repetition can abuse declares `rateLimit`, which the kernel
|
|
123
|
+
refuses with 429 and `kernel.http.rateLimited`; auth's limits come from
|
|
124
|
+
`config.auth.rateLimits` ([Sign-in
|
|
125
|
+
protection](/guide/app-setup/authentication#sign-in-protection)). Each
|
|
126
|
+
limit needs a test expecting the 429, or a dropped one goes unseen.
|
|
127
|
+
- Who acts comes from `resolveActor(context)` and the workspace from the
|
|
128
|
+
context's resolvers ([One reader per
|
|
129
|
+
fact](/guide/framework/typed-features#one-reader-per-fact)); send another
|
|
130
|
+
workspace's id in the body and see the context win.
|
|
131
|
+
|
|
132
|
+
### Refactors
|
|
133
|
+
|
|
134
|
+
Before changing a JSKIT feature, list where its behaviour lives, with the test
|
|
135
|
+
that covers each item, and walk the list again after: routes, actions and
|
|
136
|
+
their input schemas, row policies, CRUD fields, lookups and hooks, placements
|
|
137
|
+
and navigation meta, messages and their codes, events, jobs and migrations.
|
|
138
|
+
These are the places a consolidation drops behaviour without a failing build.
|
|
139
|
+
|
|
140
|
+
### Changes others depend on
|
|
141
|
+
|
|
142
|
+
- After updating JSKIT packages, read [Upgrade
|
|
143
|
+
JSKIT](/guide/app-setup/upgrading-jskit) and each package changelog's
|
|
144
|
+
"Upgrading" notes, then boot, sign in and run the browser tests before
|
|
145
|
+
anything else, because a changed default shows only at run time.
|
|
146
|
+
|
|
147
|
+
### Screens
|
|
148
|
+
|
|
149
|
+
- A long list renders what is visible, with pages or `v-virtual-scroll`,
|
|
150
|
+
because every row rendered costs DOM nodes and main-thread time.
|
|
151
|
+
- Command feedback goes through the shell's snackbar path: `ShellErrorHost`
|
|
152
|
+
keeps it clear of the FAB and the actions it reports on, and a full-screen
|
|
153
|
+
dialog puts its confirm action in the top bar, where a snackbar never covers
|
|
154
|
+
it ([Material 3](/guide/framework/material-3)).
|
|
155
|
+
- Up is the shell's, from `meta.jskit.parent`; the navigation rules forbid
|
|
156
|
+
`router.back()` and hand-made back controls, so Up lands inside the app
|
|
157
|
+
even on a page opened from a link.
|
|
158
|
+
|
|
159
|
+
### Production settings
|
|
160
|
+
|
|
161
|
+
- `NODE_ENV=production` is JSKIT's only production switch. Without it a
|
|
162
|
+
deployed server runs as development: no first-account setup code, and
|
|
163
|
+
development-only settings accepted.
|
|
164
|
+
- On an empty production database, local auth asks for the first account's
|
|
165
|
+
setup code, so whoever reaches a new deployment first cannot own it ([The
|
|
166
|
+
first account in
|
|
167
|
+
production](/guide/app-setup/authentication#the-first-account-in-production-local-auth)).
|
|
168
|
+
Give any registration of your own the same protection.
|
|
169
|
+
- `config.securityHeaders` sends the security headers, with the content
|
|
170
|
+
security policy reported, not enforced, by default; read them on a
|
|
171
|
+
production build ([Security
|
|
172
|
+
headers](/guide/app-setup/initial-scaffolding#security-headers)).
|
|
173
|
+
- Email confirmation needs `auth.local.confirmation-sender` in production, or
|
|
174
|
+
the server refuses to start while anyone may register ([Email
|
|
175
|
+
confirmation](/guide/app-setup/authentication#email-confirmation-local-auth)).
|
|
176
|
+
- Changing the assistant's instructions needs `assistant.settings.update` or
|
|
177
|
+
the surface's own permission ([Who may change the
|
|
178
|
+
instructions](/guide/app-extras/assistant#who-may-change-the-instructions)).
|
|
179
|
+
Its tools run as the typing user, through the `automation` channel, so an
|
|
180
|
+
action kept off that channel is never a tool.
|
|
@@ -193,6 +193,10 @@ and params to `details.fieldErrorCodes`, so an untranslated app shows exactly
|
|
|
193
193
|
the English it showed before. The failed check's own params (for example
|
|
194
194
|
`limit`) are sent too, and the coded entry's `params` win over them. JSKIT's
|
|
195
195
|
own schemas use this form, which is why their field messages translate.
|
|
196
|
+
With json-rest-schema 1.0.18 or later, which checks them, an array field's
|
|
197
|
+
`minItems` and `maxItems` failures send `kernel.validation.field.minItems` and
|
|
198
|
+
`kernel.validation.field.maxItems` with `{ min | max, actual }`, and its
|
|
199
|
+
`messages.minItems` and `messages.maxItems` override them.
|
|
196
200
|
|
|
197
201
|
## Placement labels
|
|
198
202
|
|