@jskit-ai/agent-docs 0.1.278 → 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 +46 -4
- package/guide/agent/app-setup/database-layer.md +35 -6
- package/guide/agent/app-setup/material-3-migration.md +53 -10
- 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 +153 -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-provider-local-core.md +1 -1
- 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 -6
- 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 +95 -4
- package/reference/autogen/packages/users-web.md +9 -2
- package/reference/autogen/packages/workspaces-core.md +2 -0
- package/reference/autogen/tooling/jskit-catalog.md +55 -6
- 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 +153 -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 +28 -2
- 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 +23 -0
- 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
|
@@ -4,6 +4,7 @@ import vue from "@vitejs/plugin-vue";
|
|
|
4
4
|
import vuetify from "vite-plugin-vuetify";
|
|
5
5
|
import VueRouter from "vue-router/vite";
|
|
6
6
|
import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
|
|
7
|
+
import { testConcurrency } from "@jskit-ai/jskit-catalog/test-budget";
|
|
7
8
|
|
|
8
9
|
const configuredDevPort = Number.parseInt(String(process.env.VITE_DEV_PORT || "").trim(), 10);
|
|
9
10
|
const devPort = Number.isInteger(configuredDevPort) && configuredDevPort > 0 ? configuredDevPort : 5173;
|
|
@@ -52,7 +53,8 @@ export default defineConfig({
|
|
|
52
53
|
}
|
|
53
54
|
],
|
|
54
55
|
test: {
|
|
55
|
-
include: ["tests/client/**/*.vitest.js"]
|
|
56
|
+
include: ["tests/client/**/*.vitest.js"],
|
|
57
|
+
maxWorkers: testConcurrency()
|
|
56
58
|
},
|
|
57
59
|
optimizeDeps: {
|
|
58
60
|
include: ["vuetify/components/**", "vuetify/directives/**"],
|
|
@@ -27,8 +27,14 @@ Those choices must be known before adapting the example.
|
|
|
27
27
|
## Framework APIs
|
|
28
28
|
|
|
29
29
|
Use the views, auth guard, HTTP client integration, sign-out runtime, providers,
|
|
30
|
-
and Playwright auth helper exported by `@jskit-ai/auth-web`.
|
|
31
|
-
|
|
30
|
+
and Playwright auth helper exported by `@jskit-ai/auth-web`. The sign-in, reset
|
|
31
|
+
and sign-out screens show the app's identity: the pages render the app's view
|
|
32
|
+
wrappers (`src/views/auth/`), and each wrapper fills the default view's `brand`
|
|
33
|
+
slot (given `{ compact }`) with `src/components/AppBrand.vue`, the app's name
|
|
34
|
+
as `index.html`'s `<title>` spells it. JSKIT lays the slot out as a pane beside
|
|
35
|
+
the card on wide windows and a row above it on phones; never restyle the views'
|
|
36
|
+
own classes. Use auth policies from `@jskit-ai/auth-core` for server
|
|
37
|
+
authorization.
|
|
32
38
|
|
|
33
39
|
Every application with accounts includes the managed-preview login. It is
|
|
34
40
|
standard, not optional: adding this pattern adds it, so a managed host's
|
|
@@ -92,6 +98,10 @@ a user as a side effect.
|
|
|
92
98
|
- Public auth routes do not require an existing session.
|
|
93
99
|
- Protected routes use the framework auth policy rather than page-local checks.
|
|
94
100
|
- Login, sign-out, and reset views use the public auth components/composables.
|
|
101
|
+
- The sign-in, reset and sign-out pages render the app's view wrappers, which
|
|
102
|
+
fill the `brand` slot with the app's identity (`AppBrand.vue`): a person
|
|
103
|
+
always sees which app they are signing in to. Renaming the app renames
|
|
104
|
+
`AppBrand.vue` with `index.html`'s `<title>`; the brand adds no heading.
|
|
95
105
|
- A custom login view must preserve the default view's initial session check:
|
|
96
106
|
already authenticated people return to the validated requested destination.
|
|
97
107
|
Process incoming login callbacks before that redirect, and keep signed-out
|
|
@@ -102,8 +112,9 @@ a user as a side effect.
|
|
|
102
112
|
|
|
103
113
|
## Example files
|
|
104
114
|
|
|
105
|
-
`example/` contains concrete route wrappers, editable view wrappers
|
|
106
|
-
|
|
115
|
+
`example/` contains concrete route wrappers, editable view wrappers that show
|
|
116
|
+
the app's brand (`src/components/AppBrand.vue`), and the client runtime helpers
|
|
117
|
+
needed by an authentication surface. It also contains
|
|
107
118
|
the standard app-owned managed-preview identity executable,
|
|
108
119
|
`tools/preview-identity`. Compose their
|
|
109
120
|
surface and placement declarations into the application's ordinary config and
|
|
@@ -116,7 +127,9 @@ placements, and post-auth destinations while retaining the runtime contracts.
|
|
|
116
127
|
|
|
117
128
|
## Verification
|
|
118
129
|
|
|
119
|
-
|
|
130
|
+
Check that the sign-in, reset and sign-out screens show the app's name, in the
|
|
131
|
+
pane on a wide window and in the row on a phone. Exercise failed and successful
|
|
132
|
+
login, sign-out, reset, protected navigation,
|
|
120
133
|
keyboard interaction, warm-cache return navigation, and browser refresh.
|
|
121
134
|
Open the login route with an existing authenticated session and confirm it
|
|
122
135
|
returns to the requested screen; repeat signed out and confirm the form remains.
|
package/skills/jskit/references/patterns/auth/auth-surface/example/src/components/AppBrand.vue
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
// The app's identity on its sign-in, reset and sign-out screens. The auth views place it in their brand slot: a pane
|
|
3
|
+
// beside the card on wide windows, and a row above the card on phones (`compact`). It is the app's name as
|
|
4
|
+
// index.html's <title> spells it, so renaming the app renames both. It holds no heading: each screen's h1 is its own.
|
|
5
|
+
defineProps({
|
|
6
|
+
compact: {
|
|
7
|
+
type: Boolean,
|
|
8
|
+
default: false
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const APP_NAME = "Reading Room";
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<div :class="compact ? 'text-title-medium' : 'text-title-large'" data-testid="app-brand">{{ APP_NAME }}</div>
|
|
17
|
+
</template>
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
</route>
|
|
10
10
|
|
|
11
11
|
<script setup>
|
|
12
|
-
import
|
|
12
|
+
import LoginView from "@/views/auth/LoginView.vue";
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
|
-
<
|
|
16
|
+
<LoginView />
|
|
17
17
|
</template>
|
package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/reset-password.vue
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
</route>
|
|
10
10
|
|
|
11
11
|
<script setup>
|
|
12
|
-
import
|
|
12
|
+
import ResetPasswordView from "@/views/auth/ResetPasswordView.vue";
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
|
-
<
|
|
16
|
+
<ResetPasswordView />
|
|
17
17
|
</template>
|
package/skills/jskit/references/patterns/auth/auth-surface/example/src/pages/auth/signout.vue
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
</route>
|
|
10
10
|
|
|
11
11
|
<script setup>
|
|
12
|
-
import
|
|
12
|
+
import SignOutView from "@/views/auth/SignOutView.vue";
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
15
|
<template>
|
|
16
|
-
<
|
|
16
|
+
<SignOutView />
|
|
17
17
|
</template>
|
package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/LoginView.vue
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import DefaultLoginView from "@jskit-ai/auth-web/client/views/DefaultLoginView";
|
|
3
|
+
import AppBrand from "@/components/AppBrand.vue";
|
|
3
4
|
</script>
|
|
4
5
|
|
|
5
6
|
<template>
|
|
6
|
-
<DefaultLoginView
|
|
7
|
+
<DefaultLoginView>
|
|
8
|
+
<template #brand="{ compact }">
|
|
9
|
+
<AppBrand :compact="compact" />
|
|
10
|
+
</template>
|
|
11
|
+
</DefaultLoginView>
|
|
7
12
|
</template>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import DefaultResetPasswordView from "@jskit-ai/auth-web/client/views/DefaultResetPasswordView";
|
|
3
|
+
import AppBrand from "@/components/AppBrand.vue";
|
|
3
4
|
</script>
|
|
4
5
|
|
|
5
6
|
<template>
|
|
6
|
-
<DefaultResetPasswordView
|
|
7
|
+
<DefaultResetPasswordView>
|
|
8
|
+
<template #brand="{ compact }">
|
|
9
|
+
<AppBrand :compact="compact" />
|
|
10
|
+
</template>
|
|
11
|
+
</DefaultResetPasswordView>
|
|
7
12
|
</template>
|
package/skills/jskit/references/patterns/auth/auth-surface/example/src/views/auth/SignOutView.vue
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import DefaultSignOutView from "@jskit-ai/auth-web/client/views/DefaultSignOutView";
|
|
3
|
+
import AppBrand from "@/components/AppBrand.vue";
|
|
3
4
|
</script>
|
|
4
5
|
|
|
5
6
|
<template>
|
|
6
|
-
<DefaultSignOutView
|
|
7
|
+
<DefaultSignOutView>
|
|
8
|
+
<template #brand="{ compact }">
|
|
9
|
+
<AppBrand :compact="compact" />
|
|
10
|
+
</template>
|
|
11
|
+
</DefaultSignOutView>
|
|
7
12
|
</template>
|
package/skills/jskit/references/patterns/connectors/aws-storage-queries/example/formats/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "s3-native-format-example",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"scripts": { "test": "node
|
|
5
|
+
"scripts": { "test": "node verify-formats.mjs" },
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"csv-parse": "7.0.2",
|
|
8
8
|
"csv-stringify": "6.8.3",
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
"db:migrate:status": "knex --knexfile ./knexfile.js migrate:list"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@jskit-ai/connectors-core": "0.2.
|
|
11
|
-
"@jskit-ai/connector-google-calendar": "0.2.
|
|
12
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
13
|
-
"@jskit-ai/database-runtime-mysql": "0.1.
|
|
10
|
+
"@jskit-ai/connectors-core": "0.2.12",
|
|
11
|
+
"@jskit-ai/connector-google-calendar": "0.2.12",
|
|
12
|
+
"@jskit-ai/database-runtime": "0.1.260",
|
|
13
|
+
"@jskit-ai/database-runtime-mysql": "0.1.258",
|
|
14
14
|
"knex": "^3.1.0"
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"./shared": "./src/shared/index.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@jskit-ai/crud-core": "0.2.
|
|
13
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
12
|
+
"@jskit-ai/crud-core": "0.2.12",
|
|
13
|
+
"@jskit-ai/resource-crud-core": "0.1.202"
|
|
14
14
|
},
|
|
15
15
|
"jskit": {
|
|
16
16
|
"kind": "runtime",
|
package/skills/jskit/references/patterns/rewards/google-rewarded/example/googlePublisherTag.js
CHANGED
|
@@ -72,8 +72,10 @@ function launchGoogleRewardedAd({
|
|
|
72
72
|
|
|
73
73
|
let readySeen = false;
|
|
74
74
|
let settled = false;
|
|
75
|
+
let availabilityTimer = null;
|
|
75
76
|
|
|
76
77
|
const cleanup = () => {
|
|
78
|
+
window.clearTimeout(availabilityTimer);
|
|
77
79
|
pubads.removeEventListener("rewardedSlotReady", handleReady);
|
|
78
80
|
pubads.removeEventListener("rewardedSlotGranted", handleGranted);
|
|
79
81
|
pubads.removeEventListener("rewardedSlotClosed", handleClosed);
|
|
@@ -120,8 +122,8 @@ function launchGoogleRewardedAd({
|
|
|
120
122
|
gptServicesEnabled = true;
|
|
121
123
|
}
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
window.setTimeout(() => {
|
|
125
|
+
// Started before display, so a slot that settles at once still clears it.
|
|
126
|
+
availabilityTimer = window.setTimeout(() => {
|
|
125
127
|
if (settled || readySeen) {
|
|
126
128
|
return;
|
|
127
129
|
}
|
|
@@ -131,6 +133,7 @@ function launchGoogleRewardedAd({
|
|
|
131
133
|
reject(new Error("No rewarded ad was available."));
|
|
132
134
|
});
|
|
133
135
|
}, 10000);
|
|
136
|
+
googletag.display(slot);
|
|
134
137
|
});
|
|
135
138
|
})
|
|
136
139
|
);
|
|
@@ -55,12 +55,17 @@ placement registry, topology, and adaptive smoke helper exported by
|
|
|
55
55
|
border widths to `0` and gives navigation a tonal surface, for example
|
|
56
56
|
`rgb(var(--v-theme-surface-container-low))` when the theme defines it.
|
|
57
57
|
- `createShellRouter()` from `@jskit-ai/kernel/client` installs
|
|
58
|
-
`shellScrollBehavior`: a new screen opens at the top (or its hash target
|
|
59
|
-
Back/Forward restore the saved
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
`shellScrollBehavior`: a new screen opens at the top (or its hash target,
|
|
59
|
+
below the target's `scroll-margin-top`), Back/Forward restore the saved
|
|
60
|
+
position, and query, hash, tab or sub-view changes within the same page keep
|
|
61
|
+
it. A tab or sub-view is the same page when both locations share a parent
|
|
62
|
+
page record below the surface root that draws a component, a layout hosting
|
|
63
|
+
the tabs; a folder's component-less record, as file routing creates for
|
|
64
|
+
`bookings/`, makes its pages different pages. Pass `scrollBehavior` to replace it
|
|
65
|
+
(calling `shellScrollBehavior` for what it leaves to the shell), or
|
|
66
|
+
`scrollBehavior: false` to disable it. A `createRouter` that replaces the
|
|
67
|
+
behaviour it is given throws, so never set one there; do not wrap the router
|
|
68
|
+
or reset scroll in page components.
|
|
64
69
|
- The adaptive browser check closes navigation through its public close control.
|
|
65
70
|
|
|
66
71
|
## Example files
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import ShellSectionTabLinkItem from "@jskit-ai/shell-web/client/components/ShellSectionTabLinkItem";
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
label: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: ""
|
|
8
|
+
},
|
|
9
|
+
to: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: ""
|
|
12
|
+
},
|
|
13
|
+
icon: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: ""
|
|
16
|
+
},
|
|
17
|
+
surface: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: ""
|
|
20
|
+
},
|
|
21
|
+
scopedSuffix: {
|
|
22
|
+
type: String,
|
|
23
|
+
default: "/"
|
|
24
|
+
},
|
|
25
|
+
unscopedSuffix: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: "/"
|
|
28
|
+
},
|
|
29
|
+
disabled: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
exact: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<ShellSectionTabLinkItem v-bind="props" />
|
|
42
|
+
</template>
|
package/skills/jskit/references/patterns/shell/application-shell/example/src/views/NotFound.vue
CHANGED
|
@@ -6,7 +6,7 @@ const message = "The page you requested does not exist.";
|
|
|
6
6
|
|
|
7
7
|
<template>
|
|
8
8
|
<!-- The shell's top app bar carries the title, and its navigation leads back into the app. -->
|
|
9
|
-
<ShellLayout title="Not Found"
|
|
9
|
+
<ShellLayout title="Not Found">
|
|
10
10
|
<section class="d-flex flex-column align-center text-center on-surface-variant py-16">
|
|
11
11
|
<v-icon icon="$searchOff" size="48" />
|
|
12
12
|
<p class="text-body-large mt-4 mb-0">{{ message }}</p>
|
|
@@ -35,6 +35,12 @@ a filename alone.
|
|
|
35
35
|
- Child links share an explicit section owner.
|
|
36
36
|
- Topology maps each semantic placement to concrete responsive outlets.
|
|
37
37
|
- Placed components are registered by token through a client provider.
|
|
38
|
+
- Section navigation is Material 3 primary tabs: the section's `page.section-nav`
|
|
39
|
+
topology names a section-tab renderer (`ShellSectionTabLinkItem`, registered as
|
|
40
|
+
`local.main.ui.section-tab-link-item`), and the page draws the tab row (one
|
|
41
|
+
row over a 1dp `outline-variant` divider that scrolls sideways). A navigation
|
|
42
|
+
bar destination (`ShellTabLinkItem`) belongs to the shell's navigation bar
|
|
43
|
+
only; placed in a page it draws a section tab.
|
|
38
44
|
- Compact navigation controls remain accessible and at least 48 CSS pixels.
|
|
39
45
|
- Application source has no generated-file marker, receipt, or overwrite state.
|
|
40
46
|
|
package/skills/jskit/references/patterns/ui/page-and-placement/example/src/pages/home/reports.vue
CHANGED
|
@@ -8,10 +8,9 @@ definePage({ meta: { jskit: { title: "Reports" } } });
|
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
10
|
<section class="reports-section d-flex flex-column ga-4">
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</v-tabs>
|
|
11
|
+
<!-- Material 3 primary tabs: one row over a divider, scrolling sideways when the tabs don't fit. -->
|
|
12
|
+
<nav class="reports-section__tabs" aria-label="Report sections">
|
|
13
|
+
<ShellOutlet target="reports:section-nav" />
|
|
15
14
|
</nav>
|
|
16
15
|
|
|
17
16
|
<div class="reports-section__content">
|
|
@@ -25,4 +24,10 @@ definePage({ meta: { jskit: { title: "Reports" } } });
|
|
|
25
24
|
.reports-section__content {
|
|
26
25
|
min-width: 0;
|
|
27
26
|
}
|
|
27
|
+
|
|
28
|
+
.reports-section__tabs {
|
|
29
|
+
border-bottom: 1px solid rgb(var(--v-theme-outline-variant));
|
|
30
|
+
display: flex;
|
|
31
|
+
overflow-x: auto;
|
|
32
|
+
}
|
|
28
33
|
</style>
|
package/skills/jskit/references/patterns/ui/page-and-placement/example/src/placementTopology.js
CHANGED
|
@@ -4,6 +4,8 @@ function addPlacementTopology(value = {}) {
|
|
|
4
4
|
placements.push(value);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
// The section's tabs: Material 3 primary tabs at every width (the shell's section-tab renderer), never navigation bar
|
|
8
|
+
// destinations, which belong to the shell's navigation bar alone.
|
|
7
9
|
addPlacementTopology({
|
|
8
10
|
id: "page.section-nav",
|
|
9
11
|
owner: "reports",
|
|
@@ -12,15 +14,15 @@ addPlacementTopology({
|
|
|
12
14
|
variants: {
|
|
13
15
|
compact: {
|
|
14
16
|
outlet: "reports:section-nav",
|
|
15
|
-
renderers: { link: "local.main.ui.tab-link-item" }
|
|
17
|
+
renderers: { link: "local.main.ui.section-tab-link-item" }
|
|
16
18
|
},
|
|
17
19
|
medium: {
|
|
18
20
|
outlet: "reports:section-nav",
|
|
19
|
-
renderers: { link: "local.main.ui.tab-link-item" }
|
|
21
|
+
renderers: { link: "local.main.ui.section-tab-link-item" }
|
|
20
22
|
},
|
|
21
23
|
expanded: {
|
|
22
24
|
outlet: "reports:section-nav",
|
|
23
|
-
renderers: { link: "local.main.ui.tab-link-item" }
|
|
25
|
+
renderers: { link: "local.main.ui.section-tab-link-item" }
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
});
|
|
@@ -38,6 +38,18 @@ app's language; the app does, for example
|
|
|
38
38
|
`watch(useAccountLocale(), (code) => { if (code) i18n.global.locale.value = code; }, { immediate: true })`.
|
|
39
39
|
Section titles may be plain text, a message key or `{ key, fallback }`.
|
|
40
40
|
|
|
41
|
+
Sections are pages of the account surface, in Material 3's list-detail layout
|
|
42
|
+
(shell-web's `ShellListDetail`): `/account` is the section list
|
|
43
|
+
(`src/pages/account/index.vue`) and `/account/<section>` one section
|
|
44
|
+
(`src/pages/account/[section].vue`, its `value`, such as
|
|
45
|
+
`/account/preferences`), both rendering `AccountSettingsClientElement`, which
|
|
46
|
+
reads the section from the route's `section` param. The section page's parent
|
|
47
|
+
is the list, the surface's `"/"`. A phone or medium window shows the list or one
|
|
48
|
+
section, and Up returns from the section to the list; an expanded or large
|
|
49
|
+
window shows the list beside the open section, or beside the first section on
|
|
50
|
+
the list's own page. A link to a section is its path, `/account/invites`, with
|
|
51
|
+
`returnTo` in the query for Done.
|
|
52
|
+
|
|
41
53
|
`runtime.signInMethods.methods` lists the auth provider's OAuth sign-in
|
|
42
54
|
methods as `{ provider, label, linked, canLink, canUnlink }`, read from the
|
|
43
55
|
settings security status. `link(provider)` leaves for the provider through
|
|
@@ -57,10 +69,22 @@ provider offers provider linking (`providerLinking` in its capabilities).
|
|
|
57
69
|
- Loading uses layout-stable skeletons, never an indeterminate spinner.
|
|
58
70
|
- Pending controls keep stable labels and prevent duplicate submission.
|
|
59
71
|
- The route and placement owner agree.
|
|
72
|
+
- The account surface renders in its own layout, `src/pages/account.vue`
|
|
73
|
+
(`ShellLayout` around `<RouterView />`, `meta.jskit.surface: "account"`), so
|
|
74
|
+
the settings page's title and Up arrow are in the top app bar. Up is the
|
|
75
|
+
page's only way out; `AccountSettingsClientElement` adds a Done action only
|
|
76
|
+
where Up can't go back (no parent, a section beside the list on a wide
|
|
77
|
+
window, or a returnTo on another origin).
|
|
78
|
+
With no navigation items, the surface shows no navigation bar, rail or drawer.
|
|
79
|
+
- Each section has its own address, `/account/<section>`, served by
|
|
80
|
+
`src/pages/account/[section].vue` with parent `"/"`; nothing selects a
|
|
81
|
+
section through the query.
|
|
60
82
|
|
|
61
83
|
## Example files
|
|
62
84
|
|
|
63
|
-
`example/` contains
|
|
85
|
+
`example/` contains the account surface's layout, the section list's route
|
|
86
|
+
(`account/index.vue`), a section's route (`account/[section].vue`) and four
|
|
87
|
+
concrete settings sections.
|
|
64
88
|
Adapt their fields and copy while retaining the shared account runtime.
|
|
65
89
|
|
|
66
90
|
## Variation points
|
|
@@ -72,7 +96,9 @@ notification vocabulary while keeping server-owned validation authoritative.
|
|
|
72
96
|
|
|
73
97
|
Navigate away and back with warm cache, exercise browser back/forward, save and
|
|
74
98
|
clear fields, verify toast errors, inspect skeleton geometry, and test keyboard
|
|
75
|
-
operation at compact and expanded widths.
|
|
99
|
+
operation at compact and expanded widths. At 390 px check that the list and a
|
|
100
|
+
section show one at a time and Up returns from the section to the list; at
|
|
101
|
+
1280 px that the list shows beside the open section, with no Up.
|
|
76
102
|
|
|
77
103
|
## Avoid
|
|
78
104
|
|
|
@@ -35,11 +35,11 @@ const notifications = props.runtime.notifications;
|
|
|
35
35
|
color="primary"
|
|
36
36
|
hide-details
|
|
37
37
|
disabled
|
|
38
|
-
class="mb-4"
|
|
39
38
|
/>
|
|
40
39
|
<v-btn
|
|
41
40
|
type="submit"
|
|
42
41
|
color="primary"
|
|
42
|
+
class="mt-4"
|
|
43
43
|
:loading="notifications.isSaving.value"
|
|
44
44
|
:disabled="notifications.isRefreshing.value"
|
|
45
45
|
>
|
|
@@ -94,7 +94,7 @@ const preferences = props.runtime.preferences;
|
|
|
94
94
|
/>
|
|
95
95
|
</v-col>
|
|
96
96
|
|
|
97
|
-
<v-col cols="12" md="
|
|
97
|
+
<v-col cols="12" md="4">
|
|
98
98
|
<v-select
|
|
99
99
|
v-model.number="preferences.form.avatarSize"
|
|
100
100
|
label="Avatar size"
|
|
@@ -110,6 +110,7 @@ const preferences = props.runtime.preferences;
|
|
|
110
110
|
<v-btn
|
|
111
111
|
type="submit"
|
|
112
112
|
color="primary"
|
|
113
|
+
class="mt-4"
|
|
113
114
|
:loading="preferences.isSaving.value"
|
|
114
115
|
:disabled="preferences.isRefreshing.value"
|
|
115
116
|
>
|
|
@@ -51,7 +51,7 @@ const profile = props.runtime.profile;
|
|
|
51
51
|
</v-row>
|
|
52
52
|
|
|
53
53
|
<v-row>
|
|
54
|
-
<v-col cols="12"
|
|
54
|
+
<v-col cols="12">
|
|
55
55
|
<v-text-field
|
|
56
56
|
v-model="profile.form.displayName"
|
|
57
57
|
label="Display name"
|
|
@@ -63,7 +63,7 @@ const profile = props.runtime.profile;
|
|
|
63
63
|
/>
|
|
64
64
|
</v-col>
|
|
65
65
|
|
|
66
|
-
<v-col cols="12"
|
|
66
|
+
<v-col cols="12">
|
|
67
67
|
<v-text-field
|
|
68
68
|
v-model="profile.form.email"
|
|
69
69
|
label="Email"
|
|
@@ -79,6 +79,7 @@ const profile = props.runtime.profile;
|
|
|
79
79
|
<v-btn
|
|
80
80
|
type="submit"
|
|
81
81
|
color="primary"
|
|
82
|
+
class="mt-4"
|
|
82
83
|
:loading="profile.isSaving.value"
|
|
83
84
|
:disabled="profile.isDeletingAvatar.value || profile.isRefreshing.value"
|
|
84
85
|
>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"guard": {
|
|
5
|
+
"policy": "authenticated"
|
|
6
|
+
},
|
|
7
|
+
"jskit": {
|
|
8
|
+
"title": "Account settings",
|
|
9
|
+
"parent": "/"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
</route>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<AccountSettingsClientElement />
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
// One section of account settings (/account/preferences). Its parent is the section list, the surface's "/", so on a
|
|
21
|
+
// phone Up returns to the list; a wide window shows the list beside the section.
|
|
22
|
+
import AccountSettingsClientElement from "@jskit-ai/users-web/client/components/AccountSettingsClientElement";
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
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 and its Up arrow back to
|
|
13
|
+
// home, the page's only way out (a Done action shows only where Up can't go back, such as a returnTo on another of
|
|
14
|
+
// the app's origins). The surface has no navigation items, so the shell draws no navigation bar, rail or drawer.
|
|
15
|
+
import ShellLayout from "@/components/ShellLayout.vue";
|
|
16
|
+
import { RouterView } from "vue-router";
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<ShellLayout>
|
|
21
|
+
<RouterView />
|
|
22
|
+
</ShellLayout>
|
|
23
|
+
</template>
|