# Changelog ## 1.10.2 ### Patch Changes - Card: expose the Layout panel per slot. Card declares an explicit `applicablePanels` that includes `layout`, so the inspector now offers display / direction / align-items / justify-content / gap controls for the `root`, `header`, `body`, and `footer` slots (previously omitted because the default panel derivation gates `layout` behind `isCanvas: true`). The adapter wrappers (shadcn / MUI / HTML) already render `composedClasses[slot]`, so no adapter changes were needed. All notable changes to `@crafted-design/editor` are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## What counts as a breaking change The version bump policy follows semver strictly. A change is **breaking** (major bump) if it: - Removes an exported function, type, or value from `@crafted-design/editor` or `@crafted-design/editor/sdk`. - Removes a built-in canonical from the registry (because saved documents reference it by id). - Changes the document envelope shape (`EditorDocument`) without shipping a migration in `src/persistence/migrations.ts`. - Changes the signature of a publicly-exported hook in a way that older callers won't compile. - Renames a Craft.js bridge type or a registered panel id. A change is **non-breaking** (minor / patch) if it: - Adds new exports. - Adds new canonicals, adapters, themes, templates, or panels. - Adds new optional fields to existing types (and to the document envelope, with a default). - Changes internal modules under `src/` that aren't re-exported through `src/sdk/`. - Changes UI strings, styling, or layout without changing the rendered HTML structure that integration consumers depend on. Between `0.x` minors the API may evolve without a major bump — preview phase. `1.0.0` freezes the SDK surface. ## [0.1.0] — 2026-05-25 (initial public preview, behind `next` dist-tag) Consolidates Phases 1 → 10 into the first publishable artifact. Install with `npm install @crafted-design/editor@next react@19 react-dom@19`. Until `1.0.0` the SDK surface may evolve between minors; see "What counts as a breaking change" above. ### Added **SDK surface** (`@crafted-design/editor/sdk`) - Adapter authoring: `registerAdapter`, `unregisterAdapter`, `listAdapters`, `useActiveAdapter`; types `Adapter`, `AdapterRenderProps`, `ClassMapFn`, `ClassMapResult`. - Canonical authoring: `registerCanonical` / `registerComponent`, `unregisterCanonical`, `getComponent`, `getComponentByDisplayName`, `listComponents`, `getCanvasSlots`, `getApplicablePanels`; types `CanonicalComponent`, `CanonicalCategory`, `CanonicalId`, `PanelId`. - Inspector panels: `registerPanel`, `unregisterPanel`, `listPanels`, `getPanelsFor`; type `PanelDefinition`. - Font tokens: `registerFontToken`, `unregisterFontToken`, `listFontTokens`; type `FontToken`. - Themes: `registerTheme`, `unregisterTheme`, `getTheme`, `listThemes`; type `Theme`. - Templates: `registerTemplate`, `unregisterTemplate`, `getTemplate`, `listTemplates`; type `TemplateDefinition`. - Hooks: `useNodeClasses`; type `Breakpoint`. - Tabs helpers: `tabSlotKeys`, `uniqueTabValues`, `TAB_SLOT_PREFIX`, `TabsProps`. - Style data: type `NodeStyle`. **Editor entry** (`@crafted-design/editor`) - 20 built-in canonicals: Alert, Avatar, Badge, Box, Button, Card, Checkbox, Divider, Heading, Icon, Image, Input, Link, Radio, Select, Stack, Switch, Tabs, Text, Textarea. - Three reference adapters: shadcn (default), MUI, Chakra (example). - Themes: `default`, `rose`. - Templates: `empty`, `form`. - Inspector panels: Layout, Size, Spacing, Typography, Appearance, Effects, Properties. - Persistence: multi-document store, share-by-URL, import / export. - Reliability infrastructure (Phase 9): error boundaries × 4 layers, async error toast, malformed-craftJson recovery banner, localStorage quota tracking + blocking save-fail dialog, cross-tab concurrent edit banner, hydration race serialisation. - Accessibility (Phase 9): canvas keyboard navigation, Toolbox roving tabindex, axe-core dev auto-scan, structural landmarks + h1. - Performance (Phase 9): hex color edit defer-to-pointerup, resize overlay direct-DOM gesture. - Hot-reload symmetry (Phase 10): post-mount `register*` calls reach the editor's dropdowns / pickers without remount, across all five registries. - Stable per-tab ids (Phase 10): tab `value` renames no longer orphan canvas content; existing documents migrate via `migrateTabsIdsV10`. - Gradient editor polish (Phase 10): nested ColorPicker per stop + drag-along-bar handles. **Distribution** - Two-entry-point dist build: `@crafted-design/editor` (full editor) + `@crafted-design/editor/sdk` (SDK boundary alone). Subpath exports route via `package.json` `exports`. - `vite-plugin-dts` emits matching `.d.ts` files alongside the JS bundles (`dist-lib/main-app.d.ts`, `dist-lib/sdk/index.d.ts`). - `src/sdk/internal/deprecate.ts` — once-per-call-site `console.warn` helper for future deprecations. - ESLint `no-restricted-imports` rule blocks `examples/**` from reaching past the SDK boundary; integration consumers can mirror this for their own source trees. - Auto-generated TypeDoc reference at `docs/api/` covering every SDK export. - Sample consumer file at `examples/sdk-smoke/consumer.tsx` proves the `.d.ts` tree resolves end-to-end for an integration consumer. ### Migrated - **Tabs documents pre-Phase-10**: legacy tabs without `id` are auto-migrated on load (`migrateTabsIdsV10`). Injected ids match the previous slot keys so existing canvas children stay attached. ### Bundle size Dist (`npm run build:dist`, unminified): | Artifact | Raw | Gzipped | | -------------------------------- | ------ | ------- | | `dist-lib/index.js` | 408 KB | 88 KB | | `dist-lib/sdk.js` + shared chunk | 148 KB | 32 KB | | `dist-lib/index.css` | 390 KB | 114 KB | App build (`npm run build`): | Artifact | Raw | Gzipped | | ------------------------- | ------ | ------- | | `dist/assets/index-*.js` | 517 KB | 157 KB | | `dist/assets/index-*.css` | 218 KB | 28 KB | ## [Unreleased] (none yet) ## [1.10.1] — 2026-06-18 ### Fixed - **Icon picker showed no icons on first open** (until you typed in the search box). The virtualizer read the scroll element through a `useRef`, which is `null` on the first render after the popover mounts, so it rendered zero rows and nothing triggered a re-measure. Now backed by a state callback ref, so the grid populates immediately on open (and on every reopen). - **`build:gallery` (and other raw-Node/tsx scripts) failed** with "module 'lucide-react/dynamic' does not provide an export named 'DynamicIcon'". lucide ships no `exports` map / `type: module`, so Node treats the extensionless `dynamic.js` as CJS and can't see its `export *` re-exports; bundlers (Vite/Rolldown) follow it fine, so the published build, tests, and browser were unaffected. Now imports the explicit `lucide-react/dynamic.mjs`. ## [1.10.0] — 2026-06-18 ### Added - **Runtime icon library.** The **Icon** canonical (and **NavItem**'s icon) now accept any icon name instead of a fixed 16-name enum; the inspector shows a **searchable, virtualized picker** over the full lucide set (~1800 glyphs). Glyphs are **lazy-loaded per icon** (code-split), so the bundle carries the name list — not all the SVGs. - **Backward compatible:** `icon.name` / `nav-item.icon` loosened from a Zod enum to `z.string()`; the old 16 names are valid lucide names, so existing documents validate and render unchanged. `ICON_NAMES` is still exported as a curated quick-pick list. - **Host-pluggable resolver:** new `registerIconResolver(resolver)` SDK export (+ `IconResolver` type) replaces the entire icon set with a host's own (design-system icons, Iconify, a subset). Pass no argument to restore the default. A custom resolver used for headless `renderDocumentToHtml` must be synchronous. - **Headless/SSR:** `renderDocumentToHtml` resolves glyphs synchronously (lucide via a runtime `createRequire`, not bundled), so server-rendered HTML carries real `` markup; the browser `` lazy-loads via the same resolver. Unknown names render a neutral fallback glyph and keep the stored name. - **MCP:** `icon.name` accepts any lucide kebab name (no enum). ### Changed - `check:size` now measures each entry's **eager** (static-import) graph; dynamic `import()` chunks (e.g. lucide's per-icon glyphs, the CLI's lazy MCP load) are excluded as the lazy chunks they are. Editor/core/SDK/headless budgets bumped for the icon resolver's eager map + name-list overhead. ## [1.9.2] — 2026-06-17 ### Fixed - **The `crafted-design` bin now actually runs when launched via npx / npm / a local install.** Its "run only when executed directly" guard compared `import.meta.url` (the resolved real path) against `process.argv[1]` with a plain string equality. npm/npx/local installs execute every bin through a `node_modules/.bin/` **symlink**, so `process.argv[1]` was the symlink path — never equal to the real module path — and the CLI **silently no-opped and exited 0**. Symptoms: `npx -y @crafted-design/editor mcp` produced no output (the MCP server never started; clients saw "connection closed"), and `npx … scaffold` did nothing. The guard now compares `realpathSync` of both sides, so a symlinked launch resolves to the same file. (Latent since the CLI first shipped; only direct `node dist-lib/cli.js …` invocations worked.) ## [1.9.1] — 2026-06-17 ### Fixed - **`npx @crafted-design/editor mcp` now works** (the documented MCP install did not). The package shipped two bins (`crafted-design`, `crafted-design-mcp`), but `npx ` cannot select a _named_ bin among several — npm picks a single default (by package-name match, or the only bin) and treats the trailing token as an _argument_. Neither bin matched the unscoped name `editor`, so `npx @crafted-design/editor crafted-design-mcp` failed with "could not determine executable to run". Consolidated to a **single `crafted-design` bin** with subcommands, so the natural commands resolve (npm auto-picks the sole bin and passes the subcommand through): - `npx @crafted-design/editor mcp` — launch the stdio MCP server (lazily loaded, so the CLI stays tiny and the optional MCP SDK is only touched when used). - `npx @crafted-design/editor scaffold ` — unchanged, now also resolves correctly. The `crafted-design-mcp` bin is removed. Update MCP client configs to run `mcp` as a subcommand (e.g. `args: ["-y", "@crafted-design/editor", "mcp"]`). ## [1.9.0] — 2026-06-17 ### Added - **Template variables** — let users drop `{{ tokens }}` into text that resolve to per-recipient values at render time (a merge-field system for emails, personalized pages, …). The token syntax is a safe Mustache/Jinja subset: `{{ path.to.value }}` interpolation only, no control flow. - **Editor:** wrap `` in `EditorTemplateVariablesProvider` with a list of `TemplateVariable` (`{ key, label?, group?, sample? }`). Inspector text fields grow a searchable, grouped `{{ }}` picker that inserts the token at the caret; users can also type tokens directly. The canvas shows the resolved value (from the optional `values` prop, else the variable's `sample`, else the raw `{{ token }}`); tokens carry a dashed underline. Dot-path keys (`contact.name`) resolve against nested values. - **Renderer:** `` substitutes a flat or nested values object; missing values fall back to the raw `{{ token }}`. - **Headless:** `renderDocumentToHtml(doc, { variables, onMissingVariable })`, plus `interpolate(text, values)` / `extractTemplateRefs(text)` for custom pipelines (exported from `@crafted-design/editor/headless`). - **MCP:** a `list_template_variables` tool reports the host-declared set (configured via the `CRAFTED_DESIGN_TEMPLATE_VARIABLES` env var, a JSON array); `get_capabilities` nudges the agent to use `{{ key }}` tokens. - New SDK exports: `EditorTemplateVariablesProvider`, `useTemplateVariables`, `TemplateVariable`. The document envelope is **unchanged** — tokens live in ordinary text props, so saved documents stay portable across hosts. ## [1.8.3] — 2026-06-15 ### Fixed - **`editorTheme` now works under the scoped stylesheet (`index.scoped.css`).** The scoping pass rehomed _every_ `:root` rule onto `.crafted-design-scope`, including the editor's `--ed-*` chrome-token defaults — setting them directly on the editor root and overriding the host's `editorTheme` (applied as inline `--ed-*` vars on ``), so the chrome theme was silently ignored under the scoped sheet. The chrome-theme rules (`:root { --ed-* }` defaults + `[data-editor-theme]` presets) now stay **global**, so the host's inline vars cascade in as intended (they're editor-only, so they never collide with a host). Document tokens remain scoped. Verified in a real browser (`src/style/chromeThemeScoped.browser.test.ts`). ## [1.8.2] — 2026-06-15 ### Fixed - **`@crafted-design/editor/index.css` no longer clobbers a host app's design tokens.** The editor's document tokens (`--primary`, `--background`, `.dark`, `[data-theme]`) now ship in a cascade layer (`@layer crafted-design`), so an embedding app's **unlayered** `:root` / `.dark` tokens always win — importing the global stylesheet stops overriding the host's brand colors app-wide (reported: a host's primary button turning neutral on every page that loaded the editor CSS). Standalone use is unchanged (nothing competes, so the layered values apply); the editor's `--ed-*` _chrome_ tokens stay unlayered. For full subtree isolation (host tokens never reach the canvas either, no second preflight), `@crafted-design/editor/index.scoped.css` (1.7.0) remains the option. Verified in a real browser (`src/style/tokenLayer.browser.test.ts`). See [INTEGRATION_GUIDE → Inline embedding](./docs/INTEGRATION_GUIDE.md#inline-embedding-into-a-tailwind-v4-app-170). ## [1.8.1] — 2026-06-14 ### Fixed - **Toolbar overflow popover (< xl): the Adapter / Theme `` (dropdowns, modals) is themed too, while the host page is untouched. Like the `adapter` prop this is **host policy** — there's no end-user chrome switcher. It is independent of the document theme system (`registerTheme` / the canvas theme switcher / `colorMode`), which styles the canvas content end users design — dark chrome around a light document works. New type-only exports `EditorChromeTheme` and `EditorChromeTokens` (the frozen runtime surface is unchanged). ### Changed - The editor chrome now styles itself through a dedicated `--ed-*` token set (light preset is byte-identical to the previous hardcoded grays — no visual change for the default `editorTheme`). Previously the chrome borrowed the document/canvas theme tokens in places, so switching the document theme or color mode could subtly shift the editor UI; the chrome and the document theme are now fully decoupled. A `check:chrome` CI guard keeps it that way. ## [1.0.2] — 2026-06-02 ### Fixed - **Toolbox: dragging from "Recently used" grabbed the wrong component.** The recently-used LRU was recorded on `mousedown`, which reordered the Recent section (and reflowed every tile below it) while the pointer was still down — so by the time `dragstart` fired, a different tile sat under the cursor and became the drag source (e.g. dragging the 4th recent tile dropped the old 3rd). The LRU bump now records on `dragend` (and on keyboard Enter-insert, as before), after the pointer is released, so the grid never reflows mid-drag. A click that never starts a drag no longer bumps the LRU (it inserts nothing, so it isn't a "use"). ## [1.0.1] — 2026-06-02 First post-1.0 release (the published `1.0.0` tarball predates this feature). Additive — no breaking changes. ### Added - **Host-pinned adapter** — `` lets the host choose the design system (the product intent: the host picks, not the host's end users). Pinning sets the active adapter before first paint, hides the toolbar AdapterSwitcher, and makes loading a document NOT override the adapter (the envelope's `adapterId` is a preference, not a command — documents are canonical-id based and render under any adapter). A separate `allowUserToSwitchAdapter` prop controls the switcher independently (defaults: `false` when `adapter` is set, `true` otherwise — legacy behavior unchanged for prop-less ``). Pinning an unregistered adapter warns and falls back to the default; **`adapter="mui"` requires the MUI peers** (`@mui/material`, `@emotion/react`, `@emotion/styled`). New `EditorProps` type export (type-only). ## [1.0.0] — 2026-06-02 First stable release. Promotes the editor off the `next` preview tag to `latest`. The **public SDK surface is now frozen under the full SemVer promise** — see [SDK_GUIDE.md](docs/SDK_GUIDE.md) "Public API stability": removing or renaming any exported name (enumerated in `src/sdk/surface.test.ts`) is from here a breaking, major-version change. No API changes vs `0.9.0`; this is the stabilization cut, folding in the `0.9.0`-cycle work below. ### Changed - **Toolbox is now a visual icon-thumbnail grid** (Unlayer-style) instead of a vertical list of text buttons. Each component shows a representative icon over its label in a 2-column grid per section; the favorite ★ reveals on hover/focus. Drag, recent-use, search, favorites, and roving-tabindex keyboard nav are unchanged (arrow keys now step in any direction). Custom canonicals with no mapped icon fall back to a category icon. - **Built-in adapter impls are lint-enforced to consume the SDK** (Phase 18 follow-up). A `no-restricted-imports` rule on `src/adapters/{shadcn,mui,html}/**` blocks reaching the `@/editor` · `@/state` · `@/lib` internals — those must come through `@design/sdk` (the same boundary third-party adapters hit). The canonical contract (`@/registry/components`) + the shadcn adapter's own primitives (`@/components/ui`) stay allowed; the adapter infrastructure at `src/adapters/` root is out of scope. Also migrated a straggler (`Box.tsx`'s `cn`) the earlier codemod missed. ### Added - **Stepper + Table dynamic-canvas slot helpers promoted to the SDK** (Phase 18 follow-up). `stepperSlotKey` / `stepperSlotKeys`, `tableCellSlotKey` / `tableCellSlotKeys`, the table merge-geometry helpers `containingMerge` / `isCellCovered`, their `STEP_SLOT_PREFIX` / `CELL_PREFIX`, and the `TableMerge` type are now exported from `@crafted-design/editor/sdk` — the Stepper/Table analog of the already-public Tabs/Carousel helpers, so a third-party Stepper/Table adapter has the full authoring surface. The pure helpers moved into the side-effect-free `dynamic-slots` module (the canonical modules re-export them for back-compat), so importing the SDK still registers no canonicals (`side-effect-free.test.ts` holds). Built-in adapters now import them from the SDK. Additive; recorded in `surface.test.ts`. ### Fixed - **Plain-HTML adapter: collapsed table cells.** `HtmlTableCell` used `h-full w-full`, which resolves to 0 height in an empty cell — a dropped Table rendered ~8px tall. It now uses the `canvas-slot` class (min-height + "Drop here" hint + `height:100%` fill) and the `` carries the `height:1px` row-fill hack, matching the shadcn / MUI tables. ## [0.9.0] — 2026-06-01 Phase 18 — architecture hygiene + SDK dogfooding (post-`0.8.0` review follow-ups). Refreshes the 1.0 release candidate. No breaking changes — the public surface only **grows** (additive, recorded in the frozen snapshot). ### Added - **Overlay-authoring SDK seam** — `useOverlayRuntime`, `readOverlayOpen`, `useOverlayStageTarget`, `OverlayCard` (+ `OverlayKind` / `OverlayDef` types) are now public, so a third-party adapter can build overlay canonicals with the same editor-stage + runtime behavior as the built-ins. - **`cn`** — the clsx + tailwind-merge class-merge util adapter impls use. - **Per-canonical prop types** — every canonical's props type (`ButtonProps`, `ModalProps`, `TableProps`, …) is exported from the SDK (type-only). - **Pure node-render-model** — `buildNodeRenderModel` extracted from `CanonicalNode` (internal), now directly unit-tested. ### Changed - **Built-in adapters now consume the public SDK boundary** — shadcn / MUI / html no longer reach into `@/editor` / `@/state` / `@/lib` internals; they import `useIsEditing` / `EditableText` / `useStartTextEdit` / the overlay seam / `cn` from `@crafted-design/editor/sdk`. Behavior identical (same bindings); this is what surfaced the missing seams above. - **`/sdk` bundle budget 60 → 70 KB gz** — the new overlay seam + `cn` (tailwind-merge) grow the full-surface number. `/sdk` is side-effect-free, so a consumer importing one symbol tree-shakes the rest. - `CanonicalNode` is now wiring over the pure render model (no behavior change). ### Fixed - **Adapter wrapper-stability hazard** — `registerAdapter` now warns when a `Wrapper`-bearing adapter registers _after_ `` mounts (a late Wrapper reshapes the composed wrapper tree and can remount Craft's ``, wiping the canvas). The contract is documented on `Adapter.Wrapper` - the DEVELOPER_GUIDE adapter recipe. - **Stale architecture docs** — `ARCHITECTURE.md` refreshed from the live registry: 48 canonicals (was "Twenty"), the real multi-canvas Pattern B (`canvasSlots` → per-slot ``, used by Card/Table/Tabs/ Stepper/Carousel), and the `html` adapter in the tree. ### Added — validation - **Semantic document validation** — alongside the structural integrity check, a lenient pass validates each node's `nodeProps` against its canonical `propsSchema` and `style` against a new `NodeStyle` zod schema. Corrupt props/styles are reported (telemetry `document.semanticIssues` metric + a dev warning) **before render** without blocking a document the editor can still best-effort display. ## [0.8.0] — 2026-06-01 Phase 17 — production-readiness completion. The **1.0 release candidate**: every non-Stretch item across PRODUCTION_READINESS §§ 8 (perf + bundle), 10 (docs), and 12 (distribution) is shipped, and the **public SDK surface is frozen and enforced**. No breaking changes — additive + internal. ### Added - **Frozen public API surface** (the 1.0 gate). `src/sdk/surface.test.ts` locks the exact exported-name list for both `@crafted-design/editor` / `/core` and `/sdk`, asserts the editor entry is a strict superset of the SDK, and that internals (`CanonicalNode`, the resolver builder) never leak. Any export change now fails CI until done deliberately. SDK_GUIDE gains a "Public API stability (toward 1.0)" section (SemVer promise + deprecation policy). - **SDK tree-shaking guard** (§ 8.4). `src/sdk/side-effect-free.test.ts` verifies importing the SDK registers nothing but the three baseline font tokens — so consumers tree-shake unused authoring symbols. - **Docs** — `docs/COOKBOOK.md` (task→recipe index), `docs/FAQ.md`, `docs/MIGRATION.md` (major-version template), `docs/RELEASE.md` (release runbook + 1.0 go/no-go criteria), and a copy-pasteable `examples/minimal-host` host app on `/core`. (§ 10.2, 10.4, 10.6, 10.7) ### Fixed - **SDK tree-shaking leak** (§ 8.4). Importing `@crafted-design/editor/sdk` registered 2 canonicals (carousel + tabs): `sdk/canonical.ts` re-exported their slot-key value helpers from the canonical modules, which `registerComponent` at load. Helpers moved to the side-effect-free `registry/components/dynamic-slots.ts`; the SDK now registers 0 canonicals. - **Inspector re-render** (§ 8.5). PropField/ObjectField/ArrayField are memoized with stable per-field `onChange` handlers — editing one prop no longer re-renders/re-walks sibling fields or nested sub-forms. - **Toolbox connector churn** (§ 8.7). `connectors.create` re-ran for every palette button on every Toolbox render; a per-element guard connects each once. ### Changed - **Distribution decision** (§ 12.5): the package stays **ESM-only, unminified with source maps** — no CJS/UMD, no separate `index.min.js` (consumers' bundlers minify). Documented in INTEGRATION_GUIDE "Bundle format". ### Notes This is a release candidate on the `next` dist-tag. `1.0.0` (promotion to `latest` + the full SemVer freeze) follows once the go/no-go checklist in [`docs/RELEASE.md`](docs/RELEASE.md) is met — chiefly host/ops actions (public repo, `NPM_TOKEN`, Actions + Pages) and an RC soak. ## [0.7.0] — 2026-05-31 Phase 16 — Adapter modularity + ecosystem. The package splits into a lean `/core` entry plus opt-in per-adapter subpaths, the heavy UI libraries become optional peer dependencies, a third built-in adapter (plain HTML, no UI library) ships, and the adapter compatibility + versioning story is documented and guarded in CI. ### Added - **Lean `/core` entry + per-adapter subpaths** (§ 8.3, 12.2). New exports: `@crafted-design/editor/core` (editor + shadcn + plain-HTML, no MUI) and `@crafted-design/editor/adapters/{shadcn,mui,html}`. The default `@crafted-design/editor` entry stays batteries-included (core + MUI). Opt-in is at the **import boundary**, not runtime — runtime registration would reshape the `AdapterProvider` wrapper tree and remount (visually wipe) the canvas — so a shadcn-only host drops MUI from its bundle simply by importing `/core`. - **Plain-HTML adapter** (`id: 'html'`, "Plain HTML") (§ 7.2). A dependency-free, semantic-HTML renderer for all 48 canonicals: the no-framework option, and the reference that proves the adapter SDK + modular structure end to end. Layout primitives compose their props (grid/flex/max-width); overlays portal to the editor's overlay stage and, at runtime, to `` with a backdrop (no Radix dependency). - **Adapter compatibility matrix** (§ 7.3). `scripts/adapter-matrix.ts` (`npm run docs:matrix`) introspects every registered adapter × the canonical registry and generates `docs/ADAPTER_MATRIX.md`; `--check` mode (wired into CI) fails if a built-in adapter drops below full coverage. Built-ins shadcn/MUI/html are 48/48; the Chakra example is intentionally partial (gaps fall back to the missing-renderer placeholder). - **Adapter peer-dependency declaration** (§ 7.4). `peerDependencies` (`{ package: testedRange }`) is now a validated field on the adapter manifest, surfaced in the matrix and documented in `docs/ADAPTER_VERSIONING.md` (install contract, the three ways an upstream breaking change surfaces, the support policy). `src/adapters/peer-deps.test.ts` guards drift between an adapter's declared ranges and `package.json`. ### Changed > ⚠ **Breaking for default-entry consumers** — see migration below. - **`@mui/material`, `@emotion/react`, and `@emotion/styled` are now OPTIONAL peer dependencies instead of direct dependencies.** They are no longer bundled into the published editor (the dist build externalizes them). - The SDK gains `peerDependencies` on the `Adapter` type / manifest schema. #### Migration The default `@crafted-design/editor` entry bundles the MUI adapter, so consuming it now requires installing the three peers yourself: ```bash npm install @mui/material @emotion/react @emotion/styled ``` If you don't use MUI, import the lean entry instead and install nothing extra (shadcn + plain-HTML need no external peers): ```ts import "@crafted-design/editor/core"; // editor + shadcn + html, no MUI ``` ### Bundle Measured at `0.7.0` (`npm run check:size` — transitive gzipped per entry, externalized peers excluded): | Entry | Gzipped | Note | | ----------------------------------------- | ------- | ------------------------------------------ | | `@crafted-design/editor` (`index.js`) | ~253 KB | + MUI/Emotion peers (installed separately) | | `@crafted-design/editor/core` (`core.js`) | ~245 KB | shadcn + html, no MUI | | `@crafted-design/editor/sdk` (`sdk.js`) | ~44 KB | SDK-only consumers | | `index.css` | ~124 KB | | The `0.6.0` "414 KB gz" `index.js` figure **bundled** MUI. Externalizing it moves that weight to a peer the host installs only when it uses the MUI adapter; the editor's own bundled code is ~253 KB gz, and `/core` consumers ship ~245 KB gz with no MUI at all. (Our two entries are close because MUI is now external for both; the real win is consumer-side — shadcn-only hosts no longer download MUI.) ## [0.6.0] — 2026-05-31 Phase 15 — Launch readiness (production hardening). CI + release automation, security/compliance gates, a corrected publishable bundle, an observability seam, and contributor + docs infrastructure. Mostly build/tooling/docs; the runtime additions are additive (telemetry is opt-in). **Fixes a packaging bug** where the published bundle shipped without its built-in adapters/canonicals registered. ### Fixed - **Published bundle registered no built-ins.** `sideEffects: ["**/*.css"]` told Rollup every JS module was side-effect-free, so the dist build tree-shook away the registration side-effect imports (`import './adapters/shadcn'`, the canonical barrel, themes, panels, templates). The published `dist-lib` rendered an editor with **no adapter renderers and few canonicals** registered. The registration modules are now listed in `sideEffects`. (Restoring them is why `index.js` grows from a broken ~120 KB gz to its true ~414 KB gz — it now actually contains both adapter sets.) ### Added - **CI + release automation** (§ 9.4–9.6, 12.3). GitHub Actions CI (lint + type-check + test + `build:dist` + bundle-budget + license + advisory `npm audit`); Changesets-driven release workflow (publishes to `next`); lefthook pre-commit/-push hooks; `scripts/check-bundle-size.ts` (`npm run check:size`). - **Security + compliance** (§ 11.1–11.4). Font-token URL/family validation and responsive/state inline-style declaration sanitization to close CSS-injection vectors in the runtime `