@keenmate/web-components-devkit 1.0.0-rc02 → 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 +64 -0
- package/README.md +53 -12
- package/package.json +2 -1
- package/src/cli/commands/doctor.js +1 -0
- package/src/config.d.ts +3 -0
- package/src/config.js +1 -0
- 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 +26 -21
- 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/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 +28 -0
- 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/templates/claude-block.md +9 -2
- package/templates/new/src/css/base.css +5 -3
- package/templates/new-minimal/src/css/base.css +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.0-rc03] - 2026-11-01 [PUBLISHED]
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Example pages no longer widen the page on a phone** — on screens ≤ 768 px a
|
|
8
|
+
`.reference-table` scrolls inside its card, a long `.controls` label (or `<code>` in it)
|
|
9
|
+
wraps and a `.controls` select is capped at the row, instead of overflowing. Besides the sideways scroll, an over-wide page made mobile
|
|
10
|
+
browsers grow the layout viewport (a 390 px phone reporting ~600 px), so pages that
|
|
11
|
+
demo device / breakpoint detection showed the wrong class.
|
|
12
|
+
- **The scaffold's component CSS no longer sets `box-sizing` on the host page** — the
|
|
13
|
+
`*, *::before, *::after { box-sizing: inherit }` reset in `templates/new/src/css/base.css`
|
|
14
|
+
(also in the `--minimal` scaffold and the playground) reached the page through the light-DOM stylesheet, in a layer that beats
|
|
15
|
+
`@layer kmwc`, so every page control rendered content-box (the spotlight input overflowed, a
|
|
16
|
+
36 px input came out 38 px). It is scoped to `:host *` now; components scaffolded with an
|
|
17
|
+
earlier devkit should make the same one-line change. A unit test now rejects any universal or
|
|
18
|
+
bare-element selector in the scaffold CSS.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Form controls: one look for native inputs on every example page** — text fields, selects
|
|
23
|
+
and list boxes, textareas, checkboxes (incl. indeterminate), radios, switches
|
|
24
|
+
(`<input type="checkbox" role="switch">`), ranges, colour and file inputs and fieldsets are
|
|
25
|
+
styled without a class, modelled on pure-admin's forms: inputs, selects and buttons share
|
|
26
|
+
`--kmwc-control-height`, so a row of them lines up in any container (not only `.controls`);
|
|
27
|
+
the focus border and halo, placeholder, disabled tone, the checkbox size and the
|
|
28
|
+
`--base-icon-check` / `-indeterminate` tick read the `--base-*` contract. Rules are
|
|
29
|
+
zero-specificity (`:where()`); `kmwc-native` on an element or an ancestor keeps the
|
|
30
|
+
browser's own controls. Outline buttons are as tall as filled ones now (transparent border).
|
|
31
|
+
The playground's new `examples-forms.html` shows them all.
|
|
32
|
+
- **Tables: every `<table>` on an example page is styled** — a header fill, row lines, hover
|
|
33
|
+
tint, caption and footer, as pure-admin's `.pa-table`. `.reference-table` looks as before;
|
|
34
|
+
new modifiers combine with it or a bare table: `.table-full`, `.table-bordered`,
|
|
35
|
+
`.table-striped`, `.table-compact` (tighter cells, and the inputs / selects / buttons in it
|
|
36
|
+
shrink with them via `--kmwc-control-height`), plus `tr.is-highlight` on any table and a
|
|
37
|
+
`.num` cell (right-aligned, tabular figures). Every table, not only `.reference-table`,
|
|
38
|
+
scrolls inside its card on a phone. Pages no longer need their own table CSS with fixed light
|
|
39
|
+
colours (core's `.rs-table`, which breaks in dark mode). New playground page
|
|
40
|
+
`examples-tables.html`.
|
|
41
|
+
- **Spotlight: Ctrl/⌘+K chapter search across every example page** — matches a chapter whose
|
|
42
|
+
section code or heading text contains the query (`th7` finds `TH07`; uncoded `Summary` /
|
|
43
|
+
reference sections by title), grouped by page with the current page first; Enter jumps,
|
|
44
|
+
Ctrl/⌘+Enter opens a new tab. The Vite plugin builds the index (`virtual:kmwc-chapters`)
|
|
45
|
+
from every page's `<h2>`s; `kmwc.examples.isSpotlightEnabled: false` turns it off. It mirrors
|
|
46
|
+
web-commandpalette's markup, matching, keyboard map, open/close and CSS without depending on
|
|
47
|
+
it — `npm run check:spotlight-mirror` lists what changed there since the last alignment.
|
|
48
|
+
New page tokens `--kmwc-overlay-bg` and `--kmwc-focused-bg` / `-text-color` / `-outline`
|
|
49
|
+
read the same `--base-*` hooks as wcp, so both look alike under a theme.
|
|
50
|
+
- **Short chapter links** — `#th07`, `#th7` or the slug of an uncoded heading scroll to that
|
|
51
|
+
chapter on arrival.
|
|
52
|
+
- **`make dev` (and `kill-port`, `build-examples`, `preview-examples`) at the repo root** — the
|
|
53
|
+
standard targets, run against the playground; `make playground` is gone.
|
|
54
|
+
- **`kmwc doctor` runs the rulebook's new `example-pages` suite** — every example `<h2>`
|
|
55
|
+
carries a permanent section code (`TH07 · Title`), a prefix belongs to one page, no code
|
|
56
|
+
is used twice. The generated CLAUDE.md block points at the rule. The convention was
|
|
57
|
+
already in the scaffold and the playground but written down nowhere, so sessions
|
|
58
|
+
building new pages didn't know it.
|
|
59
|
+
|
|
60
|
+
### Known issues
|
|
61
|
+
|
|
62
|
+
- **Opening the spotlight shifts the page sideways** on systems with classic scrollbars: the
|
|
63
|
+
mirrored core `lockBodyScroll()` only sets `overflow: hidden`, so the scrollbar disappears
|
|
64
|
+
(wcp has the same jump). pure-admin / pure-css avoid it with a `position: fixed` lock and
|
|
65
|
+
width compensation; the proposal to bring that into core is in web-commandpalette's CLAUDE.md.
|
|
66
|
+
|
|
3
67
|
## [1.0.0-rc02] - 2026-10-31 [PUBLISHED]
|
|
4
68
|
|
|
5
69
|
### Added
|
package/README.md
CHANGED
|
@@ -19,6 +19,15 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
19
19
|
> positioning) lives in [`@keenmate/web-components-core`](../web-components-core).
|
|
20
20
|
> Nothing here ships inside a component's bundle.
|
|
21
21
|
|
|
22
|
+
## What's New in v1.0.0-rc03
|
|
23
|
+
|
|
24
|
+
- **Spotlight — Ctrl/⌘+K chapter search across every example page** — Example sites grew to dozens of chapters over several pages, and the per-page chapter nav only knew its own page. The Vite plugin now builds an index of every page's `<h2>`s (`virtual:kmwc-chapters`), coded and uncoded, and Ctrl/⌘+K opens a search that matches the section code or the heading text (`th7` finds `TH07`), grouped by page with the current one first. Enter jumps, Ctrl/⌘+Enter opens a new tab. It mirrors web-commandpalette's markup, matching, keyboard map and CSS without depending on it; `npm run check:spotlight-mirror` lists what changed there since the last alignment. Turn it off with `kmwc.examples.isSpotlightEnabled: false`.
|
|
25
|
+
- **Form controls — one look for native inputs on every example page** — Pages styled their own selects and inputs, or got the browser default, so a select next to the kit's buttons came out shorter and in the platform's style (a `.controls` row was the only place the kit reached). Text fields, selects and list boxes, textareas, checkboxes, radios, `role="switch"` switches, ranges, colour and file inputs are now styled with no class, modelled on pure-admin's forms. Inputs, selects and buttons share `--kmwc-control-height`, so a mixed row lines up in any container. Focus, placeholder, disabled tone, checkbox size and the tick read the `--base-*` contract. The rules are zero-specificity, and `kmwc-native` opts an element out.
|
|
26
|
+
- **Tables — every table styled, `.reference-table` plus modifiers** — Only `.reference-table` had a look; a bare table was unstyled and pages wrote their own table CSS with fixed light colours that break in dark mode. Any `<table>` now gets a header fill, row lines and a hover tint, and modifiers combine: `.table-full`, `.table-bordered`, `.table-striped`, and `.table-compact`, whose inputs and buttons shrink with the rows. Add `tr.is-highlight` and `.num` (tabular figures). Every wide table scrolls inside its card on a phone.
|
|
27
|
+
- **Scaffold — component CSS no longer restyles the host page** — The `*, *::before, *::after { box-sizing: inherit }` reset in the `kmwc new` and `--minimal` templates also reached the page through the component's light-DOM stylesheet, in a layer that beats `@layer kmwc`, so page controls rendered content-box (a 36 px input came out 38 px). It is scoped to `:host *` now, and a unit test rejects any universal or bare-element selector in scaffold CSS. Components scaffolded on rc01/rc02 should make the same one-line change in `src/css/base.css`.
|
|
28
|
+
- **Example pages — short chapter links, phones and the example-pages suite** — `#th07`, `#th7` or an uncoded heading's slug now scroll to that chapter on arrival. On a phone, example pages no longer grow wider than the screen, which made mobile browsers report a wrong viewport to pages that demo breakpoints. `kmwc doctor` runs the rulebook's new `example-pages` suite: every example `<h2>` carries a permanent section code.
|
|
29
|
+
- **`make dev` at the devkit root** — the standard `dev` / `kill-port` / `build-examples` / `preview-examples` targets, run against the playground.
|
|
30
|
+
|
|
22
31
|
## What's New in v1.0.0-rc02
|
|
23
32
|
|
|
24
33
|
- **Theming — kit and scaffolds follow base-css-variables 2.0** — base-css-variables 2.0 dropped `--base-accent-color*`, `--base-text-color-on-accent` and `--base-input-color`, so a theme's primary colour silently stopped reaching the example pages and every newly scaffolded component. The `--kmwc-*` tokens in `src/examples/css/tokens.css` and the `templates/new*` `variables.css` now read `--base-primary-color` (with `-hover` / `-active` / `-bg-subtle`), `--base-on-primary-text-color` and `--base-input-text-color`, and the theming pages, docs and manifests set the new names. Standalone fallbacks are now the contract's own values, so a page looks the same with or without the contract loaded: a blue-500 primary instead of the old purple, slate surfaces, and on-colours computed from their fill. Re-run `kmwc new` output or diff your `variables.css` if you scaffolded on rc01.
|
|
@@ -26,16 +35,6 @@ them in the same shape: `@keenmate/web-components-devkit`.
|
|
|
26
35
|
- **`kmwc new --adopt` — migrate a component that's already registered** — Brings an existing repo onto the devkit with the same rulebook-shaped scaffold, using the registry entry's port block and CSS prefix, skipping the npm-name check (the package already exists) and leaving `components.json` untouched. Scaffold into a scratch `--dir` and move the component's code in. web-editor's one-package monorepo was flattened this way.
|
|
27
36
|
- **`kmwc doctor` — rulebook suites run from an npm install** — With the devkit installed from npm instead of linked, doctor looked for the BlissFramework rulebook at a path relative to the devkit, which ended up inside `node_modules`, and silently skipped every suite. It now uses the nearest `BlissFramework/guidelines/web-components` above the component (then above the devkit), and `kmwc.rulebook.path` resolves against the component root.
|
|
28
37
|
|
|
29
|
-
## What's New in v1.0.0-rc01
|
|
30
|
-
|
|
31
|
-
- **Devkit — one shared toolchain for every Keenmate web component** — Each `web-*` repo used to carry its own copy of the example helpers, the shared stylesheet, the Vite/Playwright configs and the deploy files, and every session "fixed" its own copy until they drifted apart. `@keenmate/web-components-devkit` owns all of it now: a component describes itself once in `package.json#kmwc` (title, prefix, ports, accent colours, analytics, image) and `defineComponentConfig()`, `definePlaywrightConfig()` and the `kmwc` CLI read it from there. A shared concern is fixed once in the devkit and every component picks it up on the next install.
|
|
32
|
-
- **Vite preset — dev, library and examples-site builds from one config** — `defineComponentConfig()` (Vite 7) runs the dev server on the repo's registry port with `strictPort`, builds the library (`es` + `umd`, `style.css` kept under its historical name, rolled-up `.d.ts`) and, with `--mode examples`, builds every root `*.html` into a static multi-page site in `dist-examples/` with relative asset URLs. The examples plugin injects the kit into each page and loads its stylesheet render-blocking, so pages don't flash unstyled on reload.
|
|
33
|
-
- **Examples kit — one design system and set of page helpers instead of six forks** — The `@layer kmwc` stylesheet merges six diverged `examples-shared.css` copies; its `--kmwc-*` tokens read the rulebook's `--base-*` taxonomy with `light-dark()` fallbacks, so a theme-designer theme restyles the demo page together with the component. Every page gets a light / dark / auto theme switch (it sets `data-theme` on `<html>`, the signal components react to), a chapter nav and copy-link buttons that share one slug algorithm, a version badge with the build stamp, and build-only Plausible. `attachControlsPersistence()` and `renderExamplesIndex()` replace the per-repo controls store and the 150-line inline index page.
|
|
34
|
-
- **Fragment pages — no more copied page boilerplate** — A page without `<html>` is a fragment: `<meta name="kmwc:title">` and friends drive the shared layout (head, entry script, header with back link, container), `<kmwc-include>` pulls parameterised partials (nesting, cycle detection), and `<kmwc-demo>` renders live markup plus a code block of exactly that markup, so a snippet can no longer drift from its demo. Full documents pass through untouched, so pages migrate one at a time.
|
|
35
|
-
- **`kmwc` CLI — generate, check and gate instead of hand-edit** — `kmwc sync` writes the Dockerfile, nginx.conf, Makefile (repo targets in `Makefile.local`), .dockerignore and a managed CLAUDE.md block from templates. `kmwc doctor` runs the devkit checks, an `npm pack` contents check (no CLAUDE.md, tests or example pages in the package) and every BlissFramework rulebook `*.checks.sh` suite. `kmwc check-vars` replaces three diverged manifest scripts (declared or consumed mode), and `kmwc check-release` gates `make publish*` on the dist-tag ⇔ version rule.
|
|
36
|
-
- **`kmwc new` — a rulebook-shaped component on the first commit** — The scaffold runs the rulebook intake (npm name free, CSS prefix 2–4 letters and not reserved, next free port block) and generates the Element / Logic / types / logger layers, the canonical eight-file CSS set with Strategy-B dark mode, a Custom Elements Manifest, README + `docs/`, CHANGELOG, `/publish`, fragment example pages and smoke + dark-mode e2e specs. It passes every rulebook suite as generated; `--minimal` gives an empty skeleton instead of the sample toggle.
|
|
37
|
-
- **Catalogue and port registry — every component listed, no port collisions** — `components.json` registers every Keenmate repo with its CSS prefix and a 100-port block (dev = block, preview = +1, image = +10), numbered inside a range per kind (components 12100–14900, libraries 15000–, showcases 16000–, tests 17000–), so any number of repos run side by side. The README catalogue is generated from it, with a ✅ for each repo on the devkit; web-commandpalette is the first.
|
|
38
|
-
|
|
39
38
|
## Catalogue
|
|
40
39
|
|
|
41
40
|
<!-- kmwc:catalogue:start — generated by `npm run readme`, do not edit inside -->
|
|
@@ -267,10 +266,26 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
267
266
|
Re-theme a site with `kmwc.theme.accentColor`. The page vocabulary is
|
|
268
267
|
unchanged from the old shared CSS — `.card`, `.example-section`, `.controls`,
|
|
269
268
|
`.demo-area`, `.demo-card`, `.code-block`, `.note(.warning|.info|.success|.error)`,
|
|
270
|
-
`.grid`/`.card-grid`, `.reference-table
|
|
269
|
+
`.grid`/`.card-grid`, `.reference-table` (+ `.table-*` modifiers), `.log-panel`, `.pill`,
|
|
271
270
|
`.status-dot`, `.control-group`, buttons (`.btn-secondary`, `.btn-outline`,
|
|
272
271
|
`.btn-danger`) — while everything the kit **generates** is prefixed BEM
|
|
273
272
|
(`kmwc-chapter-nav__item--active`, `kmwc-copy-link--copied`, …).
|
|
273
|
+
- **Form controls** look the same on every page without a class: text fields,
|
|
274
|
+
selects and list boxes (`multiple` / `size`), textareas, checkboxes, radios,
|
|
275
|
+
switches (`<input type="checkbox" role="switch">`), ranges, colour and file
|
|
276
|
+
inputs, fieldsets — modelled on pure-admin's forms and read through the
|
|
277
|
+
`--base-input-*` / `--base-checkbox-*` / `--base-icon-check` contract. Inputs,
|
|
278
|
+
selects and buttons share `--kmwc-control-height`, so a row of them lines up
|
|
279
|
+
in any container, not only `.controls`. The rules are zero-specificity
|
|
280
|
+
(`:where()`); `kmwc-native` on an element or an ancestor keeps the browser's
|
|
281
|
+
own controls (a light-DOM component that draws its own inputs). The
|
|
282
|
+
playground's `examples-forms.html` shows them all.
|
|
283
|
+
- **Tables:** any `<table>` gets a header fill, row lines and a hover tint;
|
|
284
|
+
`.reference-table` is the full-width, fully bordered API table. Modifiers
|
|
285
|
+
combine — `.table-full`, `.table-bordered`, `.table-striped`, `.table-compact`
|
|
286
|
+
(tighter cells; the controls in it shrink via `--kmwc-control-height`) — plus
|
|
287
|
+
`tr.is-highlight` and a `.num` cell (right-aligned, tabular figures). On a phone
|
|
288
|
+
a wide table scrolls inside its card. See the playground's `examples-tables.html`.
|
|
274
289
|
- **Theme switch** (top right) cycles light → dark → auto and sets
|
|
275
290
|
`data-theme` on `<html>` — the same signal components react to via
|
|
276
291
|
`:host-context([data-theme])`, so every example page is a dark-mode smoke
|
|
@@ -278,6 +293,22 @@ doctor` lists the ones still repeating the head/header boilerplate.
|
|
|
278
293
|
- **Chapter nav and copy-link** share one section/slug algorithm, so the
|
|
279
294
|
jump-list hashes and copied links always match. Floating UI is bundled (no
|
|
280
295
|
CDN). At the bottom of a short page, the last chapter gets highlighted.
|
|
296
|
+
- **Spotlight (Ctrl/⌘+K)** searches every chapter of every example page: a
|
|
297
|
+
chapter matches when its section code or heading text contains the query
|
|
298
|
+
(`th7` finds `TH07`; uncoded `Summary` / reference sections are found by
|
|
299
|
+
title), results are grouped by page with the current page first, ↑↓ / PgUp
|
|
300
|
+
/ PgDn / Home / End pick, Enter jumps, Ctrl/⌘+Enter opens a new tab. The
|
|
301
|
+
Vite plugin builds the index from every page's `<h2>`s (includes resolved)
|
|
302
|
+
as `virtual:kmwc-chapters`; the current page's chapters are read live, so
|
|
303
|
+
script-rendered sections count too. Off with
|
|
304
|
+
`kmwc.examples.isSpotlightEnabled: false` (a component that owns Ctrl+K
|
|
305
|
+
itself; a page handler calling `preventDefault()` also keeps the key). It
|
|
306
|
+
**mirrors** [web-commandpalette](https://github.com/keenmate/web-commandpalette)
|
|
307
|
+
— markup, matching, keyboard map, open/close, CSS — without depending on it;
|
|
308
|
+
see *Developing the devkit*.
|
|
309
|
+
- **Short chapter links**: `examples-theming.html#th07` (or `#th7`, or the
|
|
310
|
+
slug of an uncoded heading, `#summary`) scrolls to that chapter on arrival
|
|
311
|
+
— the codes from the rulebook's `example-pages.md` work as URLs.
|
|
281
312
|
- **Version badge**: any `[data-kmwc-version]` (or legacy `#version-badge`)
|
|
282
313
|
shows `v1.2.3`, or `v1.2.3 (built …)` in a built site.
|
|
283
314
|
- **Landing page**: `renderExamplesIndex({ pages })` renders the header, cards,
|
|
@@ -325,7 +356,7 @@ and the e2e suite before building and publishing.
|
|
|
325
356
|
**`kmwc doctor` and the rulebook.** Doctor runs every suite in the rulebook
|
|
326
357
|
(component-structure, naming-conventions, css-structure, theme-container,
|
|
327
358
|
base-variables, color-scheme, readme-structure, custom-elements-manifest,
|
|
328
|
-
responsive-overlay, publish-command) with `bash`, passing `kmwc.prefix`, and
|
|
359
|
+
responsive-overlay, publish-command, example-pages) with `bash`, passing `kmwc.prefix`, and
|
|
329
360
|
reports each `FAIL` as an error pointing at the matching `.checks.md` entry.
|
|
330
361
|
When the rulebook isn't checked out (or there's no bash) the suites are
|
|
331
362
|
skipped with a warning — the rulebook stays the single source of truth; the
|
|
@@ -421,8 +452,18 @@ npm install
|
|
|
421
452
|
npm test # unit tests (config, registry, templates, sync, checks, intake, plugin, kit)
|
|
422
453
|
npm run test:e2e # playground: the kit + scaffold in a real browser, dev server AND built site
|
|
423
454
|
npm run readme # regenerate the catalogue
|
|
455
|
+
npm run check:spotlight-mirror # what changed in web-commandpalette since the spotlight was aligned
|
|
456
|
+
make dev # the playground's dev server
|
|
424
457
|
```
|
|
425
458
|
|
|
459
|
+
**The spotlight mirrors web-commandpalette.** `src/examples/spotlight.js`,
|
|
460
|
+
`chapter-search.js` and `css/spotlight.css` keep wcp's structure block for
|
|
461
|
+
block (devkit naming), each marked `mirrors <file> <function>`;
|
|
462
|
+
[`scripts/spotlight-mirror.json`](scripts/spotlight-mirror.json) lists every
|
|
463
|
+
pair and the wcp / core commit they're aligned to. A bug fixed in one gets
|
|
464
|
+
ported to the other: run `npm run check:spotlight-mirror` (add `-- --diff`
|
|
465
|
+
for the patches), port what applies, bump `alignedCommit`.
|
|
466
|
+
|
|
426
467
|
[`playground/`](playground) is a real component scaffolded with `kmwc new` and
|
|
427
468
|
linked to this checkout. It's the reference for "what a component looks like",
|
|
428
469
|
the e2e target for every kit feature, and `npx kmwc doctor` inside it must
|
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",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"test": "node --test \"test/**/*.test.js\"",
|
|
49
49
|
"test:e2e": "cd playground && npx playwright test",
|
|
50
50
|
"check:contract": "base-contract-check src templates playground",
|
|
51
|
+
"check:spotlight-mirror": "node scripts/check-spotlight-mirror.js",
|
|
51
52
|
"playground": "cd playground && vite",
|
|
52
53
|
"playground:build": "cd playground && vite build --mode examples",
|
|
53
54
|
"playground:lib": "cd playground && vite build"
|
|
@@ -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/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: {
|
package/src/config.js
CHANGED
|
@@ -235,6 +235,7 @@ export function mapKmwcConfig(packageJson, root = process.cwd()) {
|
|
|
235
235
|
staticPaths: examples.staticPaths || [],
|
|
236
236
|
excludedPages: examples.excludedPages || [],
|
|
237
237
|
storagePrefix: examples.storagePrefix || id,
|
|
238
|
+
isSpotlightEnabled: examples.isSpotlightEnabled ?? true,
|
|
238
239
|
},
|
|
239
240
|
analytics: {
|
|
240
241
|
plausibleSiteId: analytics.plausibleSiteId || '',
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chapter search for the spotlight. MIRRORS web-commandpalette — see
|
|
3
|
+
* scripts/spotlight-mirror.json for the files and the commit it is aligned to;
|
|
4
|
+
* `npm run check:spotlight-mirror` lists what changed there since. A block
|
|
5
|
+
* marked `mirrors …` keeps the wcp structure line for line (devkit naming): fix
|
|
6
|
+
* a bug in one, port it to the other.
|
|
7
|
+
*
|
|
8
|
+
* Matching is wcp's: lower-cased substring of the whole query against any
|
|
9
|
+
* field — here the section code and the heading text. On top (spotlight only):
|
|
10
|
+
* a query that is a section code finds it without the zero padding (`th7` →
|
|
11
|
+
* TH07), and the order is exact code, then codes starting with the query, then
|
|
12
|
+
* the rest — each in index order. Pure, so it is unit-tested.
|
|
13
|
+
*/
|
|
14
|
+
import { mapToCodeKey } from './sections.js';
|
|
15
|
+
|
|
16
|
+
const RANK_EXACT_CODE = 0;
|
|
17
|
+
const RANK_CODE_PREFIX = 1;
|
|
18
|
+
const RANK_TEXT = 2;
|
|
19
|
+
|
|
20
|
+
// mirrors web-commandpalette src/filter.ts `norm`
|
|
21
|
+
const mapToNormalizedText = (value) => (value == null ? '' : String(value)).toLowerCase();
|
|
22
|
+
|
|
23
|
+
// mirrors web-commandpalette src/icons.ts escapeHtml()
|
|
24
|
+
export function mapToEscapedHtml(value) {
|
|
25
|
+
return String(value ?? '')
|
|
26
|
+
.replace(/&/g, '&')
|
|
27
|
+
.replace(/</g, '<')
|
|
28
|
+
.replace(/>/g, '>')
|
|
29
|
+
.replace(/"/g, '"')
|
|
30
|
+
.replace(/'/g, ''');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Substring match of `needle` against any of `fields`. Empty needle matches all. */
|
|
34
|
+
// mirrors web-commandpalette src/filter.ts matchesAny()
|
|
35
|
+
export function checkMatchesAny(fields, needle) {
|
|
36
|
+
const query = mapToNormalizedText(needle).trim();
|
|
37
|
+
if (!query) return true;
|
|
38
|
+
return fields.some((field) => mapToNormalizedText(field).includes(query));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The fields a chapter is searched by (wcp: commandFields()). */
|
|
42
|
+
export function getChapterFields(chapter) {
|
|
43
|
+
return [chapter.code, chapter.title];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {Array<{ code: string | null, title: string }>} chapters
|
|
48
|
+
* @param {string} query
|
|
49
|
+
* @returns the matching chapters, best first (all of them for an empty query)
|
|
50
|
+
*/
|
|
51
|
+
export function searchChapters(chapters, query) {
|
|
52
|
+
const needle = mapToNormalizedText(query).trim();
|
|
53
|
+
if (!needle) return chapters.slice();
|
|
54
|
+
const queryCodeKey = mapToCodeKey(needle);
|
|
55
|
+
|
|
56
|
+
const rankedChapters = [];
|
|
57
|
+
chapters.forEach((chapter, chapterIndex) => {
|
|
58
|
+
const code = mapToNormalizedText(chapter.code);
|
|
59
|
+
const isExactCode = code !== '' && mapToCodeKey(code) === queryCodeKey;
|
|
60
|
+
if (!isExactCode && !checkMatchesAny(getChapterFields(chapter), needle)) return;
|
|
61
|
+
const rank = isExactCode ? RANK_EXACT_CODE : code.startsWith(needle) ? RANK_CODE_PREFIX : RANK_TEXT;
|
|
62
|
+
rankedChapters.push({ chapter, rank, chapterIndex });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return rankedChapters
|
|
66
|
+
.sort((first, second) => first.rank - second.rank || first.chapterIndex - second.chapterIndex)
|
|
67
|
+
.map((rankedChapter) => rankedChapter.chapter);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Escape `text`, wrapping case-insensitive occurrences of `needle` in `<mark>`. */
|
|
71
|
+
// mirrors web-commandpalette src/render.ts highlight()
|
|
72
|
+
export function renderHighlightedText(text, needle) {
|
|
73
|
+
const query = needle.trim();
|
|
74
|
+
if (!query) return mapToEscapedHtml(text);
|
|
75
|
+
const lowerText = text.toLowerCase();
|
|
76
|
+
const lowerQuery = query.toLowerCase();
|
|
77
|
+
let html = '';
|
|
78
|
+
let index = 0;
|
|
79
|
+
for (let matchIndex = lowerText.indexOf(lowerQuery); matchIndex !== -1; matchIndex = lowerText.indexOf(lowerQuery, index)) {
|
|
80
|
+
html += mapToEscapedHtml(text.slice(index, matchIndex)) + '<mark>' + mapToEscapedHtml(text.slice(matchIndex, matchIndex + lowerQuery.length)) + '</mark>';
|
|
81
|
+
index = matchIndex + lowerQuery.length;
|
|
82
|
+
}
|
|
83
|
+
return html + mapToEscapedHtml(text.slice(index));
|
|
84
|
+
}
|
|
@@ -20,6 +20,57 @@ export const CHAPTER_NAV_CLASSES = {
|
|
|
20
20
|
|
|
21
21
|
export const CHAPTER_NAV_PANEL_ID = 'kmwc-chapter-nav-panel';
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Spotlight: the cross-page chapter search (spotlight.js). Element names
|
|
25
|
+
* mirror web-commandpalette's `pa-command-palette__*` shell (pure-admin's
|
|
26
|
+
* canonical command-palette snippet) under the kit prefix; `trigger` and
|
|
27
|
+
* `itemCode` are the spotlight's own.
|
|
28
|
+
*/
|
|
29
|
+
export const SPOTLIGHT_CLASSES = {
|
|
30
|
+
root: 'kmwc-spotlight',
|
|
31
|
+
rootActive: 'kmwc-spotlight--active',
|
|
32
|
+
rootFullscreen: 'kmwc-spotlight--fullscreen',
|
|
33
|
+
backdrop: 'kmwc-spotlight__backdrop',
|
|
34
|
+
container: 'kmwc-spotlight__container',
|
|
35
|
+
fullscreenBar: 'kmwc-spotlight__fullscreen-bar',
|
|
36
|
+
fullscreenTitle: 'kmwc-spotlight__fullscreen-title',
|
|
37
|
+
close: 'kmwc-spotlight__close',
|
|
38
|
+
search: 'kmwc-spotlight__search',
|
|
39
|
+
inputWrapper: 'kmwc-spotlight__input-wrapper',
|
|
40
|
+
input: 'kmwc-spotlight__input',
|
|
41
|
+
results: 'kmwc-spotlight__results',
|
|
42
|
+
homeSection: 'kmwc-spotlight__home-section',
|
|
43
|
+
homeHeading: 'kmwc-spotlight__home-heading',
|
|
44
|
+
item: 'kmwc-spotlight__item',
|
|
45
|
+
itemActive: 'kmwc-spotlight__item--active',
|
|
46
|
+
itemCode: 'kmwc-spotlight__item-code',
|
|
47
|
+
itemContent: 'kmwc-spotlight__item-content',
|
|
48
|
+
itemTitle: 'kmwc-spotlight__item-title',
|
|
49
|
+
empty: 'kmwc-spotlight__empty',
|
|
50
|
+
footer: 'kmwc-spotlight__footer',
|
|
51
|
+
hint: 'kmwc-spotlight__hint',
|
|
52
|
+
key: 'kmwc-spotlight__key',
|
|
53
|
+
live: 'kmwc-spotlight__live',
|
|
54
|
+
trigger: 'kmwc-spotlight__trigger',
|
|
55
|
+
triggerKey: 'kmwc-spotlight__trigger-key',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const SPOTLIGHT_RESULTS_ID = 'kmwc-spotlight-results';
|
|
59
|
+
export const SPOTLIGHT_ITEM_ID_PREFIX = 'kmwc-spotlight-item-';
|
|
60
|
+
/** The Ctrl/⌘ + letter that toggles the spotlight (wcp: `openKey`, default `k`). */
|
|
61
|
+
export const SPOTLIGHT_OPEN_KEY = 'k';
|
|
62
|
+
/** PgUp / PgDn step (wcp palette.ts PAGE_STEP). */
|
|
63
|
+
export const SPOTLIGHT_PAGE_STEP = 8;
|
|
64
|
+
/** Touch phone → fullscreen sheet (wcp: core's device classification, approximated in CSS terms). */
|
|
65
|
+
export const SPOTLIGHT_FULLSCREEN_MEDIA = '(pointer: coarse) and (max-width: 768px)';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A section code at the start of a heading label: `TH07 · Title` (rulebook
|
|
69
|
+
* example-pages.md). Forgiving on purpose — `EV4b` counts; enforcing the
|
|
70
|
+
* format is the rulebook check's job.
|
|
71
|
+
*/
|
|
72
|
+
export const CHAPTER_CODE_PATTERN = /^([A-Z]{2,3}\d+[a-z]?)\s*·\s*/;
|
|
73
|
+
|
|
23
74
|
export const COPY_LINK_CLASSES = {
|
|
24
75
|
root: 'kmwc-copy-link',
|
|
25
76
|
rootCopied: 'kmwc-copy-link--copied',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* ==============================================================================
|
|
2
2
|
EXAMPLES KIT — CONTENT
|
|
3
3
|
==============================================================================
|
|
4
|
-
Output panels, code, notes, grids,
|
|
4
|
+
Output panels, code, notes, grids, badges. Tables are tables.css. */
|
|
5
5
|
@layer kmwc {
|
|
6
6
|
/* ==========================================================================
|
|
7
7
|
OUTPUT
|
|
@@ -99,8 +99,10 @@
|
|
|
99
99
|
position: absolute;
|
|
100
100
|
top: 0.5rem;
|
|
101
101
|
right: 0.5rem;
|
|
102
|
+
min-height: 0; /* not the control height (button rule) */
|
|
102
103
|
padding: 0.25rem 0.75rem;
|
|
103
104
|
font-size: 0.75rem;
|
|
105
|
+
line-height: normal;
|
|
104
106
|
background: color-mix(in srgb, var(--kmwc-code-text-color) 10%, transparent);
|
|
105
107
|
color: var(--kmwc-code-text-color);
|
|
106
108
|
border: 1px solid color-mix(in srgb, var(--kmwc-code-text-color) 20%, transparent);
|
|
@@ -184,40 +186,8 @@
|
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
/* ==========================================================================
|
|
187
|
-
|
|
189
|
+
BADGES & HELPERS
|
|
188
190
|
========================================================================== */
|
|
189
|
-
.reference-table {
|
|
190
|
-
width: 100%;
|
|
191
|
-
border-collapse: collapse;
|
|
192
|
-
font-size: 0.875rem;
|
|
193
|
-
margin-bottom: 2rem;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.reference-table th,
|
|
197
|
-
.reference-table td {
|
|
198
|
-
padding: 0.75rem 1rem;
|
|
199
|
-
text-align: left;
|
|
200
|
-
border: 1px solid var(--kmwc-border-color);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.reference-table th {
|
|
204
|
-
background: var(--kmwc-elevated-bg);
|
|
205
|
-
font-weight: 600;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.reference-table tbody tr:hover {
|
|
209
|
-
background: var(--kmwc-hover-bg);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
.reference-table tr.is-highlight {
|
|
213
|
-
background: var(--kmwc-accent-light-bg);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
.reference-table code {
|
|
217
|
-
padding: 0.125rem 0.375rem;
|
|
218
|
-
font-size: 0.75rem;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
191
|
/* Section-header badge pill (e.g. a "NEW" tag beside an h2). */
|
|
222
192
|
.badge {
|
|
223
193
|
display: inline-block;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* ==============================================================================
|
|
2
2
|
EXAMPLES KIT — CONTROLS
|
|
3
3
|
==============================================================================
|
|
4
|
-
Demo forms, the `.controls` switch rows, demo areas and buttons.
|
|
4
|
+
Demo forms, the `.controls` switch rows, demo areas and buttons. How the
|
|
5
|
+
native controls themselves look is form-controls.css. */
|
|
5
6
|
@layer kmwc {
|
|
6
7
|
/* ==========================================================================
|
|
7
8
|
FORMS
|
|
@@ -61,30 +62,23 @@
|
|
|
61
62
|
align-items: center;
|
|
62
63
|
gap: 0.5rem;
|
|
63
64
|
cursor: pointer;
|
|
65
|
+
/* A long label wraps inside the row instead of pushing the page wider. */
|
|
66
|
+
flex-wrap: wrap;
|
|
67
|
+
max-width: 100%;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
.
|
|
67
|
-
|
|
70
|
+
.controls label code,
|
|
71
|
+
.toggle-label code {
|
|
72
|
+
overflow-wrap: anywhere;
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
accent-color: var(--kmwc-accent-color);
|
|
75
|
+
/* A select is as wide as its longest option — cap it at the row. */
|
|
76
|
+
.controls select {
|
|
77
|
+
max-width: 100%;
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
.controls input[type="number"],
|
|
79
|
-
.controls input[type="search"],
|
|
80
|
-
.control-group input,
|
|
81
|
-
.control-group select {
|
|
82
|
-
padding: 0.4rem 0.6rem;
|
|
83
|
-
border: var(--kmwc-input-border);
|
|
84
|
-
border-radius: var(--kmwc-radius-sm);
|
|
85
|
-
font-size: 0.875rem;
|
|
86
|
-
background: var(--kmwc-input-bg);
|
|
87
|
-
color: var(--kmwc-input-color);
|
|
80
|
+
.toggle-label {
|
|
81
|
+
margin-bottom: 0.75rem;
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
/* Control group — a label stacked over an input/select; rows of them wrap
|
|
@@ -155,13 +149,24 @@
|
|
|
155
149
|
cursor: pointer;
|
|
156
150
|
background: var(--kmwc-accent-color);
|
|
157
151
|
color: var(--kmwc-text-color-on-accent);
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
/* The control height (form-controls.css): a transparent border, so an
|
|
153
|
+
outline button is as tall as a filled one, and a fixed line box. */
|
|
154
|
+
border: 1px solid transparent;
|
|
155
|
+
min-height: var(--kmwc-control-height);
|
|
156
|
+
padding: calc((var(--kmwc-control-height) - 1.25rem - 2px) / 2) 1rem;
|
|
160
157
|
border-radius: var(--kmwc-radius-sm);
|
|
158
|
+
font: inherit;
|
|
161
159
|
font-size: 0.875rem;
|
|
160
|
+
line-height: 1.25rem;
|
|
161
|
+
vertical-align: middle;
|
|
162
162
|
transition: background 0.2s;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
button:focus-visible {
|
|
166
|
+
outline: 2px solid var(--kmwc-accent-color);
|
|
167
|
+
outline-offset: 2px;
|
|
168
|
+
}
|
|
169
|
+
|
|
165
170
|
button:hover,
|
|
166
171
|
button.btn-primary:hover {
|
|
167
172
|
background: var(--kmwc-accent-color-hover);
|
|
@@ -32,8 +32,10 @@
|
|
|
32
32
|
position: absolute;
|
|
33
33
|
top: 0.5rem;
|
|
34
34
|
right: 0.5rem;
|
|
35
|
+
min-height: 0; /* not the control height (button rule) */
|
|
35
36
|
padding: 0.25rem 0.75rem;
|
|
36
37
|
font-size: 0.75rem;
|
|
38
|
+
line-height: normal;
|
|
37
39
|
background: color-mix(in srgb, var(--kmwc-code-text-color) 10%, transparent);
|
|
38
40
|
color: var(--kmwc-code-text-color);
|
|
39
41
|
border: 1px solid color-mix(in srgb, var(--kmwc-code-text-color) 20%, transparent);
|