@jskit-ai/agent-docs 0.1.273 → 0.1.275

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/guide/agent/app-extras/assistant-conversation.md +7 -1
  2. package/guide/agent/app-extras/assistant-voice.md +5 -1
  3. package/guide/agent/app-setup/material-3-migration.md +180 -0
  4. package/guide/agent/app-setup/upgrading-jskit.md +5 -0
  5. package/guide/agent/framework/material-3.md +316 -73
  6. package/guide/agent/framework/ui-operations.md +18 -35
  7. package/guide/agent/framework/ui-pass.md +155 -0
  8. package/guide/agent/index.md +3 -0
  9. package/package.json +1 -1
  10. package/patterns/feature-package/example/booking-engine/package.json +1 -1
  11. package/patterns/minimal-foundation/example/package.json +5 -4
  12. package/patterns/minimal-foundation/example/src/main.js +5 -11
  13. package/patterns/minimal-foundation/example/src/pages/home/index.vue +5 -18
  14. package/patterns/minimal-foundation/example/src/theme/index.js +9 -0
  15. package/patterns/minimal-foundation/example/src/views/NotFound.vue +5 -4
  16. package/patterns/shell-foundation/example/package.json +6 -6
  17. package/patterns/shell-foundation/example/packages/main/package.json +1 -1
  18. package/patterns/shell-foundation/example/src/main.js +5 -11
  19. package/patterns/shell-foundation/example/src/pages/home/index.vue +21 -57
  20. package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +24 -19
  21. package/patterns/shell-foundation/example/src/pages/home/settings.vue +27 -84
  22. package/patterns/shell-foundation/example/src/theme/index.js +9 -0
  23. package/patterns/shell-foundation/example/src/views/NotFound.vue +9 -7
  24. package/reference/autogen/PATTERN_INDEX.md +37 -37
  25. package/reference/autogen/README.md +1 -0
  26. package/reference/autogen/packages/agent-docs.md +8 -4
  27. package/reference/autogen/packages/assistant-core.md +3 -0
  28. package/reference/autogen/packages/console-web.md +5 -0
  29. package/reference/autogen/packages/http-web.md +29 -2
  30. package/reference/autogen/packages/kernel.md +2 -1
  31. package/reference/autogen/packages/material-web.md +54 -0
  32. package/reference/autogen/packages/shell-web.md +121 -9
  33. package/reference/autogen/tooling/jskit-catalog.md +81 -0
  34. package/reference/autogen/tooling/testUtils.md +7 -0
  35. package/skills/jskit/SKILL.md +11 -6
  36. package/skills/jskit/references/heuristics/INDEX.md +1 -1
  37. package/skills/jskit/references/heuristics/ui-contract.md +10 -7
  38. package/skills/jskit/references/heuristics/ui-testing.md +5 -3
  39. package/skills/jskit/references/material-3.md +315 -72
  40. package/skills/jskit/references/pattern-index.md +37 -37
  41. package/skills/jskit/references/patterns/app/minimal-foundation/example/package.json +5 -4
  42. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/main.js +5 -11
  43. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/pages/home/index.vue +5 -18
  44. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/theme/index.js +9 -0
  45. package/skills/jskit/references/patterns/app/minimal-foundation/example/src/views/NotFound.vue +5 -4
  46. package/skills/jskit/references/patterns/app/shell-foundation/example/package.json +6 -6
  47. package/skills/jskit/references/patterns/app/shell-foundation/example/packages/main/package.json +1 -1
  48. package/skills/jskit/references/patterns/app/shell-foundation/example/src/main.js +5 -11
  49. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/index.vue +21 -57
  50. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/general/index.vue +24 -19
  51. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings.vue +27 -84
  52. package/skills/jskit/references/patterns/app/shell-foundation/example/src/theme/index.js +9 -0
  53. package/skills/jskit/references/patterns/app/shell-foundation/example/src/views/NotFound.vue +9 -7
  54. package/skills/jskit/references/patterns/assistant/assistant-surface/example/src/placement.js +1 -1
  55. package/skills/jskit/references/patterns/auth/accounts-hub/example/package.json +2 -2
  56. package/skills/jskit/references/patterns/auth/oidc-sign-in/example/package.json +2 -2
  57. package/skills/jskit/references/patterns/auth/supabase-auth/example/package.json +1 -1
  58. package/skills/jskit/references/patterns/connectors/calendar-cli/example/package.json +4 -4
  59. package/skills/jskit/references/patterns/console/console-surface/PATTERN.md +4 -2
  60. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/index.vue +10 -40
  61. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings/index.vue +3 -11
  62. package/skills/jskit/references/patterns/console/console-surface/example/src/pages/console/settings.vue +24 -72
  63. package/skills/jskit/references/patterns/console/console-surface/example/src/placement.js +28 -0
  64. package/skills/jskit/references/patterns/crud/crud-screen-set/PATTERN.md +13 -0
  65. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookEditPage.vue +2 -0
  66. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookFormFields.vue +0 -6
  67. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookListPage.vue +5 -2
  68. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookNewPage.vue +2 -0
  69. package/skills/jskit/references/patterns/crud/crud-screen-set/example/books/BookViewPage.vue +5 -9
  70. package/skills/jskit/references/patterns/crud/json-api-resource-package/example/packages/books/package.json +2 -2
  71. package/skills/jskit/references/patterns/database/mysql-application/example/package.json +1 -1
  72. package/skills/jskit/references/patterns/database/postgres-application/example/package.json +1 -1
  73. package/skills/jskit/references/patterns/realtime/realtime-application/example/package.json +2 -2
  74. package/skills/jskit/references/patterns/server/feature-package/example/booking-engine/package.json +1 -1
  75. package/skills/jskit/references/patterns/shell/application-shell/PATTERN.md +3 -1
  76. package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/pages/home/index.vue +5 -18
  77. package/skills/jskit/references/patterns/shell/application-shell/example/expected-existing/src/views/NotFound.vue +14 -0
  78. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/index.vue +21 -57
  79. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/general/index.vue +24 -19
  80. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings.vue +27 -84
  81. package/skills/jskit/references/patterns/shell/application-shell/example/src/views/NotFound.vue +15 -0
  82. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/components/SyncStatusElement.vue +2 -2
  83. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/activity.vue +1 -1
  84. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports/overview.vue +1 -1
  85. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue +6 -3
  86. package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placement.js +1 -1
  87. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue +2 -8
  88. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue +2 -8
  89. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue +5 -11
  90. package/skills/jskit/references/patterns/users/account-settings/example/src/components/account/settings/AccountSettingsSignInMethodsSection.vue +3 -9
  91. package/skills/jskit/references/patterns/users/account-settings/example/src/pages/account/index.vue +7 -0
  92. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users/package.json +2 -2
  93. package/skills/jskit/references/patterns/users/user-administration-server/example/packages/users-workspace/package.json +3 -3
  94. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/packages/main/src/client/components/AccountPendingInvitesCue.vue +1 -2
  95. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/components/WorkspaceNotFoundCard.vue +5 -6
  96. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings/index.vue +5 -12
  97. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/pages/admin/workspace/settings.vue +24 -72
  98. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/admin/index.vue +3 -4
  99. package/skills/jskit/references/patterns/workspaces/workspace-surfaces/example/src/surfaces/app/index.vue +3 -3
  100. package/skills/jskit/references/ui-operations.md +18 -35
  101. package/skills/jskit/references/ui-pass.md +150 -0
  102. package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +0 -7
  103. package/skills/jskit/references/patterns/app/shell-foundation/example/src/pages/home/settings/index.vue +0 -7
  104. package/skills/jskit/references/patterns/shell/application-shell/example/src/pages/home/settings/index.vue +0 -7
