Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0acc908
refactor(website): extract the docs shell into lib/docs-shell.ts
vivek7405 Jul 27, 2026
45e9409
feat(website): mirror the ui registry into the site for /ui previews
vivek7405 Jul 27, 2026
85cb3ee
feat(website): serve the UI component gallery at /ui
vivek7405 Jul 27, 2026
113c0df
docs(website): scope the UI kit copy to WebJs apps
vivek7405 Jul 27, 2026
e369aac
docs(website): match the docs title shape on the UI gallery
vivek7405 Jul 27, 2026
54774f2
docs(website): capitalize the component heading, drop the back link
vivek7405 Jul 27, 2026
8a3e8b5
feat(website): serve the ui registry API at /ui/registry
vivek7405 Jul 27, 2026
892022f
feat(ui-website): turn ui.webjs.dev into a redirect-only host
vivek7405 Jul 27, 2026
123d9d8
feat(website): put the gallery in the sitemap and retire the docs/ui …
vivek7405 Jul 27, 2026
825d73b
test: repoint the ui suites at the gallery's new home
vivek7405 Jul 27, 2026
3274498
test: point the ui touch e2e and CI boot checks at /ui
vivek7405 Jul 27, 2026
53bcee0
docs: sync every surface with the gallery's move to webjs.dev/ui
vivek7405 Jul 27, 2026
0778d7c
docs(website): stop the tier map tripping the component scanner
vivek7405 Jul 27, 2026
d5bd358
fix: untrack stray files, resync the lock, and repair the redirect gaps
vivek7405 Jul 27, 2026
9ae1b1a
chore: ignore stray root images and a node_modules symlink
vivek7405 Jul 27, 2026
e375b49
fix: generate the ui mirror before tests, and cover the registry routes
vivek7405 Jul 27, 2026
d807ef6
perf(website): elide the shell carriers and drop dead action seeds
vivek7405 Jul 27, 2026
38273f4
fix(website): make the gallery's a11y match what it documents
vivek7405 Jul 27, 2026
fffeb5e
fix(website): give each component page its own description, list them…
vivek7405 Jul 27, 2026
e8b54f3
fix: name real helpers in gallery descriptions, drop the accordion un…
vivek7405 Jul 27, 2026
f83656d
fix(website): correct two drawer interaction bugs found in review
vivek7405 Jul 27, 2026
8ea26db
docs(website): correct the stated reason the drawer closes the header…
vivek7405 Jul 27, 2026
5d0b901
feat(ui): dim the accordion trigger on hover instead of underlining
vivek7405 Jul 27, 2026
a246001
style(website): restore the 1200/800 docs measure for /docs and /ui
vivek7405 Jul 27, 2026
6d9393b
style(website): keep the shell aligned to the header, cap the text at…
vivek7405 Jul 27, 2026
7c281ac
revert(website): restore the original docs shell measure
vivek7405 Jul 27, 2026
a37adc7
fix(website): put the source pane's scrollbar inside its card
vivek7405 Jul 27, 2026
ee5d59d
Merge branch 'main' into feat/ui-gallery-on-webjs-dev
vivek7405 Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .claude/skills/webjs-start-work/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,20 @@ Doc drift is the #1 way a framework rots. Documentation MUST stay in sync with c
- **Publishing on a release.** The VS Code extension publishes to the VS Marketplace + Open VSX (`packages/editors/vscode/PUBLISHING.md`); webjs.nvim is a git subtree split mirrored to `webjsdev/webjs.nvim` (re-run the split + force-push after a change; `packages/editors/nvim/PUBLISHING.md`). Bump `packages/editors/vscode/package.json` `version` when its bundle changes.
- **Heuristic:** if your change would make an editor highlight wrong, resolve the wrong definition, offer a stale snippet/command, or ship a drifted bundle, the editor plugins are part of your change. Update them on the same PR (with the matching test), re-vendor the nvim copy, or write "N/A because <reason>" in the PR body.
7. **Marketing copy** at `website/app/page.ts`. Update if the change touches positioning or any landing-page claim ("no-build", "AI-first", "web components first", etc.).
8. **Dogfood apps must still build and boot. MANDATORY GATE, run it automatically, never wait to be asked.** The framework ships four in-repo apps that consume it: `examples/blog` (the demo), `website`, `docs`, and `packages/ui/packages/website`. A framework change that compiles is NOT done until all four still serve. This is a recurring miss: running only the blog e2e and stopping is the exact failure this gate exists to prevent. For ANY change to `packages/core`, `packages/server`, `packages/cli`, the dist build, the importmap, or anything that alters what the browser fetches, you MUST run the full four-app check below before marking the draft PR ready for review and report its result in the PR body. The user should never have to ask "did you check the apps?".
8. **Dogfood apps must still build and boot. MANDATORY GATE, run it automatically, never wait to be asked.** The framework ships four in-repo apps that consume it: `examples/blog` (the demo), `website` (the marketing pages plus /docs and /ui), and the redirect-only `docs` and `packages/ui/packages/website` hosts. A framework change that compiles is NOT done until they all still serve. This is a recurring miss: running only the blog e2e and stopping is the exact failure this gate exists to prevent. For ANY change to `packages/core`, `packages/server`, `packages/cli`, the dist build, the importmap, or anything that alters what the browser fetches, you MUST run the full four-app check below before marking the draft PR ready for review and report its result in the PR body. The user should never have to ask "did you check the apps?".

