@jskit-ai/shell-web 0.1.83 → 0.1.85

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/shell-web",
4
- version: "0.1.83",
4
+ version: "0.1.85",
5
5
  kind: "runtime",
6
6
  description: "Web shell layout runtime with outlet-based placement contributions.",
7
7
  dependsOn: [],
@@ -291,7 +291,7 @@ export default Object.freeze({
291
291
  dependencies: {
292
292
  runtime: {
293
293
  "@mdi/js": "^7.4.47",
294
- "@jskit-ai/kernel": "0.1.84"
294
+ "@jskit-ai/kernel": "0.1.86"
295
295
  },
296
296
  dev: {}
297
297
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/shell-web",
3
- "version": "0.1.83",
3
+ "version": "0.1.85",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@mdi/js": "^7.4.47",
29
- "@jskit-ai/kernel": "0.1.84"
29
+ "@jskit-ai/kernel": "0.1.86"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "pinia": "^3.0.4",
@@ -24,6 +24,7 @@ import {
24
24
  normalizeMenuLinkPathname,
25
25
  resolveMenuLinkTarget
26
26
  } from "../support/menuLinkTarget.js";
27
+ import { resolveShellRouteTransitionKey } from "../support/routeTransitionKey.js";
27
28
 
28
29
  const props = defineProps({
29
30
  enabled: {
@@ -232,7 +233,16 @@ const routeTransitionName = computed(() => {
232
233
  return "";
233
234
  });
234
235
 
235
- const routeTransitionKey = computed(() => normalizeComparablePathname(route?.path || route?.fullPath || "/") || "/");
236
+ const routeTransitionKey = computed(() => {
237
+ const routePathKey = routeTransitionName.value
238
+ ? normalizeComparablePathname(route?.path || route?.fullPath || "/")
239
+ : "";
240
+ return resolveShellRouteTransitionKey({
241
+ routePathKey,
242
+ routeTransitionName: routeTransitionName.value,
243
+ surfaceId: currentSurfaceId.value
244
+ });
245
+ });
236
246
 
237
247
  const swipeNavigationEnabled = computed(() =>
238
248
  Boolean(
@@ -409,7 +419,12 @@ function isSwipeIgnoredTarget(target) {
409
419
  <style scoped>
410
420
  .shell-route-transition {
411
421
  --shell-route-transition-distance: 100%;
422
+ align-items: stretch;
412
423
  --shell-route-transition-opacity: 1;
424
+ display: flex;
425
+ flex: 1 1 auto;
426
+ flex-direction: column;
427
+ min-height: 0;
413
428
  overflow-x: clip;
414
429
  position: relative;
415
430
  }
@@ -420,6 +435,10 @@ function isSwipeIgnoredTarget(target) {
420
435
 
421
436
  .shell-route-transition__pane {
422
437
  background: rgb(var(--v-theme-background));
438
+ display: flex;
439
+ flex: 1 1 auto;
440
+ flex-direction: column;
441
+ min-height: 0;
423
442
  width: 100%;
424
443
  }
425
444
 
@@ -0,0 +1,22 @@
1
+ function normalizeText(value = "") {
2
+ return String(value || "").trim();
3
+ }
4
+
5
+ function resolveShellRouteTransitionKey({
6
+ routePathKey = "",
7
+ routeTransitionName = "",
8
+ surfaceId = ""
9
+ } = {}) {
10
+ if (normalizeText(routeTransitionName)) {
11
+ return normalizeText(routePathKey) || "/";
12
+ }
13
+
14
+ const surfaceKey = normalizeText(surfaceId);
15
+ if (surfaceKey && surfaceKey !== "*") {
16
+ return `surface:${surfaceKey}`;
17
+ }
18
+
19
+ return "stable";
20
+ }
21
+
22
+ export { resolveShellRouteTransitionKey };
@@ -14,7 +14,7 @@ import { RouterView } from "vue-router";
14
14
  <v-sheet rounded="lg" border class="settings-shell__panel">
15
15
  <div class="settings-shell__body">
16
16
  <nav class="settings-shell__nav" aria-label="Home settings sections">
17
- <v-list nav density="comfortable" rounded="lg" border>
17
+ <v-list nav density="compact" rounded="lg">
18
18
  <ShellOutlet target="home-settings:primary-menu" />
19
19
  </v-list>
20
20
  </nav>
@@ -53,7 +53,31 @@ import { RouterView } from "vue-router";
53
53
  }
54
54
 
55
55
  .settings-shell__content {
56
+ border-left: 1px solid rgba(var(--v-theme-on-surface), 0.18);
56
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;
57
81
  }
58
82
 
59
83
  @media (max-width: 960px) {
@@ -72,5 +96,10 @@ import { RouterView } from "vue-router";
72
96
  flex: 0 0 auto;
73
97
  min-height: 48px;
74
98
  }
99
+
100
+ .settings-shell__content {
101
+ border-left: 0;
102
+ padding-left: 0;
103
+ }
75
104
  }
76
105
  </style>
@@ -4,6 +4,7 @@ import test from "node:test";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import {
7
+ mdiAccountKeyOutline,
7
8
  mdiCogOutline,
8
9
  mdiConsoleNetworkOutline,
9
10
  mdiViewListOutline
@@ -29,6 +30,10 @@ test("shell-web leaves unknown explicit mdi metadata icons unchanged", () => {
29
30
  assert.equal(resolveMenuLinkIcon({ icon: "mdi-not-a-real-supported-icon" }), "mdi-not-a-real-supported-icon");
30
31
  });
31
32
 
33
+ test("shell-web accepts imported @mdi/js path constants as explicit menu icons", () => {
34
+ assert.equal(resolveMenuLinkIcon({ icon: mdiAccountKeyOutline }), mdiAccountKeyOutline);
35
+ });
36
+
32
37
  test("shell-web menu icon resolution does not import the full mdi namespace", async () => {
33
38
  const source = await readFile(path.join(PACKAGE_DIR, "src", "client", "lib", "menuIcons.js"), "utf8");
34
39
 
@@ -5,6 +5,7 @@ import { readFile } from "node:fs/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { assertGeneratedUiSourceContract } from "@jskit-ai/kernel/shared/support/generatedUiContract";
7
7
  import descriptor from "../package.descriptor.mjs";
8
+ import { resolveShellRouteTransitionKey } from "../src/client/support/routeTransitionKey.js";
8
9
 
9
10
  const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
10
11
  const PACKAGE_DIR = path.resolve(TEST_DIRECTORY, "..");
@@ -159,10 +160,54 @@ test("shell-web route transition keeps mobile route motion placement-driven", as
159
160
  assert.match(source, /router\.push\(nextEntry\.href\)/);
160
161
  assert.match(source, /isSwipeIgnoredTarget/);
161
162
  assert.match(source, /touch-action:\s*pan-y/);
163
+ assert.match(source, /\.shell-route-transition\s*\{[\s\S]*display:\s*flex/);
164
+ assert.match(source, /\.shell-route-transition\s*\{[\s\S]*min-height:\s*0/);
165
+ assert.match(source, /\.shell-route-transition__pane\s*\{[\s\S]*flex:\s*1 1 auto/);
166
+ assert.match(source, /\.shell-route-transition__pane\s*\{[\s\S]*min-height:\s*0/);
162
167
  assert.match(source, /transitionDirection\.value = nextIndex > previousIndex \? "forward" : "reverse"/);
168
+ assert.match(
169
+ source,
170
+ /const routeTransitionKey = computed\(\(\) => \{[\s\S]*const routePathKey = routeTransitionName\.value[\s\S]*normalizeComparablePathname\(route\?\.path \|\| route\?\.fullPath \|\| "\/"\)[\s\S]*resolveShellRouteTransitionKey\(\{[\s\S]*routeTransitionName: routeTransitionName\.value,[\s\S]*surfaceId: currentSurfaceId\.value[\s\S]*\}\);[\s\S]*\}\);/
171
+ );
163
172
  assert.match(source, /prefers-reduced-motion:\s*reduce/);
164
173
  });
165
174
 
175
+ test("shell-web route transition key preserves no-motion surfaces and animated path transitions", () => {
176
+ const previewSurfaceKey = "surface:vibe64-preview";
177
+ assert.equal(
178
+ resolveShellRouteTransitionKey({
179
+ routeTransitionName: "",
180
+ routePathKey: "/preview",
181
+ surfaceId: "vibe64-preview"
182
+ }),
183
+ previewSurfaceKey
184
+ );
185
+ assert.equal(
186
+ resolveShellRouteTransitionKey({
187
+ routeTransitionName: "",
188
+ routePathKey: "/preview/dashboard",
189
+ surfaceId: "vibe64-preview"
190
+ }),
191
+ previewSurfaceKey
192
+ );
193
+ assert.equal(
194
+ resolveShellRouteTransitionKey({
195
+ routeTransitionName: "shell-route-slide-forward",
196
+ routePathKey: "/dashboard",
197
+ surfaceId: "home"
198
+ }),
199
+ "/dashboard"
200
+ );
201
+ assert.equal(
202
+ resolveShellRouteTransitionKey({
203
+ routeTransitionName: "",
204
+ routePathKey: "/dashboard",
205
+ surfaceId: "*"
206
+ }),
207
+ "stable"
208
+ );
209
+ });
210
+
166
211
  test("shell-web settings landing page redirects to the starter child page", async () => {
167
212
  const source = await readFile(path.join(PACKAGE_DIR, "templates", "src", "pages", "home", "settings", "index.vue"), "utf8");
168
213