feat: per-package per-version changelog + /changelog page + auto-enforce on version bumps#49
Merged
Merged
Conversation
Introduce a changelog/ directory at the repo root with one md file
per (package, version) tuple:
changelog/
README.md architecture + conventions
core/0.6.0.md one file per release
server/0.7.1.md
cli/0.7.0.md
ts-plugin/0.4.0.md
ui/0.2.0.md
…
The shape (per-package per-version, not per-commit) matches the way
we publish: every change of a packages/<pkg>/package.json version
field becomes a release entry. Each entry frontmatters package,
version, date, commit_count; the body groups qualifying commits
(feat / fix / breaking / perf) under Breaking / Features /
Performance / Fixes headings with PR links and commit SHAs.
scripts/backfill-changelog.js walks every packages/<pkg>/package.json
in git history, finds the commits that changed the version field,
and writes <pkg>/<version>.md for any (package, version) tuple that
does not yet have an entry. Existing files are left alone so
hand-curation survives re-runs.
Backfilled 43 files across the 5 packages from project inception
through 0.6.0 (core), 0.7.1 (server), 0.7.0 (cli), 0.4.0 (ts-plugin),
and 0.2.0 (ui).
Add website/app/changelog/page.ts that reads changelog/<pkg>/*.md at SSR time, parses YAML-ish frontmatter and the minimal markdown shape the backfill generator emits (h1, h2, bulleted lists, links, inline code, bold), sorts entries by date desc, and renders one card per release with a package-coloured badge, version, date, and commit count. Add the Changelog link to the layout nav. The header now uses flex-wrap with gap-y-3 and the nav itself uses flex-wrap with gap-x-3 sm:gap-x-4 + gap-y-2 so the now-six chrome elements (Docs, UI, Changelog, Blog Demo, GitHub, theme-toggle) wrap into a second row on narrow viewports instead of overflowing. Verified locally: GET /changelog returns 200 with 156 KB of rendered HTML containing every backfilled version across all five packages.
Add the "version bump triggers changelog" rule to the framework AGENTS.md and back it with two gates: 1. .hooks/pre-commit refuses any commit whose staged diff bumps a packages/<pkg>/package.json version field without a matching changelog/<pkg>/<version>.md file. Prints the exact command the author needs (node scripts/backfill-changelog.js && git add changelog/). 2. .claude/hooks/changelog-nudge.sh is a PreToolUse hook that fires on Bash tool calls whose command matches git commit. It runs the same check and emits a permissionDecision: deny JSON payload, so Claude Code refuses the commit before it leaves the agent's shell. Wired into .claude/settings.json. Both hooks are bypassable with --no-verify for emergencies. The AI agent rule in AGENTS.md instructs agents to run the generator in the same commit as the version bump, review the generated file, and edit it in place for clarity before pushing.
The previous flex-wrap nav let the items wrap below the logo on narrow viewports, but on iPhone XR (414px) the 5 links + theme toggle still felt congested. Replace it with a real mobile menu. Below md (768px): the logo, theme-toggle, and a hamburger button sit on one row; the hamburger is a native <details>/<summary> that pops the same 5 links into a dropdown panel. At md and up: hide the mobile cluster, show the inline nav as before. CSS strips the default disclosure triangle and swaps the hamburger / close icons via .mobile-menu[open]. Progressive enhancement: native <details> works without JS, so the menu opens / closes on every viewport even when scripts have not yet hydrated.
Native <details> stays open on inner-anchor activation, so tapping Changelog (or any other link) on mobile kept the panel visible. Add a delegated click listener that strips the open attribute from the parent <details> when any link inside .mobile-menu is activated. Covers both regular target=_blank links and the same-origin client- router-intercepted ones.
webjs.dev, example-blog, and ui.webjs.dev now share the same mobile header layout: hamburger LEFT, theme-toggle RIGHT, with a native <details>/<summary> dropdown panel anchored to the hamburger. - webjs website: swap the order so the hamburger sits LEFT of the theme-toggle (was the reverse). - example blog: drop the old fixed-drawer + body[data-menu-open] pattern in favour of the dropdown. Removes ~30 lines of CSS and matches the rest of the chrome surfaces. The mobile cluster is sm:hidden because the blog's existing inline nav is sm:flex. - ui.webjs.dev: introduce the same dropdown pattern from scratch. The "Webjs" cross-link, previously hidden on small screens via hidden sm:inline, now lives in the dropdown panel where it's reachable on phones. Docs site keeps its existing left-rail-drawer pattern (intentionally untouched, since the docs UI needs a full sidebar, not a dropdown). Same delegated click handler in every layout: any anchor activation inside .mobile-menu strips the open attribute on the parent <details>, so the panel auto-closes on navigation.
Tapping outside the open dropdown should dismiss it on a phone; previously only the close icon (the toggled summary) closed it, which felt off because the user has nothing visually grabbing their attention to that one tap target. Extend the existing delegated click handler in all three layouts to walk `.mobile-menu[open]` and strip the open attribute on any that don't contain the click target. The link-click branch is unchanged: links inside the panel still close the panel directly. Docs site keeps its own drawer pattern; this change does not touch it.
3 tasks
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
Three packages have user-facing changes accumulated since their last bump: - @webjskit/core 0.6.0 -> 0.7.0 Breaking: signals replace this.state / setState across the stack (#43). Slot projection cycle fix in light DOM (#44). - @webjskit/cli 0.7.0 -> 0.8.0 Scaffold templates updated for the signals migration (#43); a newly-scaffolded app now ships signal-based state out of the box and the scaffold tests assert the new shape. - @webjskit/ui 0.2.0 -> 0.3.0 Env-driven sibling URLs with localhost dev fallbacks (#42), workspace dep alignment (#35), signal migration in the registry components and ui-website (#43), and the responsive mobile menu / changelog page that landed alongside the changelog system (#49). Cross-package dependency ranges in website, docs, examples/blog, ui-website, server, and cli updated to track the new versions (^0.6.0 -> ^0.7.0 for core, ^0.7.0 -> ^0.8.0 for cli, ^0.2.0 -> ^0.3.0 for ui). package-lock.json regenerated. After this lands on main, the Auto-release workflow will publish all three to npm and create matching GitHub Releases.
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
…rce on version bumps (#49) * feat(changelog): per-package per-version changelog + backfill from git Introduce a changelog/ directory at the repo root with one md file per (package, version) tuple: changelog/ README.md architecture + conventions core/0.6.0.md one file per release server/0.7.1.md cli/0.7.0.md ts-plugin/0.4.0.md ui/0.2.0.md … The shape (per-package per-version, not per-commit) matches the way we publish: every change of a packages/<pkg>/package.json version field becomes a release entry. Each entry frontmatters package, version, date, commit_count; the body groups qualifying commits (feat / fix / breaking / perf) under Breaking / Features / Performance / Fixes headings with PR links and commit SHAs. scripts/backfill-changelog.js walks every packages/<pkg>/package.json in git history, finds the commits that changed the version field, and writes <pkg>/<version>.md for any (package, version) tuple that does not yet have an entry. Existing files are left alone so hand-curation survives re-runs. Backfilled 43 files across the 5 packages from project inception through 0.6.0 (core), 0.7.1 (server), 0.7.0 (cli), 0.4.0 (ts-plugin), and 0.2.0 (ui). * feat(website): /changelog page + Changelog nav link, responsive nav Add website/app/changelog/page.ts that reads changelog/<pkg>/*.md at SSR time, parses YAML-ish frontmatter and the minimal markdown shape the backfill generator emits (h1, h2, bulleted lists, links, inline code, bold), sorts entries by date desc, and renders one card per release with a package-coloured badge, version, date, and commit count. Add the Changelog link to the layout nav. The header now uses flex-wrap with gap-y-3 and the nav itself uses flex-wrap with gap-x-3 sm:gap-x-4 + gap-y-2 so the now-six chrome elements (Docs, UI, Changelog, Blog Demo, GitHub, theme-toggle) wrap into a second row on narrow viewports instead of overflowing. Verified locally: GET /changelog returns 200 with 156 KB of rendered HTML containing every backfilled version across all five packages. * feat(changelog): auto-enforce changelog when a package version bumps Add the "version bump triggers changelog" rule to the framework AGENTS.md and back it with two gates: 1. .hooks/pre-commit refuses any commit whose staged diff bumps a packages/<pkg>/package.json version field without a matching changelog/<pkg>/<version>.md file. Prints the exact command the author needs (node scripts/backfill-changelog.js && git add changelog/). 2. .claude/hooks/changelog-nudge.sh is a PreToolUse hook that fires on Bash tool calls whose command matches git commit. It runs the same check and emits a permissionDecision: deny JSON payload, so Claude Code refuses the commit before it leaves the agent's shell. Wired into .claude/settings.json. Both hooks are bypassable with --no-verify for emergencies. The AI agent rule in AGENTS.md instructs agents to run the generator in the same commit as the version bump, review the generated file, and edit it in place for clarity before pushing. * fix(hooks): correct off-by-one in awk slice that picked up trailing slash in pkg name * feat(website): mobile hamburger menu using native <details>/<summary> The previous flex-wrap nav let the items wrap below the logo on narrow viewports, but on iPhone XR (414px) the 5 links + theme toggle still felt congested. Replace it with a real mobile menu. Below md (768px): the logo, theme-toggle, and a hamburger button sit on one row; the hamburger is a native <details>/<summary> that pops the same 5 links into a dropdown panel. At md and up: hide the mobile cluster, show the inline nav as before. CSS strips the default disclosure triangle and swaps the hamburger / close icons via .mobile-menu[open]. Progressive enhancement: native <details> works without JS, so the menu opens / closes on every viewport even when scripts have not yet hydrated. * fix(website): close mobile menu when a link inside it is clicked Native <details> stays open on inner-anchor activation, so tapping Changelog (or any other link) on mobile kept the panel visible. Add a delegated click listener that strips the open attribute from the parent <details> when any link inside .mobile-menu is activated. Covers both regular target=_blank links and the same-origin client- router-intercepted ones. * feat(website,blog,ui): unify mobile-menu pattern across the three apps webjs.dev, example-blog, and ui.webjs.dev now share the same mobile header layout: hamburger LEFT, theme-toggle RIGHT, with a native <details>/<summary> dropdown panel anchored to the hamburger. - webjs website: swap the order so the hamburger sits LEFT of the theme-toggle (was the reverse). - example blog: drop the old fixed-drawer + body[data-menu-open] pattern in favour of the dropdown. Removes ~30 lines of CSS and matches the rest of the chrome surfaces. The mobile cluster is sm:hidden because the blog's existing inline nav is sm:flex. - ui.webjs.dev: introduce the same dropdown pattern from scratch. The "Webjs" cross-link, previously hidden on small screens via hidden sm:inline, now lives in the dropdown panel where it's reachable on phones. Docs site keeps its existing left-rail-drawer pattern (intentionally untouched, since the docs UI needs a full sidebar, not a dropdown). Same delegated click handler in every layout: any anchor activation inside .mobile-menu strips the open attribute on the parent <details>, so the panel auto-closes on navigation. * fix(website,blog,ui): close mobile menu on outside click too Tapping outside the open dropdown should dismiss it on a phone; previously only the close icon (the toggled summary) closed it, which felt off because the user has nothing visually grabbing their attention to that one tap target. Extend the existing delegated click handler in all three layouts to walk `.mobile-menu[open]` and strip the open attribute on any that don't contain the click target. The link-click branch is unchanged: links inside the panel still close the panel directly. Docs site keeps its own drawer pattern; this change does not touch it.
vivek7405
added a commit
that referenced
this pull request
May 21, 2026
Three packages have user-facing changes accumulated since their last bump: - @webjskit/core 0.6.0 -> 0.7.0 Breaking: signals replace this.state / setState across the stack (#43). Slot projection cycle fix in light DOM (#44). - @webjskit/cli 0.7.0 -> 0.8.0 Scaffold templates updated for the signals migration (#43); a newly-scaffolded app now ships signal-based state out of the box and the scaffold tests assert the new shape. - @webjskit/ui 0.2.0 -> 0.3.0 Env-driven sibling URLs with localhost dev fallbacks (#42), workspace dep alignment (#35), signal migration in the registry components and ui-website (#43), and the responsive mobile menu / changelog page that landed alongside the changelog system (#49). Cross-package dependency ranges in website, docs, examples/blog, ui-website, server, and cli updated to track the new versions (^0.6.0 -> ^0.7.0 for core, ^0.7.0 -> ^0.8.0 for cli, ^0.2.0 -> ^0.3.0 for ui). package-lock.json regenerated. After this lands on main, the Auto-release workflow will publish all three to npm and create matching GitHub Releases.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three layers, one feature.
1. Architecture: per-package per-version under
changelog/Each file has frontmatter (
package,version,date,commit_count) and a body that groups conventional-commits under## Breaking/## Features/## Performance/## Fixeswith PR + commit links.The model: a version bump in any
packages/<pkg>/package.jsontriggers a changelog file, populated from the commits since the last bump of the same package.chore/refactor/test/docs/style/build/cicommits never appear (they don't change the user contract).scripts/backfill-changelog.jswalks everypackages/<pkg>/package.jsonhistory, finds version-changing commits, scans the inter-bump commits that touched that package, and writes any missing<pkg>/<version>.md. Re-runs are idempotent (existing files are never overwritten, so hand-curation survives).2. Backfill from project inception
43 files generated across all 5 packages: core (7 versions), server (14), cli (18), ts-plugin (3), ui (1).
3. Website:
/changelogpage with responsive navwebsite/app/changelog/page.tsreads every backfilled file at SSR time, parses the minimal markdown shape the generator emits (h1, h2, bulleted lists, links, inline code, bold), and renders one card per release with a package-coloured badge, version, date, and commit count.Layout nav (
website/app/layout.ts) gets the new "Changelog" link. The header now usesflex-wrap+gap-yon both row and nav, so the six chrome elements (Docs, UI, Changelog, Blog Demo, GitHub, theme-toggle) wrap into a second row on narrow viewports instead of overflowing. Mobile / laptop / desktop all unblocked.Verified locally:
GET /changelogreturns 200 with 156 KB containing every version across all packages.4. Automation: hooks that won't let an agent forget
Two gates fire when staged diffs bump a
packages/<pkg>/package.jsonversion without a matchingchangelog/<pkg>/<version>.md:.hooks/pre-commitrefuses the commit and prints the exact fix (node scripts/backfill-changelog.js && git add changelog/)..claude/hooks/changelog-nudge.shis a Claude CodePreToolUsehook onBashthat catchesgit commitinvocations from agents and emitspermissionDecision: denyso the tool call is refused with the same reason.Root AGENTS.md gets a "Changelog: per-package, per-version, auto-generated" section spelling out the rule for AI agents working in the monorepo.
Test plan
node scripts/backfill-changelog.js→ 43 entries created across 5 packages.GET /changelogreturns 200, renders all entries, package badges colour-coded.coreversion bump that lacked a matching file. Fixed an off-by-one in the awk slice that previously emittedcore/instead ofcore.npm test(1150) + browser (265) + e2e (48) suites continue to pass since nothing in the runtime changed.Follow-ups (separate PRs)
breaking:entries (core 0.5.0 signals migration, core 0.4.0 light-DOM defaults) to add migration code samples./changelog.releases/<version>.mdsnapshot for the framework as a whole.