**The check (copy-paste, runs in seconds):**
- `examples/blog`: covered by the e2e suite. Run `WEBJS_E2E=1 node --test test/e2e/e2e.test.mjs` (it exercises the blog in a real browser; if `dist/` is built it runs in dist mode, so it covers the production wire too).
- `website` / `docs` / `packages/ui/packages/website`: boot each through `createRequestHandler` in PROD mode and GET a real route, asserting status < 400. Write this harness to a file INSIDE the repo (bare `@webjsdev/*` specifiers only resolve from the repo's `node_modules`, NOT from `/tmp`), run it, delete it:
- `website`: boot it through `createRequestHandler` in PROD mode and GET a real route, asserting status < 400. It is the app that serves every HTML surface the project has, the marketing pages plus the documentation at `/docs` (#1098) and the component gallery at `/ui` (#1099), so its routes are where a break shows up. `docs/` and `packages/ui/packages/website/` are redirect-only hosts whose every route is an empty 301: they pass a status check vacuously, so probe their MAPPINGS via `test/docs/docs-host-redirect.test.mjs` and `test/ui/ui-host-redirect.test.mjs` instead of booting them here. Write this harness to a file INSIDE the repo (bare `@webjsdev/*` specifiers only resolve from the repo's `node_modules`, NOT from `/tmp`), run it, delete it:

```js
// ./.boot-check.mjs (write at repo root, run `node ./.boot-check.mjs`, then rm)
import { createRequestHandler } from '@webjsdev/server';
import { resolve } from 'node:path';
const apps = [
{ name: 'website', dir: 'website', routes: ['/'] },
{ name: 'docs', dir: 'docs', routes: ['/', '/docs/<a-page-you-touched>'] },
{ name: 'ui-website', dir: 'packages/ui/packages/website', routes: ['/'] },
// /ui/button is the heaviest page: it pulls the mirrored kit component
// sources, the exact import class a broken preload shows up in.
{ name: 'website', dir: 'website', routes: ['/', '/docs/<a-page-you-touched>', '/ui', '/ui/button'] },
];
let fail = false;
for (const app of apps) {
Expand Down Expand Up @@ -200,7 +200,7 @@ Doc drift is the #1 way a framework rots. Documentation MUST stay in sync with c
Add `GET` routes for any page you edited (a 307/308 redirect is a pass; it has no body to inspect). If a change is browser-wire-affecting (dist build, importmap, core exports), also assert the served `<script type="importmap">` reflects the change (e.g. grep the response HTML for the expected URLs), since a 200 alone does not prove the right modules were wired. The harness above also asserts no `modulepreload` hint 404s, which is how the #158 / #159 class of bug (a preload pointing at a server-only or phantom file the auth gate refuses to serve) gets caught automatically. **Auditing a LIVE deployed app instead of an in-process handler? Probe each preload URL with GET, never HEAD: the dev/prod server only serves source files on GET, so a HEAD probe 404s every source URL and makes a healthy app look completely broken.**
- The scaffold: `webjs create` generates an app whose `package.json` pins `@webjsdev/*: 'latest'` (see `packages/cli/lib/create.js`). If the change alters generated code, agent-config files, or expected scaffold behaviour, update `packages/cli/templates/` AND the generators in `packages/cli/lib/`, then confirm a freshly scaffolded app passes `webjs check` and `webjs test` (the `test/scaffolds/` suite covers this; run it). Even when you believe no scaffold change is needed, grep `packages/cli/templates/` for anything the change renamed or removed (e.g. a dropped dist filename, a changed import path) so a stale template reference does not ship into every new app.

Report the four-app result in the PR body (e.g. "Dogfood: blog e2e 50/50; website / docs / ui-website boot 200 in dist mode; scaffold N/A"). "All four apps verified" with no evidence is not acceptable.
Report the dogfood result in the PR body (e.g. "Dogfood: blog e2e 50/50; website boots 200 on /, /docs/*, /ui, /ui/button in dist mode with no broken preloads; redirect hosts covered by their mapping tests; scaffold N/A"). "All apps verified" with no evidence is not acceptable.
9. **Version bumps must keep the workspace consistent.** When you bump a `packages/<pkg>/package.json` `version` (which the pre-commit hook turns into a changelog):
- Every in-repo dependent that pins that package (grep `"@webjsdev/<pkg>"` across all `package.json` files) must have its range updated so the new version still satisfies it. A minor bump (`0.7.x` -> `0.8.0`) falls outside a `^0.7.0` range, so npm stops linking the local workspace and pulls the published copy instead.
- Regenerate `package-lock.json` (`npm install --package-lock-only`) and commit it. `npm ci` (which CI runs) fails on any lockfile desync, so an unsynced lock is a guaranteed red CI.
Expand Down
44 changes: 23 additions & 21 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
cache: npm
- run: npm ci
# Dogfood the framework's own correctness checks on the in-repo apps.
- name: webjs check (blog, website, docs, ui website)
- name: webjs check (blog, website, docs host, ui host)
run: |
for app in examples/blog website docs packages/ui/packages/website; do
echo "::group::webjs check $app"
Expand Down Expand Up @@ -146,14 +146,14 @@ jobs:
# a node:crypto digest, so it must be byte-identical on the Bun.serve path.
- name: App-source deploy signal on Bun
run: bun test/bun/app-source-signal.mjs
# Boot website (which serves /docs) + ui-website on Bun and GET real
# Boot the website (which serves /docs and /ui) on Bun and GET real
# routes (#542). Every in-repo app deploys on Bun, but only examples/blog
# had a Bun boot in CI, so a per-route break only on Bun (the #526
# ui-website 500) could reach production. The script runs each app's
# webjs.start.before presteps (the ui-website registry copy is the #526
# root cause) and probes an ui-website component detail page. Node runs it
# too in the "In-repo app tests" job.
- name: App boot-check on Bun (website incl. /docs + ui-website)
# component-page 500) could reach production. The script runs the app's
# webjs.start.before presteps (the ui registry copy is the #526 root
# cause) and probes a gallery component detail page. Node runs it too in
# the "In-repo app tests" job.
- name: App boot-check on Bun (website incl. /docs + /ui)
run: bun test/bun/app-boot.mjs
# SSR action-result seeding on Bun (#529): seeding rode Node's
# module.registerHooks, which Bun lacks; it now installs via a Bun.plugin
Expand Down Expand Up @@ -311,7 +311,7 @@ jobs:
WEBJS_E2E: '1'
run: node --test test/e2e/browser-harness.test.mjs
# Touch-emulation e2e for interactive Tier-2 ui components (#745/#747):
# boots the ui-website and taps hover-card / dropdown-submenu / sonner
# boots the site serving the gallery and taps hover-card / dropdown-submenu / sonner
# under a Chromium iPhone context (faithful touch events, no real device).
- name: Run ui touch e2e
run: npm run test:e2e:touch --workspace=@webjsdev/ui
Expand Down Expand Up @@ -383,11 +383,12 @@ jobs:
# app's tests gates the merge (issue #342). The website's `test` runs both
# its node + browser suites (hence Playwright), and covers the docs too
# since they are its own /docs routes; the blog is node-only and touches
# its SQLite DB (the same setup the unit + e2e jobs do). The ui-website
# ships no `webjs test` suite, so it is covered by the
# `node test/bun/app-boot.mjs` boot-check step below (#627), which boots it
# via createRequestHandler and asserts it serves real routes with no
# broken modulepreload; the same script runs on Bun in the `bun` job (#542).
# its SQLite DB (the same setup the unit + e2e jobs do). The website's
# suite also covers the component gallery, which is its own /ui routes
# since #1099. The `node test/bun/app-boot.mjs` boot-check step below
# (#627) additionally boots it via createRequestHandler and asserts it
# serves real routes with no broken modulepreload; the same script runs on
# Bun in the `bun` job (#542).
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
Expand All @@ -407,14 +408,15 @@ jobs:
run: npm test --workspace=@webjsdev/website
- name: blog tests (node)
run: npm test --workspace=@webjsdev/example-blog
# The ui-website ships no `webjs test` suite (#627): boot the non-blog
# apps on Node and assert each serves real routes with no broken
# modulepreload, including the website's /docs routes. Runs the apps'
# `webjs.start.before` presteps first (the ui-website registry copy). The
# blog is covered by the e2e job. The docs.webjs.dev redirect host is not
# here on purpose: every route on it is an empty 301, which would pass
# vacuously; it is covered by test/docs/docs-host-redirect.test.mjs.
- name: App boot-check on Node (website incl. /docs + ui-website)
# Boot the website on Node and assert it serves real routes with no
# broken modulepreload, covering its /docs (#1098) and /ui (#1099)
# routes. Runs the app's `webjs.start.before` presteps first (the ui
# registry copy, the #526 root cause). The blog is covered by the e2e
# job. The docs.webjs.dev and ui.webjs.dev redirect hosts are not here on
# purpose: every route on them is an empty 301, which would pass
# vacuously; they are covered by test/docs/docs-host-redirect.test.mjs
# and test/ui/ui-host-redirect.test.mjs.
- name: App boot-check on Node (website incl. /docs + /ui)
run: node test/bun/app-boot.mjs

docker:
Expand Down
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# deps
node_modules/
# No trailing slash on purpose: with one, this matches DIRECTORIES only, so a
Comment thread
vivek7405 marked this conversation as resolved.
# stray `node_modules` SYMLINK is not ignored and a `git add -A` commits it.
# That happened, and a self-referential symlink resolves to an ELOOP path that
# npm and Node's resolver both have to stat in every fresh clone.
node_modules

# Screenshots and scratch images dropped at the repo root while working. Only
# root level, so the real assets under */public/ stay tracked.
/*.png
/*.jpg
/*.jpeg

# build / caches
dist/
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ webjs ui init | add <names...> | list | view <name>
webjs vendor pin|unpin|list|audit|outdated|update [--from PROVIDER] # importmap pinning, .webjs/vendor/importmap.json
```

`--from PROVIDER` accepts `jspm` (default), `jsdelivr`, `unpkg`, `skypack` and is persisted in the pin file. `PORT` is honoured when `--port` is absent; `webjs dev` emits `routes.d.ts` automatically. Running this repo's own apps (`website/`, `docs/`, `examples/blog/`, `packages/ui/packages/website/`): `cd` in and use **its** `npm run dev` / `npm start`; as of #550 a bare `webjs dev` / `webjs start` is equivalent (the per-app orchestration moved into the `webjs.dev` / `webjs.start` tasks config, which the primitive runs), so the npm scripts are now just thin aliases.
`--from PROVIDER` accepts `jspm` (default), `jsdelivr`, `unpkg`, `skypack` and is persisted in the pin file. `PORT` is honoured when `--port` is absent; `webjs dev` emits `routes.d.ts` automatically. Running this repo's own apps (`website/`, which serves the docs and the UI gallery, plus the redirect-only `docs/` and `packages/ui/packages/website/` hosts, and `examples/blog/`): `cd` in and use **its** `npm run dev` / `npm start`; as of #550 a bare `webjs dev` / `webjs start` is equivalent (the per-app orchestration moved into the `webjs.dev` / `webjs.start` tasks config, which the primitive runs), so the npm scripts are now just thin aliases.

---

Expand Down
29 changes: 16 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -107,26 +107,29 @@ RUN npm run build:dist --workspace=@webjsdev/core
# `webjs.start.before` (`webjs db migrate`), not at build time.

# UI registry: the registry JSON is composed on demand by the route handlers
# (no build step). But the ui-website's component DETAIL pages statically import
# the component SOURCES from `components/ui/*.ts`, which the `prestart` hook
# generates by copying them out of packages/ui/packages/registry/. A start
# command that serves directly (the `bun webjs.js start` form, which bypasses
# npm `prestart`, the same way the Tailwind step below is needed) would 500 on
# every component page without these files, so bake them at build time here.
# `copy-registry.js` is pure filesystem (no runtime / network / DB), so it is
# build-safe.
RUN cd packages/ui/packages/website && node scripts/copy-registry.js
# (no build step). But the gallery's component DETAIL pages statically import
# the component SOURCES from `components/ui/*.ts`, which are a gitignored
# mirror of packages/ui/packages/registry/. A start command that serves
# directly (the `bun webjs.js start` form, which bypasses npm `prestart`, the
# same way the Tailwind step below is needed) would 500 on every component page
# without these files, so bake them at build time here. The mirror now lands in
# the marketing app, since that is where the gallery lives (#1099).
# `copy-registry.mjs` is pure filesystem (no runtime / network / DB), so it is
# build-safe, and it MUST run before the Tailwind step below: the generated
# sources are a scanned @source, so their utility classes are missing from the
# stylesheet otherwise.
RUN cd website && node scripts/copy-registry.mjs

# Tailwind: compile per-app CSS (every app with a stylesheet uses the CLI, no
# browser runtime). The `docs` service is absent on purpose: it renders no HTML
# at all now, only redirects to webjs.dev/docs, so it has no stylesheet.
# browser runtime). The `docs` and `ui-website` services are absent on purpose:
# both render no HTML at all now, only redirects (to webjs.dev/docs and
# webjs.dev/ui), so neither has a stylesheet.
# Each compose service's command invokes `webjs.js start` directly, which
# bypasses the per-package `prestart: css:build` hook in npm; the CSS has
# to be ready in the image. Keep this list in sync with the apps that
# have a public/input.css and a `css:build` script in their package.json.
RUN npx tailwindcss -i website/public/input.css -o website/public/tailwind.css --minify \
&& npx tailwindcss -i examples/blog/public/input.css -o examples/blog/public/tailwind.css --minify \
&& npx tailwindcss -i packages/ui/packages/website/public/input.css -o packages/ui/packages/website/public/tailwind.css --minify
&& npx tailwindcss -i examples/blog/public/input.css -o examples/blog/public/tailwind.css --minify

# Default env vars. Railway / compose set their own per service.
ENV NODE_ENV=production
Expand Down
Loading