@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.
Files changed (46) hide show
  1. package/fixtures/adaptive-shell/src/ScreenPage.vue +1 -1
  2. package/package.json +8 -253
  3. package/patterns/application-shell/PATTERN.md +66 -0
  4. package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home/index.vue +8 -8
  5. package/{templates → patterns/application-shell/example}/src/pages/home/index.vue +8 -8
  6. package/{templates → patterns/application-shell/example}/src/pages/home/settings/general/index.vue +1 -1
  7. package/{templates → patterns/application-shell/example}/src/pages/home/settings.vue +8 -8
  8. package/patterns/page-and-placement/PATTERN.md +76 -0
  9. package/patterns/page-and-placement/example/packages/main/src/client/providers/MainClientProvider.js +14 -0
  10. package/patterns/page-and-placement/example/src/components/SyncStatusElement.vue +16 -0
  11. package/patterns/page-and-placement/example/src/pages/home/reports/activity.vue +8 -0
  12. package/patterns/page-and-placement/example/src/pages/home/reports/index.vue +7 -0
  13. package/patterns/page-and-placement/example/src/pages/home/reports/overview.vue +8 -0
  14. package/patterns/page-and-placement/example/src/pages/home/reports.vue +33 -0
  15. package/patterns/page-and-placement/example/src/placement.js +52 -0
  16. package/patterns/page-and-placement/example/src/placementTopology.js +29 -0
  17. package/src/client/bootstrap/bootstrapPayloadHandlerRegistry.js +30 -42
  18. package/src/client/bootstrap/index.js +2 -3
  19. package/src/client/index.js +2 -11
  20. package/src/client/placement/runtime.js +9 -12
  21. package/src/client/providers/ShellWebClientProvider.js +95 -129
  22. package/src/client/requestRecovery/runtime.js +9 -29
  23. package/src/client/runtime/bootstrapRuntime.js +18 -29
  24. package/src/test/adaptiveShellSmoke.js +3 -9
  25. package/test/bootstrapRuntime.test.js +10 -61
  26. package/test/pageAndPlacementPattern.test.js +38 -0
  27. package/test/placementRuntime.test.js +36 -50
  28. package/test/playwrightContract.test.js +2 -12
  29. package/test/provider.test.js +126 -737
  30. package/test/settingsPlacementContract.test.js +75 -126
  31. package/src/server/support/localLinkItemScaffolds.js +0 -80
  32. package/test/bootstrapClaimContract.test.js +0 -76
  33. package/test/linkItemScaffoldContract.test.js +0 -248
  34. /package/{templates → patterns/application-shell/example}/expected-existing/src/App.vue +0 -0
  35. /package/{templates → patterns/application-shell/example}/expected-existing/src/pages/home.vue +0 -0
  36. /package/{templates → patterns/application-shell/example}/src/App.vue +0 -0
  37. /package/{templates → patterns/application-shell/example}/src/components/ShellLayout.vue +0 -0
  38. /package/{templates → patterns/application-shell/example}/src/components/menus/MenuLinkItem.vue +0 -0
  39. /package/{templates → patterns/application-shell/example}/src/components/menus/SurfaceAwareMenuLinkItem.vue +0 -0
  40. /package/{templates → patterns/application-shell/example}/src/components/menus/TabLinkItem.vue +0 -0
  41. /package/{templates → patterns/application-shell/example}/src/error.js +0 -0
  42. /package/{templates → patterns/application-shell/example}/src/pages/home/settings/index.vue +0 -0
  43. /package/{templates → patterns/application-shell/example}/src/pages/home.vue +0 -0
  44. /package/{templates → patterns/application-shell/example}/src/placement.js +0 -0
  45. /package/{templates → patterns/application-shell/example}/src/placementTopology.js +0 -0
  46. /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 class="generated-ui-screen" tabindex="-1">
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.163",
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.159"
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
- "runtime.web-placement",
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": "templates/src/pages/home/settings.vue"
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": "templates/src/placement.js"
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": "templates/src/placement.js"
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": "templates/src/placement.js"
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
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <section class="generated-ui-screen generated-ui-screen--app home-start-screen d-flex flex-column ga-4">
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
- .generated-ui-screen {
22
- --generated-ui-screen-title-size: 2rem;
23
- --generated-ui-screen-panel-padding: 1rem;
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(--generated-ui-screen-title-size);
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(--generated-ui-screen-panel-padding);
40
+ padding: var(--home-start-panel-padding);
41
41
  }
42
42
 
43
43
  @media (max-width: 640px) {
44
- .generated-ui-screen {
45
- --generated-ui-screen-title-size: 1.5rem;
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="generated-ui-screen generated-ui-screen--app home-surface-screen d-flex flex-column ga-4">
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
- .generated-ui-screen {
59
- --generated-ui-screen-title-size: 2rem;
60
- --generated-ui-screen-panel-padding: 1rem;
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(--generated-ui-screen-title-size);
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(--generated-ui-screen-panel-padding);
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
- .generated-ui-screen {
94
- --generated-ui-screen-title-size: 1.5rem;
93
+ .home-surface-screen {
94
+ --home-surface-title-size: 1.5rem;
95
95
  }
96
96
 
97
97
  .home-surface-screen__header {
@@ -15,7 +15,7 @@ const drawerDefaultOpenModel = computed({
15
15
  </script>
16
16
 
17
17
  <template>
18
- <section class="generated-ui-screen generated-ui-screen--settings settings-general-screen d-flex flex-column ga-4">
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="generated-ui-screen generated-ui-screen--settings settings-shell d-flex flex-column ga-4">
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
- .generated-ui-screen {
32
- --generated-ui-screen-title-size: 1.85rem;
33
- --generated-ui-screen-panel-padding: 1rem;
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(--generated-ui-screen-title-size);
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(--generated-ui-screen-panel-padding);
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
- .generated-ui-screen {
85
- --generated-ui-screen-title-size: 1.35rem;
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
@@ -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>
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <section aria-labelledby="reports-activity-title">
3
+ <h2 id="reports-activity-title" class="text-h5 mb-2">Activity</h2>
4
+ <p class="text-body-1 text-medium-emphasis mb-0">
5
+ Recent catalogue changes appear here.
6
+ </p>
7
+ </section>
8
+ </template>