Repository navigation
Conversation
ForgeDialog had a README and a manual test page but nothing to point people at. site/ is a landing page with an interactive appearance playground, written in plain HTML, CSS, and browser JavaScript so the site carries no dependencies of its own, matching the library it advertises. The page runs the build from this checkout rather than a CDN: scripts/build-site.mjs copies dist/index.global.js and dist/index.css into site/vendor/, which is git-ignored. It uses only Node built-ins and rewrites no source files. The version badge reads the library's own VERSION export at runtime, so nothing has to be kept in sync by hand. The playground calls instance.update() on every control change, so the dialog restyles while it is open, and it prints the matching source alongside, so the demo doubles as documentation for the appearance API. site/_headers ships a strict CSP for Cloudflare Pages limited to same-origin scripts and styles, which the library already satisfies: test:csp enforces that dist contains no eval or new Function. docs/website.md covers local use and the Cloudflare build settings. Verified in Chromium: no console errors or failed requests, the alert traps focus and returns it to the trigger on Escape, the playground applies a 30px radius and casts its shadow right at 90 degrees, live updates reach an open dialog, and the theme toggle drives the library and the page together. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
…ise for search and answer engines The site had a landing page and an emoji favicon; it now has a full API reference, a real mark, and the metadata that search engines and answer engines read. Naming: the product is Forge Dialog, two words, and every page and document now says so. Identifiers keep the one-word spelling because that is what they are: the npm package (forgedialog), the browser global (ForgeDialog), the custom element (forge-dialog), and the repository. Nothing under src/ changed, so the published API and its runtime strings are untouched. Docs: site/docs.html is a 23-section reference written against the reviewed API report in etc/, covering every option, method, and entry point. The shared chrome (version badge, theme toggle, copy buttons) moved out of site.js into common.js so both pages use it without the landing page's demo wiring. Icon: site/icon.svg is the source of truth and the favicon browsers load. scripts/build-icons.mjs renders the committed apple-touch-icon.png and og-image.png from it through Chromium, which is already a dev dependency. The touch variant squares off the mark's ground because iOS applies its own mask. The first draft of the OG image used a gradient and cost 296 KB; a flat ground looks the same at preview size and costs 37 KB. SEO: canonical, og:url, and og:image are absolute. The canonical tag in index.html is the single source of truth for the domain — build-site.mjs reads it back to generate sitemap.xml, robots.txt, and llms.txt, so moving the site is an edit to the canonical tags and nothing else. Both pages carry JSON-LD; the FAQ entries are generated from the visible questions so the markup and the page cannot disagree. Answer engines: llms.txt follows the llmstxt.org convention and states the facts models most often get wrong, with the version read from package.json so it cannot drift. robots.txt names the AI crawlers explicitly rather than leaving them to the default, and deliberately does not disallow /vendor/ — blocking a site's own CSS and JS is what breaks rendering for crawlers. Two layout bugs found while measuring, both pre-existing: - The header never wrapped, so at 380px the page scrolled sideways by 102px. - The playground's grid track had an automatic minimum of min-content, which the long code lines inside .snippet pushed open — 812px of horizontal overflow at 320px. Capping the track at minmax(0, 1fr) and giving .snippet min-width: 0 fixes it. Both pages now fit at 320, 380, 480, and 768px with zero horizontal overflow. Verified in Chromium against the production CSP: no console errors and no failed requests on either page, the inline JSON-LD survives script-src 'self' (it is a data block, not an executable script), all 23 sidebar links resolve to real sections, scroll-spy tracks the section being read, the hero alert still gets role="alertdialog" with focus returning to its trigger, and robots.txt, sitemap.xml, llms.txt, and all three icons serve with the right content types. npm run validate and format:check pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
The Cloudflare Worker was built with `npm run build` and deployed with `wrangler deploy --assets ./dist`, so it uploaded 97 files of compiled library — .mjs, .cjs, .d.ts, .css — with no index.html among them. Workers Assets had nothing to answer `/` with, which is why the domain served nothing. wrangler.jsonc now pins the assets directory to `site/`, the directory `npm run site:build` actually fills, so the configuration lives in the repository rather than in a dashboard field. A deploy command that passes `--assets` overrides it, so docs/website.md says to drop the flag. While verifying the routing, the docs page turned out to be reachable only by redirect. Workers Assets serves `foo.html` at `/foo` and 307s the extension form, and `serve` (which `npm run site` uses) does the same, so every URL naming `/docs.html` — the canonical tag, og:url, the TechArticle and BreadcrumbList nodes, the sitemap entry, the llms.txt link, and the in-page links — pointed at a URL that redirects. A canonical or sitemap entry that redirects is a conflicting signal, so `/docs` is now the URL everywhere, and the docs page links back to `./` rather than `./index.html`. Verified against `wrangler dev` with the production `_headers` CSP: both pages and every asset return 200, every relative link resolves without a redirect, the pages load with no console errors or failed requests, the JSON-LD parses, and the hero alert still opens as `role="alertdialog"` and closes on Escape. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
A repo-wide pass before the worker is recreated from scratch. An unmatched path got the platform's bare 404, so site/404.html now answers for them through not_found_handling. It links its assets root-relative rather than with "./" because it also answers for nested paths, where a relative href would resolve against the missing directory, and it carries noindex with no canonical tag since it has no URL of its own. The sitemap stamped every entry with the build date, which told crawlers both pages change daily. lastmod now comes from the last commit that touched each page's HTML. A build with no reachable git history writes the entry without a lastmod rather than with a plausible-looking date. npm run check:release reported what the docs had not caught up with: forgedialog 0.7.0 was published on 1 September. docs/releasing.md still said the package was unpublished and framed the manual first publish and the E404 troubleshooting entry as the likely path; both are now scoped to a fork starting from an unpublished name. package.json homepage moves from the README anchor to the site, listed under Unreleased because 0.7.0 is already out and cannot carry it. README no longer calls the deployment Cloudflare Pages. Verified against wrangler dev with the production _headers: /, /docs and every asset return 200; /nope and /deep/nested/nope return 404 with the site's own page, its assets loading and the CSP applied; both pages and the 404 render with no console errors, no failed requests, and zero horizontal overflow at 320, 380, 480 and 768px. npm run validate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
main added a wrangler.jsonc pointing `assets.directory` at `./dist`, which is the compiled library — .mjs, .cjs, .d.ts and .css with no index.html — so a deploy from it serves nothing at /. This branch's version, pointing at `./site`, is kept. The conflict is also why CI stopped running here: workflows triggered by `pull_request` build the merge ref, and GitHub cannot compute one for a conflicting PR, so no run was created for the last two pushes. Merging the base back in restores it. "$schema" is left out rather than carried over: it resolves into node_modules, and wrangler is deliberately not a dependency of this package — deploys invoke it through npx. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
The site advertised the library but barely demonstrated it. The landing page had four hero buttons and the appearance playground; the "examples" section was static code with a Copy button and nothing that ran. The full demo — the one covering drawers, sheets, toasts, the palette, forms and wizards — was demo/index.html at the repository root, which loads ../dist/ directly and is never deployed. site/demo.html runs all of it at /demo, against the same vendored build the rest of the site uses: alert, confirm, prompt, validated prompt, open(), stacking, animation options, drawers on both sides, bottom sheets, three toast tones, the lightbox, loading states, the command palette, the notification centre, form(), wizard(), and formWizard(). Each card pairs a control that really runs with the snippet of the call being made, so the page cannot drift from what it documents — a wrong snippet would show up as a misbehaving button beside it. A plugin registered on the page logs afterOpen and afterClose, and every demo prints its return value, so the result log is also evidence the hooks fire. demo.js uses a single delegated listener keyed by data-demo. The site's CSP is script-src 'self', so inline handlers would be blocked; a demo that throws prints the failure into the log rather than only the console. The mark was only ever a favicon and an apple-touch icon: no page displayed it. It now sits in the header of every page beside the wordmark, with alt="" because the adjacent text already names the product and a screen reader should not announce it twice. The API calls were taken from the reviewed report in etc/ and the manual demo page rather than written from memory, and the global surface was read out of a real browser first, so nothing here calls an export that does not exist. Verified in Chromium against wrangler dev with the production _headers CSP: all 22 demo controls surface what they claim with no console errors and no failed requests, the header mark renders on all four pages, the version badge reads from the library's own export, and every page fits at 320, 380, 480, 768 and 1280px with zero horizontal overflow. npm run validate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
PR #9 was squashed onto main as 631f43c, so this branch was restarted from main and the demo work sits on top of it. The remote branch still points at the pre-squash commits, whose content main already carries; recording them as a parent lets this push fast-forward without discarding the branch history a force push would have rewritten. The tree is unchanged by this merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
Read against autumn.konexforge.com and forgeselect.konexforge.com — their sources, since this environment's egress proxy refuses both hostnames — and closed the gaps that were real rather than cosmetic. The checker is the substantial one. ForgeSelect verifies its built site for broken local links; scripts/check-site.mjs does that and the four other things this site has actually got wrong: a fragment naming an id that does not exist on the page it lands on, a link written in the .html form the server answers with a redirect, JSON-LD that does not parse or an FAQPage whose questions have drifted from the visible ones, and a page missing a canonical, title, description or og:image — plus a 404 that declares a canonical or forgets noindex, and any disagreement between pages about the canonical origin. Each rule was verified by breaking the site and watching it fail, not by watching it pass. It runs at the end of site:build, so a broken site fails the Cloudflare deploy instead of reaching the domain. Entity graph: an Organization node for KonexForge, referenced as the site's publisher and the software's author. Both sibling sites are under the same domain, and a shared @id is what lets a search engine treat them as one publisher's products rather than three unrelated pages. Positioning: the FAQ answered how the library differs from window.alert but named no library. It now answers the comparison people actually search for — SweetAlert2 and Bootstrap modals — factually: the native dialog element does the focus and layering work, there are no runtime dependencies where a Bootstrap modal needs Bootstrap, and an alert-only build is about 5.5 KB. Metadata parity with the siblings: robots, author, explicit twitter:title, twitter:description and twitter:image rather than relying on the og: tags being inherited, og:image:type, rel=sitemap, and og:locale corrected from "en" to the en_US form the property actually takes. favicon.ico, rendered from the same icon.svg as every other icon: a 32×32 PNG in an ICO container, which needs no bitmap encoder and costs 1.2 KB. Clients request that path whatever the markup says. build-icons.mjs writes it alongside the other two and reproduces all three byte-for-byte. Deliberately not adopted: meta keywords. Both siblings carry one and Google has ignored it for years; the competitor terms belong in the visible FAQ answer, where they are also true, and that is where they went. Verified in Chromium against wrangler dev with the production _headers CSP: all four pages carry the new tags in the live DOM, /favicon.ico serves as image/vnd.microsoft.icon, the four schema types on the landing page parse with Organization among them, and every page still fits at 320, 380 and 768px with zero horizontal overflow and no console errors. npm run validate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
Autumn Note ships its site in English and Vietnamese under /vi/; this one did not. All three pages now do: /vi/, /vi/demo, and /vi/docs, translations of the English pages rather than different content, mirroring them section for section and keeping the same element ids so the scripts drive both languages. Nothing user-visible stayed hard-coded in those scripts. site.js, demo.js and common.js now read their strings from a JSON data block in the page, falling back to English — a data block rather than an inline script, because the site's CSP is script-src 'self' and real inline code would be blocked. The block's id is prefixed: "i18n" alone is already a section id on the docs page, and getElementById would have found that section instead. Nothing broke on that today, since the docs page loads neither script, but it was a trap set for whoever added one. common.js also passes the page's `labels` to the library's own setLabels(), so a dialog opened from a Vietnamese page says Huỷ and Gửi rather than Cancel and Submit. The theme toggle and copy buttons read their four strings from data- attributes for the same reason. The code shown beside each demo is translated too: the snippet has to be the call actually made, or the page stops documenting itself. hreflang: every page declares en, vi and x-default, and the sitemap repeats the set as xhtml:link alternates on all six entries, as Autumn's does. Assets on a /vi/ page are linked root-relative, because the page sits a directory down and "./" would resolve inside /vi/. check-site.mjs gained the hreflang rules, and they earned their place immediately: writing this, both /vi/demo and /vi/docs shipped a language switcher still marking EN as current, because the replacement that flips it silently matched nothing. The checker now fails on a one-way annotation, a missing x-default, and a page whose hreflang set omits its own canonical — each verified by breaking the site and watching it fail. None of the three is visible in a browser. Verified in Chromium against wrangler dev with the production CSP: all six pages return 200 with the right lang attribute, the switcher marking the right side, the localised theme label, the mark rendered, and the version badge read from the library's own export — with zero console errors, zero failed requests, and zero horizontal overflow at 320 and 768px. The Vietnamese demo opens a Vietnamese dialog with Vietnamese buttons while the English one is unchanged. npm run validate passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
forgedialog advertised having no runtime dependencies while declaring react and vue as peer dependencies and shipping a build of each. They were marked optional, so nothing broke, but two frameworks sat in the dependency tree of a library that has nothing to do with either, and every tool that reads peer dependencies had to be told to ignore them. The adapters move to packages/react and packages/vue, published as forgedialog-react and forgedialog-vue, matching how the sibling repository already ships forge-select-react and forge-select-vue. Each declares its own framework as a peer dependency and the core as a real one, and each is built with the framework and the core external so a wrapper never ships a second copy of either. Breaking, hence 0.8.0: forgedialog/react and forgedialog/vue are gone, and the core no longer declares peerDependencies at all. The hook is unchanged, so the migration is one import line, spelled out in the changelog. forgedialog/svelte and /web-component stay in the core — neither needs a framework runtime, so neither costs a peer dependency. The wrappers depend on `forgedialog: >=0.7.0 <1.0.0`, not a caret: on a 0.x core a caret pins the minor and would exclude the very release these ship beside. check-release.mjs now covers every package a release publishes and verifies each wrapper's range is one the release satisfies — proved by setting three wrong ranges and watching each fail. It also refuses a caret range rather than guessing at it. Release plumbing: `npm publish --workspaces` runs after the core, since a consumer installing a wrapper the moment it appears must find the core already there; each package gets prepublishOnly so a clean checkout cannot publish an empty dist; validate, lint, and check:package now cover the workspaces. The core's tarball is 63 files and 118.2 KiB, down from 71 and 120.3. Verified end to end rather than by inspection: all three packages were packed and installed into an empty project, where forgedialog-react and forgedialog-vue export the hook, forgedialog/react and /vue no longer resolve, /svelte still does, the core reports no peerDependencies, and a real React render through the packed dist opens a dialog, clears `active` on close, and destroys a still-open dialog on unmount. publint and attw pass on both packages across node10, node16 CJS and ESM, and bundler. One note for the record: this branch's lockfile gains tsup/node_modules/esbuild@0.27.7, which npm audit flags low. It is not a regression — main's lockfile simply omitted the entry while tsup@8.5.1 has always required esbuild ^0.27.0, which the hoisted 0.28.1 cannot satisfy. No patched 0.27.x exists and tsup@latest still pins that range, so forcing 0.28 on it would be an unsupported combination to silence a dev-server advisory this repository never runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
PR #10 was squashed onto main as 19a9303, so this branch was restarted from main and the package split sits on top of it. The remote branch still points at the pre-squash commits, whose content main already carries; recording them as a parent lets this push fast-forward without rewriting history. The tree is unchanged by this merge.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
forge-dialog | 75f3c72 | Commit Preview URL Branch Preview URL |
Sep 02 2026, 12:29 PM |
This branch has not been deployed
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
forgedialogadvertised having no runtime dependencies while declaringreactandvueas peer dependencies and shipping a build of each. They were marked optional, so nothing broke — but two frameworks sat in the dependency tree of a library that has nothing to do with either, and every tool that reads peer dependencies had to be told to ignore them.The adapters move to
packages/reactandpackages/vue, published asforgedialog-reactandforgedialog-vue, matching how the sibling repository already shipsforge-select-reactandforge-select-vue. Each declares its own framework as a peer dependency and the core as a real one, and each builds with the framework and the core external, so a wrapper never ships a second copy of either.forgedialog/svelteandforgedialog/web-componentstay in the core. Neither needs a framework runtime, so neither costs a peer dependency.Breaking, hence 0.8.0
forgedialog/reactandforgedialog/vueare gone, and the core no longer declarespeerDependenciesat all. The hook itself is unchanged, so the migration is one import line:npm install forgedialog-react # or forgedialog-vueThe core's tarball drops to 63 files / 118.2 KiB, from 71 / 120.3.
Why the dependency range is not a caret
The wrappers depend on
forgedialog: ">=0.7.0 <1.0.0". On a 0.x core a caret pins the minor, so^0.7.0would exclude 0.8.0 — the very release these ship beside.check-release.mjsnow refuses a caret range on the core rather than guessing at what it was meant to mean.Release plumbing
check-release.mjspreviously checked the one package it was pointed at. It now covers every package a release publishes and verifies each wrapper's range against the release — proved by setting three wrong ranges and watching each one fail, not by watching it pass.npm publish --workspacesruns after the core: a consumer installing a wrapper the moment it appears has to find the core already on the registry.prepublishOnly, so a clean checkout cannot publish an emptydist.validate,lint, andcheck:packagenow cover the workspaces, so CI gates them.docs/releasing.mdgains a step: npm scopes trusted publishing per package, so all three need their own publisher entry. Miss one and the release half-succeeds — core published, wrappers not.Verification
npm run validate— now including the workspaces' tests, typecheck, and package checksnpm run format:checknpm run test:e2e— not run locally (the pinned Playwright wants a browser build this container does not have). It drivesdemo/and the library, neither of which changed. CI runs it.Verified end to end rather than by inspection. All three packages were packed and installed into an empty project, where:
forgedialog-reactandforgedialog-vueexport the hookforgedialog/reactandforgedialog/vueno longer resolve;forgedialog/sveltestill doespeerDependenciesdistopens a dialog with the right title and role, clearsactiveon close, and destroys a still-open dialog on unmountfound 0 vulnerabilitiespublintandattwpass on both packages across node10, node16 (CJS and ESM), and bundler.Checklist
CHANGELOG.mdby hand, and 0.8.0 is written there with the migration.demo/is unchanged.One note for the record
This branch's lockfile gains
tsup/node_modules/esbuild@0.27.7, whichnpm auditflags low. It is not a regression:main's lockfile simply omitted the entry, whiletsup@8.5.1has always requiredesbuild ^0.27.0, which the hoisted0.28.1cannot satisfy. No patched0.27.xexists andtsup@lateststill pins that range, so forcing0.28on it would be an unsupported combination adopted to silence a dev-server advisory this repository never runs. Nothing gates onnpm audit, and nothing reaches the published packages.🤖 Generated with Claude Code
https://claude.ai/code/session_01MtJ3nDatGiv7dHjvZL1uZ4
Generated by Claude Code