@jskit-ai/agent-docs 0.1.131 → 0.1.133
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 +29 -605
- package/guide/agent/app-extras/mobile-capacitor.md +29 -362
- package/guide/agent/app-extras/realtime.md +29 -277
- package/guide/agent/app-setup/a-more-interesting-shell.md +44 -815
- package/guide/agent/app-setup/authentication.md +43 -1073
- package/guide/agent/app-setup/console.md +26 -298
- package/guide/agent/app-setup/database-layer.md +110 -790
- package/guide/agent/app-setup/existing-application-migration.md +273 -0
- package/guide/agent/app-setup/initial-scaffolding.md +50 -784
- package/guide/agent/app-setup/multi-homing.md +39 -712
- package/guide/agent/app-setup/quickstart.md +43 -179
- package/guide/agent/app-setup/users.md +34 -353
- package/guide/agent/index.md +17 -23
- package/package.json +2 -2
- package/patterns/INDEX.md +7 -7
- package/patterns/child-cruds.md +3 -3
- package/patterns/client-requests.md +6 -6
- package/patterns/crud-authoring.md +94 -0
- package/patterns/crud-links.md +1 -1
- package/patterns/feature-package/PATTERN.md +108 -0
- package/patterns/feature-package/example/booking-engine/package.json +48 -0
- package/patterns/feature-package/example/booking-engine/src/server/BookingEngineProvider.js +33 -0
- package/patterns/feature-package/example/booking-engine/src/server/actions.js +26 -0
- package/patterns/feature-package/example/booking-engine/src/server/inputSchemas.js +19 -0
- package/patterns/feature-package/example/variations/AvailabilityEngineProvider.js +31 -0
- package/patterns/feature-package/example/variations/InvoiceRollupProvider.js +36 -0
- package/patterns/feature-package/example/variations/customKnexRepository.js +34 -0
- package/patterns/feature-package/example/variations/orchestratorService.js +23 -0
- package/patterns/filters.md +8 -8
- package/patterns/live-actions.md +5 -18
- package/patterns/minimal-foundation/PATTERN.md +98 -0
- package/patterns/minimal-foundation/example/.nvmrc +1 -0
- package/patterns/minimal-foundation/example/AGENTS.md +17 -0
- package/patterns/minimal-foundation/example/Procfile +2 -0
- package/patterns/minimal-foundation/example/app.json +14 -0
- package/patterns/minimal-foundation/example/bin/develop.js +71 -0
- package/patterns/minimal-foundation/example/bin/server.js +8 -0
- package/patterns/minimal-foundation/example/config/public.js +40 -0
- package/patterns/minimal-foundation/example/config/server.js +1 -0
- package/patterns/minimal-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/minimal-foundation/example/eslint.config.mjs +19 -0
- package/patterns/minimal-foundation/example/favicon.svg +7 -0
- package/patterns/minimal-foundation/example/gitignore +9 -0
- package/patterns/minimal-foundation/example/index.html +13 -0
- package/patterns/minimal-foundation/example/jsconfig.json +8 -0
- package/patterns/minimal-foundation/example/package.json +57 -0
- package/patterns/minimal-foundation/example/packages/main/package.json +42 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/minimal-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/minimal-foundation/example/playwright.config.mjs +31 -0
- package/patterns/minimal-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/minimal-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/minimal-foundation/example/server.js +195 -0
- package/patterns/minimal-foundation/example/src/App.vue +13 -0
- package/patterns/minimal-foundation/example/src/main.js +85 -0
- package/patterns/minimal-foundation/example/src/pages/home/index.vue +48 -0
- package/patterns/minimal-foundation/example/src/pages/home.vue +13 -0
- package/patterns/minimal-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/minimal-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/minimal-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/minimal-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/minimal-foundation/example/vite.config.mjs +81 -0
- package/patterns/page-scaffolding.md +20 -17
- package/patterns/placements.md +17 -15
- package/patterns/row-policies.md +4 -5
- package/patterns/server-search.md +3 -3
- package/patterns/shell-foundation/PATTERN.md +104 -0
- package/patterns/shell-foundation/example/.nvmrc +1 -0
- package/patterns/shell-foundation/example/AGENTS.md +17 -0
- package/patterns/shell-foundation/example/Procfile +2 -0
- package/patterns/shell-foundation/example/app.json +14 -0
- package/patterns/shell-foundation/example/bin/develop.js +71 -0
- package/patterns/shell-foundation/example/bin/server.js +8 -0
- package/patterns/shell-foundation/example/config/public.js +40 -0
- package/patterns/shell-foundation/example/config/server.js +1 -0
- package/patterns/shell-foundation/example/config/surfaceAccessPolicies.js +3 -0
- package/patterns/shell-foundation/example/eslint.config.mjs +19 -0
- package/patterns/shell-foundation/example/favicon.svg +7 -0
- package/patterns/shell-foundation/example/gitignore +9 -0
- package/patterns/shell-foundation/example/index.html +13 -0
- package/patterns/shell-foundation/example/jsconfig.json +8 -0
- package/patterns/shell-foundation/example/package.json +59 -0
- package/patterns/shell-foundation/example/packages/main/package.json +56 -0
- package/patterns/shell-foundation/example/packages/main/src/client/index.js +9 -0
- package/patterns/shell-foundation/example/packages/main/src/client/providers/MainClientProvider.js +18 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/index.js +10 -0
- package/patterns/shell-foundation/example/packages/main/src/shared/schemas/index.js +22 -0
- package/patterns/shell-foundation/example/playwright.config.mjs +31 -0
- package/patterns/shell-foundation/example/server/lib/runtimeEnv.js +45 -0
- package/patterns/shell-foundation/example/server/lib/surfaceRuntime.js +10 -0
- package/patterns/shell-foundation/example/server.js +195 -0
- package/patterns/shell-foundation/example/src/App.vue +11 -0
- package/patterns/shell-foundation/example/src/components/ShellLayout.vue +12 -0
- package/patterns/shell-foundation/example/src/components/menus/MenuLinkItem.vue +30 -0
- package/patterns/shell-foundation/example/src/components/menus/SurfaceAwareMenuLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/components/menus/TabLinkItem.vue +42 -0
- package/patterns/shell-foundation/example/src/error.js +19 -0
- package/patterns/shell-foundation/example/src/main.js +85 -0
- package/patterns/shell-foundation/example/src/pages/home/index.vue +116 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/general/index.vue +40 -0
- package/patterns/shell-foundation/example/src/pages/home/settings/index.vue +7 -0
- package/patterns/shell-foundation/example/src/pages/home/settings.vue +109 -0
- package/patterns/shell-foundation/example/src/pages/home.vue +20 -0
- package/patterns/shell-foundation/example/src/placement.js +56 -0
- package/patterns/shell-foundation/example/src/placementTopology.js +149 -0
- package/patterns/shell-foundation/example/src/views/NotFound.vue +13 -0
- package/patterns/shell-foundation/example/tests/client/smoke.vitest.js +7 -0
- package/patterns/shell-foundation/example/tests/e2e/adaptive-shell.spec.ts +10 -0
- package/patterns/shell-foundation/example/tests/e2e/base-shell.spec.ts +23 -0
- package/patterns/shell-foundation/example/tests/server/smoke.test.js +16 -0
- package/patterns/shell-foundation/example/vite.config.mjs +81 -0
- package/patterns/ui-contract.md +56 -0
- package/patterns/ui-testing.md +10 -12
- package/reference/autogen/KERNEL_MAP.md +29 -107
- package/reference/autogen/PATTERN_INDEX.md +230 -0
- package/reference/autogen/README.md +4 -8
- package/reference/autogen/packages/agent-docs.md +259 -0
- package/reference/autogen/packages/assistant-core.md +3 -3
- package/reference/autogen/packages/assistant-runtime.md +32 -17
- package/reference/autogen/packages/auth-core.md +31 -33
- package/reference/autogen/packages/auth-provider-local-core.md +4 -12
- package/reference/autogen/packages/auth-provider-local-db-core.md +4 -4
- package/reference/autogen/packages/auth-provider-supabase-core.md +14 -18
- package/reference/autogen/packages/auth-web.md +42 -22
- package/reference/autogen/packages/console-core.md +8 -25
- package/reference/autogen/packages/console-web.md +5 -5
- package/reference/autogen/packages/crud-core.md +61 -17
- package/reference/autogen/packages/database-runtime-mysql.md +12 -2
- package/reference/autogen/packages/database-runtime-postgres.md +12 -2
- package/reference/autogen/packages/database-runtime.md +26 -25
- package/reference/autogen/packages/google-rewarded-core.md +19 -104
- package/reference/autogen/packages/http-runtime.md +4 -8
- package/reference/autogen/packages/http-web.md +32 -0
- package/reference/autogen/packages/json-rest-api-core.md +4 -6
- package/reference/autogen/packages/kernel.md +109 -390
- package/reference/autogen/packages/mobile-capacitor.md +2 -13
- package/reference/autogen/packages/realtime.md +29 -26
- package/reference/autogen/packages/resource-crud-core.md +6 -0
- package/reference/autogen/packages/shell-web.md +69 -54
- package/reference/autogen/packages/storage-runtime.md +3 -3
- package/reference/autogen/packages/uploads-image-web.md +0 -1
- package/reference/autogen/packages/uploads-runtime.md +3 -3
- package/reference/autogen/packages/users-core.md +45 -90
- package/reference/autogen/packages/users-web.md +5 -7
- package/reference/autogen/packages/workspaces-core.md +53 -74
- package/reference/autogen/packages/workspaces-web.md +15 -16
- package/reference/autogen/tooling/jskit-catalog.md +34 -0
- package/reference/autogen/tooling/testUtils.md +4 -4
- package/skills/jskit/SKILL.md +38 -31
- package/skills/jskit/references/app-operations.md +68 -53
- package/skills/jskit/references/crud-operations.md +58 -106
- package/skills/jskit/references/material-3.md +1 -1
- package/skills/jskit/references/ui-operations.md +41 -44
- package/templates/app/AGENTS.md +7 -3
- package/guide/agent/app-setup/upgrade-beta-1-to-final.md +0 -252
- package/guide/agent/app-setup/working-with-the-jskit-cli.md +0 -325
- package/guide/agent/generators/advanced-cruds.md +0 -1935
- package/guide/agent/generators/crud-generators.md +0 -948
- package/guide/agent/generators/intro.md +0 -65
- package/guide/agent/generators/row-policies.md +0 -537
- package/guide/agent/generators/ui-generators.md +0 -690
- package/patterns/crud-scaffolding.md +0 -198
- package/patterns/generated-ui-contract-tracking.md +0 -66
- package/reference/autogen/packages/assistant.md +0 -68
- package/reference/autogen/packages/crud-server-generator.md +0 -215
- package/reference/autogen/packages/crud-ui-generator.md +0 -192
- package/reference/autogen/packages/feature-server-generator.md +0 -65
- package/reference/autogen/packages/ui-generator.md +0 -127
- package/reference/autogen/tooling/create-app.md +0 -317
- package/reference/autogen/tooling/jskit-cli.md +0 -933
- package/reference/autogen/tooling/test-support.md +0 -27
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
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
|
+
<header>
|
|
9
|
+
<p class="text-overline text-medium-emphasis mb-1">Settings</p>
|
|
10
|
+
<h1 class="settings-shell__title">Home settings</h1>
|
|
11
|
+
<p class="text-body-2 text-medium-emphasis mb-0">Configure shell behavior for this surface.</p>
|
|
12
|
+
</header>
|
|
13
|
+
|
|
14
|
+
<v-sheet rounded="lg" border class="settings-shell__panel">
|
|
15
|
+
<div class="settings-shell__body">
|
|
16
|
+
<nav class="settings-shell__nav" aria-label="Home settings sections">
|
|
17
|
+
<v-list nav density="compact" rounded="lg">
|
|
18
|
+
<ShellOutlet target="home-settings:primary-menu" />
|
|
19
|
+
</v-list>
|
|
20
|
+
</nav>
|
|
21
|
+
|
|
22
|
+
<main class="settings-shell__content">
|
|
23
|
+
<RouterView />
|
|
24
|
+
</main>
|
|
25
|
+
</div>
|
|
26
|
+
</v-sheet>
|
|
27
|
+
</section>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<style scoped>
|
|
31
|
+
.settings-shell {
|
|
32
|
+
--settings-title-size: 1.85rem;
|
|
33
|
+
--settings-panel-padding: 1rem;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.settings-shell__title {
|
|
37
|
+
font-size: var(--settings-title-size);
|
|
38
|
+
font-weight: 650;
|
|
39
|
+
letter-spacing: 0;
|
|
40
|
+
line-height: 1.15;
|
|
41
|
+
margin: 0 0 0.35rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.settings-shell__panel {
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.settings-shell__body {
|
|
49
|
+
display: grid;
|
|
50
|
+
gap: 1rem;
|
|
51
|
+
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
|
|
52
|
+
padding: var(--settings-panel-padding);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.settings-shell__content {
|
|
56
|
+
border-left: 1px solid rgba(var(--v-theme-on-surface), 0.18);
|
|
57
|
+
min-width: 0;
|
|
58
|
+
padding-left: 1rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.settings-shell__nav :deep(.v-list) {
|
|
62
|
+
padding: 0.35rem;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.settings-shell__nav :deep(.v-list-item) {
|
|
66
|
+
min-height: 48px;
|
|
67
|
+
padding-inline: 0.6rem 0.7rem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.settings-shell__nav :deep(.v-list-item__prepend) {
|
|
71
|
+
margin-inline-end: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.settings-shell__nav :deep(.v-list-item__spacer) {
|
|
75
|
+
min-width: 0.5rem;
|
|
76
|
+
width: 0.5rem;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.settings-shell__nav :deep(.v-list-item-title) {
|
|
80
|
+
line-height: 1.2;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@media (max-width: 960px) {
|
|
84
|
+
.settings-shell {
|
|
85
|
+
--settings-title-size: 1.35rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.settings-shell__body {
|
|
89
|
+
grid-template-columns: 1fr;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.settings-shell__nav :deep(.v-list) {
|
|
93
|
+
display: flex;
|
|
94
|
+
gap: 0.25rem;
|
|
95
|
+
overflow-x: auto;
|
|
96
|
+
scrollbar-width: thin;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.settings-shell__nav :deep(.v-list-item) {
|
|
100
|
+
flex: 0 0 auto;
|
|
101
|
+
min-height: 48px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.settings-shell__content {
|
|
105
|
+
border-left: 0;
|
|
106
|
+
padding-left: 0;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<route lang="json">
|
|
2
|
+
{
|
|
3
|
+
"meta": {
|
|
4
|
+
"jskit": {
|
|
5
|
+
"surface": "home"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
</route>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import ShellLayout from "@/components/ShellLayout.vue";
|
|
13
|
+
import { RouterView } from "vue-router";
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<ShellLayout title="" subtitle="">
|
|
18
|
+
<RouterView />
|
|
19
|
+
</ShellLayout>
|
|
20
|
+
</template>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createPlacementRegistry } from "@jskit-ai/shell-web/client/placement";
|
|
2
|
+
|
|
3
|
+
const registry = createPlacementRegistry();
|
|
4
|
+
const { addPlacement } = registry;
|
|
5
|
+
|
|
6
|
+
export { addPlacement };
|
|
7
|
+
|
|
8
|
+
// Keep the default export near the top so module installers can append addPlacement(...)
|
|
9
|
+
// blocks at the bottom of this file without changing the export section.
|
|
10
|
+
export default function getPlacements() {
|
|
11
|
+
return registry.build();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
addPlacement({
|
|
15
|
+
id: "shell-web.home.menu.home",
|
|
16
|
+
target: "shell.primary-nav",
|
|
17
|
+
kind: "link",
|
|
18
|
+
surfaces: ["home"],
|
|
19
|
+
order: 50,
|
|
20
|
+
props: {
|
|
21
|
+
label: "Home",
|
|
22
|
+
surface: "home",
|
|
23
|
+
scopedSuffix: "/",
|
|
24
|
+
unscopedSuffix: "/",
|
|
25
|
+
exact: true
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
addPlacement({
|
|
30
|
+
id: "shell-web.home.menu.settings",
|
|
31
|
+
target: "shell.primary-nav",
|
|
32
|
+
kind: "link",
|
|
33
|
+
surfaces: ["home"],
|
|
34
|
+
order: 100,
|
|
35
|
+
props: {
|
|
36
|
+
label: "Settings",
|
|
37
|
+
surface: "home",
|
|
38
|
+
scopedSuffix: "/settings",
|
|
39
|
+
unscopedSuffix: "/settings"
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
addPlacement({
|
|
44
|
+
id: "shell-web.home.settings.general",
|
|
45
|
+
target: "page.section-nav",
|
|
46
|
+
owner: "home-settings",
|
|
47
|
+
kind: "link",
|
|
48
|
+
surfaces: ["home"],
|
|
49
|
+
order: 100,
|
|
50
|
+
props: {
|
|
51
|
+
label: "General",
|
|
52
|
+
surface: "home",
|
|
53
|
+
scopedSuffix: "/settings/general",
|
|
54
|
+
unscopedSuffix: "/settings/general"
|
|
55
|
+
}
|
|
56
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const placements = [];
|
|
2
|
+
|
|
3
|
+
function addPlacementTopology(value = {}) {
|
|
4
|
+
placements.push(value);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export { addPlacementTopology };
|
|
8
|
+
export default { placements };
|
|
9
|
+
|
|
10
|
+
const menuLinkRenderers = Object.freeze({
|
|
11
|
+
link: "local.main.ui.surface-aware-menu-link-item"
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const bottomNavLinkRenderers = Object.freeze({
|
|
15
|
+
link: "local.main.ui.tab-link-item"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
addPlacementTopology({
|
|
19
|
+
id: "shell.primary-nav",
|
|
20
|
+
description: "Primary top-level navigation for the current surface.",
|
|
21
|
+
surfaces: ["*"],
|
|
22
|
+
default: true,
|
|
23
|
+
variants: {
|
|
24
|
+
compact: {
|
|
25
|
+
outlet: "shell-layout:primary-bottom-nav",
|
|
26
|
+
renderers: bottomNavLinkRenderers
|
|
27
|
+
},
|
|
28
|
+
medium: {
|
|
29
|
+
outlet: "shell-layout:primary-menu",
|
|
30
|
+
renderers: menuLinkRenderers
|
|
31
|
+
},
|
|
32
|
+
expanded: {
|
|
33
|
+
outlet: "shell-layout:primary-menu",
|
|
34
|
+
renderers: menuLinkRenderers
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
addPlacementTopology({
|
|
40
|
+
id: "shell.secondary-nav",
|
|
41
|
+
description: "Secondary navigation for the current surface.",
|
|
42
|
+
surfaces: ["*"],
|
|
43
|
+
variants: {
|
|
44
|
+
compact: {
|
|
45
|
+
outlet: "shell-layout:secondary-menu",
|
|
46
|
+
renderers: menuLinkRenderers
|
|
47
|
+
},
|
|
48
|
+
medium: {
|
|
49
|
+
outlet: "shell-layout:secondary-menu",
|
|
50
|
+
renderers: menuLinkRenderers
|
|
51
|
+
},
|
|
52
|
+
expanded: {
|
|
53
|
+
outlet: "shell-layout:secondary-menu",
|
|
54
|
+
renderers: menuLinkRenderers
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
addPlacementTopology({
|
|
60
|
+
id: "shell.identity",
|
|
61
|
+
description: "Current surface identity and switcher controls.",
|
|
62
|
+
surfaces: ["*"],
|
|
63
|
+
variants: {
|
|
64
|
+
compact: {
|
|
65
|
+
outlet: "shell-layout:top-left"
|
|
66
|
+
},
|
|
67
|
+
medium: {
|
|
68
|
+
outlet: "shell-layout:top-left"
|
|
69
|
+
},
|
|
70
|
+
expanded: {
|
|
71
|
+
outlet: "shell-layout:top-left"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
addPlacementTopology({
|
|
77
|
+
id: "shell.status",
|
|
78
|
+
description: "Surface status, connection, and utility indicators.",
|
|
79
|
+
surfaces: ["*"],
|
|
80
|
+
variants: {
|
|
81
|
+
compact: {
|
|
82
|
+
outlet: "shell-layout:top-right"
|
|
83
|
+
},
|
|
84
|
+
medium: {
|
|
85
|
+
outlet: "shell-layout:top-right"
|
|
86
|
+
},
|
|
87
|
+
expanded: {
|
|
88
|
+
outlet: "shell-layout:top-right"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
addPlacementTopology({
|
|
94
|
+
id: "shell.global-actions",
|
|
95
|
+
description: "Global surface actions that should stay outside primary navigation.",
|
|
96
|
+
surfaces: ["*"],
|
|
97
|
+
variants: {
|
|
98
|
+
compact: {
|
|
99
|
+
outlet: "shell-layout:top-right",
|
|
100
|
+
renderers: menuLinkRenderers
|
|
101
|
+
},
|
|
102
|
+
medium: {
|
|
103
|
+
outlet: "shell-layout:top-right",
|
|
104
|
+
renderers: menuLinkRenderers
|
|
105
|
+
},
|
|
106
|
+
expanded: {
|
|
107
|
+
outlet: "shell-layout:top-right",
|
|
108
|
+
renderers: menuLinkRenderers
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
addPlacementTopology({
|
|
114
|
+
id: "page.supporting-content",
|
|
115
|
+
description: "Supporting page content that opens as a bottom sheet on compact layouts and a side panel on wider layouts.",
|
|
116
|
+
surfaces: ["*"],
|
|
117
|
+
variants: {
|
|
118
|
+
compact: {
|
|
119
|
+
outlet: "shell-layout:supporting-bottom-sheet"
|
|
120
|
+
},
|
|
121
|
+
medium: {
|
|
122
|
+
outlet: "shell-layout:supporting-side-panel"
|
|
123
|
+
},
|
|
124
|
+
expanded: {
|
|
125
|
+
outlet: "shell-layout:supporting-side-panel"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
addPlacementTopology({
|
|
131
|
+
id: "page.section-nav",
|
|
132
|
+
owner: "home-settings",
|
|
133
|
+
description: "Navigation between child pages in the home settings section.",
|
|
134
|
+
surfaces: ["home"],
|
|
135
|
+
variants: {
|
|
136
|
+
compact: {
|
|
137
|
+
outlet: "home-settings:primary-menu",
|
|
138
|
+
renderers: menuLinkRenderers
|
|
139
|
+
},
|
|
140
|
+
medium: {
|
|
141
|
+
outlet: "home-settings:primary-menu",
|
|
142
|
+
renderers: menuLinkRenderers
|
|
143
|
+
},
|
|
144
|
+
expanded: {
|
|
145
|
+
outlet: "home-settings:primary-menu",
|
|
146
|
+
renderers: menuLinkRenderers
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
const title = "Not Found";
|
|
3
|
+
const message = "The page you requested does not exist.";
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<template>
|
|
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>
|
|
12
|
+
</v-container>
|
|
13
|
+
</template>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { DEFAULT_VIEWPORTS, runAdaptiveShellSmokeCase } from "@jskit-ai/shell-web/test/adaptiveShellSmoke";
|
|
3
|
+
|
|
4
|
+
test.describe("adaptive shell smoke", () => {
|
|
5
|
+
for (const viewport of DEFAULT_VIEWPORTS) {
|
|
6
|
+
test(`${viewport.name} layout has reachable navigation and no horizontal overflow`, async ({ page }) => {
|
|
7
|
+
await runAdaptiveShellSmokeCase({ page, expect, viewport });
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
const VIEWPORTS = Object.freeze([
|
|
4
|
+
Object.freeze({ name: "compact", width: 390, height: 844 }),
|
|
5
|
+
Object.freeze({ name: "medium", width: 768, height: 1024 }),
|
|
6
|
+
Object.freeze({ name: "expanded", width: 1280, height: 900 })
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
test.describe("application responsive smoke", () => {
|
|
10
|
+
for (const viewport of VIEWPORTS) {
|
|
11
|
+
test(`${viewport.name} home route renders without horizontal overflow`, async ({ page }) => {
|
|
12
|
+
await page.setViewportSize(viewport);
|
|
13
|
+
await page.goto("/home");
|
|
14
|
+
await expect(page.getByRole("heading", { name: "Ready" })).toBeVisible();
|
|
15
|
+
|
|
16
|
+
const dimensions = await page.evaluate(() => ({
|
|
17
|
+
clientWidth: document.documentElement.clientWidth,
|
|
18
|
+
scrollWidth: document.documentElement.scrollWidth
|
|
19
|
+
}));
|
|
20
|
+
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth + 1);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createServer } from "../../server.js";
|
|
4
|
+
|
|
5
|
+
test("GET /api/health returns built-in health response", async () => {
|
|
6
|
+
const app = await createServer();
|
|
7
|
+
const response = await app.inject({
|
|
8
|
+
method: "GET",
|
|
9
|
+
url: "/api/health"
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
assert.equal(response.statusCode, 200);
|
|
13
|
+
assert.equal(response.json().ok, true);
|
|
14
|
+
|
|
15
|
+
await app.close();
|
|
16
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { fileURLToPath, URL } from "node:url";
|
|
2
|
+
import { defineConfig } from "vite";
|
|
3
|
+
import vue from "@vitejs/plugin-vue";
|
|
4
|
+
import vuetify from "vite-plugin-vuetify";
|
|
5
|
+
import VueRouter from "vue-router/vite";
|
|
6
|
+
import { createJskitClientBootstrapPlugin } from "@jskit-ai/kernel/client/vite";
|
|
7
|
+
|
|
8
|
+
const configuredDevPort = Number.parseInt(String(process.env.VITE_DEV_PORT || "").trim(), 10);
|
|
9
|
+
const devPort = Number.isInteger(configuredDevPort) && configuredDevPort > 0 ? configuredDevPort : 5173;
|
|
10
|
+
const apiProxyTarget = String(process.env.VITE_API_PROXY_TARGET || "").trim() || "http://localhost:3000";
|
|
11
|
+
const clientEntry = (() => {
|
|
12
|
+
const normalized = String(process.env.VITE_CLIENT_ENTRY || "").trim();
|
|
13
|
+
if (!normalized) {
|
|
14
|
+
return "/src/main.js";
|
|
15
|
+
}
|
|
16
|
+
if (normalized.startsWith("/")) {
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
if (normalized.startsWith("src/")) {
|
|
20
|
+
return `/${normalized}`;
|
|
21
|
+
}
|
|
22
|
+
return `/src/${normalized}`;
|
|
23
|
+
})();
|
|
24
|
+
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
resolve: {
|
|
27
|
+
preserveSymlinks: true,
|
|
28
|
+
alias: {
|
|
29
|
+
"@": fileURLToPath(new URL("./src", import.meta.url))
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
plugins: [
|
|
33
|
+
createJskitClientBootstrapPlugin({
|
|
34
|
+
proxyTarget: apiProxyTarget
|
|
35
|
+
}),
|
|
36
|
+
VueRouter({
|
|
37
|
+
routesFolder: "src/pages",
|
|
38
|
+
// Generated on the first Vite dev/build scan and intentionally gitignored.
|
|
39
|
+
dts: "src/typed-router.d.ts",
|
|
40
|
+
nestedChildren: false
|
|
41
|
+
}),
|
|
42
|
+
vue(),
|
|
43
|
+
vuetify({
|
|
44
|
+
autoImport: true
|
|
45
|
+
}),
|
|
46
|
+
{
|
|
47
|
+
name: "jskit-client-entry",
|
|
48
|
+
transformIndexHtml(source) {
|
|
49
|
+
return String(source || "")
|
|
50
|
+
.replace(/\/src\/%VITE_CLIENT_ENTRY%/g, clientEntry)
|
|
51
|
+
.replace(/\/src\/main\.js/g, clientEntry);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
test: {
|
|
56
|
+
include: ["tests/client/**/*.vitest.js"]
|
|
57
|
+
},
|
|
58
|
+
optimizeDeps: {
|
|
59
|
+
entries: [
|
|
60
|
+
"index.html",
|
|
61
|
+
"src/**/*.{js,ts,vue}"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
server: {
|
|
65
|
+
port: devPort,
|
|
66
|
+
warmup: {
|
|
67
|
+
clientFiles: [
|
|
68
|
+
"src/main.{js,ts}",
|
|
69
|
+
"src/router/**/*.{js,ts}",
|
|
70
|
+
"src/pages/**/*.{js,ts,vue}",
|
|
71
|
+
"src/components/**/*.{js,ts,vue}"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
proxy: {
|
|
75
|
+
"/api": {
|
|
76
|
+
target: apiProxyTarget,
|
|
77
|
+
changeOrigin: true
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# JSKIT UI contract
|
|
2
|
+
|
|
3
|
+
Use this contract when creating or reviewing JSKIT shell, page, CRUD, settings,
|
|
4
|
+
and action UI.
|
|
5
|
+
|
|
6
|
+
## Product-quality defaults
|
|
7
|
+
|
|
8
|
+
- Live pages contain product language, not framework instructions or placeholder
|
|
9
|
+
copy.
|
|
10
|
+
- Layouts adapt at compact, medium, and expanded widths without horizontal
|
|
11
|
+
overflow.
|
|
12
|
+
- Compact interactive targets are at least 48 CSS pixels.
|
|
13
|
+
- Navigation uses semantic placements and explicit navigation roles.
|
|
14
|
+
- Pages do not stack decorative cards inside structural cards.
|
|
15
|
+
- Shared screens own repeated chrome; app pages own product fields and domain
|
|
16
|
+
sections.
|
|
17
|
+
|
|
18
|
+
## Loading and feedback
|
|
19
|
+
|
|
20
|
+
- Initial and structural loading uses shape-matching skeletons.
|
|
21
|
+
- Never use circular progress, indeterminate spinners, or loading icons.
|
|
22
|
+
- Pending buttons keep their position and use stable disabled labels.
|
|
23
|
+
- Resource-load failures stay in the affected region with retry.
|
|
24
|
+
- Command and mutation failures use the shared toast/snackbar feedback path;
|
|
25
|
+
they do not insert banners that push page content down.
|
|
26
|
+
- Cached query/resource data and route state may already exist at component
|
|
27
|
+
creation. Prefer computed truth; when writable state is necessary, hydrate it
|
|
28
|
+
synchronously or with an immediate watcher.
|
|
29
|
+
|
|
30
|
+
## Shared CRUD screens
|
|
31
|
+
|
|
32
|
+
Use `CrudListScreen`, `CrudViewScreen`, and `CrudAddEditScreen` with their
|
|
33
|
+
matching composables. Use the public filter, bulk-action, row-action, synthetic
|
|
34
|
+
row, and detail-slot seams before replacing shared chrome.
|
|
35
|
+
|
|
36
|
+
Filters derive query parameters through the shared screen runtime. Server-side
|
|
37
|
+
filters use `createCrudListFilterContract()` so route/action validation, JSON
|
|
38
|
+
REST search schema, and repository normalization remain one contract.
|
|
39
|
+
|
|
40
|
+
## Shell and placements
|
|
41
|
+
|
|
42
|
+
Keep placement identities semantic and surface-aware. `primary`, `secondary`,
|
|
43
|
+
`utility`, `detail`, `workflow`, and `none` describe product navigation intent.
|
|
44
|
+
`page.supporting-content` is a semantic secondary region: compact layouts expose
|
|
45
|
+
it as a closed sheet and wider layouts as a closed side panel.
|
|
46
|
+
|
|
47
|
+
## Verification
|
|
48
|
+
|
|
49
|
+
- test skeleton, empty, loaded, local error, retry, and mutation feedback states
|
|
50
|
+
- navigate away and back with warm query cache, including browser back/forward
|
|
51
|
+
- exercise compact, medium, and expanded viewports
|
|
52
|
+
- verify accessible names, keyboard operation, target size, and no overflow
|
|
53
|
+
- assert that no spinner or layout-shifting transient error banner appears
|
|
54
|
+
|
|
55
|
+
Framework source contracts and package tests enforce the reusable parts of this
|
|
56
|
+
policy. Application browser tests prove product-specific behavior.
|
package/patterns/ui-testing.md
CHANGED
|
@@ -10,11 +10,13 @@ Use when:
|
|
|
10
10
|
Rules:
|
|
11
11
|
|
|
12
12
|
- Any chunk that adds or changes user-facing UI must include a Playwright flow that exercises the changed behavior before the chunk is done.
|
|
13
|
-
-
|
|
14
|
-
-
|
|
13
|
+
- Pattern and framework UI changes must be checked at compact phone, tablet-ish medium, and expanded desktop widths.
|
|
14
|
+
- Check horizontal overflow, clipped or invisible text, duplicate navigation, and broken route placement at every standard viewport. On compact screens, also check screen tap targets under 48 px; medium and expanded layouts may use their documented denser controls.
|
|
15
15
|
- Apps with `shell-web` installed should start from `tests/e2e/adaptive-shell.spec.ts` and extend it with feature-specific assertions.
|
|
16
16
|
- The package helper reads the shell's rendered `data-layout` contract, waits for drawer transitions without fixed sleeps, verifies compact Escape/outside dismissal, checks content-aware drawer fit, and measures rail icon centring against the configured width. Do not replace it with viewport-name assumptions or immediate animation-time geometry reads.
|
|
17
|
-
-
|
|
17
|
+
- Start from the application-foundation Playwright configuration and keep it
|
|
18
|
+
app-owned. Do not duplicate base-URL, web-server, or storage-state logic in
|
|
19
|
+
individual tests.
|
|
18
20
|
- Use relative paths such as `page.goto("/home")`. The shared config owns the browser base URL.
|
|
19
21
|
- A managed runner supplies `PLAYWRIGHT_BASE_URL`. When it is set, JSKIT does not start another app server.
|
|
20
22
|
- Vibe64 supplies an authenticated context through `VIBE64_PLAYWRIGHT_STORAGE_STATE`. Treat that file as a temporary secret: do not commit it, print it, or retain it after the run.
|
|
@@ -22,15 +24,14 @@ Rules:
|
|
|
22
24
|
|
|
23
25
|
## Preserve baseline tests
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
Foundation-pattern baseline tests are app-owned and customizable. Adapt infrastructure
|
|
26
28
|
tests in place when routes or behavior change so the baseline coverage remains
|
|
27
29
|
truthful.
|
|
28
30
|
|
|
29
31
|
When the starter product route is replaced, update the scaffold smoke test to
|
|
30
32
|
visit and assert the new canonical route. Do not delete baseline browser
|
|
31
33
|
coverage such as `tests/e2e/base-shell.spec.ts` or
|
|
32
|
-
`tests/e2e/adaptive-shell.spec.ts`.
|
|
33
|
-
managed test that is missing.
|
|
34
|
+
`tests/e2e/adaptive-shell.spec.ts`.
|
|
34
35
|
|
|
35
36
|
## Direct local authentication
|
|
36
37
|
|
|
@@ -78,7 +79,7 @@ VIBE64_PLAYWRIGHT_STORAGE_STATE=/secure/temp/playwright-state.json \
|
|
|
78
79
|
playwright test tests/e2e/contacts.spec.ts
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
The
|
|
82
|
+
The app-owned config applies that state to Playwright contexts and omits its local `webServer`. Tests then navigate with relative paths and begin with the runner-provided identity already authenticated.
|
|
82
83
|
|
|
83
84
|
Do not call `loginAsExistingUser()` against a managed preview. It is deliberately localhost-only. An ordinary request to `/api/dev-auth/login-as` without the private exchange header must return `403`.
|
|
84
85
|
|
|
@@ -90,11 +91,8 @@ Run the focused Playwright command directly:
|
|
|
90
91
|
npx playwright test tests/e2e/contacts.spec.ts -g filters
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
For local pre-merge review, follow the focused run with
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
npx jskit doctor --against origin/main
|
|
97
|
-
```
|
|
94
|
+
For local pre-merge review, follow the focused run with the application's
|
|
95
|
+
ordinary lint, package, build, and test scripts.
|
|
98
96
|
|
|
99
97
|
Do not mark the chunk done if:
|
|
100
98
|
|