@@ -0,0 +1,155 @@
1
+ <!-- Generated by `npm run agent-docs:build` from `packages/agent-docs/site/guide/framework/ui-pass.md`. Do not edit manually. -->
2
+
3
+ ---
4
+ title: UI pass
5
+ description: Make a task's changes, a section of the app, or the whole app exact Material 3 without changing behavior.
6
+ ---
7
+
8
+ # UI pass
9
+
10
+ A UI pass makes JSKIT UI exact Material 3 at one of three scopes: **the
11
+ changes** a task made (the default), **a section** of the app, or **the whole
12
+ app**. Read the complete [Material 3 rules](./material-3.md) first; navigation
13
+ and Up come first there.
14
+
15
+ ## Every scope
16
+
17
+ - **The same rules:** the [Material 3 rules](./material-3.md), their exact sizes,
18
+ `jskit check`, `jskit check m3`, and screenshots at 390, 768 and 1280 wide, in
19
+ light and dark.
20
+ - **Navigation is must-fix at every scope:** fix every `nav/*` error inside the
21
+ scope (declare parents, resolve links through JSKIT, delete back controls and
22
+ back logic); never only report one. Plain `jskit check` must not end with a
23
+ new error. Errors outside the scope, in an app not yet converted, are
24
+ reported as must-fix findings.
25
+ - **Behavior otherwise stays exactly as it is:** no feature, field, action,
26
+ permission, validation or data change. A defect, missing behavior or test gap
27
+ you notice is a finding for the report, not a fix.
28
+ - **Exceptions:** take the [approved routes](./material-3.md#exceptions) first.
29
+ Add an `m3-check: allow` exception only after the person agreed to it, and
30
+ list every exception the changes add, with its file, line and reason, in the
31
+ report.
32
+ - **JSKIT first:** if JSKIT itself blocks an exact result, report the exact JSKIT
33
+ change (package, file, what is missing). Never compensate with app CSS,
34
+ `!important` or a copied package file.
35
+ - **Screenshots** come from the managed browser or a temporary Playwright spec.
36
+ Keep them out of the repository (`test-results/` is ignored) and delete the
37
+ temporary spec afterwards. With `defaultTheme: "system"`, `emulateMedia`
38
+ selects the scheme:
39
+
40
+ ```js
41
+ import { test } from "@playwright/test";
42
+
43
+ for (const scheme of ["light", "dark"]) {
44
+ for (const width of [390, 768, 1280]) {
45
+ test(`suppliers ${width} ${scheme}`, async ({ page }) => {
46
+ await page.emulateMedia({ colorScheme: scheme });
47
+ await page.setViewportSize({ width, height: 900 });
48
+ await page.goto("/home/suppliers");
49
+ await page.screenshot({ path: `test-results/ui-pass/after/suppliers-${width}-${scheme}.png`, fullPage: true });
50
+ });
51
+ }
52
+ }
53
+ ```
54
+
55
+ - **Look at every screenshot** before reporting. They are the only proof of
56
+ what the checks cannot see: layout, spacing, contrast, overflow, clipped
57
+ labels, borders where tones belong, colour that only works in one scheme, an
58
+ Up arrow where it should not be or missing where it should.
59
+ - **Prove Up** on the screens in scope, with `shellBackButton(page)` and
60
+ `pressShellBack(page)` from `@jskit-ai/shell-web/test/shellNavigation`:
61
+ contact → supplier → Up shows the contact; a supplier opened from a pasted
62
+ link → Up shows the Suppliers list; no arrow on main-menu pages; no "Back to
63
+ X" anywhere.
64
+
65
+ ## 1. The changes
66
+
67
+ The default, and what an automatic UI pass after a plan runs.
68
+
69
+ - **In scope:** every screen, component and style the task created or edited,
70
+ plus the screens that render them. Explicit commits replace "the task's own
71
+ changes" when they are named.
72
+ - **Method:**
73
+ 1. List the changed UI files and the screens that render them.
74
+ 2. Check each against the Material 3 rules and fix what is off, inside the
75
+ changes only.
76
+ 3. Run `jskit check m3` on the changed UI files until it reports nothing,
77
+ and plain `jskit check` until it passes.
78
+ 4. Take after screenshots of each affected screen, and look at them.
79
+ 5. Run the focused tests of the affected screens.
80
+ - **Report:** what was fixed; anything outside the changes that looks wrong, as
81
+ findings only (not fixed; navigation ones marked must-fix); the check
82
+ results; the screenshots; what is unverified.
83
+
84
+ Example: a task added a Delete button and a "Back to suppliers" link to the
85
+ supplier page. The pass replaces the button's `color="#d32f2f"` and
86
+ `variant="outlined"` with the error colour role, removes the link, declares the
87
+ page's parent (`/suppliers`), checks `src/pages/home/suppliers/[supplierId].vue`
88
+ clean, and shows the supplier at the six sizes. It reports, unfixed, that the
89
+ Contacts list scrolls sideways at 390.
90
+
91
+ ## 2. A section
92
+
93
+ On request ("UI pass the Suppliers section").
94
+
95
+ - **In scope:** a named area: a module, a route subtree, a set of screens or a
96
+ shared component family. Every screen in it, touched or not, so the section is
97
+ consistent.
98
+ - **Method:**
99
+ 1. Inventory the section's screens and the components they render.
100
+ 2. Take before screenshots of each screen, with its empty, error and dialog
101
+ states where they exist.
102
+ 3. Map each screen pattern to its Material 3 canonical layout (list-detail,
103
+ supporting pane, feed) and components, in a table.
104
+ 4. Convert, navigation first: declare each inner page's parent, route every
105
+ inner link through JSKIT navigation, delete back handlers and "Back to X"
106
+ controls.
107
+ 5. Take after screenshots beside the before ones.
108
+ 6. Run `jskit check m3` on the section until it reports nothing.
109
+ 7. Delete the app CSS that is no longer needed.
110
+ 8. Change browser tests only where markup changed; every behavior assertion
111
+ stays.
112
+ - **Report:** a before/after gallery, the mapping, the non-blank CSS lines
113
+ (`<style>` blocks and CSS files) before and after, the check results, the
114
+ Up proof and the test results.
115
+
116
+ Example: the Suppliers section has a list, a record, a new and an edit page.
117
+ The mapping reads: list as a list with filter chips and a FAB, list-detail on
118
+ expanded widths; record as a header with status chips and value-first lists;
119
+ new and edit as a form with Cancel and Save in the bar. The pass removes a
120
+ bordered filter panel and 46 lines of CSS, declares the three inner pages'
121
+ parents, and the check reports nothing for `src/pages/home/suppliers`.
122
+
123
+ ## 3. The whole app
124
+
125
+ On request ("UI pass the whole app"). Every screen, the shell and navigation
126
+ included.
127
+
128
+ 1. **Inventory and before screenshots** of every screen type: sign-in, home,
129
+ dashboard, every list, one record per module, every form type, settings, a
130
+ dialog, an empty state and an error state.
131
+ 2. **A short spec:** the foundation decisions (seed colour, scheme generation,
132
+ type, shape, icons, Vuetify defaults) and a table mapping every screen
133
+ pattern to its Material 3 layout and components.
134
+ 3. **Foundation first:** the theme from one seed, type, shape, icons and Vuetify
135
+ defaults, then the app shell and navigation (every inner page's parent, so
136
+ plain `jskit check` passes).
137
+ 4. **Screens in packages,** at most three at a time, never two on the same file.
138
+ 5. **After screenshots** for each package.
139
+ 6. **Delete as you go:** app CSS shrinks to almost nothing, with no hex colour
140
+ outside `src/theme/`; `jskit check m3` reports nothing for the whole app.
141
+ 7. **Tests:** browser tests change only where markup changed, every behavior
142
+ assertion stays the same, and nothing regresses.
143
+ 8. **An independent review:** a fresh agent, given only the screenshots and the
144
+ spec, lists every departure from Material 3 as MUST or SHOULD. Fix every MUST
145
+ and review again until there are none.
146
+
147
+ - **Report:** the gallery, the reviewer's final pass with no MUST items, the
148
+ counts (screens, CSS lines before and after, check findings before and
149
+ after), the Up proof and the test results.
150
+
151
+ Example: a twelve-screen app. The spec picks the existing brand orange as seed
152
+ and maps four lists, four records, three forms and settings. The foundation
153
+ package replaces a hand-picked palette and MDI icons; three screen packages
154
+ follow. App CSS goes from 640 lines to 38, the check from 212 findings to none,
155
+ and the second review has no MUST items.
@@ -11,6 +11,7 @@ apply or adapt. It does not prescribe an agent host or project orchestrator.
11
11
  - [Quickstart](/guide/app-setup/quickstart)
12
12
  - [Application foundations](/guide/app-setup/initial-scaffolding)
13
13
  - [Upgrade JSKIT](/guide/app-setup/upgrading-jskit)
14
+ - [Move an app to the Material 3 defaults](/guide/app-setup/material-3-migration)
14
15
  - [Migrate an existing application](/guide/app-setup/existing-application-migration)
15
16
  - [A more interesting shell](/guide/app-setup/a-more-interesting-shell)
16
17
  - [Authentication](/guide/app-setup/authentication)
@@ -32,6 +33,8 @@ apply or adapt. It does not prescribe an agent host or project orchestrator.
32
33
  composable, component, client factory or client provider
33
34
  - [UI operations](/guide/framework/ui-operations)
34
35
  - [Material 3](/guide/framework/material-3)
36
+ - [UI pass](/guide/framework/ui-pass): make a task's changes, a section or the
37
+ whole app exact Material 3
35
38
  - [Translating a JSKIT app](/guide/framework/translation)
36
39
  - [Complete source pattern library](/patterns/)
37
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.273",
3
+ "version": "0.1.275",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -5,7 +5,7 @@
5
5
  "private": true,
6
6
  "type": "module",
7
7
  "dependencies": {
8
- "@jskit-ai/kernel": "0.2.9",
8
+ "@jskit-ai/kernel": "0.2.10",
9
9
  "json-rest-schema": "^1.0.17"
10
10
  },
11
11
  "exports": {
@@ -34,7 +34,8 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.9",
37
+ "@jskit-ai/kernel": "0.2.10",
38
+ "@jskit-ai/material-web": "0.1.0",
38
39
  "@tanstack/vue-query": "^5.101.0",
39
40
  "fastify": "^5.12.7",
40
41
  "pino": "^10.3.1",
@@ -42,14 +43,14 @@
42
43
  "pinia": "^4.0.3",
43
44
  "vue": "^3.5.44",
44
45
  "vue-router": "^5.3.1",
45
- "vuetify": "^4.1.2",
46
- "@jskit-ai/http-runtime": "0.2.9",
46
+ "vuetify": "^4.3.0",
47
+ "@jskit-ai/http-runtime": "0.2.10",
47
48
  "@fastify/fast-json-stringify-compiler": "^5.1.0",
48
49
  "@vue/devtools-api": "^8.2.1"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@jskit-ai/config-eslint": "0.1.245",
52
- "@jskit-ai/jskit-catalog": "0.2.25",
53
+ "@jskit-ai/jskit-catalog": "0.2.27",
53
54
  "@playwright/test": "1.61.1",
54
55
  "@vitejs/plugin-vue": "^6.0.7",
55
56
  "eslint": "^10.8.0",
@@ -4,9 +4,11 @@ import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
4
4
  import { createRouter, createWebHistory } from "vue-router/auto";
5
5
  import { routes } from "vue-router/auto-routes";
6
6
  import "vuetify/styles";
7
+ import "@jskit-ai/material-web/client/material.css";
7
8
  import { createVuetify } from "vuetify";
8
- import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
9
+ import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
9
10
  import App from "./App.vue";
11
+ import { theme } from "./theme/index.js";
10
12
  import NotFoundView from "./views/NotFound.vue";
11
13
  import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
12
14
  import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
@@ -39,16 +41,8 @@ const { router, fallbackRoute } = createShellRouter({
39
41
  }
40
42
  });
41
43
 
42
- const vuetify = createVuetify({
43
- theme: {
44
- defaultTheme: "light"
45
- },
46
- icons: {
47
- defaultSet: "mdi",
48
- aliases: mdiAliases,
49
- sets: { mdi }
50
- }
51
- });
44
+ // Material 3 from the app's theme (src/theme/index.js), with Material Symbols behind every icon alias.
45
+ const vuetify = createVuetify(createMaterialVuetifyOptions(theme));
52
46
  const pinia = createPinia();
53
47
  const queryClient = new QueryClient({
54
48
  defaultOptions: {
@@ -1,21 +1,8 @@
1
1
  <template>
2
- <section class="home-start-screen d-flex flex-column ga-4">
3
- <v-sheet rounded="lg" border class="home-start-screen__panel">
4
- <p class="text-body-2 text-medium-emphasis mb-0">
5
- Recent work, saved records, and next actions will appear here once this app has data.
6
- </p>
7
- </v-sheet>
2
+ <section class="d-flex flex-column align-center ga-4 py-16 text-center on-surface-variant">
3
+ <v-icon icon="$inbox" size="48" />
4
+ <p class="text-body-large mb-0">
5
+ Recent work, saved records, and next actions will appear here once this app has data.
6
+ </p>
8
7
  </section>
9
8
  </template>
10
-
11
- <style scoped>
12
- .home-start-screen {
13
- --home-start-panel-padding: 1rem;
14
- margin-inline: auto;
15
- max-width: 48rem;
16
- }
17
-
18
- .home-start-screen__panel {
19
- padding: var(--home-start-panel-padding);
20
- }
21
- </style>
@@ -0,0 +1,9 @@
1
+ // The app's only theme facts. Material 3's light and dark colour schemes are generated from this one seed colour and
2
+ // follow the device's setting: change the seed to rebrand. A colour the app really owns goes in `colors` by camelCase
3
+ // name (it gets its own container roles, harmonized to the seed), and an icon of its own goes in `icons` as
4
+ // `{ name: "<24x24 SVG path>" }`, used as `$name`.
5
+ export const theme = {
6
+ seed: "#6750A4",
7
+ colors: {},
8
+ icons: {}
9
+ };
@@ -5,9 +5,10 @@ const message = "The page you requested does not exist.";
5
5
 
6
6
  <template>
7
7
  <v-container class="fill-height d-flex align-center justify-center">
8
- <v-card class="pa-6 text-center" max-width="560" rounded="lg">
9
- <v-card-title class="text-h4">{{ title }}</v-card-title>
10
- <v-card-text class="text-medium-emphasis">{{ message }}</v-card-text>
11
- </v-card>
8
+ <section class="on-surface-variant text-center">
9
+ <v-icon icon="$searchOff" size="48" />
10
+ <h1 class="text-headline-small on-surface mt-4 mb-2">{{ title }}</h1>
11
+ <p class="text-body-large mb-0">{{ message }}</p>
12
+ </section>
12
13
  </v-container>
13
14
  </template>
@@ -34,7 +34,8 @@
34
34
  "dependencies": {
35
35
  "@local/main": "0.1.0",
36
36
  "@fastify/static": "^10.1.3",
37
- "@jskit-ai/kernel": "0.2.9",
37
+ "@jskit-ai/kernel": "0.2.10",
38
+ "@jskit-ai/material-web": "0.1.0",
38
39
  "@tanstack/vue-query": "^5.101.0",
39
40
  "fastify": "^5.12.7",
40
41
  "pino": "^10.3.1",
@@ -42,16 +43,15 @@
42
43
  "pinia": "^4.0.3",
43
44
  "vue": "^3.5.44",
44
45
  "vue-router": "^5.3.1",
45
- "vuetify": "^4.1.2",
46
- "@jskit-ai/http-runtime": "0.2.9",
47
- "@mdi/js": "^7.4.47",
48
- "@jskit-ai/shell-web": "0.2.10",
46
+ "vuetify": "^4.3.0",
47
+ "@jskit-ai/http-runtime": "0.2.10",
48
+ "@jskit-ai/shell-web": "0.2.11",
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.25",
54
+ "@jskit-ai/jskit-catalog": "0.2.27",
55
55
  "@playwright/test": "1.61.1",
56
56
  "@vitejs/plugin-vue": "^6.0.7",
57
57
  "eslint": "^10.8.0",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "description": "App-local runtime composition and lightweight glue.",
11
11
  "dependencies": {
12
- "@jskit-ai/kernel": "0.2.9"
12
+ "@jskit-ai/kernel": "0.2.10"
13
13
  },
14
14
  "jskit": {
15
15
  "kind": "runtime",
@@ -4,9 +4,11 @@ import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
4
4
  import { createRouter, createWebHistory } from "vue-router/auto";
5
5
  import { routes } from "vue-router/auto-routes";
6
6
  import "vuetify/styles";
7
+ import "@jskit-ai/material-web/client/material.css";
7
8
  import { createVuetify } from "vuetify";
8
- import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
9
+ import { createMaterialVuetifyOptions } from "@jskit-ai/material-web/client";
9
10
  import App from "./App.vue";
11
+ import { theme } from "./theme/index.js";
10
12
  import NotFoundView from "./views/NotFound.vue";
11
13
  import { bootInstalledClientModules } from "virtual:jskit-client-bootstrap";
12
14
  import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
@@ -39,16 +41,8 @@ const { router, fallbackRoute } = createShellRouter({
39
41
  }
40
42
  });
41
43
 
42
- const vuetify = createVuetify({
43
- theme: {
44
- defaultTheme: "light"
45
- },
46
- icons: {
47
- defaultSet: "mdi",
48
- aliases: mdiAliases,
49
- sets: { mdi }
50
- }
51
- });
44
+ // Material 3 from the app's theme (src/theme/index.js), with Material Symbols behind every icon alias.
45
+ const vuetify = createVuetify(createMaterialVuetifyOptions(theme));
52
46
  const pinia = createPinia();
53
47
  const queryClient = new QueryClient({
54
48
  defaultOptions: {
@@ -1,7 +1,9 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
3
  import { useQuery } from "@tanstack/vue-query";
4
+ import { usePaths } from "@jskit-ai/shell-web/client/navigation/usePaths";
4
5
 
6
+ const paths = usePaths();
5
7
  const healthQuery = useQuery({
6
8
  queryKey: ["shell-web", "health"],
7
9
  queryFn: async () => {
@@ -28,63 +30,25 @@ const healthPending = computed(
28
30
  </script>
29
31
 
30
32
  <template>
31
- <section class="home-surface-screen d-flex flex-column ga-4">
32
- <div class="home-surface-screen__actions">
33
- <v-btn color="primary" variant="flat" to="/home/settings/general">Settings</v-btn>
33
+ <section class="d-flex flex-column ga-4">
34
+ <v-card>
35
+ <v-list>
36
+ <v-list-item subtitle="Service health">
37
+ <template #title>
38
+ <v-skeleton-loader
39
+ v-if="healthPending"
40
+ aria-label="Loading service health"
41
+ type="text"
42
+ max-width="80"
43
+ />
44
+ <span v-else>{{ health }}</span>
45
+ </template>
46
+ </v-list-item>
47
+ <v-list-item title="/home" subtitle="Route" />
48
+ </v-list>
49
+ </v-card>
50
+ <div class="d-flex justify-end">
51
+ <v-btn color="secondary-container" prepend-icon="$settings" :to="paths.page('/settings')">Settings</v-btn>
34
52
  </div>
35
-
36
- <v-sheet rounded="lg" border class="home-surface-screen__panel">
37
- <div class="home-surface-screen__status">
38
- <span class="text-caption text-medium-emphasis">Service health</span>
39
- <v-skeleton-loader
40
- v-if="healthPending"
41
- aria-label="Loading service health"
42
- class="home-surface-screen__health-skeleton"
43
- type="text"
44
- />
45
- <strong v-else>{{ health }}</strong>
46
- </div>
47
- <v-divider vertical class="d-none d-sm-block" />
48
- <div class="home-surface-screen__status">
49
- <span class="text-caption text-medium-emphasis">Route</span>
50
- <strong>/home</strong>
51
- </div>
52
- </v-sheet>
53
53
  </section>
54
54
  </template>
55
-
56
- <style scoped>
57
- .home-surface-screen {
58
- --home-surface-panel-padding: 1rem;
59
- }
60
-
61
- .home-surface-screen__actions {
62
- display: flex;
63
- justify-content: flex-end;
64
- }
65
-
66
- .home-surface-screen__panel {
67
- align-items: stretch;
68
- display: flex;
69
- flex-wrap: wrap;
70
- gap: 1rem;
71
- padding: var(--home-surface-panel-padding);
72
- }
73
-
74
- .home-surface-screen__status {
75
- display: grid;
76
- gap: 0.15rem;
77
- min-width: 9rem;
78
- }
79
-
80
- .home-surface-screen__health-skeleton {
81
- max-width: 5rem;
82
- }
83
-
84
- @media (max-width: 640px) {
85
- .home-surface-screen__actions :deep(.v-btn) {
86
- min-height: 48px;
87
- width: 100%;
88
- }
89
- }
90
- </style>
@@ -2,6 +2,15 @@
2
2
  import { computed } from "vue";
3
3
  import { useShellLayoutState } from "@jskit-ai/shell-web/client/composables/useShellLayoutState";
4
4
 
5
+ definePage({
6
+ meta: {
7
+ jskit: {
8
+ title: "General",
9
+ parent: "/settings"
10
+ }
11
+ }
12
+ });
13
+
5
14
  const { drawerDefaultOpen, setDrawerDefaultOpen } = useShellLayoutState();
6
15
 
7
16
  const drawerDefaultOpenModel = computed({
@@ -15,23 +24,19 @@ const drawerDefaultOpenModel = computed({
15
24
  </script>
16
25
 
17
26
  <template>
18
- <section class="settings-general-screen d-flex flex-column ga-4">
19
- <p class="text-body-2 text-medium-emphasis mb-0">
20
- On wider screens, collapsed navigation remains available as a rail. Phone layouts close the drawer and keep primary navigation in the bottom bar.
21
- </p>
22
-
23
- <v-switch
24
- v-model="drawerDefaultOpenModel"
25
- color="primary"
26
- inset
27
- hide-details="auto"
28
- label="Start with expanded navigation on wider screens"
29
- />
30
- </section>
27
+ <v-card>
28
+ <v-list lines="three">
29
+ <v-list-item
30
+ title="Start with expanded navigation on wider screens"
31
+ subtitle="On wider screens, collapsed navigation remains available as a rail. Phone layouts close the drawer and keep primary navigation in the bottom bar."
32
+ >
33
+ <template #append>
34
+ <v-switch
35
+ v-model="drawerDefaultOpenModel"
36
+ aria-label="Start with expanded navigation on wider screens"
37
+ />
38
+ </template>
39
+ </v-list-item>
40
+ </v-list>
41
+ </v-card>
31
42
  </template>
32
-
33
- <style scoped>
34
- .settings-general-screen :deep(.v-switch) {
35
- min-height: 48px;
36
- }
37
- </style>
@@ -1,90 +1,33 @@
1
1
  <script setup>
2
+ import { computed } from "vue";
3
+ import { RouterView, useRoute } from "vue-router";
4
+ import ShellListDetail from "@jskit-ai/shell-web/client/components/ShellListDetail";
2
5
  import ShellOutlet from "@jskit-ai/shell-web/client/components/ShellOutlet";
3
- import { RouterView } from "vue-router";
4
- </script>
5
-
6
- <template>
7
- <section class="settings-shell d-flex flex-column ga-4">
8
- <v-sheet rounded="lg" border class="settings-shell__panel">
9
- <div class="settings-shell__body">
10
- <nav class="settings-shell__nav" aria-label="Home settings sections">
11
- <v-list nav density="compact" rounded="lg">
12
- <ShellOutlet target="home-settings:primary-menu" />
13
- </v-list>
14
- </nav>
15
-
16
- <main class="settings-shell__content">
17
- <RouterView />
18
- </main>
19
- </div>
20
- </v-sheet>
21
- </section>
22
- </template>
23
-
24
- <style scoped>
25
- .settings-shell {
26
- --settings-shell-panel-padding: 1rem;
27
- }
28
-
29
- .settings-shell__panel {
30
- overflow: hidden;
31
- }
32
-
33
- .settings-shell__body {
34
- display: grid;
35
- gap: 1rem;
36
- grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
37
- padding: var(--settings-shell-panel-padding);
38
- }
39
-
40
- .settings-shell__content {
41
- border-left: 1px solid rgba(var(--v-theme-on-surface), 0.18);
42
- min-width: 0;
43
- padding-left: 1rem;
44
- }
45
-
46
- .settings-shell__nav :deep(.v-list) {
47
- padding: 0.35rem;
48
- }
49
-
50
- .settings-shell__nav :deep(.v-list-item) {
51
- min-height: 48px;
52
- padding-inline: 0.6rem 0.7rem;
53
- }
54
6
 
55
- .settings-shell__nav :deep(.v-list-item__prepend) {
56
- margin-inline-end: 0;
57
- }
58
-
59
- .settings-shell__nav :deep(.v-list-item__spacer) {
60
- min-width: 0.5rem;
61
- width: 0.5rem;
62
- }
63
-
64
- .settings-shell__nav :deep(.v-list-item-title) {
65
- line-height: 1.2;
66
- }
67
-
68
- @media (max-width: 960px) {
69
- .settings-shell__body {
70
- grid-template-columns: 1fr;
71
- }
72
-
73
- .settings-shell__nav :deep(.v-list) {
74
- display: flex;
75
- gap: 0.25rem;
76
- overflow-x: auto;
77
- scrollbar-width: thin;
7
+ definePage({
8
+ meta: {
9
+ jskit: {
10
+ title: "Settings"
11
+ }
78
12
  }
13
+ });
79
14
 
80
- .settings-shell__nav :deep(.v-list-item) {
81
- flex: 0 0 auto;
82
- min-height: 48px;
83
- }
15
+ const route = useRoute();
16
+ // The section list shows beside the open section on wide windows, and on its own at /home/settings.
17
+ const detailOpen = computed(() => !/\/settings\/?$/u.test(route.path));
18
+ </script>
84
19
 
85
- .settings-shell__content {
86
- border-left: 0;
87
- padding-left: 0;
88
- }
89
- }
90
- </style>
20
+ <template>
21
+ <ShellListDetail :detail-open="detailOpen">
22
+ <template #list>
23
+ <nav aria-label="Home settings sections">
24
+ <v-list nav>
25
+ <ShellOutlet target="home-settings:primary-menu" />
26
+ </v-list>
27
+ </nav>
28
+ </template>
29
+ <template #detail>
30
+ <RouterView />
31
+ </template>
32
+ </ShellListDetail>
33
+ </template>
@@ -0,0 +1,9 @@
1
+ // The app's only theme facts. Material 3's light and dark colour schemes are generated from this one seed colour and
2
+ // follow the device's setting: change the seed to rebrand. A colour the app really owns goes in `colors` by camelCase
3
+ // name (it gets its own container roles, harmonized to the seed), and an icon of its own goes in `icons` as
4
+ // `{ name: "<24x24 SVG path>" }`, used as `$name`.
5
+ export const theme = {
6
+ seed: "#6750A4",
7
+ colors: {},
8
+ icons: {}
9
+ };