@jskit-ai/jskit-catalog 0.2.30 → 0.2.31

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.
@@ -6,54 +6,54 @@
6
6
  "release": {
7
7
  "packages": {
8
8
  "@jskit-ai/agent": "0.1.1",
9
- "@jskit-ai/agent-docs": "0.1.278",
9
+ "@jskit-ai/agent-docs": "0.1.279",
10
10
  "@jskit-ai/agent-runtime": "0.1.2",
11
11
  "@jskit-ai/architecture-inspector": "0.1.8",
12
- "@jskit-ai/assistant-core": "0.2.23",
13
- "@jskit-ai/assistant-runtime": "0.2.25",
14
- "@jskit-ai/assistant-voice": "0.2.25",
15
- "@jskit-ai/assistant-voice-sherpa": "0.1.72",
12
+ "@jskit-ai/assistant-core": "0.2.24",
13
+ "@jskit-ai/assistant-runtime": "0.2.26",
14
+ "@jskit-ai/assistant-voice": "0.2.26",
15
+ "@jskit-ai/assistant-voice-sherpa": "0.1.73",
16
16
  "@jskit-ai/auth-core": "0.2.10",
17
- "@jskit-ai/auth-provider-local-core": "0.2.12",
18
- "@jskit-ai/auth-provider-local-db-core": "0.1.153",
19
- "@jskit-ai/auth-provider-oidc-core": "0.2.16",
17
+ "@jskit-ai/auth-provider-local-core": "0.2.13",
18
+ "@jskit-ai/auth-provider-local-db-core": "0.1.154",
19
+ "@jskit-ai/auth-provider-oidc-core": "0.2.17",
20
20
  "@jskit-ai/auth-provider-supabase-core": "0.2.12",
21
- "@jskit-ai/auth-web": "0.2.14",
21
+ "@jskit-ai/auth-web": "0.2.15",
22
22
  "@jskit-ai/config-eslint": "0.1.245",
23
23
  "@jskit-ai/connector-google-calendar": "0.2.11",
24
24
  "@jskit-ai/connectors-catalog": "0.2.11",
25
25
  "@jskit-ai/connectors-core": "0.2.11",
26
26
  "@jskit-ai/connectors-web": "0.2.11",
27
27
  "@jskit-ai/console-core": "0.2.12",
28
- "@jskit-ai/console-web": "0.1.236",
28
+ "@jskit-ai/console-web": "0.1.237",
29
29
  "@jskit-ai/crud-core": "0.2.11",
30
30
  "@jskit-ai/database-runtime": "0.1.259",
31
31
  "@jskit-ai/database-runtime-mysql": "0.1.257",
32
32
  "@jskit-ai/database-runtime-postgres": "0.1.256",
33
33
  "@jskit-ai/http-runtime": "0.2.10",
34
- "@jskit-ai/http-web": "0.2.17",
35
- "@jskit-ai/jskit-catalog": "0.2.30",
34
+ "@jskit-ai/http-web": "0.2.18",
35
+ "@jskit-ai/jskit-catalog": "0.2.31",
36
36
  "@jskit-ai/json-rest-api-core": "0.2.11",
37
37
  "@jskit-ai/kernel": "0.2.10",
38
- "@jskit-ai/material-web": "0.1.1",
39
- "@jskit-ai/mobile-capacitor": "0.2.12",
40
- "@jskit-ai/oidc-issuer-core": "0.1.17",
38
+ "@jskit-ai/material-web": "0.1.2",
39
+ "@jskit-ai/mobile-capacitor": "0.2.13",
40
+ "@jskit-ai/oidc-issuer-core": "0.1.18",
41
41
  "@jskit-ai/payments-core": "0.2.1",
42
42
  "@jskit-ai/payments-web": "0.2.10",
43
- "@jskit-ai/realtime": "0.2.15",
43
+ "@jskit-ai/realtime": "0.2.16",
44
44
  "@jskit-ai/release": "0.1.9",
45
45
  "@jskit-ai/resource-core": "0.1.201",
46
46
  "@jskit-ai/resource-crud-core": "0.1.201",
47
47
  "@jskit-ai/rewarded-core": "0.2.12",
48
- "@jskit-ai/rewarded-web": "0.2.14",
49
- "@jskit-ai/shell-web": "0.2.12",
48
+ "@jskit-ai/rewarded-web": "0.2.15",
49
+ "@jskit-ai/shell-web": "0.2.13",
50
50
  "@jskit-ai/storage-runtime": "0.1.256",
51
51
  "@jskit-ai/uploads-image-web": "0.2.10",
52
52
  "@jskit-ai/uploads-runtime": "0.2.10",
53
53
  "@jskit-ai/users-core": "0.2.12",
54
- "@jskit-ai/users-web": "0.2.19",
54
+ "@jskit-ai/users-web": "0.2.20",
55
55
  "@jskit-ai/workspaces-core": "0.2.12",
56
- "@jskit-ai/workspaces-web": "0.2.19"
56
+ "@jskit-ai/workspaces-web": "0.2.20"
57
57
  },
58
58
  "singletonPackages": [
59
59
  "@jskit-ai/assistant-runtime",
@@ -179,7 +179,7 @@
179
179
  },
180
180
  {
181
181
  "packageId": "@jskit-ai/assistant-core",
182
- "version": "0.2.23",
182
+ "version": "0.2.24",
183
183
  "description": "Reusable assistant client/server/shared primitives without surface-specific routes or settings ownership.",
184
184
  "jskit": {
185
185
  "kind": "runtime",
@@ -364,7 +364,7 @@
364
364
  },
365
365
  {
366
366
  "packageId": "@jskit-ai/assistant-runtime",
367
- "version": "0.2.25",
367
+ "version": "0.2.26",
368
368
  "description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
369
369
  "jskit": {
370
370
  "kind": "runtime",
@@ -458,15 +458,15 @@
458
458
  },
459
459
  "packageJson": {
460
460
  "dependencies": {
461
- "@jskit-ai/assistant-core": "0.2.23",
461
+ "@jskit-ai/assistant-core": "0.2.24",
462
462
  "json-rest-schema": "^1.0.17"
463
463
  },
464
464
  "peerDependencies": {
465
465
  "@jskit-ai/http-runtime": "0.2.10",
466
- "@jskit-ai/http-web": "0.2.17",
466
+ "@jskit-ai/http-web": "0.2.18",
467
467
  "@jskit-ai/kernel": "0.2.10",
468
- "@jskit-ai/realtime": "0.2.15",
469
- "@jskit-ai/shell-web": "0.2.12",
468
+ "@jskit-ai/realtime": "0.2.16",
469
+ "@jskit-ai/shell-web": "0.2.13",
470
470
  "@tanstack/vue-query": "^5.90.5",
471
471
  "vue": "^3.5.13",
472
472
  "vuetify": "^4.0.0",
@@ -476,7 +476,7 @@
476
476
  },
477
477
  {
478
478
  "packageId": "@jskit-ai/assistant-voice",
479
- "version": "0.2.25",
479
+ "version": "0.2.26",
480
480
  "description": "Duplex voice conversations with application-owned targets, authorization and speech services.",
481
481
  "jskit": {
482
482
  "kind": "runtime",
@@ -518,7 +518,7 @@
518
518
  },
519
519
  "packageJson": {
520
520
  "dependencies": {
521
- "@jskit-ai/assistant-core": "0.2.23",
521
+ "@jskit-ai/assistant-core": "0.2.24",
522
522
  "ws": "^8.20.1"
523
523
  },
524
524
  "peerDependencies": {
@@ -530,7 +530,7 @@
530
530
  },
531
531
  {
532
532
  "packageId": "@jskit-ai/assistant-voice-sherpa",
533
- "version": "0.1.72",
533
+ "version": "0.1.73",
534
534
  "description": "Optional native Sherpa speech engine and explicit model preparation for assistant voice.",
535
535
  "jskit": {
536
536
  "kind": "runtime",
@@ -568,7 +568,7 @@
568
568
  },
569
569
  "packageJson": {
570
570
  "dependencies": {
571
- "@jskit-ai/assistant-voice": "0.2.25",
571
+ "@jskit-ai/assistant-voice": "0.2.26",
572
572
  "sherpa-onnx-node": "1.13.6"
573
573
  }
574
574
  }
@@ -664,7 +664,7 @@
664
664
  },
665
665
  {
666
666
  "packageId": "@jskit-ai/auth-provider-local-core",
667
- "version": "0.2.12",
667
+ "version": "0.2.13",
668
668
  "description": "Local auth provider with a file backend default, no database requirement, and optional Google, Microsoft and GitHub sign-in.",
669
669
  "jskit": {
670
670
  "kind": "runtime",
@@ -729,7 +729,7 @@
729
729
  },
730
730
  {
731
731
  "packageId": "@jskit-ai/auth-provider-local-db-core",
732
- "version": "0.1.153",
732
+ "version": "0.1.154",
733
733
  "description": "Database-backed local auth storage backend for JSKIT local auth.",
734
734
  "jskit": {
735
735
  "kind": "runtime",
@@ -804,7 +804,7 @@
804
804
  },
805
805
  "packageJson": {
806
806
  "dependencies": {
807
- "@jskit-ai/auth-provider-local-core": "0.2.12",
807
+ "@jskit-ai/auth-provider-local-core": "0.2.13",
808
808
  "@jskit-ai/database-runtime": "0.1.259"
809
809
  },
810
810
  "peerDependencies": {
@@ -814,7 +814,7 @@
814
814
  },
815
815
  {
816
816
  "packageId": "@jskit-ai/auth-provider-oidc-core",
817
- "version": "0.2.16",
817
+ "version": "0.2.17",
818
818
  "description": "Sign in through any OpenID Connect issuer: self-registration or a static client, app-owned sessions, and back-channel logout.",
819
819
  "jskit": {
820
820
  "kind": "runtime",
@@ -953,7 +953,7 @@
953
953
  },
954
954
  {
955
955
  "packageId": "@jskit-ai/auth-web",
956
- "version": "0.2.14",
956
+ "version": "0.2.15",
957
957
  "description": "Auth web module: Fastify auth routes plus web login/sign-out scaffolds.",
958
958
  "jskit": {
959
959
  "kind": "runtime",
@@ -1228,7 +1228,7 @@
1228
1228
  "@jskit-ai/auth-core": "0.2.10",
1229
1229
  "@jskit-ai/http-runtime": "0.2.10",
1230
1230
  "@jskit-ai/kernel": "0.2.10",
1231
- "@jskit-ai/shell-web": "0.2.12",
1231
+ "@jskit-ai/shell-web": "0.2.13",
1232
1232
  "@tanstack/vue-query": "^5.90.5",
1233
1233
  "pinia": "^4.0.3",
1234
1234
  "vue": "^3.5.44",
@@ -1480,7 +1480,7 @@
1480
1480
  },
1481
1481
  {
1482
1482
  "packageId": "@jskit-ai/console-web",
1483
- "version": "0.1.236",
1483
+ "version": "0.1.237",
1484
1484
  "description": "Authenticated console surface scaffold and surface policy wiring.",
1485
1485
  "jskit": {
1486
1486
  "kind": "runtime",
@@ -1585,8 +1585,8 @@
1585
1585
  "@jskit-ai/console-core": "0.2.12"
1586
1586
  },
1587
1587
  "peerDependencies": {
1588
- "@jskit-ai/auth-web": "0.2.14",
1589
- "@jskit-ai/shell-web": "0.2.12"
1588
+ "@jskit-ai/auth-web": "0.2.15",
1589
+ "@jskit-ai/shell-web": "0.2.13"
1590
1590
  }
1591
1591
  }
1592
1592
  },
@@ -1842,7 +1842,7 @@
1842
1842
  },
1843
1843
  {
1844
1844
  "packageId": "@jskit-ai/http-web",
1845
- "version": "0.2.17",
1845
+ "version": "0.2.18",
1846
1846
  "description": "Neutral Vue request, command, resource, and CRUD UI runtime for JSKIT web applications.",
1847
1847
  "jskit": {
1848
1848
  "kind": "runtime",
@@ -1921,8 +1921,8 @@
1921
1921
  "peerDependencies": {
1922
1922
  "@jskit-ai/http-runtime": "0.2.10",
1923
1923
  "@jskit-ai/kernel": "0.2.10",
1924
- "@jskit-ai/realtime": "0.2.15",
1925
- "@jskit-ai/shell-web": "0.2.12",
1924
+ "@jskit-ai/realtime": "0.2.16",
1925
+ "@jskit-ai/shell-web": "0.2.13",
1926
1926
  "@tanstack/vue-query": "^5.90.5",
1927
1927
  "vue": "^3.5.13",
1928
1928
  "vue-router": "^5.0.4",
@@ -1986,7 +1986,7 @@
1986
1986
  },
1987
1987
  {
1988
1988
  "packageId": "@jskit-ai/material-web",
1989
- "version": "0.1.1",
1989
+ "version": "0.1.2",
1990
1990
  "description": "Exact Material 3 for JSKIT Vuetify apps: seed-generated light and dark schemes, Material Symbols Rounded icon aliases, Roboto Flex and Material 3 component defaults.",
1991
1991
  "jskit": {
1992
1992
  "kind": "runtime",
@@ -2015,7 +2015,7 @@
2015
2015
  },
2016
2016
  {
2017
2017
  "packageId": "@jskit-ai/mobile-capacitor",
2018
- "version": "0.2.12",
2018
+ "version": "0.2.13",
2019
2019
  "description": "Thin Capacitor client integration for JSKIT mobile-shell launch routing and auth callback completion.",
2020
2020
  "jskit": {
2021
2021
  "kind": "runtime",
@@ -2067,13 +2067,13 @@
2067
2067
  },
2068
2068
  "peerDependencies": {
2069
2069
  "@jskit-ai/kernel": "0.2.10",
2070
- "@jskit-ai/shell-web": "0.2.12"
2070
+ "@jskit-ai/shell-web": "0.2.13"
2071
2071
  }
2072
2072
  }
2073
2073
  },
2074
2074
  {
2075
2075
  "packageId": "@jskit-ai/oidc-issuer-core",
2076
- "version": "0.1.17",
2076
+ "version": "0.1.18",
2077
2077
  "description": "OpenID Connect accounts hub: one sign-in for all of an owner's apps, with approval of every production app.",
2078
2078
  "jskit": {
2079
2079
  "kind": "runtime",
@@ -2258,7 +2258,7 @@
2258
2258
  },
2259
2259
  {
2260
2260
  "packageId": "@jskit-ai/realtime",
2261
- "version": "0.2.15",
2261
+ "version": "0.2.16",
2262
2262
  "description": "Thin, generic realtime runtime wrappers for socket.io server and client.",
2263
2263
  "jskit": {
2264
2264
  "kind": "runtime",
@@ -2582,7 +2582,7 @@
2582
2582
  },
2583
2583
  {
2584
2584
  "packageId": "@jskit-ai/rewarded-web",
2585
- "version": "0.2.14",
2585
+ "version": "0.2.15",
2586
2586
  "description": "Rewarded gate client workflow with explicitly composed application delivery.",
2587
2587
  "jskit": {
2588
2588
  "kind": "runtime",
@@ -2636,14 +2636,14 @@
2636
2636
  "peerDependencies": {
2637
2637
  "@jskit-ai/http-runtime": "0.2.10",
2638
2638
  "@jskit-ai/kernel": "0.2.10",
2639
- "@jskit-ai/shell-web": "0.2.12",
2639
+ "@jskit-ai/shell-web": "0.2.13",
2640
2640
  "vue": "^3.5.13"
2641
2641
  }
2642
2642
  }
2643
2643
  },
2644
2644
  {
2645
2645
  "packageId": "@jskit-ai/shell-web",
2646
- "version": "0.2.12",
2646
+ "version": "0.2.13",
2647
2647
  "description": "Web shell layout runtime with outlet-based placement contributions.",
2648
2648
  "jskit": {
2649
2649
  "kind": "runtime",
@@ -3404,7 +3404,7 @@
3404
3404
  },
3405
3405
  {
3406
3406
  "packageId": "@jskit-ai/users-web",
3407
- "version": "0.2.19",
3407
+ "version": "0.2.20",
3408
3408
  "description": "Users web module with account, profile, and user-specific shell UI.",
3409
3409
  "jskit": {
3410
3410
  "kind": "runtime",
@@ -3613,11 +3613,11 @@
3613
3613
  "@jskit-ai/users-core": "0.2.12"
3614
3614
  },
3615
3615
  "peerDependencies": {
3616
- "@jskit-ai/auth-web": "0.2.14",
3616
+ "@jskit-ai/auth-web": "0.2.15",
3617
3617
  "@jskit-ai/http-runtime": "0.2.10",
3618
- "@jskit-ai/http-web": "0.2.17",
3618
+ "@jskit-ai/http-web": "0.2.18",
3619
3619
  "@jskit-ai/kernel": "0.2.10",
3620
- "@jskit-ai/shell-web": "0.2.12",
3620
+ "@jskit-ai/shell-web": "0.2.13",
3621
3621
  "@tanstack/vue-query": "^5.90.5",
3622
3622
  "vue": "^3.5.13",
3623
3623
  "vue-router": "^5.0.4",
@@ -3814,7 +3814,7 @@
3814
3814
  },
3815
3815
  {
3816
3816
  "packageId": "@jskit-ai/workspaces-web",
3817
- "version": "0.2.19",
3817
+ "version": "0.2.20",
3818
3818
  "description": "Workspace web module: workspace selector, tools widget, workspace surfaces, members UI, and settings hosts.",
3819
3819
  "jskit": {
3820
3820
  "kind": "runtime",
@@ -4066,11 +4066,11 @@
4066
4066
  "@jskit-ai/workspaces-core": "0.2.12"
4067
4067
  },
4068
4068
  "peerDependencies": {
4069
- "@jskit-ai/auth-web": "0.2.14",
4070
- "@jskit-ai/http-web": "0.2.17",
4069
+ "@jskit-ai/auth-web": "0.2.15",
4070
+ "@jskit-ai/http-web": "0.2.18",
4071
4071
  "@jskit-ai/kernel": "0.2.10",
4072
- "@jskit-ai/shell-web": "0.2.12",
4073
- "@jskit-ai/users-web": "0.2.19",
4072
+ "@jskit-ai/shell-web": "0.2.13",
4073
+ "@jskit-ai/users-web": "0.2.20",
4074
4074
  "@tanstack/vue-query": "^5.90.5",
4075
4075
  "vue": "^3.5.13",
4076
4076
  "vue-router": "^5.0.4",
@@ -4102,7 +4102,7 @@
4102
4102
  "@jskit-ai/shell-web"
4103
4103
  ],
4104
4104
  "packageId": "@jskit-ai/assistant-runtime",
4105
- "packageVersion": "0.2.25",
4105
+ "packageVersion": "0.2.26",
4106
4106
  "documentPath": "patterns/assistant-surface/PATTERN.md",
4107
4107
  "examplePath": "patterns/assistant-surface/example",
4108
4108
  "files": [
@@ -4134,7 +4134,7 @@
4134
4134
  "@jskit-ai/oidc-issuer-core"
4135
4135
  ],
4136
4136
  "packageId": "@jskit-ai/oidc-issuer-core",
4137
- "packageVersion": "0.1.17",
4137
+ "packageVersion": "0.1.18",
4138
4138
  "documentPath": "patterns/accounts-hub/PATTERN.md",
4139
4139
  "examplePath": "patterns/accounts-hub/example",
4140
4140
  "files": [
@@ -4164,7 +4164,7 @@
4164
4164
  "@jskit-ai/shell-web"
4165
4165
  ],
4166
4166
  "packageId": "@jskit-ai/auth-web",
4167
- "packageVersion": "0.2.14",
4167
+ "packageVersion": "0.2.15",
4168
4168
  "documentPath": "patterns/auth-surface/PATTERN.md",
4169
4169
  "examplePath": "patterns/auth-surface/example",
4170
4170
  "files": [
@@ -4202,7 +4202,7 @@
4202
4202
  "@jskit-ai/auth-web"
4203
4203
  ],
4204
4204
  "packageId": "@jskit-ai/auth-provider-oidc-core",
4205
- "packageVersion": "0.2.16",
4205
+ "packageVersion": "0.2.17",
4206
4206
  "documentPath": "patterns/oidc-sign-in/PATTERN.md",
4207
4207
  "examplePath": "patterns/oidc-sign-in/example",
4208
4208
  "files": [
@@ -4673,7 +4673,7 @@
4673
4673
  "@jskit-ai/shell-web"
4674
4674
  ],
4675
4675
  "packageId": "@jskit-ai/console-web",
4676
- "packageVersion": "0.1.236",
4676
+ "packageVersion": "0.1.237",
4677
4677
  "documentPath": "patterns/console-surface/PATTERN.md",
4678
4678
  "examplePath": "patterns/console-surface/example",
4679
4679
  "files": [
@@ -4706,7 +4706,7 @@
4706
4706
  "@jskit-ai/resource-crud-core"
4707
4707
  ],
4708
4708
  "packageId": "@jskit-ai/http-web",
4709
- "packageVersion": "0.2.17",
4709
+ "packageVersion": "0.2.18",
4710
4710
  "documentPath": "patterns/crud-screen-set/PATTERN.md",
4711
4711
  "examplePath": "patterns/crud-screen-set/example",
4712
4712
  "files": [
@@ -4850,7 +4850,7 @@
4850
4850
  "@jskit-ai/mobile-capacitor"
4851
4851
  ],
4852
4852
  "packageId": "@jskit-ai/mobile-capacitor",
4853
- "packageVersion": "0.2.12",
4853
+ "packageVersion": "0.2.13",
4854
4854
  "documentPath": "patterns/android-application/PATTERN.md",
4855
4855
  "examplePath": "patterns/android-application/example",
4856
4856
  "files": [
@@ -4874,7 +4874,7 @@
4874
4874
  "@jskit-ai/shell-web"
4875
4875
  ],
4876
4876
  "packageId": "@jskit-ai/realtime",
4877
- "packageVersion": "0.2.15",
4877
+ "packageVersion": "0.2.16",
4878
4878
  "documentPath": "patterns/realtime-application/PATTERN.md",
4879
4879
  "examplePath": "patterns/realtime-application/example",
4880
4880
  "files": [
@@ -4898,7 +4898,7 @@
4898
4898
  "@jskit-ai/rewarded-web"
4899
4899
  ],
4900
4900
  "packageId": "@jskit-ai/rewarded-web",
4901
- "packageVersion": "0.2.14",
4901
+ "packageVersion": "0.2.15",
4902
4902
  "documentPath": "patterns/google-rewarded/PATTERN.md",
4903
4903
  "examplePath": "patterns/google-rewarded/example",
4904
4904
  "files": [
@@ -4925,7 +4925,7 @@
4925
4925
  "@jskit-ai/shell-web"
4926
4926
  ],
4927
4927
  "packageId": "@jskit-ai/shell-web",
4928
- "packageVersion": "0.2.12",
4928
+ "packageVersion": "0.2.13",
4929
4929
  "documentPath": "patterns/application-shell/PATTERN.md",
4930
4930
  "examplePath": "patterns/application-shell/example",
4931
4931
  "files": [
@@ -4970,7 +4970,7 @@
4970
4970
  "@jskit-ai/shell-web"
4971
4971
  ],
4972
4972
  "packageId": "@jskit-ai/shell-web",
4973
- "packageVersion": "0.2.12",
4973
+ "packageVersion": "0.2.13",
4974
4974
  "documentPath": "patterns/page-and-placement/PATTERN.md",
4975
4975
  "examplePath": "patterns/page-and-placement/example",
4976
4976
  "files": [
@@ -5006,7 +5006,7 @@
5006
5006
  "@jskit-ai/users-web"
5007
5007
  ],
5008
5008
  "packageId": "@jskit-ai/users-web",
5009
- "packageVersion": "0.2.19",
5009
+ "packageVersion": "0.2.20",
5010
5010
  "documentPath": "patterns/account-settings/PATTERN.md",
5011
5011
  "examplePath": "patterns/account-settings/example",
5012
5012
  "files": [
@@ -5014,7 +5014,8 @@
5014
5014
  "src/components/account/settings/AccountSettingsPreferencesSection.vue",
5015
5015
  "src/components/account/settings/AccountSettingsProfileSection.vue",
5016
5016
  "src/components/account/settings/AccountSettingsSignInMethodsSection.vue",
5017
- "src/pages/account/index.vue"
5017
+ "src/pages/account/index.vue",
5018
+ "src/pages/account.vue"
5018
5019
  ]
5019
5020
  },
5020
5021
  {
@@ -5102,7 +5103,7 @@
5102
5103
  "@jskit-ai/workspaces-web"
5103
5104
  ],
5104
5105
  "packageId": "@jskit-ai/workspaces-web",
5105
- "packageVersion": "0.2.19",
5106
+ "packageVersion": "0.2.20",
5106
5107
  "documentPath": "patterns/workspace-surfaces/PATTERN.md",
5107
5108
  "examplePath": "patterns/workspace-surfaces/example",
5108
5109
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/jskit-catalog",
3
- "version": "0.2.30",
3
+ "version": "0.2.31",
4
4
  "description": "Published metadata catalog for JSKIT package metadata.",
5
5
  "type": "module",
6
6
  "files": [
@@ -7,7 +7,8 @@ import {
7
7
  checkLinkAttribute,
8
8
  checkNavigationCode,
9
9
  checkPageParents,
10
- checkScriptBackNames
10
+ checkScriptBackNames,
11
+ readTitledFiles
11
12
  } from "./navigation.mjs";
12
13
  import {
13
14
  HEX_COLOUR,
@@ -52,6 +53,10 @@ const SKIPPED_DIRECTORIES = new Set([
52
53
  const SKIPPED_FILE = /\.(?:test|spec)\.[cm]?[jt]s$|\.d\.ts$/u;
53
54
  // The generated colour scheme is the one home of hex values.
54
55
  const THEME_DIRECTORY = "theme";
56
+ // Colours that are content, not UI, keep one value in both schemes, so they are not theme colours (which are toned per
57
+ // scheme): they are named hex exports of src/theme/palettes.js, which this check, like all of src/theme/, reads as
58
+ // the place for hex values.
59
+ const CONTENT_COLOUR = "A content colour (an identity palette, artwork, a brand mark, a chart series) is a named export of src/theme/palettes.js, used as it is.";
55
60
  const WHOLE_APP_ROOTS = Object.freeze(["src", "packages"]);
56
61
 
57
62
  const hexPattern = () => new RegExp(HEX_COLOUR, "gu");
@@ -67,6 +72,7 @@ const GENERATED_SCHEMES = /\bcreateMaterialVuetifyOptions\s*\(/u;
67
72
  const PINNED_LIGHT = /\bdefaultTheme\s*:\s*["']light["']/u;
68
73
  const SHELL_PACKAGE = "@jskit-ai/shell-web";
69
74
  const isNavigationRule = (rule) => rule.startsWith("nav/");
75
+ const SMALLER_BUTTON = "A smaller button is size=\"small\" or \"x-small\", which keeps the touch target.";
70
76
 
71
77
  function isSkippedDirectory(name) {
72
78
  return SKIPPED_DIRECTORIES.has(name);
@@ -156,7 +162,7 @@ function createReporter(file, source, { navigationOnly = false } = {}) {
156
162
  function checkCode(code, offset, { report, themed }) {
157
163
  if (!themed) {
158
164
  for (const match of code.matchAll(quotedHex())) {
159
- report("m3/raw-hex", offset + match.index, `Raw colour ${match[2]}. Use a theme colour role; hex values live only in the generated scheme under src/theme/.`);
165
+ report("m3/raw-hex", offset + match.index, `Raw colour ${match[2]}. Use a theme colour role. ${CONTENT_COLOUR}`);
160
166
  }
161
167
  }
162
168
  checkNavigationCode(code, offset, report);
@@ -190,12 +196,12 @@ function checkDeclaration(declaration, context) {
190
196
  const { report, themed } = context;
191
197
  if (!themed) {
192
198
  for (const match of declaration.value.matchAll(hexPattern())) {
193
- report("m3/raw-hex", declaration.valueOffset + match.index, `Raw colour ${match[0]}. Use rgb(var(--v-theme-<role>)); hex values live only in the generated scheme under src/theme/.`);
199
+ report("m3/raw-hex", declaration.valueOffset + match.index, `Raw colour ${match[0]}. Use rgb(var(--v-theme-<role>)). ${CONTENT_COLOUR}`);
194
200
  }
195
201
  }
196
202
  const buttonRule = declaration.selectors.some((selector) => /\.v-btn\b/u.test(selector));
197
203
  if (buttonRule && ["height", "min-height", "--v-btn-height"].includes(declaration.property)) {
198
- report("m3/fixed-button-height", declaration.offset, `A fixed ${declaration.property} on buttons. JSKIT ships the 40dp button with its 48dp touch target; remove it.`);
204
+ report("m3/fixed-button-height", declaration.offset, `A fixed ${declaration.property} on buttons. JSKIT ships the 40dp button with its 48dp touch target; remove it. ${SMALLER_BUTTON}`);
199
205
  }
200
206
  }
201
207
 
@@ -220,7 +226,7 @@ function checkTemplate(template, context) {
220
226
  if (tag.name === "h1") {
221
227
  headings += 1;
222
228
  if (headings > 1) report("m3/duplicate-h1", tag.offset, "A second h1 in one template. A page has one title, carried by the top app bar.");
223
- else if (context.shell) report("m3/duplicate-h1", tag.offset, "An in-page h1. The top app bar carries the page title; use a title role (text-title-large) for a section title.");
229
+ else if (context.titled) report("m3/duplicate-h1", tag.offset, "An in-page h1 under the top app bar, which carries the page title; use a title role (text-title-large) for a section title.");
224
230
  }
225
231
  }
226
232
  }
@@ -234,7 +240,7 @@ function checkAttribute(tag, entry, context) {
234
240
  checkDeclaration({ ...declaration, selectors: tag.name === "v-btn" ? [".v-btn"] : [] }, context);
235
241
  }
236
242
  } else if (!themed && exactHex.test(entry.value.trim())) {
237
- report("m3/raw-hex", entry.valueOffset, `Raw colour ${entry.value.trim()}. Use a theme colour role such as color="primary".`);
243
+ report("m3/raw-hex", entry.valueOffset, `Raw colour ${entry.value.trim()}. Use a theme colour role such as color="primary". ${CONTENT_COLOUR}`);
238
244
  }
239
245
  checkLinkAttribute(tag, entry, context.report);
240
246
  }
@@ -253,7 +259,7 @@ function checkSurface(tag, report) {
253
259
  function checkButtonSize(tag, report) {
254
260
  for (const name of ["height", "min-height"]) {
255
261
  const entry = attribute(tag, name);
256
- if (entry) report("m3/fixed-button-height", entry.offset, `A fixed ${name} on v-btn. JSKIT ships the 40dp button with its 48dp touch target; remove it.`);
262
+ if (entry) report("m3/fixed-button-height", entry.offset, `A fixed ${name} on v-btn. JSKIT ships the 40dp button with its 48dp touch target; remove it. ${SMALLER_BUTTON}`);
257
263
  }
258
264
  }
259
265
 
@@ -320,11 +326,15 @@ async function checkMaterial3({ projectRoot = process.cwd(), paths = [], navigat
320
326
  const root = path.resolve(projectRoot);
321
327
  const { files, missing } = await collectFiles(root, paths);
322
328
  const appFiles = paths.length === 0 ? files : (await collectFiles(root, [])).files;
323
- const context = { shell: usesShell(await readProjectManifest(root)), navigationOnly };
329
+ const context = { navigationOnly };
330
+ // Only a page under the shell's top app bar has its title there; one without a bar has its own h1.
331
+ const titledFiles = usesShell(await readProjectManifest(root))
332
+ ? await readTitledFiles({ root, appFiles: [...new Set([...appFiles, ...files])] })
333
+ : new Set();
324
334
  const findings = [];
325
335
  const exceptions = [];
326
336
  for (const file of files) {
327
- const checked = checkFile(file, await readFile(path.join(root, file), "utf8"), context);
337
+ const checked = checkFile(file, await readFile(path.join(root, file), "utf8"), { ...context, titled: titledFiles.has(file) });
328
338
  findings.push(...checked.findings);
329
339
  exceptions.push(...checked.exceptions);
330
340
  }
@@ -13,7 +13,9 @@ const RULES = Object.freeze({
13
13
  "nav/raw-link": "forbidden-in-app-code",
14
14
  "nav/back-control": "forbidden-in-app-code",
15
15
  "nav/missing-parent": "what-an-app-declares",
16
- "nav/top-level-parent": "what-an-app-declares"
16
+ "nav/top-level-parent": "what-an-app-declares",
17
+ "nav/placement-data": "what-an-app-declares",
18
+ "nav/missing-shell-layout": "what-an-app-declares"
17
19
  });
18
20
 
19
21
  const HISTORY_CALL = /(?<![\w$.])(?:(?:window|globalThis)\s*\.\s*)?history\s*\.\s*(?:back|go|forward|pushState|replaceState)\s*\(/gu;
@@ -60,6 +62,7 @@ const MAIN_NAV_TARGET = /\btarget\s*:\s*["']shell\.(?:primary|secondary)-nav["']
60
62
  // draws its own bar renders shell-web's Up control in it.
61
63
  const SHELL_LAYOUT_IMPORT = /\bfrom\s*["'](?:@\/components\/ShellLayout|@jskit-ai\/shell-web\/client\/components\/ShellLayout)(?:\.vue)?["']/u;
62
64
  const SHELL_UP_TAG = /<\s*(?:ShellUpButton|shell-up-button)\b/u;
65
+ const SHELL_TITLE_TAG = /<\s*(?:ShellPageTitle|shell-page-title)\b/u;
63
66
  const LOCAL_IMPORT = /\bfrom\s*["']((?:@\/|\.{1,2}\/)[^"']+)["']/gu;
64
67
  const ROUTER_VIEW_TAG = /<\s*(?:RouterView|router-view)\b/u;
65
68
 
@@ -247,21 +250,47 @@ function enclosingObject(text, offset) {
247
250
 
248
251
  const stringValue = (text, key) => new RegExp(`\\b${key}\\s*:\\s*(["'\\u0060])([^"'\\u0060]*)\\1`, "u").exec(text)?.[2];
249
252
 
250
- // Main-navigation destinations, by surface: every main-navigation placement's suffixes.
253
+ // Main-navigation destinations, by surface: every main-navigation placement's suffixes. A link that names its
254
+ // surface (props.surface) leads to that surface's page wherever it shows, so it is a destination of that surface only;
255
+ // one that names none leads to the page on each surface it shows on. A placement is literal data; one whose suffixes or
256
+ // surface are computed (built by a helper, spread or interpolated) can't be read, and is returned as unreadable.
251
257
  function readMainNavigation(sources) {
252
258
  const destinations = [];
253
- for (const source of sources) {
259
+ const unreadable = [];
260
+ for (const { file, source } of sources) {
254
261
  for (const match of source.matchAll(MAIN_NAV_TARGET)) {
255
262
  const placement = enclosingObject(source, match.index)?.text || "";
256
- const surfaces = [stringValue(placement, "surface"), ...(/\bsurfaces\s*:\s*\[([^\]]*)\]/u.exec(placement)?.[1] || "")
257
- .split(",").map((entry) => entry.trim().replace(/^["'`]|["'`]$/gu, ""))].filter(Boolean);
263
+ const computed = computedPlacementPart(placement);
264
+ if (computed) {
265
+ unreadable.push({ file, source, offset: match.index, computed });
266
+ continue;
267
+ }
268
+ const surface = stringValue(placement, "surface");
269
+ const surfaces = surface ? [surface] : (/\bsurfaces\s*:\s*\[([^\]]*)\]/u.exec(placement)?.[1] || "")
270
+ .split(",").map((entry) => entry.trim().replace(/^["'`]|["'`]$/gu, "")).filter(Boolean);
258
271
  for (const key of ["scopedSuffix", "unscopedSuffix"]) {
259
272
  const suffix = stringValue(placement, key);
260
273
  if (suffix !== undefined) destinations.push({ suffix: normalisePath(suffix.split(/[?#]/u)[0]), surfaces });
261
274
  }
262
275
  }
263
276
  }
264
- return destinations;
277
+ return { destinations, unreadable };
278
+ }
279
+
280
+ // What makes a placement computed rather than literal data, or "": a spread, props that aren't an object literal, or
281
+ // a link key that isn't a plain string (or a list of strings, for surfaces), such as a variable, a call or an
282
+ // interpolated template.
283
+ function computedPlacementPart(placement) {
284
+ if (/[{,]\s*\.\.\./u.test(placement)) return "it spreads another object";
285
+ if (/(?<=[{,]\s*)props\s*:(?!\s*\{)/u.test(placement)) return "its props are computed";
286
+ for (const key of ["scopedSuffix", "unscopedSuffix", "surface", "to"]) {
287
+ // The key as a property, `key: value` or shorthand `key`, whose value is not a quoted string without ${}.
288
+ const property = new RegExp(`(?<=[{,]\\s*)${key}\\s*(?::(?!\\s*(["'\\u0060])[^"'\\u0060$]*\\1)|(?=[,}]))`, "u");
289
+ if (property.test(placement)) return `its ${key} is computed`;
290
+ }
291
+ const surfaces = /(?<=[{,]\s*)surfaces\s*:\s*(\[[^\]]*\])?/u.exec(placement);
292
+ if (surfaces && !/^\[\s*(?:(["'`])[^"'`$]*\1\s*,?\s*)*\]$/u.test(surfaces[1] || "")) return "its surfaces are computed";
293
+ return "";
265
294
  }
266
295
 
267
296
  function normalisePath(value) {
@@ -333,15 +362,16 @@ function readPage(file, source) {
333
362
  }
334
363
 
335
364
  // Whether a component draws the shell's top app bar, and with it Up: it, or a component it imports from the app,
336
- // renders ShellLayout (directly or through the app's wrapper) or shell-web's ShellUpButton in a bar of its own.
337
- function createShellBarReader(appFiles, read) {
365
+ // renders ShellLayout (directly or through the app's wrapper) or shell-web's ShellUpButton in a bar of its own (or,
366
+ // given SHELL_TITLE_TAG, the bar's page title).
367
+ function createShellBarReader(appFiles, read, tag = SHELL_UP_TAG) {
338
368
  const known = new Set(appFiles);
339
369
  const verdicts = new Map();
340
370
  async function drawsShellBar(file) {
341
371
  if (verdicts.has(file)) return verdicts.get(file);
342
372
  verdicts.set(file, false);
343
373
  const source = await read(file);
344
- let verdict = SHELL_LAYOUT_IMPORT.test(source) || SHELL_UP_TAG.test(splitVue(source).template?.text || "");
374
+ let verdict = SHELL_LAYOUT_IMPORT.test(source) || tag.test(splitVue(source).template?.text || "");
345
375
  for (const match of source.matchAll(LOCAL_IMPORT)) {
346
376
  if (verdict) break;
347
377
  const imported = localComponent(file, match[1], known);
@@ -353,6 +383,59 @@ function createShellBarReader(appFiles, read) {
353
383
  return drawsShellBar;
354
384
  }
355
385
 
386
+ /**
387
+ * The app's .vue files that render under the shell's page title, where an in-page h1 would be a second one: a file
388
+ * that shows the title (ShellLayout's bar, or ShellPageTitle in a bar of its own), a page a layout showing it renders,
389
+ * and a component such a file imports. A component no .vue file imports (routed or placed from JavaScript) counts
390
+ * too, as where it renders can't be read; App.vue counts only when it shows the title itself.
391
+ * @param {{ root: string, appFiles: string[] }} options
392
+ */
393
+ async function readTitledFiles({ root, appFiles }) {
394
+ const sources = new Map();
395
+ const read = async (file) => {
396
+ if (!sources.has(file)) sources.set(file, await readFile(path.join(root, file), "utf8"));
397
+ return sources.get(file);
398
+ };
399
+ const known = new Set(appFiles);
400
+ const vueFiles = appFiles.filter((file) => file.endsWith(".vue"));
401
+ const showsTitle = createShellBarReader(appFiles, read, SHELL_TITLE_TAG);
402
+ const importers = new Map();
403
+ for (const file of vueFiles) {
404
+ for (const match of (await read(file)).matchAll(LOCAL_IMPORT)) {
405
+ const imported = localComponent(file, match[1], known);
406
+ if (imported) importers.set(imported, [...(importers.get(imported) || []), file]);
407
+ }
408
+ }
409
+ const verdicts = new Map();
410
+ async function titled(file) {
411
+ if (verdicts.has(file)) return verdicts.get(file);
412
+ verdicts.set(file, false);
413
+ let verdict = await showsTitle(file);
414
+ if (!verdict && isPageFile(file)) {
415
+ // The layouts that render the page: src/pages/home/settings.vue renders home/settings/*.
416
+ const segments = pageKey(file).split("/");
417
+ for (let length = segments.length - 1; length > 0 && !verdict; length -= 1) {
418
+ const layout = `src/pages/${segments.slice(0, length).join("/")}.vue`;
419
+ if (known.has(layout) && ROUTER_VIEW_TAG.test(splitVue(await read(layout)).template?.text || "")) {
420
+ verdict = await showsTitle(layout);
421
+ }
422
+ }
423
+ } else if (!verdict && path.posix.basename(file) !== "App.vue") {
424
+ const users = importers.get(file) || [];
425
+ verdict = users.length === 0;
426
+ for (const user of users) {
427
+ if (verdict) break;
428
+ verdict = await titled(user);
429
+ }
430
+ }
431
+ verdicts.set(file, verdict);
432
+ return verdict;
433
+ }
434
+ const result = new Set();
435
+ for (const file of vueFiles) if (await titled(file)) result.add(file);
436
+ return result;
437
+ }
438
+
356
439
  // The app's .vue file an import names: "@/components/Bar.vue" is src/components/Bar.vue; "./Bar" is beside the file.
357
440
  function localComponent(file, specifier, known) {
358
441
  const base = specifier.startsWith("@/")
@@ -383,10 +466,16 @@ async function checkPageParents({ root, appFiles, pageFiles, report }) {
383
466
  }
384
467
  const surfaces = readSurfaces(configSource, [...pages.values()].map(({ file }) => routeOf(file)));
385
468
  const scripts = appFiles.filter((file) => /\.(?:[cm]?[jt]s|vue)$/u.test(file));
386
- const navigation = readMainNavigation(await Promise.all(scripts.map(async (file) => blankJsComments(await read(file)))));
469
+ const navigation = readMainNavigation(await Promise.all(scripts.map(async (file) => ({ file, source: blankJsComments(await read(file)) }))));
470
+ for (const { file, source, offset, computed } of navigation.unreadable) {
471
+ if (!pageFiles.includes(file)) continue;
472
+ report({ rule: "nav/placement-data", file, ...lineIndex(source)(offset), message: `A main-navigation placement that isn't literal data: ${computed}. Declare each placement literally, with plain string suffixes and surface, so its destination can be read; until then no page is judged for nav/missing-parent.` });
473
+ }
387
474
  const surfacePath = (file, surface) => normalisePath(routePattern(routeOf(file).slice(surface.pagesRoot.length)));
388
- const isDestination = (relative, surface) => navigation.some(({ suffix, surfaces: owners }) =>
475
+ const isDestination = (relative, surface) => navigation.destinations.some(({ suffix, surfaces: owners }) =>
389
476
  suffix === relative && (owners.length === 0 || owners.includes(surface.id) || owners.includes("*")));
477
+ // An App.vue that wraps every page in the shell's bar leaves no page outside it.
478
+ const appShell = appFiles.includes("src/App.vue") && await drawsShellBar("src/App.vue");
390
479
  const judged = [];
391
480
  for (const file of pageFiles.filter(isPageFile)) {
392
481
  const page = pages.get(pageKey(file));
@@ -396,11 +485,19 @@ async function checkPageParents({ root, appFiles, pageFiles, report }) {
396
485
  const segments = pageKey(file).split("/");
397
486
  const layouts = segments.slice(1).map((_, index) => pages.get(segments.slice(0, segments.length - 1 - index).join("/")))
398
487
  .filter((entry) => entry?.layout);
399
- // Up lives in the shell's top app bar, so only pages a shell layout renders have one.
400
- if (!layouts.some((entry) => entry.shell)) continue;
401
488
  const relative = surfacePath(file, surface);
402
- const destination = isDestination(relative, surface);
403
489
  const position = lineIndex(page.source);
490
+ // Up lives in the shell's top app bar, so only pages a shell layout renders have one. A page that declares a
491
+ // parent expects that bar, with its title, Up and page actions; under no shell layout it shows none of them.
492
+ if (!layouts.some((entry) => entry.shell)) {
493
+ if (page.parent && !appShell && !await drawsShellBar(file)) {
494
+ const at = position(page.parent.offset + PARENT_KEY.exec(page.parent.text).index);
495
+ const layout = surface.pagesRoot ? `, src/pages/${surface.pagesRoot}.vue` : "";
496
+ report({ rule: "nav/missing-shell-layout", file, ...at, message: `${relative} of ${surface.id} declares a parent, but no layout that draws the shell's top app bar renders it, so its title, Up and page actions never show. Give the surface its layout${layout}: ShellLayout around <RouterView />, with meta.jskit.surface "${surface.id}".` });
497
+ }
498
+ continue;
499
+ }
500
+ const destination = isDestination(relative, surface);
404
501
  if (destination && page.parent) {
405
502
  const at = position(page.parent.offset + PARENT_KEY.exec(page.parent.text).index);
406
503
  report({ rule: "nav/top-level-parent", file, ...at, message: `${relative} is a main-navigation destination of ${surface.id}, so it declares no parent: choosing it starts a fresh trail.` });
@@ -415,7 +512,7 @@ async function checkPageParents({ root, appFiles, pageFiles, report }) {
415
512
  const inherited = chain.find((entry) => entry.parent);
416
513
  const topLevel = destination || relative === "/" || chain.some((entry) => isDestination(surfacePath(entry.file, surface), surface));
417
514
  judged.push({ file, route: relative, surface: surface.id, topLevel, parent: page.parent ? "own" : inherited ? "inherited" : null });
418
- if (!topLevel && !page.parent && !inherited) {
515
+ if (!topLevel && !page.parent && !inherited && navigation.unreadable.length === 0) {
419
516
  const at = position(page.declarations[0]?.offset ?? 0);
420
517
  report({ rule: "nav/missing-parent", file, ...at, message: `Inner page ${relative} of ${surface.id} has no parent. Declare it once in the page's route meta (or its layout's, for tabs), so Up works after a direct entry.` });
421
518
  }
@@ -441,5 +538,6 @@ export {
441
538
  checkPageParents,
442
539
  checkScriptBackNames,
443
540
  isInAppPath,
444
- isPageFile
541
+ isPageFile,
542
+ readTitledFiles
445
543
  };