@keenmate/web-components-devkit 1.0.0-rc01 → 1.0.0-rc03
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/CHANGELOG.md +114 -0
- package/README.md +70 -12
- package/components.json +206 -189
- package/package.json +4 -1
- package/src/cli/commands/doctor.js +1 -0
- package/src/cli/commands/new.js +29 -8
- package/src/cli/kmwc.js +1 -0
- package/src/config.d.ts +5 -0
- package/src/config.js +26 -3
- package/src/examples/chapter-links.js +35 -0
- package/src/examples/chapter-search.js +84 -0
- package/src/examples/constants.js +51 -0
- package/src/examples/css/content.css +4 -34
- package/src/examples/css/controls.css +30 -25
- package/src/examples/css/copy-link.css +1 -0
- package/src/examples/css/demo.css +2 -0
- package/src/examples/css/form-controls.css +285 -0
- package/src/examples/css/index-page.css +1 -1
- package/src/examples/css/spotlight.css +417 -0
- package/src/examples/css/style.css +3 -0
- package/src/examples/css/tables.css +141 -0
- package/src/examples/css/tokens.css +69 -27
- package/src/examples/index.d.ts +38 -0
- package/src/examples/index.js +6 -3
- package/src/examples/kit.js +11 -4
- package/src/examples/page-config.js +1 -0
- package/src/examples/sections.js +38 -1
- package/src/examples/spotlight.js +473 -0
- package/src/vite/chapter-index.js +107 -0
- package/src/vite/index.js +19 -1
- package/src/vite/page-composer.js +17 -10
- package/templates/claude-block.md +9 -2
- package/templates/new/component-variables.manifest.json +3 -3
- package/templates/new/docs/examples.md +1 -1
- package/templates/new/docs/theming.md +1 -1
- package/templates/new/e2e/dark-mode.spec.ts +3 -3
- package/templates/new/examples-theming.html +3 -3
- package/templates/new/src/css/base.css +5 -3
- package/templates/new/src/css/variables.css +10 -8
- package/templates/new-minimal/src/css/base.css +5 -3
- package/templates/new-minimal/src/css/variables.css +7 -5
package/components.json
CHANGED
|
@@ -1,189 +1,206 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$comment": "Registry of Keenmate web components — source for the README catalogue (`npm run readme`). `port` is the repo's block: dev server = port, preview = port + 1, examples image = port + 10. Each kind has its own range with headroom (components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900, tests 17000–17900), numbered by 100 in registry order; `kmwc doctor` checks package.json#kmwc.ports against it and `kmwc new` takes the next block. `prefix` mirrors the reserved-prefix table in BlissFramework guidelines/web-components/base-variables.md (the authority).",
|
|
3
|
-
"components": [
|
|
4
|
-
{
|
|
5
|
-
"id": "web-multiselect",
|
|
6
|
-
"kind": "component",
|
|
7
|
-
"prefix": "ms",
|
|
8
|
-
"port": 12100,
|
|
9
|
-
"path": "../web-multiselect",
|
|
10
|
-
"npm": "@keenmate/web-multiselect",
|
|
11
|
-
"repo": "https://github.com/keenmate/web-multiselect",
|
|
12
|
-
"examples": "https://examples.web-multiselect.keenmate.dev",
|
|
13
|
-
"docs": "https://web-multiselect.keenmate.dev",
|
|
14
|
-
"showcase": "web-multiselect-showcase"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"id": "web-daterangepicker",
|
|
18
|
-
"kind": "component",
|
|
19
|
-
"prefix": "drp",
|
|
20
|
-
"port": 12200,
|
|
21
|
-
"path": "../web-daterangepicker",
|
|
22
|
-
"npm": "@keenmate/web-daterangepicker",
|
|
23
|
-
"repo": "https://github.com/keenmate/web-daterangepicker",
|
|
24
|
-
"docs": "https://web-daterangepicker.keenmate.dev",
|
|
25
|
-
"showcase": "web-daterangepicker-showcase"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"id": "web-treeview",
|
|
29
|
-
"kind": "component",
|
|
30
|
-
"prefix": "wtv",
|
|
31
|
-
"port": 12300,
|
|
32
|
-
"path": "../web-treeview",
|
|
33
|
-
"npm": "@keenmate/web-treeview",
|
|
34
|
-
"repo": "https://github.com/keenmate/web-treeview",
|
|
35
|
-
"showcase": "web-treeview-showcase"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"id": "web-commandpalette",
|
|
39
|
-
"kind": "component",
|
|
40
|
-
"prefix": "cp",
|
|
41
|
-
"port": 12400,
|
|
42
|
-
"path": "../web-commandpalette",
|
|
43
|
-
"npm": "@keenmate/web-commandpalette",
|
|
44
|
-
"repo": "https://github.com/keenmate/web-commandpalette",
|
|
45
|
-
"examples": "https://examples.web-commandpalette.keenmate.dev"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"id": "web-dropzone",
|
|
49
|
-
"kind": "component",
|
|
50
|
-
"prefix": "dz",
|
|
51
|
-
"port": 12500,
|
|
52
|
-
"path": "../web-dropzone",
|
|
53
|
-
"npm": "@keenmate/web-dropzone",
|
|
54
|
-
"repo": "https://github.com/keenmate/web-dropzone",
|
|
55
|
-
"monorepo": true,
|
|
56
|
-
"note": "Monorepo: renderer + headless core."
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"id": "web-grid",
|
|
60
|
-
"kind": "component",
|
|
61
|
-
"prefix": "wg",
|
|
62
|
-
"port": 12600,
|
|
63
|
-
"path": "../web-grid",
|
|
64
|
-
"npm": "@keenmate/web-grid",
|
|
65
|
-
"repo": "https://github.com/keenmate/web-grid",
|
|
66
|
-
"docs": "https://web-grid.keenmate.com",
|
|
67
|
-
"showcase": "web-grid-showcase",
|
|
68
|
-
"monorepo": true
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id": "web-editor",
|
|
72
|
-
"kind": "component",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"note": "
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"id": "web-switch",
|
|
82
|
-
"kind": "component",
|
|
83
|
-
"port": 12800,
|
|
84
|
-
"path": "../web-switch",
|
|
85
|
-
"npm": "@keenmate/web-switch",
|
|
86
|
-
"repo": "https://github.com/keenmate/web-switch",
|
|
87
|
-
"note": "Svelte-based wrapper around @keenmate/svelte-switch."
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"id": "web-timepicker",
|
|
91
|
-
"kind": "component",
|
|
92
|
-
"port": 12900,
|
|
93
|
-
"path": "../web-timepicker",
|
|
94
|
-
"note": "Vanilla-JS prototypes (spiral / slider time pickers); no package yet."
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
"id": "web-wysiwyg",
|
|
98
|
-
"kind": "component",
|
|
99
|
-
"port": 13000,
|
|
100
|
-
"path": "../web-wysiwyg",
|
|
101
|
-
"note": "ProseMirror exploration; superseded by web-editor."
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
"id": "web-components-core",
|
|
105
|
-
"kind": "library",
|
|
106
|
-
"port": 15000,
|
|
107
|
-
"path": "../web-components-core",
|
|
108
|
-
"npm": "@keenmate/web-components-core",
|
|
109
|
-
"note": "Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset."
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
"id": "web-components",
|
|
113
|
-
"kind": "library",
|
|
114
|
-
"port": 15100,
|
|
115
|
-
"path": ".",
|
|
116
|
-
"npm": "@keenmate/web-components-devkit",
|
|
117
|
-
"repo": "https://github.com/keenmate/web-components",
|
|
118
|
-
"note": "This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI)."
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"id": "
|
|
122
|
-
"kind": "
|
|
123
|
-
"port":
|
|
124
|
-
"path": "../
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
"
|
|
178
|
-
"
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Registry of Keenmate web components — source for the README catalogue (`npm run readme`). `port` is the repo's block: dev server = port, preview = port + 1, examples image = port + 10. Each kind has its own range with headroom (components 12100–14900, libraries 15000–15900, showcases/docs 16000–16900, tests 17000–17900), numbered by 100 in registry order; `kmwc doctor` checks package.json#kmwc.ports against it and `kmwc new` takes the next block. `prefix` mirrors the reserved-prefix table in BlissFramework guidelines/web-components/base-variables.md (the authority).",
|
|
3
|
+
"components": [
|
|
4
|
+
{
|
|
5
|
+
"id": "web-multiselect",
|
|
6
|
+
"kind": "component",
|
|
7
|
+
"prefix": "ms",
|
|
8
|
+
"port": 12100,
|
|
9
|
+
"path": "../web-multiselect",
|
|
10
|
+
"npm": "@keenmate/web-multiselect",
|
|
11
|
+
"repo": "https://github.com/keenmate/web-multiselect",
|
|
12
|
+
"examples": "https://examples.web-multiselect.keenmate.dev",
|
|
13
|
+
"docs": "https://web-multiselect.keenmate.dev",
|
|
14
|
+
"showcase": "web-multiselect-showcase"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": "web-daterangepicker",
|
|
18
|
+
"kind": "component",
|
|
19
|
+
"prefix": "drp",
|
|
20
|
+
"port": 12200,
|
|
21
|
+
"path": "../web-daterangepicker",
|
|
22
|
+
"npm": "@keenmate/web-daterangepicker",
|
|
23
|
+
"repo": "https://github.com/keenmate/web-daterangepicker",
|
|
24
|
+
"docs": "https://web-daterangepicker.keenmate.dev",
|
|
25
|
+
"showcase": "web-daterangepicker-showcase"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "web-treeview",
|
|
29
|
+
"kind": "component",
|
|
30
|
+
"prefix": "wtv",
|
|
31
|
+
"port": 12300,
|
|
32
|
+
"path": "../web-treeview",
|
|
33
|
+
"npm": "@keenmate/web-treeview",
|
|
34
|
+
"repo": "https://github.com/keenmate/web-treeview",
|
|
35
|
+
"showcase": "web-treeview-showcase"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "web-commandpalette",
|
|
39
|
+
"kind": "component",
|
|
40
|
+
"prefix": "cp",
|
|
41
|
+
"port": 12400,
|
|
42
|
+
"path": "../web-commandpalette",
|
|
43
|
+
"npm": "@keenmate/web-commandpalette",
|
|
44
|
+
"repo": "https://github.com/keenmate/web-commandpalette",
|
|
45
|
+
"examples": "https://examples.web-commandpalette.keenmate.dev"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "web-dropzone",
|
|
49
|
+
"kind": "component",
|
|
50
|
+
"prefix": "dz",
|
|
51
|
+
"port": 12500,
|
|
52
|
+
"path": "../web-dropzone",
|
|
53
|
+
"npm": "@keenmate/web-dropzone",
|
|
54
|
+
"repo": "https://github.com/keenmate/web-dropzone",
|
|
55
|
+
"monorepo": true,
|
|
56
|
+
"note": "Monorepo: renderer + headless core."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "web-grid",
|
|
60
|
+
"kind": "component",
|
|
61
|
+
"prefix": "wg",
|
|
62
|
+
"port": 12600,
|
|
63
|
+
"path": "../web-grid",
|
|
64
|
+
"npm": "@keenmate/web-grid",
|
|
65
|
+
"repo": "https://github.com/keenmate/web-grid",
|
|
66
|
+
"docs": "https://web-grid.keenmate.com",
|
|
67
|
+
"showcase": "web-grid-showcase",
|
|
68
|
+
"monorepo": true
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "web-editor",
|
|
72
|
+
"kind": "component",
|
|
73
|
+
"prefix": "km",
|
|
74
|
+
"port": 12700,
|
|
75
|
+
"path": "../web-editor",
|
|
76
|
+
"npm": "@keenmate/web-editor",
|
|
77
|
+
"examples": "https://examples.web-editor.keenmate.dev",
|
|
78
|
+
"note": "ProseMirror-based WYSIWYG / Markdown editor."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"id": "web-switch",
|
|
82
|
+
"kind": "component",
|
|
83
|
+
"port": 12800,
|
|
84
|
+
"path": "../web-switch",
|
|
85
|
+
"npm": "@keenmate/web-switch",
|
|
86
|
+
"repo": "https://github.com/keenmate/web-switch",
|
|
87
|
+
"note": "Svelte-based wrapper around @keenmate/svelte-switch."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "web-timepicker",
|
|
91
|
+
"kind": "component",
|
|
92
|
+
"port": 12900,
|
|
93
|
+
"path": "../web-timepicker",
|
|
94
|
+
"note": "Vanilla-JS prototypes (spiral / slider time pickers); no package yet."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "web-wysiwyg",
|
|
98
|
+
"kind": "component",
|
|
99
|
+
"port": 13000,
|
|
100
|
+
"path": "../web-wysiwyg",
|
|
101
|
+
"note": "ProseMirror exploration; superseded by web-editor."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "web-components-core",
|
|
105
|
+
"kind": "library",
|
|
106
|
+
"port": 15000,
|
|
107
|
+
"path": "../web-components-core",
|
|
108
|
+
"npm": "@keenmate/web-components-core",
|
|
109
|
+
"note": "Runtime foundation: BlissElement, reactive input table, converters, logging, positioning, CEM preset."
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "web-components",
|
|
113
|
+
"kind": "library",
|
|
114
|
+
"port": 15100,
|
|
115
|
+
"path": ".",
|
|
116
|
+
"npm": "@keenmate/web-components-devkit",
|
|
117
|
+
"repo": "https://github.com/keenmate/web-components",
|
|
118
|
+
"note": "This repo — dev/demo/build tooling (Vite preset, examples kit, kmwc CLI)."
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "pure-css",
|
|
122
|
+
"kind": "library",
|
|
123
|
+
"port": 15200,
|
|
124
|
+
"path": "../pure-css",
|
|
125
|
+
"npm": "@keenmate/pure-css",
|
|
126
|
+
"note": "CSS framework mirroring the base-css-variables token contract (demo site + e2e on the dev port)."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"id": "base-css-variables",
|
|
130
|
+
"kind": "library",
|
|
131
|
+
"port": 15300,
|
|
132
|
+
"path": "../base-css-variables",
|
|
133
|
+
"npm": "@keenmate/base-css-variables",
|
|
134
|
+
"repo": "https://github.com/keenmate/base-css-variables",
|
|
135
|
+
"note": "The shared --base-* token contract (base-variables.css) + base-contract-check; examples site + e2e on the dev port."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "web-multiselect-showcase",
|
|
139
|
+
"kind": "showcase",
|
|
140
|
+
"port": 16000,
|
|
141
|
+
"path": "../web-multiselect-showcase",
|
|
142
|
+
"for": "web-multiselect"
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"id": "web-daterangepicker-showcase",
|
|
146
|
+
"kind": "showcase",
|
|
147
|
+
"port": 16100,
|
|
148
|
+
"path": "../web-daterangepicker-showcase",
|
|
149
|
+
"for": "web-daterangepicker"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "web-treeview-showcase",
|
|
153
|
+
"kind": "showcase",
|
|
154
|
+
"port": 16200,
|
|
155
|
+
"path": "../web-treeview-showcase",
|
|
156
|
+
"for": "web-treeview"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"id": "web-grid-showcase",
|
|
160
|
+
"kind": "showcase",
|
|
161
|
+
"port": 16300,
|
|
162
|
+
"path": "../web-grid-showcase",
|
|
163
|
+
"for": "web-grid",
|
|
164
|
+
"url": "https://web-grid.keenmate.com"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"id": "web-multiselect-docs",
|
|
168
|
+
"kind": "docs",
|
|
169
|
+
"port": 16400,
|
|
170
|
+
"path": "../web-multiselect-docs",
|
|
171
|
+
"for": "web-multiselect",
|
|
172
|
+
"note": "Content-only docs (keendocs)."
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "web-component-framework-test",
|
|
176
|
+
"kind": "test",
|
|
177
|
+
"port": 17000,
|
|
178
|
+
"path": "../web-component-framework-test",
|
|
179
|
+
"note": "Smoke + contrast tests inside CSS host frameworks (plain / Bootstrap / …)."
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"id": "web-multiselect-test",
|
|
183
|
+
"kind": "test",
|
|
184
|
+
"port": 17100,
|
|
185
|
+
"path": "../web-multiselect-test",
|
|
186
|
+
"for": "web-multiselect",
|
|
187
|
+
"note": "npm-install vs direct-file consumption checks before publishing."
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"id": "web-multiselect-positioning-demo",
|
|
191
|
+
"kind": "test",
|
|
192
|
+
"port": 17200,
|
|
193
|
+
"path": "../web-multiselect-positioning-demo",
|
|
194
|
+
"for": "web-multiselect",
|
|
195
|
+
"note": "Single-page positioning repro."
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"id": "web-playground",
|
|
199
|
+
"kind": "test",
|
|
200
|
+
"port": 17300,
|
|
201
|
+
"path": "playground",
|
|
202
|
+
"for": "web-components",
|
|
203
|
+
"note": "Devkit e2e test bed — a component scaffolded with `kmwc new`, linked to this repo."
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/web-components-devkit",
|
|
3
|
-
"version": "1.0.0-
|
|
3
|
+
"version": "1.0.0-rc03",
|
|
4
4
|
"description": "Shared dev, example-site and build infrastructure for Keenmate web components: Vite preset, examples kit (chapter nav, copy-link, controls store, shared styles), Playwright preset and the `kmwc` CLI (sync, doctor, check-vars, new, readme).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"readme:check": "node src/cli/kmwc.js readme --check",
|
|
48
48
|
"test": "node --test \"test/**/*.test.js\"",
|
|
49
49
|
"test:e2e": "cd playground && npx playwright test",
|
|
50
|
+
"check:contract": "base-contract-check src templates playground",
|
|
51
|
+
"check:spotlight-mirror": "node scripts/check-spotlight-mirror.js",
|
|
50
52
|
"playground": "cd playground && vite",
|
|
51
53
|
"playground:build": "cd playground && vite build --mode examples",
|
|
52
54
|
"playground:lib": "cd playground && vite build"
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
}
|
|
66
68
|
},
|
|
67
69
|
"devDependencies": {
|
|
70
|
+
"@keenmate/base-css-variables": "^2.0.0-rc07",
|
|
68
71
|
"typescript": "^5.9.3",
|
|
69
72
|
"vite": "^7.3.7"
|
|
70
73
|
},
|
|
@@ -37,6 +37,7 @@ export const RULEBOOK_SUITES = [
|
|
|
37
37
|
{ name: 'custom-elements-manifest', isPrefixed: false },
|
|
38
38
|
{ name: 'responsive-overlay', isPrefixed: true },
|
|
39
39
|
{ name: 'publish-command', isPrefixed: false },
|
|
40
|
+
{ name: 'example-pages', isPrefixed: false },
|
|
40
41
|
];
|
|
41
42
|
|
|
42
43
|
function getMajorVersion(versionRange) {
|
package/src/cli/commands/new.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* kmwc new web-colorpicker --prefix cpk [--title "Web Color Picker"] [--tag web-colorpicker]
|
|
5
5
|
* [--dir ../web-colorpicker] [--port 13200] [--minimal] [--register] [--offline]
|
|
6
|
+
* kmwc new web-editor --adopt --dir /tmp/web-editor [--prefix …]
|
|
6
7
|
*
|
|
7
8
|
* Default: a small working sample (a toggle with `label` / `active` inputs and
|
|
8
9
|
* a `change` event) to rename or replace. `--minimal`: an empty skeleton — no
|
|
@@ -21,6 +22,12 @@
|
|
|
21
22
|
* `.claude/commands/publish.md`, examples, fixtures + e2e specs, then the
|
|
22
23
|
* `kmwc sync` files. The port block (dev = block, image = +10) is the next free
|
|
23
24
|
* one in the component range (see PORT_RANGES in config.js; `--port` overrides).
|
|
25
|
+
*
|
|
26
|
+
* `--adopt` is for MIGRATING a component that is already registered: the id must
|
|
27
|
+
* be in components.json, the port block (and prefix, unless the entry has none —
|
|
28
|
+
* then `--prefix`) come from its entry, the npm-name check is skipped (the package
|
|
29
|
+
* may exist), and the registry is left alone. Scaffold into a scratch `--dir`,
|
|
30
|
+
* then move the component's own code into the generated shape.
|
|
24
31
|
*/
|
|
25
32
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
26
33
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
@@ -184,7 +191,7 @@ export function generatePackageJson({ componentId, packageName, tagline, tag, pr
|
|
|
184
191
|
export async function run(args) {
|
|
185
192
|
const requestedName = args._[0];
|
|
186
193
|
if (!requestedName) {
|
|
187
|
-
throw new Error('usage: kmwc new <name> --prefix <2-4 letters> [--title "…"] [--tag …] [--dir …] [--port …] [--register] [--offline]');
|
|
194
|
+
throw new Error('usage: kmwc new <name> --prefix <2-4 letters> [--title "…"] [--tag …] [--dir …] [--port …] [--register] [--offline] [--adopt]');
|
|
188
195
|
}
|
|
189
196
|
|
|
190
197
|
const unscopedName = getUnscopedName(requestedName);
|
|
@@ -201,24 +208,35 @@ export async function run(args) {
|
|
|
201
208
|
|
|
202
209
|
// ── intake ───────────────────────────────────────────────────────────────
|
|
203
210
|
const registry = getRegistry();
|
|
204
|
-
|
|
211
|
+
const isAdopting = !!args.adopt;
|
|
212
|
+
const adoptedEntry = registry.components.find((entry) => entry.id === componentId);
|
|
213
|
+
if (isAdopting && !adoptedEntry) throw new Error(`--adopt: "${componentId}" is not in components.json — register it, or drop --adopt for a new component`);
|
|
214
|
+
if (!isAdopting && adoptedEntry) throw new Error(`"${componentId}" is already in components.json (migrating it? add --adopt)`);
|
|
215
|
+
if (isAdopting && args.register) throw new Error('--adopt never edits the registry — drop --register');
|
|
205
216
|
if (existsSync(targetPath) && readdirSync(targetPath).length) throw new Error(`${targetPath} exists and is not empty`);
|
|
206
217
|
|
|
207
|
-
|
|
218
|
+
if (isAdopting && adoptedEntry.prefix && args.prefix && args.prefix !== adoptedEntry.prefix) {
|
|
219
|
+
throw new Error(`--prefix ${args.prefix} ≠ the registered prefix "${adoptedEntry.prefix}"`);
|
|
220
|
+
}
|
|
221
|
+
const prefix = (isAdopting && adoptedEntry.prefix) || args.prefix || generatePrefix(componentId);
|
|
208
222
|
if (!prefix) {
|
|
209
223
|
throw new Error(
|
|
210
224
|
`cannot derive a CSS prefix for "${componentId}" — is "${featureName}" one English word or several? ` +
|
|
211
225
|
`Pass --prefix (initials of each word, e.g. daterangepicker → drp; component-intake.md "C — CSS prefix derivation").`
|
|
212
226
|
);
|
|
213
227
|
}
|
|
214
|
-
|
|
228
|
+
// An adopted component's own registered prefix isn't a clash with itself.
|
|
229
|
+
const reservedPrefixes = getReservedPrefixes(registry);
|
|
230
|
+
if (isAdopting && adoptedEntry.prefix) reservedPrefixes.delete(adoptedEntry.prefix);
|
|
231
|
+
const prefixError = checkPrefix(prefix, reservedPrefixes);
|
|
215
232
|
if (prefixError) throw new Error(prefixError);
|
|
216
233
|
|
|
217
|
-
if (!args.offline && checkIsPackageNameTaken(packageName)) throw new Error(`${packageName} already exists on npm (component-intake.md "B")`);
|
|
234
|
+
if (!isAdopting && !args.offline && checkIsPackageNameTaken(packageName)) throw new Error(`${packageName} already exists on npm (component-intake.md "B")`);
|
|
218
235
|
|
|
219
|
-
|
|
236
|
+
if (isAdopting && args.port && Number(args.port) !== adoptedEntry.port) throw new Error(`--port ${args.port} ≠ the registered port block ${adoptedEntry.port}`);
|
|
237
|
+
const devPort = isAdopting ? adoptedEntry.port : Number(args.port) || getNextPortBlock(registry);
|
|
220
238
|
if (devPort % PORT_BLOCK_SIZE !== 0) throw new Error(`--port ${devPort} is not a port block (a multiple of ${PORT_BLOCK_SIZE})`);
|
|
221
|
-
const portOwner = registry.components.find((entry) => entry.port === devPort);
|
|
239
|
+
const portOwner = registry.components.find((entry) => entry.port === devPort && entry.id !== componentId);
|
|
222
240
|
if (portOwner) throw new Error(`port block ${devPort} belongs to ${portOwner.id} — omit --port to take the next free block`);
|
|
223
241
|
const corePackageJsonPath = join(DEVKIT_ROOT, '..', 'web-components-core', 'package.json');
|
|
224
242
|
const coreVersion = existsSync(corePackageJsonPath) ? getJsonFile(corePackageJsonPath).version : '1.0.0-rc12';
|
|
@@ -279,7 +297,10 @@ export async function run(args) {
|
|
|
279
297
|
repo: `https://github.com/keenmate/${componentId}`,
|
|
280
298
|
port: devPort,
|
|
281
299
|
};
|
|
282
|
-
if (
|
|
300
|
+
if (isAdopting) {
|
|
301
|
+
if (!adoptedEntry.prefix) console.log(`
|
|
302
|
+
The registry entry has no prefix — add "prefix": "${prefix}" to its components.json entry.`);
|
|
303
|
+
} else if (args.register) {
|
|
283
304
|
const registryPath = join(DEVKIT_ROOT, 'components.json');
|
|
284
305
|
const registryJson = getJsonFile(registryPath);
|
|
285
306
|
const lastComponentIndex = registryJson.components.map((entry) => entry.kind).lastIndexOf('component');
|
package/src/cli/kmwc.js
CHANGED
|
@@ -41,6 +41,7 @@ Usage: kmwc <command> [options]
|
|
|
41
41
|
new <name> Scaffold a new component (e.g. \`kmwc new web-colorpicker --prefix cpk\`).
|
|
42
42
|
--title "…" --tag … --prefix … --dir … --port … --register
|
|
43
43
|
--minimal: empty skeleton (no inputs/events) instead of the sample
|
|
44
|
+
--adopt: an already registered component (migration scratch scaffold)
|
|
44
45
|
readme [--check] Regenerate the README catalogue from components.json (devkit repo).
|
|
45
46
|
|
|
46
47
|
Global: --root <dir> Act on <dir> instead of the current directory.`);
|
package/src/config.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface KmwcPackageConfig {
|
|
|
38
38
|
excludedPages?: string[];
|
|
39
39
|
/** localStorage namespace for the controls persistence; defaults to `id`. */
|
|
40
40
|
storagePrefix?: string;
|
|
41
|
+
/** The Ctrl/Cmd+K chapter spotlight (search every page's chapters); default true. */
|
|
42
|
+
isSpotlightEnabled?: boolean;
|
|
41
43
|
};
|
|
42
44
|
analytics?: { plausibleSiteId?: string; plausibleHostUrl?: string };
|
|
43
45
|
image?: {
|
|
@@ -101,6 +103,7 @@ export interface KmwcConfig {
|
|
|
101
103
|
staticPaths: string[];
|
|
102
104
|
excludedPages: string[];
|
|
103
105
|
storagePrefix: string;
|
|
106
|
+
isSpotlightEnabled: boolean;
|
|
104
107
|
};
|
|
105
108
|
analytics: { plausibleSiteId: string; plausibleHostUrl: string };
|
|
106
109
|
image: {
|
|
@@ -123,6 +126,8 @@ export interface KmwcConfig {
|
|
|
123
126
|
|
|
124
127
|
export const DEVKIT_ROOT: string;
|
|
125
128
|
export const DEFAULT_RULEBOOK_PATH: string;
|
|
129
|
+
/** The nearest `<ancestor>/BlissFramework/guidelines/web-components` above `startPath`, or null. */
|
|
130
|
+
export function getRulebookPath(startPath: string): string | null;
|
|
126
131
|
export const LEGACY_FILES: string[];
|
|
127
132
|
export const PACKAGE_LEAK_RULES: Array<{ pattern: RegExp; label: string }>;
|
|
128
133
|
export const PACKAGE_FILES_EXCLUSIONS: string[];
|
package/src/config.js
CHANGED
|
@@ -19,8 +19,28 @@ import { createRequire } from 'node:module';
|
|
|
19
19
|
/** Absolute path of the devkit package root (this repo). */
|
|
20
20
|
export const DEVKIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const RULEBOOK_RELATIVE_PATH = join('BlissFramework', 'guidelines', 'web-components');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The nearest BlissFramework rulebook checkout: `<ancestor>/BlissFramework/
|
|
26
|
+
* guidelines/web-components` for the first ancestor of `startPath` that has one
|
|
27
|
+
* (e.g. C:\Git\KM\web-editor → C:\Git\BlissFramework\…), or null. Searching
|
|
28
|
+
* from the component (not a fixed path from the devkit) is what keeps it
|
|
29
|
+
* working when the devkit is installed into node_modules from npm.
|
|
30
|
+
*/
|
|
31
|
+
export function getRulebookPath(startPath) {
|
|
32
|
+
let directory = resolve(startPath);
|
|
33
|
+
for (;;) {
|
|
34
|
+
const candidatePath = join(directory, RULEBOOK_RELATIVE_PATH);
|
|
35
|
+
if (existsSync(candidatePath)) return candidatePath;
|
|
36
|
+
const parentDirectory = dirname(directory);
|
|
37
|
+
if (parentDirectory === directory) return null;
|
|
38
|
+
directory = parentDirectory;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Fallback rulebook location: the nearest checkout above the devkit, else next to the KM folder. */
|
|
43
|
+
export const DEFAULT_RULEBOOK_PATH = getRulebookPath(DEVKIT_ROOT) ?? resolve(DEVKIT_ROOT, '..', '..', RULEBOOK_RELATIVE_PATH);
|
|
24
44
|
|
|
25
45
|
/** Files the devkit now owns — finding one of these in a component is a doctor error. */
|
|
26
46
|
export const LEGACY_FILES = [
|
|
@@ -215,6 +235,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
215
235
|
staticPaths: examples.staticPaths || [],
|
|
216
236
|
excludedPages: examples.excludedPages || [],
|
|
217
237
|
storagePrefix: examples.storagePrefix || id,
|
|
238
|
+
isSpotlightEnabled: examples.isSpotlightEnabled ?? true,
|
|
218
239
|
},
|
|
219
240
|
analytics: {
|
|
220
241
|
plausibleSiteId: analytics.plausibleSiteId || '',
|
|
@@ -235,7 +256,9 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
235
256
|
checkMode: cssVariables.checkMode || 'declared',
|
|
236
257
|
},
|
|
237
258
|
rulebook: {
|
|
238
|
-
path:
|
|
259
|
+
path:
|
|
260
|
+
process.env.KMWC_RULEBOOK_PATH ||
|
|
261
|
+
(rulebook.path ? resolve(root, rulebook.path) : getRulebookPath(root) ?? DEFAULT_RULEBOOK_PATH),
|
|
239
262
|
skippedChecks: rulebook.skippedChecks || [],
|
|
240
263
|
},
|
|
241
264
|
sync: {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CHAPTER LINKS — make every chapter reachable by a short link.
|
|
3
|
+
*
|
|
4
|
+
* Anchor ids are assigned by the kit after the page loads (sections.js), so the
|
|
5
|
+
* browser can't scroll to them on arrival by itself. This resolves the hash
|
|
6
|
+
* once the anchors exist, and on every hash change, accepting:
|
|
7
|
+
*
|
|
8
|
+
* #bu02-declarative-markup the anchor id (chapter nav, copy-link)
|
|
9
|
+
* #bu02 / #bu2 the section code — the link the codes exist for
|
|
10
|
+
* #summary the slug of an uncoded heading
|
|
11
|
+
*
|
|
12
|
+
* A hash that names some other element on the page is left to the browser.
|
|
13
|
+
*/
|
|
14
|
+
import { ensureSectionAnchors, findSectionByHash } from './sections.js';
|
|
15
|
+
|
|
16
|
+
let isHashChangeAttached = false;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Scroll to the chapter `hash` points at.
|
|
20
|
+
* @returns {boolean} whether a chapter was found
|
|
21
|
+
*/
|
|
22
|
+
export function scrollToChapterHash(hash = location.hash) {
|
|
23
|
+
const section = findSectionByHash(hash, ensureSectionAnchors());
|
|
24
|
+
if (!section) return false;
|
|
25
|
+
section.section.scrollIntoView({ block: 'start' });
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Resolve the current hash now, and every later hash change. Idempotent. */
|
|
30
|
+
export function attachChapterLinks() {
|
|
31
|
+
if (location.hash) scrollToChapterHash();
|
|
32
|
+
if (isHashChangeAttached) return;
|
|
33
|
+
isHashChangeAttached = true;
|
|
34
|
+
window.addEventListener('hashchange', () => scrollToChapterHash());
|
|
35
|
+
}
|