@jskit-ai/shell-web 0.1.163 → 0.1.165
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/fixtures/adaptive-shell/src/ScreenPage.vue +1 -1
- package/package.json +8 -253
- package/patterns/application-shell/PATTERN.md +66 -0
- package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home/index.vue +8 -8
- package/{templates → patterns/application-shell/example}/src/pages/home/index.vue +8 -8
- package/{templates → patterns/application-shell/example}/src/pages/home/settings/general/index.vue +1 -1
- package/{templates → patterns/application-shell/example}/src/pages/home/settings.vue +8 -8
- package/patterns/page-and-placement/PATTERN.md +76 -0
- package/patterns/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js +14 -0
- package/patterns/page-and-placement/example/src/components/SyncStatusElement.vue +16 -0
- package/patterns/page-and-placement/example/src/pages/home/reports/activity.vue +8 -0
- package/patterns/page-and-placement/example/src/pages/home/reports/index.vue +7 -0
- package/patterns/page-and-placement/example/src/pages/home/reports/overview.vue +8 -0
- package/patterns/page-and-placement/example/src/pages/home/reports.vue +33 -0
- package/patterns/page-and-placement/example/src/placement.js +52 -0
- package/patterns/page-and-placement/example/src/placementTopology.js +29 -0
- package/src/client/bootstrap/bootstrapPayloadHandlerRegistry.js +30 -42
- package/src/client/bootstrap/index.js +2 -3
- package/src/client/index.js +2 -11
- package/src/client/placement/runtime.js +9 -12
- package/src/client/providers/ShellWebClientProvider.js +95 -129
- package/src/client/requestRecovery/runtime.js +9 -29
- package/src/client/runtime/bootstrapRuntime.js +18 -29
- package/src/test/adaptiveShellSmoke.js +3 -9
- package/test/bootstrapRuntime.test.js +10 -61
- package/test/pageAndPlacementPattern.test.js +38 -0
- package/test/placementRuntime.test.js +36 -50
- package/test/playwrightContract.test.js +2 -12
- package/test/provider.test.js +126 -737
- package/test/settingsPlacementContract.test.js +75 -126
- package/src/server/support/localLinkItemScaffolds.js +0 -80
- package/test/bootstrapClaimContract.test.js +0 -76
- package/test/linkItemScaffoldContract.test.js +0 -248
- /package/{templates → patterns/application-shell/example}/expected-existing/src/App.vue +0 -0
- /package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/App.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/components/ShellLayout.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/components/menus/MenuLinkItem.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/components/menus/SurfaceAwareMenuLinkItem.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/components/menus/TabLinkItem.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/error.js +0 -0
- /package/{templates → patterns/application-shell/example}/src/pages/home/settings/index.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/pages/home.vue +0 -0
- /package/{templates → patterns/application-shell/example}/src/placement.js +0 -0
- /package/{templates → patterns/application-shell/example}/src/placementTopology.js +0 -0
- /package/{templates → patterns/application-shell/example}/tests/e2e/adaptive-shell.spec.ts +0 -0
|
@@ -7,7 +7,7 @@ const title = computed(() => String(route.meta.title || "Shell fixture"));
|
|
|
7
7
|
</script>
|
|
8
8
|
|
|
9
9
|
<template>
|
|
10
|
-
<main
|
|
10
|
+
<main tabindex="-1">
|
|
11
11
|
<h1>{{ title }}</h1>
|
|
12
12
|
<p>Package-owned browser fixture for the adaptive shell contract.</p>
|
|
13
13
|
</main>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/shell-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.165",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"./client/asyncModuleRecovery": "./src/client/asyncModuleRecovery/index.js",
|
|
13
13
|
"./client/requestRecovery": "./src/client/requestRecovery/index.js",
|
|
14
14
|
"./client/bootstrap": "./src/client/bootstrap/index.js",
|
|
15
|
-
"./server/support/localLinkItemScaffolds": "./src/server/support/localLinkItemScaffolds.js",
|
|
16
15
|
"./client/navigation/linkResolver": "./src/client/navigation/linkResolver.js",
|
|
17
16
|
"./client/navigation/usePaths": "./src/client/navigation/usePaths.js",
|
|
18
17
|
"./client/navigation/useSurfaceRouteContext": "./src/client/navigation/useSurfaceRouteContext.js",
|
|
@@ -30,7 +29,7 @@
|
|
|
30
29
|
},
|
|
31
30
|
"dependencies": {
|
|
32
31
|
"@mdi/js": "^7.4.47",
|
|
33
|
-
"@jskit-ai/kernel": "0.1.
|
|
32
|
+
"@jskit-ai/kernel": "0.1.161"
|
|
34
33
|
},
|
|
35
34
|
"peerDependencies": {
|
|
36
35
|
"pinia": "^3.0.4",
|
|
@@ -43,10 +42,7 @@
|
|
|
43
42
|
"kind": "runtime",
|
|
44
43
|
"capabilities": {
|
|
45
44
|
"provides": [
|
|
46
|
-
"
|
|
47
|
-
"runtime.web-error",
|
|
48
|
-
"runtime.web-async-module-recovery",
|
|
49
|
-
"runtime.web-request-recovery"
|
|
45
|
+
"client.shell"
|
|
50
46
|
],
|
|
51
47
|
"requires": []
|
|
52
48
|
},
|
|
@@ -109,19 +105,7 @@
|
|
|
109
105
|
"subpath": "./test/adaptiveShellSmoke",
|
|
110
106
|
"summary": "Exports reusable Playwright smoke coverage for generated adaptive shell layouts."
|
|
111
107
|
}
|
|
112
|
-
]
|
|
113
|
-
"containerTokens": {
|
|
114
|
-
"server": [],
|
|
115
|
-
"client": [
|
|
116
|
-
"runtime.web-placement.client",
|
|
117
|
-
"runtime.web-bootstrap.client",
|
|
118
|
-
"runtime.web-refresh.client",
|
|
119
|
-
"runtime.web-async-module-recovery.client",
|
|
120
|
-
"runtime.web-request-recovery.client",
|
|
121
|
-
"runtime.web-error.client",
|
|
122
|
-
"runtime.web-error.presentation-store.client"
|
|
123
|
-
]
|
|
124
|
-
}
|
|
108
|
+
]
|
|
125
109
|
},
|
|
126
110
|
"ui": {
|
|
127
111
|
"placements": {
|
|
@@ -180,7 +164,7 @@
|
|
|
180
164
|
"surfaces": [
|
|
181
165
|
"home"
|
|
182
166
|
],
|
|
183
|
-
"source": "
|
|
167
|
+
"source": "patterns/application-shell/example/src/pages/home/settings.vue"
|
|
184
168
|
}
|
|
185
169
|
],
|
|
186
170
|
"topology": {
|
|
@@ -360,7 +344,7 @@
|
|
|
360
344
|
"home"
|
|
361
345
|
],
|
|
362
346
|
"order": 50,
|
|
363
|
-
"source": "
|
|
347
|
+
"source": "patterns/application-shell/example/src/placement.js"
|
|
364
348
|
},
|
|
365
349
|
{
|
|
366
350
|
"id": "shell-web.home.menu.settings",
|
|
@@ -370,7 +354,7 @@
|
|
|
370
354
|
"home"
|
|
371
355
|
],
|
|
372
356
|
"order": 100,
|
|
373
|
-
"source": "
|
|
357
|
+
"source": "patterns/application-shell/example/src/placement.js"
|
|
374
358
|
},
|
|
375
359
|
{
|
|
376
360
|
"id": "shell-web.home.settings.general",
|
|
@@ -381,240 +365,11 @@
|
|
|
381
365
|
"home"
|
|
382
366
|
],
|
|
383
367
|
"order": 100,
|
|
384
|
-
"source": "
|
|
368
|
+
"source": "patterns/application-shell/example/src/placement.js"
|
|
385
369
|
}
|
|
386
370
|
]
|
|
387
371
|
}
|
|
388
372
|
}
|
|
389
|
-
},
|
|
390
|
-
"mutations": {
|
|
391
|
-
"dependencies": {
|
|
392
|
-
"runtime": {
|
|
393
|
-
"@mdi/js": "^7.4.47",
|
|
394
|
-
"@jskit-ai/kernel": "0.1.159"
|
|
395
|
-
},
|
|
396
|
-
"dev": {
|
|
397
|
-
"@playwright/test": "1.61.1"
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
"packageJson": {
|
|
401
|
-
"scripts": {
|
|
402
|
-
"dev:all": "vite",
|
|
403
|
-
"dev:home": "VITE_SURFACE=home vite"
|
|
404
|
-
}
|
|
405
|
-
},
|
|
406
|
-
"procfile": {},
|
|
407
|
-
"text": [],
|
|
408
|
-
"source": [
|
|
409
|
-
{
|
|
410
|
-
"op": "ensure-import",
|
|
411
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
412
|
-
"defaultImport": "MenuLinkItem",
|
|
413
|
-
"from": "/src/components/menus/MenuLinkItem.vue",
|
|
414
|
-
"reason": "Bind app-owned shell menu link-item scaffold into local main client provider imports.",
|
|
415
|
-
"category": "shell-web",
|
|
416
|
-
"id": "shell-web-main-client-provider-menu-link-item-import"
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
"op": "ensure-import",
|
|
420
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
421
|
-
"defaultImport": "SurfaceAwareMenuLinkItem",
|
|
422
|
-
"from": "/src/components/menus/SurfaceAwareMenuLinkItem.vue",
|
|
423
|
-
"reason": "Bind app-owned shell surface-aware menu link-item scaffold into local main client provider imports.",
|
|
424
|
-
"category": "shell-web",
|
|
425
|
-
"id": "shell-web-main-client-provider-surface-aware-menu-link-item-import"
|
|
426
|
-
},
|
|
427
|
-
{
|
|
428
|
-
"op": "ensure-import",
|
|
429
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
430
|
-
"defaultImport": "TabLinkItem",
|
|
431
|
-
"from": "/src/components/menus/TabLinkItem.vue",
|
|
432
|
-
"reason": "Bind app-owned shell tab link-item scaffold into local main client provider imports.",
|
|
433
|
-
"category": "shell-web",
|
|
434
|
-
"id": "shell-web-main-client-provider-tab-link-item-import"
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
"op": "ensure-call",
|
|
438
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
439
|
-
"callee": "registerMainClientComponent",
|
|
440
|
-
"args": [
|
|
441
|
-
"\"local.main.ui.menu-link-item\"",
|
|
442
|
-
"() => MenuLinkItem"
|
|
443
|
-
],
|
|
444
|
-
"beforeClass": "MainClientProvider",
|
|
445
|
-
"reason": "Bind app-owned shell menu link-item token into local main client provider registry.",
|
|
446
|
-
"category": "shell-web",
|
|
447
|
-
"id": "shell-web-main-client-provider-menu-link-item-register"
|
|
448
|
-
},
|
|
449
|
-
{
|
|
450
|
-
"op": "ensure-call",
|
|
451
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
452
|
-
"callee": "registerMainClientComponent",
|
|
453
|
-
"args": [
|
|
454
|
-
"\"local.main.ui.surface-aware-menu-link-item\"",
|
|
455
|
-
"() => SurfaceAwareMenuLinkItem"
|
|
456
|
-
],
|
|
457
|
-
"beforeClass": "MainClientProvider",
|
|
458
|
-
"reason": "Bind app-owned shell surface-aware menu link-item token into local main client provider registry.",
|
|
459
|
-
"category": "shell-web",
|
|
460
|
-
"id": "shell-web-main-client-provider-surface-aware-menu-link-item-register"
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
"op": "ensure-call",
|
|
464
|
-
"file": "packages/main/src/client/providers/MainClientProvider.js",
|
|
465
|
-
"callee": "registerMainClientComponent",
|
|
466
|
-
"args": [
|
|
467
|
-
"\"local.main.ui.tab-link-item\"",
|
|
468
|
-
"() => TabLinkItem"
|
|
469
|
-
],
|
|
470
|
-
"beforeClass": "MainClientProvider",
|
|
471
|
-
"reason": "Bind app-owned shell tab link-item token into local main client provider registry.",
|
|
472
|
-
"category": "shell-web",
|
|
473
|
-
"id": "shell-web-main-client-provider-tab-link-item-register"
|
|
474
|
-
}
|
|
475
|
-
],
|
|
476
|
-
"files": [
|
|
477
|
-
{
|
|
478
|
-
"from": "templates/src/App.vue",
|
|
479
|
-
"to": "src/App.vue",
|
|
480
|
-
"ownership": "app",
|
|
481
|
-
"expectedExistingFrom": "templates/expected-existing/src/App.vue",
|
|
482
|
-
"reason": "Install full-width shell app root with shell-web error host and edge-to-edge layout.",
|
|
483
|
-
"category": "shell-web",
|
|
484
|
-
"id": "shell-web-app-root"
|
|
485
|
-
},
|
|
486
|
-
{
|
|
487
|
-
"from": "templates/src/components/ShellLayout.vue",
|
|
488
|
-
"to": "src/components/ShellLayout.vue",
|
|
489
|
-
"ownership": "app",
|
|
490
|
-
"reason": "Install app-owned shell layout component so apps can customize structure and slots.",
|
|
491
|
-
"category": "shell-web",
|
|
492
|
-
"id": "shell-web-component-shell-layout"
|
|
493
|
-
},
|
|
494
|
-
{
|
|
495
|
-
"from": "templates/src/components/menus/MenuLinkItem.vue",
|
|
496
|
-
"to": "src/components/menus/MenuLinkItem.vue",
|
|
497
|
-
"ownership": "app",
|
|
498
|
-
"reason": "Install app-owned shell menu link-item scaffold for local placement customization.",
|
|
499
|
-
"category": "shell-web",
|
|
500
|
-
"id": "shell-web-component-menu-link-item"
|
|
501
|
-
},
|
|
502
|
-
{
|
|
503
|
-
"from": "templates/src/components/menus/SurfaceAwareMenuLinkItem.vue",
|
|
504
|
-
"to": "src/components/menus/SurfaceAwareMenuLinkItem.vue",
|
|
505
|
-
"ownership": "app",
|
|
506
|
-
"reason": "Install app-owned surface-aware shell menu link-item scaffold for local placement customization.",
|
|
507
|
-
"category": "shell-web",
|
|
508
|
-
"id": "shell-web-component-surface-aware-menu-link-item"
|
|
509
|
-
},
|
|
510
|
-
{
|
|
511
|
-
"from": "templates/src/components/menus/TabLinkItem.vue",
|
|
512
|
-
"to": "src/components/menus/TabLinkItem.vue",
|
|
513
|
-
"ownership": "app",
|
|
514
|
-
"reason": "Install app-owned shell tab link-item scaffold for local placement customization.",
|
|
515
|
-
"category": "shell-web",
|
|
516
|
-
"id": "shell-web-component-tab-link-item"
|
|
517
|
-
},
|
|
518
|
-
{
|
|
519
|
-
"from": "templates/src/error.js",
|
|
520
|
-
"to": "src/error.js",
|
|
521
|
-
"ownership": "app",
|
|
522
|
-
"reason": "Install app-owned error runtime policy and presenter config scaffold.",
|
|
523
|
-
"category": "shell-web",
|
|
524
|
-
"id": "shell-web-error-config"
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
"from": "templates/src/placement.js",
|
|
528
|
-
"to": "src/placement.js",
|
|
529
|
-
"ownership": "app",
|
|
530
|
-
"reason": "Install app-owned placement registry scaffold used by shell-web placement runtime.",
|
|
531
|
-
"category": "shell-web",
|
|
532
|
-
"id": "shell-web-placement-registry"
|
|
533
|
-
},
|
|
534
|
-
{
|
|
535
|
-
"from": "templates/src/placementTopology.js",
|
|
536
|
-
"to": "src/placementTopology.js",
|
|
537
|
-
"ownership": "app",
|
|
538
|
-
"reason": "Install app-owned semantic placement topology used by shell-web placement runtime.",
|
|
539
|
-
"category": "shell-web",
|
|
540
|
-
"id": "shell-web-placement-topology"
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
"from": "templates/src/pages/home.vue",
|
|
544
|
-
"toSurface": "home",
|
|
545
|
-
"toSurfaceRoot": true,
|
|
546
|
-
"ownership": "app",
|
|
547
|
-
"expectedExistingFrom": "templates/expected-existing/src/pages/home.vue",
|
|
548
|
-
"reason": "Install shell-driven home wrapper page.",
|
|
549
|
-
"category": "shell-web",
|
|
550
|
-
"id": "shell-web-page-home-wrapper",
|
|
551
|
-
"when": {
|
|
552
|
-
"config": "surfaceDefinitions.home.enabled",
|
|
553
|
-
"equals": "true"
|
|
554
|
-
}
|
|
555
|
-
},
|
|
556
|
-
{
|
|
557
|
-
"from": "templates/src/pages/home/index.vue",
|
|
558
|
-
"toSurface": "home",
|
|
559
|
-
"toSurfacePath": "index.vue",
|
|
560
|
-
"ownership": "app",
|
|
561
|
-
"expectedExistingFrom": "templates/expected-existing/src/pages/home/index.vue",
|
|
562
|
-
"reason": "Install shell-driven home surface starter page.",
|
|
563
|
-
"category": "shell-web",
|
|
564
|
-
"id": "shell-web-page-home",
|
|
565
|
-
"when": {
|
|
566
|
-
"config": "surfaceDefinitions.home.enabled",
|
|
567
|
-
"equals": "true"
|
|
568
|
-
}
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
"from": "templates/src/pages/home/settings.vue",
|
|
572
|
-
"toSurface": "home",
|
|
573
|
-
"toSurfacePath": "settings.vue",
|
|
574
|
-
"ownership": "app",
|
|
575
|
-
"reason": "Install shell-driven home settings shell route with section navigation.",
|
|
576
|
-
"category": "shell-web",
|
|
577
|
-
"id": "shell-web-page-home-settings-shell",
|
|
578
|
-
"when": {
|
|
579
|
-
"config": "surfaceDefinitions.home.enabled",
|
|
580
|
-
"equals": "true"
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
{
|
|
584
|
-
"from": "templates/src/pages/home/settings/index.vue",
|
|
585
|
-
"toSurface": "home",
|
|
586
|
-
"toSurfacePath": "settings/index.vue",
|
|
587
|
-
"ownership": "app",
|
|
588
|
-
"reason": "Install shell-driven home settings redirect so the starter settings shell lands on a real child page.",
|
|
589
|
-
"category": "shell-web",
|
|
590
|
-
"id": "shell-web-page-home-settings",
|
|
591
|
-
"when": {
|
|
592
|
-
"config": "surfaceDefinitions.home.enabled",
|
|
593
|
-
"equals": "true"
|
|
594
|
-
}
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
"from": "templates/src/pages/home/settings/general/index.vue",
|
|
598
|
-
"toSurface": "home",
|
|
599
|
-
"toSurfacePath": "settings/general/index.vue",
|
|
600
|
-
"ownership": "app",
|
|
601
|
-
"reason": "Install shell-driven general settings child page with a tiny browser-local shell preference example.",
|
|
602
|
-
"category": "shell-web",
|
|
603
|
-
"id": "shell-web-page-home-settings-general",
|
|
604
|
-
"when": {
|
|
605
|
-
"config": "surfaceDefinitions.home.enabled",
|
|
606
|
-
"equals": "true"
|
|
607
|
-
}
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
"from": "templates/tests/e2e/adaptive-shell.spec.ts",
|
|
611
|
-
"to": "tests/e2e/adaptive-shell.spec.ts",
|
|
612
|
-
"ownership": "app",
|
|
613
|
-
"reason": "Install compact/medium/expanded Playwright smoke coverage for the adaptive shell.",
|
|
614
|
-
"category": "shell-web",
|
|
615
|
-
"id": "shell-web-test-adaptive-shell-smoke"
|
|
616
|
-
}
|
|
617
|
-
]
|
|
618
373
|
}
|
|
619
374
|
}
|
|
620
375
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: shell/application-shell
|
|
3
|
+
title: Responsive application shell
|
|
4
|
+
summary: Compose the JSKIT responsive shell, semantic placements, settings navigation, and accessible adaptive-shell browser checks.
|
|
5
|
+
keywords: adaptive, app, layout, navigation, placement, responsive, settings, shell
|
|
6
|
+
requires: @jskit-ai/kernel, @jskit-ai/shell-web
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Responsive application shell
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
Use this pattern when adding the standard responsive JSKIT shell to an existing
|
|
14
|
+
application foundation.
|
|
15
|
+
|
|
16
|
+
## Do not use when
|
|
17
|
+
|
|
18
|
+
Do not copy the entire shell into an application that already has a coherent
|
|
19
|
+
JSKIT shell. Use the narrower page-and-placement pattern for incremental UI.
|
|
20
|
+
|
|
21
|
+
## Product decisions
|
|
22
|
+
|
|
23
|
+
Choose the product navigation, settings sections, surfaces, labels, icons, and
|
|
24
|
+
which controls remain visible at compact, medium, and expanded widths.
|
|
25
|
+
|
|
26
|
+
## Framework APIs
|
|
27
|
+
|
|
28
|
+
Use the shell provider, `ShellLayout`, `ShellOutlet`, link components,
|
|
29
|
+
placement registry, topology, and adaptive smoke helper exported by
|
|
30
|
+
`@jskit-ai/shell-web`.
|
|
31
|
+
|
|
32
|
+
## Invariants
|
|
33
|
+
|
|
34
|
+
- `App.vue`, the shell layout, placements, topology, and route pages agree.
|
|
35
|
+
- Navigation uses semantic placements rather than direct component injection.
|
|
36
|
+
- Compact controls have accessible names and at least 48 CSS-pixel targets.
|
|
37
|
+
- Cached route/resource state hydrates writable UI state synchronously or with
|
|
38
|
+
immediate watchers.
|
|
39
|
+
- Loading uses stable skeletons, not spinners or shifting status banners.
|
|
40
|
+
- The adaptive browser check closes navigation through its public close control.
|
|
41
|
+
|
|
42
|
+
## Example files
|
|
43
|
+
|
|
44
|
+
`example/src/` contains the complete shell composition, standard link
|
|
45
|
+
components, settings routes, placement registry, and topology. The browser
|
|
46
|
+
example under `example/tests/` exercises the public adaptive-shell contract.
|
|
47
|
+
The `expected-existing/` files show the minimal foundation shapes replaced by
|
|
48
|
+
the complete shell; they are reference inputs, not merge instructions.
|
|
49
|
+
|
|
50
|
+
## Variation points
|
|
51
|
+
|
|
52
|
+
Change product navigation, surfaces, routes, settings sections, placements,
|
|
53
|
+
responsive outlet mappings, labels, icons, and application-owned components.
|
|
54
|
+
|
|
55
|
+
## Verification
|
|
56
|
+
|
|
57
|
+
Exercise direct navigation, browser back/forward with a warm cache, keyboard
|
|
58
|
+
navigation, current-link state, and compact/medium/expanded layouts. Run client
|
|
59
|
+
tests and a production build.
|
|
60
|
+
|
|
61
|
+
## Avoid
|
|
62
|
+
|
|
63
|
+
- custom navigation registries beside placements
|
|
64
|
+
- clicking Vuetify scrims or coordinates in browser tests
|
|
65
|
+
- magic generated-page classes
|
|
66
|
+
- source mutation or overwrite machinery
|
package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home/index.vue
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section class="
|
|
2
|
+
<section class="home-start-screen d-flex flex-column ga-4">
|
|
3
3
|
<header>
|
|
4
4
|
<p class="text-overline text-medium-emphasis mb-1">Home</p>
|
|
5
5
|
<h1 class="home-start-screen__title">Home base</h1>
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
</template>
|
|
19
19
|
|
|
20
20
|
<style scoped>
|
|
21
|
-
.
|
|
22
|
-
--
|
|
23
|
-
--
|
|
21
|
+
.home-start-screen {
|
|
22
|
+
--home-start-title-size: 2rem;
|
|
23
|
+
--home-start-panel-padding: 1rem;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
.home-start-screen {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.home-start-screen__title {
|
|
32
|
-
font-size: var(--
|
|
32
|
+
font-size: var(--home-start-title-size);
|
|
33
33
|
font-weight: 700;
|
|
34
34
|
letter-spacing: 0;
|
|
35
35
|
line-height: 1.08;
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
.home-start-screen__panel {
|
|
40
|
-
padding: var(--
|
|
40
|
+
padding: var(--home-start-panel-padding);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
@media (max-width: 640px) {
|
|
44
|
-
.
|
|
45
|
-
--
|
|
44
|
+
.home-start-screen {
|
|
45
|
+
--home-start-title-size: 1.5rem;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
</style>
|
|
@@ -28,7 +28,7 @@ const health = computed(() => {
|
|
|
28
28
|
</script>
|
|
29
29
|
|
|
30
30
|
<template>
|
|
31
|
-
<section class="
|
|
31
|
+
<section class="home-surface-screen d-flex flex-column ga-4">
|
|
32
32
|
<header class="home-surface-screen__header">
|
|
33
33
|
<div>
|
|
34
34
|
<p class="text-overline text-medium-emphasis mb-1">Home</p>
|
|
@@ -55,9 +55,9 @@ const health = computed(() => {
|
|
|
55
55
|
</template>
|
|
56
56
|
|
|
57
57
|
<style scoped>
|
|
58
|
-
.
|
|
59
|
-
--
|
|
60
|
-
--
|
|
58
|
+
.home-surface-screen {
|
|
59
|
+
--home-surface-title-size: 2rem;
|
|
60
|
+
--home-surface-panel-padding: 1rem;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
.home-surface-screen__header {
|
|
@@ -68,7 +68,7 @@ const health = computed(() => {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.home-surface-screen__title {
|
|
71
|
-
font-size: var(--
|
|
71
|
+
font-size: var(--home-surface-title-size);
|
|
72
72
|
font-weight: 700;
|
|
73
73
|
letter-spacing: 0;
|
|
74
74
|
line-height: 1.1;
|
|
@@ -80,7 +80,7 @@ const health = computed(() => {
|
|
|
80
80
|
display: flex;
|
|
81
81
|
flex-wrap: wrap;
|
|
82
82
|
gap: 1rem;
|
|
83
|
-
padding: var(--
|
|
83
|
+
padding: var(--home-surface-panel-padding);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
.home-surface-screen__status {
|
|
@@ -90,8 +90,8 @@ const health = computed(() => {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
@media (max-width: 640px) {
|
|
93
|
-
.
|
|
94
|
-
--
|
|
93
|
+
.home-surface-screen {
|
|
94
|
+
--home-surface-title-size: 1.5rem;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
.home-surface-screen__header {
|
package/{templates → patterns/application-shell/example}/src/pages/home/settings/general/index.vue
RENAMED
|
@@ -15,7 +15,7 @@ const drawerDefaultOpenModel = computed({
|
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
17
|
<template>
|
|
18
|
-
<section class="
|
|
18
|
+
<section class="settings-general-screen d-flex flex-column ga-4">
|
|
19
19
|
<div>
|
|
20
20
|
<h2 class="text-h6 mb-2">Navigation</h2>
|
|
21
21
|
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
@@ -4,7 +4,7 @@ import { RouterView } from "vue-router";
|
|
|
4
4
|
</script>
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
|
-
<section class="
|
|
7
|
+
<section class="settings-shell d-flex flex-column ga-4">
|
|
8
8
|
<header>
|
|
9
9
|
<p class="text-overline text-medium-emphasis mb-1">Settings</p>
|
|
10
10
|
<h1 class="settings-shell__title">Home settings</h1>
|
|
@@ -28,13 +28,13 @@ import { RouterView } from "vue-router";
|
|
|
28
28
|
</template>
|
|
29
29
|
|
|
30
30
|
<style scoped>
|
|
31
|
-
.
|
|
32
|
-
--
|
|
33
|
-
--
|
|
31
|
+
.settings-shell {
|
|
32
|
+
--settings-shell-title-size: 1.85rem;
|
|
33
|
+
--settings-shell-panel-padding: 1rem;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
.settings-shell__title {
|
|
37
|
-
font-size: var(--
|
|
37
|
+
font-size: var(--settings-shell-title-size);
|
|
38
38
|
font-weight: 650;
|
|
39
39
|
letter-spacing: 0;
|
|
40
40
|
line-height: 1.15;
|
|
@@ -49,7 +49,7 @@ import { RouterView } from "vue-router";
|
|
|
49
49
|
display: grid;
|
|
50
50
|
gap: 1rem;
|
|
51
51
|
grid-template-columns: minmax(12rem, 16rem) minmax(0, 1fr);
|
|
52
|
-
padding: var(--
|
|
52
|
+
padding: var(--settings-shell-panel-padding);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.settings-shell__content {
|
|
@@ -81,8 +81,8 @@ import { RouterView } from "vue-router";
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
@media (max-width: 960px) {
|
|
84
|
-
.
|
|
85
|
-
--
|
|
84
|
+
.settings-shell {
|
|
85
|
+
--settings-shell-title-size: 1.35rem;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.settings-shell__body {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: ui/page-and-placement
|
|
3
|
+
title: Routed page, section navigation, and placed element
|
|
4
|
+
summary: Add product routes and shell extensions through file routing, semantic placements, topology, and registered component tokens.
|
|
5
|
+
keywords: component, navigation, outlet, page, placement, routes, section, shell, subpages, vue
|
|
6
|
+
requires: @jskit-ai/kernel, @jskit-ai/shell-web
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Routed page, section navigation, and placed element
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
Use this pattern for an application-owned route page, a section with child
|
|
14
|
+
routes, or a component rendered in an existing shell placement. It demonstrates
|
|
15
|
+
the same public contracts for all three without a page generator.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
Do not use section navigation for a single page, add a primary link for a detail
|
|
20
|
+
or workflow route, or create a new placement system when an existing semantic
|
|
21
|
+
placement fits. CRUD routes should use the CRUD screen pattern.
|
|
22
|
+
|
|
23
|
+
## Product decisions
|
|
24
|
+
|
|
25
|
+
Decide the route, surface, navigation role, label, icon, section ownership,
|
|
26
|
+
ordering, and whether the content belongs in navigation or a component outlet.
|
|
27
|
+
These are product information architecture decisions, not defaults to infer from
|
|
28
|
+
a filename alone.
|
|
29
|
+
|
|
30
|
+
## Invariants
|
|
31
|
+
|
|
32
|
+
- File routes and placement destinations agree.
|
|
33
|
+
- Placement ids describe product ownership, not the authoring mechanism.
|
|
34
|
+
- Child links share an explicit section owner.
|
|
35
|
+
- Topology maps each semantic placement to concrete responsive outlets.
|
|
36
|
+
- Placed components are registered by token through a client provider.
|
|
37
|
+
- Compact navigation controls remain accessible and at least 48 CSS pixels.
|
|
38
|
+
- Application source has no generated-file marker, receipt, or overwrite state.
|
|
39
|
+
|
|
40
|
+
## Framework APIs
|
|
41
|
+
|
|
42
|
+
Use `createPlacementRegistry()`, `addPlacement()`, `addPlacementTopology()`, and
|
|
43
|
+
`ShellOutlet` from `@jskit-ai/shell-web`. Use file routes and
|
|
44
|
+
`redirectToChild()` from `@jskit-ai/kernel`. Register app-owned component tokens
|
|
45
|
+
through the application's normal client provider.
|
|
46
|
+
|
|
47
|
+
## Example files
|
|
48
|
+
|
|
49
|
+
`example/` contains a Reports section with Overview and Activity child routes,
|
|
50
|
+
the matching section-navigation placements/topology, and a Sync Status component
|
|
51
|
+
placed in the existing shell status area.
|
|
52
|
+
|
|
53
|
+
## Variation points
|
|
54
|
+
|
|
55
|
+
Change the route hierarchy, surface, semantic target, section owner, component
|
|
56
|
+
token, order, label, icon, and responsive outlet mapping. Omit the parent host
|
|
57
|
+
and topology when no child navigation is needed. Omit provider registration for
|
|
58
|
+
link-only placements.
|
|
59
|
+
|
|
60
|
+
## Verification
|
|
61
|
+
|
|
62
|
+
- navigate directly to each route and through each visible link
|
|
63
|
+
- verify the section index redirects to its intended child
|
|
64
|
+
- verify the placed component resolves through its provider token
|
|
65
|
+
- exercise compact, medium, and expanded variants
|
|
66
|
+
- verify current-link state, keyboard operation, target size, and no overflow
|
|
67
|
+
- run client tests and the production build
|
|
68
|
+
|
|
69
|
+
## Avoid
|
|
70
|
+
|
|
71
|
+
- generator-branded placement ids or CSS classes
|
|
72
|
+
- navigation links for detail/workflow routes unless explicitly chosen
|
|
73
|
+
- duplicated route tables beside file routing
|
|
74
|
+
- hard-coded component instances where a semantic placement is required
|
|
75
|
+
- placeholder pages that instruct developers to replace their content
|
|
76
|
+
- provenance comments, receipts, or hidden mutation markers
|
package/patterns/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
|
|
2
|
+
import SyncStatusElement from "/src/components/SyncStatusElement.vue";
|
|
3
|
+
|
|
4
|
+
const MainClientProvider = defineProvider({
|
|
5
|
+
id: "local.main.client",
|
|
6
|
+
requires: {
|
|
7
|
+
components: "client.components"
|
|
8
|
+
},
|
|
9
|
+
setup({ components }) {
|
|
10
|
+
components.register("local.main.ui.sync-status", SyncStatusElement);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export { MainClientProvider };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="sync-status" role="status" aria-label="Catalogue synchronized">
|
|
3
|
+
<v-icon icon="mdi-cloud-check-outline" size="small" aria-hidden="true" />
|
|
4
|
+
<span class="text-body-2">Synced</span>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<style scoped>
|
|
9
|
+
.sync-status {
|
|
10
|
+
align-items: center;
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
gap: 0.5rem;
|
|
13
|
+
min-height: 48px;
|
|
14
|
+
min-width: 0;
|
|
15
|
+
}
|
|
16
|
+
</style>
|