@techrox/page-studio 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @techrox/page-studio
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@techrox/page-studio?color=0b60d8&label=npm)](https://www.npmjs.com/package/@techrox/page-studio)
4
+ [![downloads](https://img.shields.io/npm/dm/@techrox/page-studio?color=64748b&label=downloads)](https://www.npmjs.com/package/@techrox/page-studio)
5
+ [![size](https://img.shields.io/bundlephobia/minzip/@techrox/page-studio?color=64748b&label=minzip)](https://bundlephobia.com/package/@techrox/page-studio)
6
+ [![license](https://img.shields.io/badge/license-MIT-64748b)](../../LICENSE)
7
+
3
8
  The editor shell for Page Studio — a drop-in `<PageStudio />` component that gives you a visual page builder backed by [Puck](https://puckeditor.com).
4
9
 
5
10
  ```bash
package/dist/index.cjs CHANGED
@@ -379,23 +379,26 @@ function buildStableOverrides(hostOverrides) {
379
379
  }
380
380
  function setCssVars(branding) {
381
381
  if (typeof document === "undefined" || !branding) return;
382
- const root = document.documentElement.style;
383
- if (branding.primaryColor) root.setProperty("--tps-primary", branding.primaryColor);
384
- if (branding.accentColor) root.setProperty("--tps-accent", branding.accentColor);
385
- if (branding.inkColor) root.setProperty("--tps-ink", branding.inkColor);
386
382
  const lines = [];
387
383
  if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);
388
384
  if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);
389
385
  if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);
390
- if (!lines.length) return;
391
- let tag = document.getElementById("tps-brand-vars");
392
- if (!tag) {
393
- tag = document.createElement("style");
394
- tag.id = "tps-brand-vars";
395
- document.head.appendChild(tag);
386
+ const cssVars = branding.cssVars || {};
387
+ for (const [k, v] of Object.entries(cssVars)) {
388
+ if (v == null) continue;
389
+ lines.push(`${k}: ${v};`);
396
390
  }
397
- const next = `:root { ${lines.join(" ")} }`;
398
- if (tag.textContent !== next) tag.textContent = next;
391
+ if (!lines.length) return;
392
+ const bg = cssVars["--tps-bg-section"];
393
+ const canvas = bg ? ` html { background: ${bg}; }` : "";
394
+ const next = `:root { ${lines.join(" ")} }${canvas}`;
395
+ const existing = document.getElementById("tps-brand-vars");
396
+ if (existing && existing.textContent === next) return;
397
+ if (existing) existing.remove();
398
+ const tag = document.createElement("style");
399
+ tag.id = "tps-brand-vars";
400
+ tag.textContent = next;
401
+ document.head.appendChild(tag);
399
402
  }
400
403
  function PageStudio({
401
404
  pageKey,
@@ -405,6 +408,12 @@ function PageStudio({
405
408
  livePath,
406
409
  homeHref,
407
410
  branding,
411
+ // Active editor theme — 'light' | 'dark' | any future token-scope name.
412
+ // Stamped as data-tps-theme on the editor root so the whole chrome (top
413
+ // bar, sidebars, field panel, inputs) re-paints through the CSS cascade,
414
+ // alongside the canvas (which is themed via branding.cssVars). Falls back
415
+ // to branding.theme so a host that only passes branding still themes.
416
+ theme,
408
417
  studio,
409
418
  adapter = {},
410
419
  config,
@@ -434,6 +443,7 @@ function PageStudio({
434
443
  );
435
444
  const [loading, setLoading] = (0, import_react2.useState)(!data && !!adapter.loadPage);
436
445
  setCssVars(branding);
446
+ const resolvedTheme = theme || branding?.theme || "light";
437
447
  (0, import_react2.useEffect)(() => {
438
448
  if (data || !adapter.loadPage) return;
439
449
  let cancelled = false;
@@ -505,9 +515,16 @@ function PageStudio({
505
515
  [overrides]
506
516
  );
507
517
  if (loading || !data) {
508
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "psd-builder-page psd-builder-page--loading", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "psd-builder-loading", children: "Loading editor\u2026" }) });
518
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
519
+ "div",
520
+ {
521
+ className: "psd-builder-page psd-builder-page--loading",
522
+ "data-tps-theme": resolvedTheme,
523
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "psd-builder-loading", children: "Loading editor\u2026" })
524
+ }
525
+ );
509
526
  }
510
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_page_studio_blocks.PageStudioProvider, { value: studio, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HeaderPropsContext.Provider, { value: headerLive, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "psd-builder-page", children: [
527
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_page_studio_blocks.PageStudioProvider, { value: studio, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HeaderPropsContext.Provider, { value: headerLive, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "psd-builder-page", "data-tps-theme": resolvedTheme, children: [
511
528
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
512
529
  BuilderEnhancements,
513
530
  {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js","../src/PageStudio.jsx","../src/BuilderEnhancements.jsx","../src/BuilderTopBar.jsx"],"sourcesContent":["export { default as PageStudio } from './PageStudio.jsx';\nexport { default as BuilderEnhancements } from './BuilderEnhancements.jsx';\nexport { default as BuilderTopBar } from './BuilderTopBar.jsx';\n\n// Re-export the blocks package's context so consumers don't need a separate\n// import to wrap their public-site renderer with the same studio config.\nexport {\n PageStudioProvider,\n useStudio,\n createPuckConfig,\n defaultBlocks,\n defaultCategories,\n defaultOverrides,\n emptyPuckData,\n} from '@techrox/page-studio-blocks';\n","'use client';\n\n// PageStudio — the editor entry point consumers render. Wraps Puck with:\n// - an adapter for persistence (loadPage / savePage / onCreatePage)\n// - a StudioProvider that injects host primitives (Link, services, …)\n// into the block tree\n// - a replaceable top bar (header prop) with sane brand-agnostic defaults\n// - sidebar enhancements (Blocks / Layers tabs, count badges)\n//\n// Everything except `pageKey` is optional. Passing only `pageKey` works —\n// you'll get the default block library, default top bar, and a console\n// warning when Publish is clicked without an adapter wired.\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n useTransition,\n} from 'react';\nimport { Puck, useGetPuck, legacySideBarPlugin } from '@puckeditor/core';\nimport { App as AntdApp } from 'antd';\n\nimport {\n applyConfigDefaults,\n createPuckConfig,\n defaultOverrides,\n emptyPuckData,\n normalizePuckData,\n PageStudioProvider,\n} from '@techrox/page-studio-blocks';\n\nimport BuilderEnhancements from './BuilderEnhancements.jsx';\nimport DefaultTopBar from './BuilderTopBar.jsx';\n\nimport '@puckeditor/core/puck.css';\n\n// 0.21 ships a new side navigation rail. Our BuilderEnhancements observes\n// the legacy `_Sidebar--left` layout, so keep that DOM by applying the\n// legacy plugin once at module load.\nconst PSD_LEGACY_SIDEBAR = legacySideBarPlugin();\n\n// The header override is passed to Puck via `overrides.header`. Puck treats\n// the value as a component type — when its reference changes, React diffs\n// and unmounts the old override before mounting the new one. During that\n// gap Puck briefly paints its default top bar, which flashes a stock\n// Publish button on screen. To avoid that we keep the override component\n// reference stable for the entire lifetime of the editor and pipe live\n// props (branding, savedAt, callbacks, etc.) through a context provider\n// rendered by PageStudio. Live updates flow as normal context updates —\n// the component never gets unmounted.\nconst HeaderPropsContext = createContext(null);\n\nfunction StableHeaderOverride() {\n const getPuck = useGetPuck();\n const live = useContext(HeaderPropsContext);\n if (!live) return null;\n const props = {\n pageKey: live.pageKey,\n pageTitle: live.pageTitle,\n account: live.account,\n livePath: live.livePath,\n homeHref: live.homeHref,\n savedAt: live.savedAt,\n pending: live.pending,\n onPublish: () => live.handlePublish(getPuck().appState.data),\n onSignOut: live.onSignOut,\n onCreatePage: live.onCreatePage,\n branding: live.branding,\n extraActions: live.headerActions,\n LinkComponent: live.LinkComponent,\n };\n if (typeof live.header === 'function') return live.header(props);\n if (live.header) return live.header;\n return <DefaultTopBar {...props} />;\n}\n\n// `overrides.header` is referentially stable so Puck never remounts the\n// override. The internal stack also stabilizes `headerActions: () => null`\n// — that's how we suppress Puck's own actions area.\nconst NO_OP_ACTIONS = () => null;\nfunction buildStableOverrides(hostOverrides) {\n return { ...hostOverrides, header: StableHeaderOverride, headerActions: NO_OP_ACTIONS };\n}\n\n// Apply brand colors via BOTH an inline style on <html> (for the outer\n// editor chrome) and a <style> tag in <head> (so Puck's iframe canvas,\n// which clones head styles into its document, picks them up too). Pure\n// inline styles on document.documentElement do not propagate to the\n// iframe — only <style>/<link> elements do.\n//\n// We write to `--tps-*` because that's what the block library reads. The\n// host page may also paint these vars on a wrapping element (e.g. a\n// brand-switcher's `[data-brand]` div), but that element doesn't exist\n// inside Puck's iframe — so without this :root injection the canvas\n// falls back to the package's default values and ignores the brand.\nfunction setCssVars(branding) {\n if (typeof document === 'undefined' || !branding) return;\n const root = document.documentElement.style;\n if (branding.primaryColor) root.setProperty('--tps-primary', branding.primaryColor);\n if (branding.accentColor) root.setProperty('--tps-accent', branding.accentColor);\n if (branding.inkColor) root.setProperty('--tps-ink', branding.inkColor);\n\n const lines = [];\n if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);\n if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);\n if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);\n if (!lines.length) return;\n\n let tag = document.getElementById('tps-brand-vars');\n if (!tag) {\n tag = document.createElement('style');\n tag.id = 'tps-brand-vars';\n document.head.appendChild(tag);\n }\n const next = `:root { ${lines.join(' ')} }`;\n if (tag.textContent !== next) tag.textContent = next;\n}\n\nexport default function PageStudio({\n pageKey,\n initialData,\n pageTitle,\n account,\n livePath,\n homeHref,\n branding,\n studio,\n adapter = {},\n config,\n blockDefaults,\n overrides = defaultOverrides,\n header,\n headerActions,\n onSignOut,\n sidebarLabels,\n LinkComponent,\n // Forwarded to Puck. Defaults to iframe enabled — Puck v0.20 mounts the\n // @dnd-kit context inside the canvas iframe; with the iframe disabled,\n // strict-mode double-mount and Vite HMR can leave the canvas with no live\n // drop targets, so dropped blocks vanish into an empty content array.\n // Hosts that need the canvas to share the host document (e.g. to inherit\n // global CSS without copying it across) can pass { enabled: false }.\n iframe = { enabled: true },\n}) {\n const { message } = AntdApp.useApp();\n const [pending, startTransition] = useTransition();\n const [savedAt, setSavedAt] = useState(null);\n\n // Resolve the config ONCE on first mount and freeze the reference. Puck\n // treats `config` as a structural prop — passing a new object on every\n // brand switch makes it re-diff its block registry, drop @dnd-kit's\n // collision cache and rebuild a chunk of internal memos. That's the\n // bulk of what makes brand switching feel sluggish.\n //\n // Trade-off: a `blockDefaults` change after mount (e.g. host swapping\n // tenant defaults mid-edit) no longer updates the per-block defaults\n // applied to newly dropped blocks — those will use whatever defaults\n // were active at first mount. CSS vars (color, ink, accent) still\n // update live via setCssVars. For the showcase that's exactly what we\n // want: brand swaps repaint colors instantly without churning Puck.\n // Hosts that genuinely need brand-aware drop defaults should remount\n // PageStudio with a fresh key when they swap tenants.\n const [resolvedConfig] = useState(\n () => config || createPuckConfig({ defaults: blockDefaults }),\n );\n\n const [data, setData] = useState(() =>\n initialData && Array.isArray(initialData.content)\n ? applyConfigDefaults(normalizePuckData(initialData), resolvedConfig)\n : null,\n );\n const [loading, setLoading] = useState(!data && !!adapter.loadPage);\n\n // Apply brand CSS variables synchronously during render — before Puck\n // mounts its iframe and before the first paint. Running this in useEffect\n // makes the canvas paint once with the package-default teal and then\n // repaint when the effect commits, producing a visible colour flash on\n // every load. The function is idempotent (it only writes when the head\n // <style> content actually differs) so repeated calls during strict-mode\n // double-render are a no-op.\n setCssVars(branding);\n\n // Initial load when initialData wasn't server-supplied. Hosts that SSR\n // their CMS read should always pass initialData and skip this code path.\n useEffect(() => {\n if (data || !adapter.loadPage) return;\n let cancelled = false;\n setLoading(true);\n adapter.loadPage(pageKey)\n .then((loaded) => {\n if (cancelled) return;\n setData(\n loaded && Array.isArray(loaded.content)\n ? applyConfigDefaults(normalizePuckData(loaded), resolvedConfig)\n : emptyPuckData,\n );\n })\n .catch((err) => {\n if (cancelled) return;\n message.error(err?.message || 'Could not load page.');\n setData(emptyPuckData);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [pageKey, adapter, data, message]);\n\n const handlePublish = (nextData) => {\n if (!adapter.savePage) {\n message.error('No savePage adapter configured.');\n return;\n }\n startTransition(async () => {\n try {\n await adapter.savePage(pageKey, nextData);\n setSavedAt(new Date().toISOString());\n message.success('Published.');\n } catch (err) {\n message.error(err?.message || 'Save failed.');\n }\n });\n };\n\n // Live values consumed by the stable header override via context. The\n // context VALUE may change every render — that's fine, it triggers a\n // normal re-render of StableHeaderOverride. The override COMPONENT\n // reference (passed to Puck via overrides.header) never changes, so\n // Puck never unmounts it. That's what kills the default-button flash\n // on brand switch.\n const headerLive = useMemo(() => ({\n pageKey,\n pageTitle,\n account,\n livePath,\n homeHref,\n savedAt,\n pending,\n handlePublish,\n onSignOut,\n onCreatePage: adapter.onCreatePage,\n branding,\n headerActions,\n LinkComponent,\n header,\n }), [\n pageKey, pageTitle, account, livePath, homeHref, savedAt, pending,\n handlePublish, onSignOut, adapter.onCreatePage, branding, headerActions,\n LinkComponent, header,\n ]);\n\n // Build the overrides object ONCE per `overrides` prop change. The\n // header slot is the stable component declared at module scope —\n // brand swaps don't invalidate the reference.\n const mergedOverrides = useMemo(\n () => buildStableOverrides(overrides),\n [overrides],\n );\n\n if (loading || !data) {\n return (\n <div className=\"psd-builder-page psd-builder-page--loading\">\n <div className=\"psd-builder-loading\">Loading editor…</div>\n </div>\n );\n }\n\n return (\n <PageStudioProvider value={studio}>\n <HeaderPropsContext.Provider value={headerLive}>\n <div className=\"psd-builder-page\">\n <BuilderEnhancements\n blocksLabel={sidebarLabels?.blocks}\n layersLabel={sidebarLabels?.layers}\n searchPlaceholder={sidebarLabels?.search}\n />\n <Puck\n config={resolvedConfig}\n data={data}\n overrides={mergedOverrides}\n onPublish={handlePublish}\n iframe={iframe}\n plugins={[PSD_LEGACY_SIDEBAR]}\n />\n </div>\n </HeaderPropsContext.Provider>\n </PageStudioProvider>\n );\n}\n","'use client';\n\n// Post-mount DOM enhancements for Puck's left sidebar:\n// 1. Hide Puck's \"Components\" / \"Outline\" section titles\n// 2. Inject a tab bar at the top that toggles between the two sections\n// 3. Inject a search input that filters the block cards by name + summary\n// 4. Append a count badge to each component-category header (visible-only)\n//\n// IMPORTANT: every DOM mutation we make MUST be idempotent (no-op when the\n// target state already matches) AND we disconnect the MutationObserver\n// while applying so our own writes don't feed back into the observer and\n// cause an infinite loop / tab freeze.\n\nimport { useEffect } from 'react';\n\nconst STORAGE_KEY = 'psd.builderTab';\n\nexport default function BuilderEnhancements({\n blocksLabel = 'Blocks',\n layersLabel = 'Layers',\n searchPlaceholder = 'Search blocks',\n} = {}) {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n let observer = null;\n // Search query persists across re-renders even if Puck wipes the sidebar\n // and we have to re-inject the input. Lives in this closure (per mount).\n let query = '';\n\n // Filtering is done via attributes on elements WE own (.tps-block-card\n // and _ComponentList_ groups). CSS in editor/styles.css hides the\n // matching Puck wrapper via :has(). Writing only attributes — not\n // inline styles on Puck's wrappers — keeps us out of a fight with\n // Puck's React reconciler when it re-renders the picker after a drop.\n const matchesQuery = (card, q) => {\n if (!q) return true;\n const name = (card.querySelector('.tps-block-card__name')?.textContent || '').toLowerCase();\n const desc = (card.querySelector('.tps-block-card__desc')?.textContent || '').toLowerCase();\n return name.includes(q) || desc.includes(q);\n };\n\n const setAttrIfChanged = (el, name, value) => {\n const current = el.getAttribute(name);\n if (value == null) {\n if (current !== null) el.removeAttribute(name);\n } else if (current !== value) {\n el.setAttribute(name, value);\n }\n };\n\n const updateCounts = (sidebar) => {\n const headers = sidebar.querySelectorAll('[class*=\"_ComponentList-title_\"]');\n headers.forEach((header) => {\n const parent = header.closest('[class*=\"_ComponentList_\"]');\n if (!parent) return;\n const list = parent.querySelector('[class*=\"_ComponentList-content_\"]');\n if (!list) return;\n const cards = list.querySelectorAll('.tps-block-card');\n const visible = Array.from(cards).filter(\n (c) => c.getAttribute('data-psd-hidden') !== 'true',\n ).length;\n const count = String(visible);\n let badge = header.querySelector('.psd-cat-count');\n if (!badge) {\n badge = document.createElement('span');\n badge.className = 'psd-cat-count';\n badge.textContent = count;\n const chevron = header.querySelector('[class*=\"_ComponentList-titleIcon_\"]');\n if (chevron) {\n header.insertBefore(badge, chevron);\n } else {\n header.appendChild(badge);\n }\n } else if (badge.textContent !== count) {\n badge.textContent = count;\n }\n });\n };\n\n const applyFilter = (sidebar) => {\n const q = query.trim().toLowerCase();\n const cards = sidebar.querySelectorAll('.tps-block-card');\n cards.forEach((card) => {\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(card, 'data-psd-hidden', hidden ? 'true' : null);\n });\n\n // Mark the actual grid cell so it collapses out of layout — not just\n // our inner card. Puck's <DrawerItem> renders an unnamed <div> as the\n // direct child of [data-puck-drawer]; that <div> IS the grid cell.\n // Hiding only the .tps-block-card or any class-bearing inner wrapper\n // leaves the cell empty so visible matches stay in their original\n // grid slots (which is what the user was seeing). We tag the cell\n // directly here so a single CSS attribute selector can collapse it.\n const drawers = sidebar.querySelectorAll('[data-puck-drawer]');\n drawers.forEach((drawer) => {\n Array.from(drawer.children).forEach((cell) => {\n const card = cell.querySelector('.tps-block-card');\n if (!card) {\n setAttrIfChanged(cell, 'data-psd-cell-hidden', null);\n return;\n }\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(cell, 'data-psd-cell-hidden', hidden ? 'true' : null);\n });\n });\n\n // Hide entire category groups when all of their cards are filtered out.\n const groups = sidebar.querySelectorAll('[class*=\"_ComponentList_\"]');\n groups.forEach((g) => {\n const items = g.querySelectorAll('.tps-block-card');\n if (!items.length) {\n setAttrIfChanged(g, 'data-psd-empty', null);\n return;\n }\n const allHidden = Array.from(items).every(\n (c) => c.getAttribute('data-psd-hidden') === 'true',\n );\n setAttrIfChanged(g, 'data-psd-empty', q && allHidden ? 'true' : null);\n });\n\n // Counts must refresh on every filter change AND every drag/drop\n // (which adds/removes cards). Call from here so the search-input\n // handler also gets badge updates without re-running apply().\n updateCounts(sidebar);\n };\n\n const apply = () => {\n const sidebar = document.querySelector('[class*=\"_Sidebar--left\"]');\n if (!sidebar) return;\n\n const sections = sidebar.querySelectorAll('[class*=\"_SidebarSection_\"]');\n if (sections.length < 2) return;\n\n sections.forEach((s) => {\n const isComponents = !!s.querySelector('[class*=\"_ComponentList_\"]');\n const want = isComponents ? 'components' : 'outline';\n if (s.getAttribute('data-psd-section') !== want) {\n s.setAttribute('data-psd-section', want);\n }\n });\n\n sections.forEach((s) => {\n const title = s.querySelector('[class*=\"_SidebarSection-title_\"]');\n if (title && !title.hasAttribute('data-psd-hidden-title')) {\n title.setAttribute('data-psd-hidden-title', 'true');\n }\n });\n\n let tabs = sidebar.querySelector('.psd-sidebar-tabs');\n if (!tabs) {\n tabs = document.createElement('div');\n tabs.className = 'psd-sidebar-tabs';\n tabs.innerHTML = `\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"components\">${blocksLabel}</button>\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"outline\">${layersLabel}</button>\n `;\n sidebar.insertBefore(tabs, sidebar.firstChild);\n\n const setActive = (which) => {\n if (sidebar.getAttribute('data-psd-active-tab') === which) return;\n sidebar.setAttribute('data-psd-active-tab', which);\n try { sessionStorage.setItem(STORAGE_KEY, which); } catch {}\n tabs.querySelectorAll('button').forEach((b) => {\n const active = b.dataset.tab === which;\n if (b.classList.contains('is-active') !== active) {\n b.classList.toggle('is-active', active);\n }\n });\n };\n\n tabs.addEventListener('click', (e) => {\n const btn = e.target.closest('button[data-tab]');\n if (btn) setActive(btn.dataset.tab);\n });\n\n let initial = 'components';\n try { initial = sessionStorage.getItem(STORAGE_KEY) || 'components'; } catch {}\n setActive(initial);\n }\n\n // Search input — visible only when the components tab is active\n // (the [data-psd-active-tab=\"components\"] CSS gate does the toggling).\n let search = sidebar.querySelector('.psd-sidebar-search');\n if (!search) {\n search = document.createElement('div');\n search.className = 'psd-sidebar-search';\n const input = document.createElement('input');\n input.type = 'search';\n input.className = 'psd-sidebar-search__input';\n input.placeholder = searchPlaceholder;\n input.setAttribute('aria-label', searchPlaceholder);\n search.appendChild(input);\n // Place directly after the tab bar.\n if (tabs.nextSibling) sidebar.insertBefore(search, tabs.nextSibling);\n else sidebar.appendChild(search);\n\n input.addEventListener('input', () => {\n query = input.value || '';\n applyFilter(sidebar);\n });\n }\n // Re-sync the input value across Puck re-renders.\n const searchInput = search.querySelector('input');\n if (searchInput && searchInput.value !== query) searchInput.value = query;\n\n applyFilter(sidebar);\n };\n\n const safeApply = () => {\n if (observer) observer.disconnect();\n try { apply(); } catch { /* never break the host page */ }\n if (observer) observer.observe(document.body, { childList: true, subtree: true });\n };\n\n safeApply();\n\n let queued = false;\n observer = new MutationObserver(() => {\n if (queued) return;\n queued = true;\n requestAnimationFrame(() => {\n queued = false;\n safeApply();\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n if (observer) observer.disconnect();\n };\n }, [blocksLabel, layersLabel, searchPlaceholder]);\n\n return null;\n}\n","'use client';\n\n// Default top bar for the editor. Replaceable via the `header` prop on\n// <PageStudio /> — if you need full control, pass a render function.\n//\n// Designed to be brand-agnostic: every visible label/color comes from\n// `branding` or sensible neutrals. Account/home/publish are all optional;\n// hide what you don't need by leaving the corresponding prop unset.\n\nimport { Avatar, Button, ConfigProvider, Dropdown, Space, theme as antdTheme } from 'antd';\nimport {\n ArrowLeftOutlined,\n EyeOutlined,\n HomeOutlined,\n LogoutOutlined,\n PlusOutlined,\n RocketOutlined,\n UserOutlined,\n} from '@ant-design/icons';\n\nfunction DefaultLogo() {\n return (\n <svg width={20} height={20} viewBox=\"0 0 64 64\" aria-hidden>\n <rect width=\"64\" height=\"64\" rx=\"14\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nexport default function BuilderTopBar({\n pageKey,\n pageTitle,\n account,\n livePath,\n savedAt,\n pending,\n onPublish,\n onSignOut,\n onCreatePage,\n homeHref,\n branding = {},\n extraActions,\n LinkComponent = 'a',\n}) {\n const brand = {\n name: 'Page Studio',\n logo: <DefaultLogo />,\n primaryColor: '#0F766E',\n ...branding,\n };\n const Link = LinkComponent;\n\n const accountMenu = account\n ? {\n items: [\n {\n key: 'who',\n disabled: true,\n label: (\n <div style={{ padding: '4px 0', minWidth: 200 }}>\n <div style={{ fontSize: 13, fontWeight: 600, color: '#0f172a' }}>\n {account.name}\n </div>\n {account.email && account.email !== account.name && (\n <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 2 }}>\n {account.email}\n </div>\n )}\n </div>\n ),\n },\n { type: 'divider' },\n homeHref && {\n key: 'home',\n icon: <HomeOutlined />,\n label: <Link href={homeHref}>Admin home</Link>,\n },\n onSignOut && {\n key: 'signout',\n icon: <LogoutOutlined />,\n label: (\n <button\n type=\"button\"\n onClick={onSignOut}\n style={{ all: 'unset', cursor: 'pointer', width: '100%', display: 'block' }}\n >\n Sign out\n </button>\n ),\n },\n ].filter(Boolean),\n }\n : null;\n\n // Locally re-theme AntD so the Publish button + avatar pick up the\n // configured brand colors regardless of the host's ConfigProvider — the\n // top bar should look like the brand, not like the surrounding admin.\n const brandTheme = {\n algorithm: antdTheme.defaultAlgorithm,\n token: {\n colorPrimary: brand.primaryColor,\n colorInfo: brand.primaryColor,\n },\n };\n\n return (\n <ConfigProvider theme={brandTheme}>\n <div className=\"psd-builder-bar\" style={{ color: '#fff' }}>\n {homeHref ? (\n <Link\n href={homeHref}\n className=\"psd-builder-bar__brand\"\n title=\"Back to admin\"\n style={{ color: 'inherit' }}\n >\n <ArrowLeftOutlined style={{ fontSize: 12 }} />\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </Link>\n ) : (\n <div className=\"psd-builder-bar__brand\">\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </div>\n )}\n\n <div className=\"psd-builder-bar__crumbs\">\n <span className=\"psd-builder-bar__current\">{pageTitle || pageKey}</span>\n {pageKey && (\n <span style={{ marginLeft: 8, opacity: 0.5, fontSize: 11 }}>\n <code style={{ fontSize: 10 }}>{pageKey}</code>\n </span>\n )}\n {savedAt && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>\n · Saved {new Date(savedAt).toLocaleTimeString()}\n </span>\n )}\n {pending && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>· Saving…</span>\n )}\n </div>\n\n <Space size={6} className=\"psd-builder-bar__actions\">\n {extraActions}\n {onCreatePage && (\n <Button size=\"small\" icon={<PlusOutlined />} onClick={onCreatePage}>\n New page\n </Button>\n )}\n {livePath && (\n <Link href={livePath} target=\"_blank\" rel=\"noreferrer\">\n <Button size=\"small\" icon={<EyeOutlined />}>\n View live\n </Button>\n </Link>\n )}\n <Button\n type=\"primary\"\n size=\"small\"\n icon={<RocketOutlined />}\n onClick={onPublish}\n loading={pending}\n >\n Publish\n </Button>\n {account && accountMenu && (\n <Dropdown menu={accountMenu} placement=\"bottomRight\">\n <Button type=\"text\" size=\"small\" style={{ color: '#fff' }}>\n <Avatar\n size={22}\n icon={<UserOutlined />}\n style={{ background: brand.primaryColor }}\n />\n </Button>\n </Dropdown>\n )}\n </Space>\n </div>\n </ConfigProvider>\n );\n}\n"],"mappings":"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","names":["import_react","import_antd","antdTheme","import_jsx_runtime","AntdApp","import_page_studio_blocks"]}
1
+ {"version":3,"sources":["../src/index.js","../src/PageStudio.jsx","../src/BuilderEnhancements.jsx","../src/BuilderTopBar.jsx"],"sourcesContent":["export { default as PageStudio } from './PageStudio.jsx';\nexport { default as BuilderEnhancements } from './BuilderEnhancements.jsx';\nexport { default as BuilderTopBar } from './BuilderTopBar.jsx';\n\n// Re-export the blocks package's context so consumers don't need a separate\n// import to wrap their public-site renderer with the same studio config.\nexport {\n PageStudioProvider,\n useStudio,\n createPuckConfig,\n defaultBlocks,\n defaultCategories,\n defaultOverrides,\n emptyPuckData,\n} from '@techrox/page-studio-blocks';\n","'use client';\n\n// PageStudio — the editor entry point consumers render. Wraps Puck with:\n// - an adapter for persistence (loadPage / savePage / onCreatePage)\n// - a StudioProvider that injects host primitives (Link, services, …)\n// into the block tree\n// - a replaceable top bar (header prop) with sane brand-agnostic defaults\n// - sidebar enhancements (Blocks / Layers tabs, count badges)\n//\n// Everything except `pageKey` is optional. Passing only `pageKey` works —\n// you'll get the default block library, default top bar, and a console\n// warning when Publish is clicked without an adapter wired.\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n useTransition,\n} from 'react';\nimport { Puck, useGetPuck, legacySideBarPlugin } from '@puckeditor/core';\nimport { App as AntdApp } from 'antd';\n\nimport {\n applyConfigDefaults,\n createPuckConfig,\n defaultOverrides,\n emptyPuckData,\n normalizePuckData,\n PageStudioProvider,\n} from '@techrox/page-studio-blocks';\n\nimport BuilderEnhancements from './BuilderEnhancements.jsx';\nimport DefaultTopBar from './BuilderTopBar.jsx';\n\nimport '@puckeditor/core/puck.css';\n\n// 0.21 ships a new side navigation rail. Our BuilderEnhancements observes\n// the legacy `_Sidebar--left` layout, so keep that DOM by applying the\n// legacy plugin once at module load.\nconst PSD_LEGACY_SIDEBAR = legacySideBarPlugin();\n\n// The header override is passed to Puck via `overrides.header`. Puck treats\n// the value as a component type — when its reference changes, React diffs\n// and unmounts the old override before mounting the new one. During that\n// gap Puck briefly paints its default top bar, which flashes a stock\n// Publish button on screen. To avoid that we keep the override component\n// reference stable for the entire lifetime of the editor and pipe live\n// props (branding, savedAt, callbacks, etc.) through a context provider\n// rendered by PageStudio. Live updates flow as normal context updates —\n// the component never gets unmounted.\nconst HeaderPropsContext = createContext(null);\n\nfunction StableHeaderOverride() {\n const getPuck = useGetPuck();\n const live = useContext(HeaderPropsContext);\n if (!live) return null;\n const props = {\n pageKey: live.pageKey,\n pageTitle: live.pageTitle,\n account: live.account,\n livePath: live.livePath,\n homeHref: live.homeHref,\n savedAt: live.savedAt,\n pending: live.pending,\n onPublish: () => live.handlePublish(getPuck().appState.data),\n onSignOut: live.onSignOut,\n onCreatePage: live.onCreatePage,\n branding: live.branding,\n extraActions: live.headerActions,\n LinkComponent: live.LinkComponent,\n };\n if (typeof live.header === 'function') return live.header(props);\n if (live.header) return live.header;\n return <DefaultTopBar {...props} />;\n}\n\n// `overrides.header` is referentially stable so Puck never remounts the\n// override. The internal stack also stabilizes `headerActions: () => null`\n// — that's how we suppress Puck's own actions area.\nconst NO_OP_ACTIONS = () => null;\nfunction buildStableOverrides(hostOverrides) {\n return { ...hostOverrides, header: StableHeaderOverride, headerActions: NO_OP_ACTIONS };\n}\n\n// Apply brand colors + theme tokens by injecting a single <style id=\"tps-brand-vars\">\n// into <head>. Its `:root { --tps-*: … }` rule themes BOTH surfaces: the outer\n// editor chrome reads it directly, and Puck clones <head> <style>/<link> nodes\n// into its canvas iframe (keeping them in sync via a MutationObserver), so the\n// blocks pick up the same tokens.\n//\n// Do NOT also write these tokens as inline styles on document.documentElement.\n// It looks harmless — inline styles on the outer <html> don't \"leak\" into the\n// iframe through the cascade — but Puck SNAPSHOTS the outer <html>'s style\n// attribute onto the iframe's <html> ONCE at canvas mount and never re-syncs it.\n// On a later theme flip the head <style> updates (and re-clones), but the iframe's\n// inline <html> tokens stay frozen at their mount-time values. Inline styles beat\n// every stylesheet rule, so those stale light tokens override the synced dark\n// :root rule — the chrome goes dark while the canvas stays light. Keeping tokens\n// in the <style> tag only (no inline) lets the synced :root rule win cleanly.\n//\n// We write to `--tps-*` because that's what the block library reads. The host\n// page may also paint these vars on a wrapping element (e.g. a brand-switcher's\n// `[data-brand]` div), but that element doesn't exist inside Puck's iframe — so\n// without this :root injection the canvas falls back to the package defaults.\nfunction setCssVars(branding) {\n if (typeof document === 'undefined' || !branding) return;\n\n const lines = [];\n if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);\n if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);\n if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);\n\n // Arbitrary theme tokens — a flat { '--tps-bg': '#141414', … } map. This is\n // how a host flips the whole canvas to a non-default theme (dark, sepia, …):\n // the iframe only inherits <head> :root rules, not a `data-tps-theme`\n // attribute on the host wrapper, so we write the resolved token values\n // straight into :root. The host picks the set; the package owns the values.\n const cssVars = branding.cssVars || {};\n for (const [k, v] of Object.entries(cssVars)) {\n if (v == null) continue;\n lines.push(`${k}: ${v};`);\n }\n if (!lines.length) return;\n\n // Paint the canvas backdrop from the theme's section token when supplied, so\n // plain (transparent) sections sit on the themed surface inside the iframe.\n const bg = cssVars['--tps-bg-section'];\n const canvas = bg ? ` html { background: ${bg}; }` : '';\n const next = `:root { ${lines.join(' ')} }${canvas}`;\n\n const existing = document.getElementById('tps-brand-vars');\n if (existing && existing.textContent === next) return;\n\n // Replace the node — do NOT just edit textContent. Puck clones <head> styles\n // into the canvas iframe and keeps them in sync with a MutationObserver that\n // only reacts to childList changes (nodes added/removed), not characterData.\n // Editing an existing tag's text therefore never reaches the iframe, so the\n // canvas keeps the theme it mounted with while the chrome (which reads\n // data-tps-theme directly, outside the iframe) flips — the exact split where\n // sidebars go dark but the blocks stay light. Removing + re-appending a fresh\n // node fires a childList mutation, so Puck re-clones the new tokens and the\n // canvas flips with the chrome.\n if (existing) existing.remove();\n const tag = document.createElement('style');\n tag.id = 'tps-brand-vars';\n tag.textContent = next;\n document.head.appendChild(tag);\n}\n\nexport default function PageStudio({\n pageKey,\n initialData,\n pageTitle,\n account,\n livePath,\n homeHref,\n branding,\n // Active editor theme — 'light' | 'dark' | any future token-scope name.\n // Stamped as data-tps-theme on the editor root so the whole chrome (top\n // bar, sidebars, field panel, inputs) re-paints through the CSS cascade,\n // alongside the canvas (which is themed via branding.cssVars). Falls back\n // to branding.theme so a host that only passes branding still themes.\n theme,\n studio,\n adapter = {},\n config,\n blockDefaults,\n overrides = defaultOverrides,\n header,\n headerActions,\n onSignOut,\n sidebarLabels,\n LinkComponent,\n // Forwarded to Puck. Defaults to iframe enabled — Puck v0.20 mounts the\n // @dnd-kit context inside the canvas iframe; with the iframe disabled,\n // strict-mode double-mount and Vite HMR can leave the canvas with no live\n // drop targets, so dropped blocks vanish into an empty content array.\n // Hosts that need the canvas to share the host document (e.g. to inherit\n // global CSS without copying it across) can pass { enabled: false }.\n iframe = { enabled: true },\n}) {\n const { message } = AntdApp.useApp();\n const [pending, startTransition] = useTransition();\n const [savedAt, setSavedAt] = useState(null);\n\n // Resolve the config ONCE on first mount and freeze the reference. Puck\n // treats `config` as a structural prop — passing a new object on every\n // brand switch makes it re-diff its block registry, drop @dnd-kit's\n // collision cache and rebuild a chunk of internal memos. That's the\n // bulk of what makes brand switching feel sluggish.\n //\n // Trade-off: a `blockDefaults` change after mount (e.g. host swapping\n // tenant defaults mid-edit) no longer updates the per-block defaults\n // applied to newly dropped blocks — those will use whatever defaults\n // were active at first mount. CSS vars (color, ink, accent) still\n // update live via setCssVars. For the showcase that's exactly what we\n // want: brand swaps repaint colors instantly without churning Puck.\n // Hosts that genuinely need brand-aware drop defaults should remount\n // PageStudio with a fresh key when they swap tenants.\n const [resolvedConfig] = useState(\n () => config || createPuckConfig({ defaults: blockDefaults }),\n );\n\n const [data, setData] = useState(() =>\n initialData && Array.isArray(initialData.content)\n ? applyConfigDefaults(normalizePuckData(initialData), resolvedConfig)\n : null,\n );\n const [loading, setLoading] = useState(!data && !!adapter.loadPage);\n\n // Apply brand CSS variables synchronously during render — before Puck\n // mounts its iframe and before the first paint. Running this in useEffect\n // makes the canvas paint once with the package-default teal and then\n // repaint when the effect commits, producing a visible colour flash on\n // every load. The function is idempotent (it only writes when the head\n // <style> content actually differs) so repeated calls during strict-mode\n // double-render are a no-op.\n setCssVars(branding);\n\n // Single external theme switch for the editor chrome. The canvas gets its\n // theme from branding.cssVars (the iframe can't see this attribute); the\n // chrome — top bar, both sidebars, the field panel and its inputs — gets\n // it from data-tps-theme on the root below. Default 'light'.\n const resolvedTheme = theme || branding?.theme || 'light';\n\n // Initial load when initialData wasn't server-supplied. Hosts that SSR\n // their CMS read should always pass initialData and skip this code path.\n useEffect(() => {\n if (data || !adapter.loadPage) return;\n let cancelled = false;\n setLoading(true);\n adapter.loadPage(pageKey)\n .then((loaded) => {\n if (cancelled) return;\n setData(\n loaded && Array.isArray(loaded.content)\n ? applyConfigDefaults(normalizePuckData(loaded), resolvedConfig)\n : emptyPuckData,\n );\n })\n .catch((err) => {\n if (cancelled) return;\n message.error(err?.message || 'Could not load page.');\n setData(emptyPuckData);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [pageKey, adapter, data, message]);\n\n const handlePublish = (nextData) => {\n if (!adapter.savePage) {\n message.error('No savePage adapter configured.');\n return;\n }\n startTransition(async () => {\n try {\n await adapter.savePage(pageKey, nextData);\n setSavedAt(new Date().toISOString());\n message.success('Published.');\n } catch (err) {\n message.error(err?.message || 'Save failed.');\n }\n });\n };\n\n // Live values consumed by the stable header override via context. The\n // context VALUE may change every render — that's fine, it triggers a\n // normal re-render of StableHeaderOverride. The override COMPONENT\n // reference (passed to Puck via overrides.header) never changes, so\n // Puck never unmounts it. That's what kills the default-button flash\n // on brand switch.\n const headerLive = useMemo(() => ({\n pageKey,\n pageTitle,\n account,\n livePath,\n homeHref,\n savedAt,\n pending,\n handlePublish,\n onSignOut,\n onCreatePage: adapter.onCreatePage,\n branding,\n headerActions,\n LinkComponent,\n header,\n }), [\n pageKey, pageTitle, account, livePath, homeHref, savedAt, pending,\n handlePublish, onSignOut, adapter.onCreatePage, branding, headerActions,\n LinkComponent, header,\n ]);\n\n // Build the overrides object ONCE per `overrides` prop change. The\n // header slot is the stable component declared at module scope —\n // brand swaps don't invalidate the reference.\n const mergedOverrides = useMemo(\n () => buildStableOverrides(overrides),\n [overrides],\n );\n\n if (loading || !data) {\n return (\n <div\n className=\"psd-builder-page psd-builder-page--loading\"\n data-tps-theme={resolvedTheme}\n >\n <div className=\"psd-builder-loading\">Loading editor…</div>\n </div>\n );\n }\n\n return (\n <PageStudioProvider value={studio}>\n <HeaderPropsContext.Provider value={headerLive}>\n <div className=\"psd-builder-page\" data-tps-theme={resolvedTheme}>\n <BuilderEnhancements\n blocksLabel={sidebarLabels?.blocks}\n layersLabel={sidebarLabels?.layers}\n searchPlaceholder={sidebarLabels?.search}\n />\n <Puck\n config={resolvedConfig}\n data={data}\n overrides={mergedOverrides}\n onPublish={handlePublish}\n iframe={iframe}\n plugins={[PSD_LEGACY_SIDEBAR]}\n />\n </div>\n </HeaderPropsContext.Provider>\n </PageStudioProvider>\n );\n}\n","'use client';\n\n// Post-mount DOM enhancements for Puck's left sidebar:\n// 1. Hide Puck's \"Components\" / \"Outline\" section titles\n// 2. Inject a tab bar at the top that toggles between the two sections\n// 3. Inject a search input that filters the block cards by name + summary\n// 4. Append a count badge to each component-category header (visible-only)\n//\n// IMPORTANT: every DOM mutation we make MUST be idempotent (no-op when the\n// target state already matches) AND we disconnect the MutationObserver\n// while applying so our own writes don't feed back into the observer and\n// cause an infinite loop / tab freeze.\n\nimport { useEffect } from 'react';\n\nconst STORAGE_KEY = 'psd.builderTab';\n\nexport default function BuilderEnhancements({\n blocksLabel = 'Blocks',\n layersLabel = 'Layers',\n searchPlaceholder = 'Search blocks',\n} = {}) {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n let observer = null;\n // Search query persists across re-renders even if Puck wipes the sidebar\n // and we have to re-inject the input. Lives in this closure (per mount).\n let query = '';\n\n // Filtering is done via attributes on elements WE own (.tps-block-card\n // and _ComponentList_ groups). CSS in editor/styles.css hides the\n // matching Puck wrapper via :has(). Writing only attributes — not\n // inline styles on Puck's wrappers — keeps us out of a fight with\n // Puck's React reconciler when it re-renders the picker after a drop.\n const matchesQuery = (card, q) => {\n if (!q) return true;\n const name = (card.querySelector('.tps-block-card__name')?.textContent || '').toLowerCase();\n const desc = (card.querySelector('.tps-block-card__desc')?.textContent || '').toLowerCase();\n return name.includes(q) || desc.includes(q);\n };\n\n const setAttrIfChanged = (el, name, value) => {\n const current = el.getAttribute(name);\n if (value == null) {\n if (current !== null) el.removeAttribute(name);\n } else if (current !== value) {\n el.setAttribute(name, value);\n }\n };\n\n const updateCounts = (sidebar) => {\n const headers = sidebar.querySelectorAll('[class*=\"_ComponentList-title_\"]');\n headers.forEach((header) => {\n const parent = header.closest('[class*=\"_ComponentList_\"]');\n if (!parent) return;\n const list = parent.querySelector('[class*=\"_ComponentList-content_\"]');\n if (!list) return;\n const cards = list.querySelectorAll('.tps-block-card');\n const visible = Array.from(cards).filter(\n (c) => c.getAttribute('data-psd-hidden') !== 'true',\n ).length;\n const count = String(visible);\n let badge = header.querySelector('.psd-cat-count');\n if (!badge) {\n badge = document.createElement('span');\n badge.className = 'psd-cat-count';\n badge.textContent = count;\n const chevron = header.querySelector('[class*=\"_ComponentList-titleIcon_\"]');\n if (chevron) {\n header.insertBefore(badge, chevron);\n } else {\n header.appendChild(badge);\n }\n } else if (badge.textContent !== count) {\n badge.textContent = count;\n }\n });\n };\n\n const applyFilter = (sidebar) => {\n const q = query.trim().toLowerCase();\n const cards = sidebar.querySelectorAll('.tps-block-card');\n cards.forEach((card) => {\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(card, 'data-psd-hidden', hidden ? 'true' : null);\n });\n\n // Mark the actual grid cell so it collapses out of layout — not just\n // our inner card. Puck's <DrawerItem> renders an unnamed <div> as the\n // direct child of [data-puck-drawer]; that <div> IS the grid cell.\n // Hiding only the .tps-block-card or any class-bearing inner wrapper\n // leaves the cell empty so visible matches stay in their original\n // grid slots (which is what the user was seeing). We tag the cell\n // directly here so a single CSS attribute selector can collapse it.\n const drawers = sidebar.querySelectorAll('[data-puck-drawer]');\n drawers.forEach((drawer) => {\n Array.from(drawer.children).forEach((cell) => {\n const card = cell.querySelector('.tps-block-card');\n if (!card) {\n setAttrIfChanged(cell, 'data-psd-cell-hidden', null);\n return;\n }\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(cell, 'data-psd-cell-hidden', hidden ? 'true' : null);\n });\n });\n\n // Hide entire category groups when all of their cards are filtered out.\n const groups = sidebar.querySelectorAll('[class*=\"_ComponentList_\"]');\n groups.forEach((g) => {\n const items = g.querySelectorAll('.tps-block-card');\n if (!items.length) {\n setAttrIfChanged(g, 'data-psd-empty', null);\n return;\n }\n const allHidden = Array.from(items).every(\n (c) => c.getAttribute('data-psd-hidden') === 'true',\n );\n setAttrIfChanged(g, 'data-psd-empty', q && allHidden ? 'true' : null);\n });\n\n // Counts must refresh on every filter change AND every drag/drop\n // (which adds/removes cards). Call from here so the search-input\n // handler also gets badge updates without re-running apply().\n updateCounts(sidebar);\n };\n\n const apply = () => {\n const sidebar = document.querySelector('[class*=\"_Sidebar--left\"]');\n if (!sidebar) return;\n\n const sections = sidebar.querySelectorAll('[class*=\"_SidebarSection_\"]');\n if (sections.length < 2) return;\n\n sections.forEach((s) => {\n const isComponents = !!s.querySelector('[class*=\"_ComponentList_\"]');\n const want = isComponents ? 'components' : 'outline';\n if (s.getAttribute('data-psd-section') !== want) {\n s.setAttribute('data-psd-section', want);\n }\n });\n\n sections.forEach((s) => {\n const title = s.querySelector('[class*=\"_SidebarSection-title_\"]');\n if (title && !title.hasAttribute('data-psd-hidden-title')) {\n title.setAttribute('data-psd-hidden-title', 'true');\n }\n });\n\n let tabs = sidebar.querySelector('.psd-sidebar-tabs');\n if (!tabs) {\n tabs = document.createElement('div');\n tabs.className = 'psd-sidebar-tabs';\n tabs.innerHTML = `\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"components\">${blocksLabel}</button>\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"outline\">${layersLabel}</button>\n `;\n sidebar.insertBefore(tabs, sidebar.firstChild);\n\n const setActive = (which) => {\n if (sidebar.getAttribute('data-psd-active-tab') === which) return;\n sidebar.setAttribute('data-psd-active-tab', which);\n try { sessionStorage.setItem(STORAGE_KEY, which); } catch {}\n tabs.querySelectorAll('button').forEach((b) => {\n const active = b.dataset.tab === which;\n if (b.classList.contains('is-active') !== active) {\n b.classList.toggle('is-active', active);\n }\n });\n };\n\n tabs.addEventListener('click', (e) => {\n const btn = e.target.closest('button[data-tab]');\n if (btn) setActive(btn.dataset.tab);\n });\n\n let initial = 'components';\n try { initial = sessionStorage.getItem(STORAGE_KEY) || 'components'; } catch {}\n setActive(initial);\n }\n\n // Search input — visible only when the components tab is active\n // (the [data-psd-active-tab=\"components\"] CSS gate does the toggling).\n let search = sidebar.querySelector('.psd-sidebar-search');\n if (!search) {\n search = document.createElement('div');\n search.className = 'psd-sidebar-search';\n const input = document.createElement('input');\n input.type = 'search';\n input.className = 'psd-sidebar-search__input';\n input.placeholder = searchPlaceholder;\n input.setAttribute('aria-label', searchPlaceholder);\n search.appendChild(input);\n // Place directly after the tab bar.\n if (tabs.nextSibling) sidebar.insertBefore(search, tabs.nextSibling);\n else sidebar.appendChild(search);\n\n input.addEventListener('input', () => {\n query = input.value || '';\n applyFilter(sidebar);\n });\n }\n // Re-sync the input value across Puck re-renders.\n const searchInput = search.querySelector('input');\n if (searchInput && searchInput.value !== query) searchInput.value = query;\n\n applyFilter(sidebar);\n };\n\n const safeApply = () => {\n if (observer) observer.disconnect();\n try { apply(); } catch { /* never break the host page */ }\n if (observer) observer.observe(document.body, { childList: true, subtree: true });\n };\n\n safeApply();\n\n let queued = false;\n observer = new MutationObserver(() => {\n if (queued) return;\n queued = true;\n requestAnimationFrame(() => {\n queued = false;\n safeApply();\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n if (observer) observer.disconnect();\n };\n }, [blocksLabel, layersLabel, searchPlaceholder]);\n\n return null;\n}\n","'use client';\n\n// Default top bar for the editor. Replaceable via the `header` prop on\n// <PageStudio /> — if you need full control, pass a render function.\n//\n// Designed to be brand-agnostic: every visible label/color comes from\n// `branding` or sensible neutrals. Account/home/publish are all optional;\n// hide what you don't need by leaving the corresponding prop unset.\n\nimport { Avatar, Button, ConfigProvider, Dropdown, Space, theme as antdTheme } from 'antd';\nimport {\n ArrowLeftOutlined,\n EyeOutlined,\n HomeOutlined,\n LogoutOutlined,\n PlusOutlined,\n RocketOutlined,\n UserOutlined,\n} from '@ant-design/icons';\n\nfunction DefaultLogo() {\n return (\n <svg width={20} height={20} viewBox=\"0 0 64 64\" aria-hidden>\n <rect width=\"64\" height=\"64\" rx=\"14\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nexport default function BuilderTopBar({\n pageKey,\n pageTitle,\n account,\n livePath,\n savedAt,\n pending,\n onPublish,\n onSignOut,\n onCreatePage,\n homeHref,\n branding = {},\n extraActions,\n LinkComponent = 'a',\n}) {\n const brand = {\n name: 'Page Studio',\n logo: <DefaultLogo />,\n primaryColor: '#0F766E',\n ...branding,\n };\n const Link = LinkComponent;\n\n const accountMenu = account\n ? {\n items: [\n {\n key: 'who',\n disabled: true,\n label: (\n <div style={{ padding: '4px 0', minWidth: 200 }}>\n <div style={{ fontSize: 13, fontWeight: 600, color: '#0f172a' }}>\n {account.name}\n </div>\n {account.email && account.email !== account.name && (\n <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 2 }}>\n {account.email}\n </div>\n )}\n </div>\n ),\n },\n { type: 'divider' },\n homeHref && {\n key: 'home',\n icon: <HomeOutlined />,\n label: <Link href={homeHref}>Admin home</Link>,\n },\n onSignOut && {\n key: 'signout',\n icon: <LogoutOutlined />,\n label: (\n <button\n type=\"button\"\n onClick={onSignOut}\n style={{ all: 'unset', cursor: 'pointer', width: '100%', display: 'block' }}\n >\n Sign out\n </button>\n ),\n },\n ].filter(Boolean),\n }\n : null;\n\n // Locally re-theme AntD so the Publish button + avatar pick up the\n // configured brand colors regardless of the host's ConfigProvider — the\n // top bar should look like the brand, not like the surrounding admin.\n const brandTheme = {\n algorithm: antdTheme.defaultAlgorithm,\n token: {\n colorPrimary: brand.primaryColor,\n colorInfo: brand.primaryColor,\n },\n };\n\n return (\n <ConfigProvider theme={brandTheme}>\n <div className=\"psd-builder-bar\" style={{ color: '#fff' }}>\n {homeHref ? (\n <Link\n href={homeHref}\n className=\"psd-builder-bar__brand\"\n title=\"Back to admin\"\n style={{ color: 'inherit' }}\n >\n <ArrowLeftOutlined style={{ fontSize: 12 }} />\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </Link>\n ) : (\n <div className=\"psd-builder-bar__brand\">\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </div>\n )}\n\n <div className=\"psd-builder-bar__crumbs\">\n <span className=\"psd-builder-bar__current\">{pageTitle || pageKey}</span>\n {pageKey && (\n <span style={{ marginLeft: 8, opacity: 0.5, fontSize: 11 }}>\n <code style={{ fontSize: 10 }}>{pageKey}</code>\n </span>\n )}\n {savedAt && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>\n · Saved {new Date(savedAt).toLocaleTimeString()}\n </span>\n )}\n {pending && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>· Saving…</span>\n )}\n </div>\n\n <Space size={6} className=\"psd-builder-bar__actions\">\n {extraActions}\n {onCreatePage && (\n <Button size=\"small\" icon={<PlusOutlined />} onClick={onCreatePage}>\n New page\n </Button>\n )}\n {livePath && (\n <Link href={livePath} target=\"_blank\" rel=\"noreferrer\">\n <Button size=\"small\" icon={<EyeOutlined />}>\n View live\n </Button>\n </Link>\n )}\n <Button\n type=\"primary\"\n size=\"small\"\n icon={<RocketOutlined />}\n onClick={onPublish}\n loading={pending}\n >\n Publish\n </Button>\n {account && accountMenu && (\n <Dropdown menu={accountMenu} placement=\"bottomRight\">\n <Button type=\"text\" size=\"small\" style={{ color: '#fff' }}>\n <Avatar\n size={22}\n icon={<UserOutlined />}\n style={{ background: brand.primaryColor }}\n />\n </Button>\n </Dropdown>\n )}\n </Space>\n </div>\n </ConfigProvider>\n );\n}\n"],"mappings":"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","names":["import_react","import_antd","antdTheme","import_jsx_runtime","AntdApp","import_page_studio_blocks"]}
package/dist/index.js CHANGED
@@ -367,23 +367,26 @@ function buildStableOverrides(hostOverrides) {
367
367
  }
368
368
  function setCssVars(branding) {
369
369
  if (typeof document === "undefined" || !branding) return;
370
- const root = document.documentElement.style;
371
- if (branding.primaryColor) root.setProperty("--tps-primary", branding.primaryColor);
372
- if (branding.accentColor) root.setProperty("--tps-accent", branding.accentColor);
373
- if (branding.inkColor) root.setProperty("--tps-ink", branding.inkColor);
374
370
  const lines = [];
375
371
  if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);
376
372
  if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);
377
373
  if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);
378
- if (!lines.length) return;
379
- let tag = document.getElementById("tps-brand-vars");
380
- if (!tag) {
381
- tag = document.createElement("style");
382
- tag.id = "tps-brand-vars";
383
- document.head.appendChild(tag);
374
+ const cssVars = branding.cssVars || {};
375
+ for (const [k, v] of Object.entries(cssVars)) {
376
+ if (v == null) continue;
377
+ lines.push(`${k}: ${v};`);
384
378
  }
385
- const next = `:root { ${lines.join(" ")} }`;
386
- if (tag.textContent !== next) tag.textContent = next;
379
+ if (!lines.length) return;
380
+ const bg = cssVars["--tps-bg-section"];
381
+ const canvas = bg ? ` html { background: ${bg}; }` : "";
382
+ const next = `:root { ${lines.join(" ")} }${canvas}`;
383
+ const existing = document.getElementById("tps-brand-vars");
384
+ if (existing && existing.textContent === next) return;
385
+ if (existing) existing.remove();
386
+ const tag = document.createElement("style");
387
+ tag.id = "tps-brand-vars";
388
+ tag.textContent = next;
389
+ document.head.appendChild(tag);
387
390
  }
388
391
  function PageStudio({
389
392
  pageKey,
@@ -393,6 +396,12 @@ function PageStudio({
393
396
  livePath,
394
397
  homeHref,
395
398
  branding,
399
+ // Active editor theme — 'light' | 'dark' | any future token-scope name.
400
+ // Stamped as data-tps-theme on the editor root so the whole chrome (top
401
+ // bar, sidebars, field panel, inputs) re-paints through the CSS cascade,
402
+ // alongside the canvas (which is themed via branding.cssVars). Falls back
403
+ // to branding.theme so a host that only passes branding still themes.
404
+ theme,
396
405
  studio,
397
406
  adapter = {},
398
407
  config,
@@ -422,6 +431,7 @@ function PageStudio({
422
431
  );
423
432
  const [loading, setLoading] = useState(!data && !!adapter.loadPage);
424
433
  setCssVars(branding);
434
+ const resolvedTheme = theme || branding?.theme || "light";
425
435
  useEffect2(() => {
426
436
  if (data || !adapter.loadPage) return;
427
437
  let cancelled = false;
@@ -493,9 +503,16 @@ function PageStudio({
493
503
  [overrides]
494
504
  );
495
505
  if (loading || !data) {
496
- return /* @__PURE__ */ jsx2("div", { className: "psd-builder-page psd-builder-page--loading", children: /* @__PURE__ */ jsx2("div", { className: "psd-builder-loading", children: "Loading editor\u2026" }) });
506
+ return /* @__PURE__ */ jsx2(
507
+ "div",
508
+ {
509
+ className: "psd-builder-page psd-builder-page--loading",
510
+ "data-tps-theme": resolvedTheme,
511
+ children: /* @__PURE__ */ jsx2("div", { className: "psd-builder-loading", children: "Loading editor\u2026" })
512
+ }
513
+ );
497
514
  }
498
- return /* @__PURE__ */ jsx2(PageStudioProvider, { value: studio, children: /* @__PURE__ */ jsx2(HeaderPropsContext.Provider, { value: headerLive, children: /* @__PURE__ */ jsxs2("div", { className: "psd-builder-page", children: [
515
+ return /* @__PURE__ */ jsx2(PageStudioProvider, { value: studio, children: /* @__PURE__ */ jsx2(HeaderPropsContext.Provider, { value: headerLive, children: /* @__PURE__ */ jsxs2("div", { className: "psd-builder-page", "data-tps-theme": resolvedTheme, children: [
499
516
  /* @__PURE__ */ jsx2(
500
517
  BuilderEnhancements,
501
518
  {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/PageStudio.jsx","../src/BuilderEnhancements.jsx","../src/BuilderTopBar.jsx","../src/index.js"],"sourcesContent":["'use client';\n\n// PageStudio — the editor entry point consumers render. Wraps Puck with:\n// - an adapter for persistence (loadPage / savePage / onCreatePage)\n// - a StudioProvider that injects host primitives (Link, services, …)\n// into the block tree\n// - a replaceable top bar (header prop) with sane brand-agnostic defaults\n// - sidebar enhancements (Blocks / Layers tabs, count badges)\n//\n// Everything except `pageKey` is optional. Passing only `pageKey` works —\n// you'll get the default block library, default top bar, and a console\n// warning when Publish is clicked without an adapter wired.\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n useTransition,\n} from 'react';\nimport { Puck, useGetPuck, legacySideBarPlugin } from '@puckeditor/core';\nimport { App as AntdApp } from 'antd';\n\nimport {\n applyConfigDefaults,\n createPuckConfig,\n defaultOverrides,\n emptyPuckData,\n normalizePuckData,\n PageStudioProvider,\n} from '@techrox/page-studio-blocks';\n\nimport BuilderEnhancements from './BuilderEnhancements.jsx';\nimport DefaultTopBar from './BuilderTopBar.jsx';\n\nimport '@puckeditor/core/puck.css';\n\n// 0.21 ships a new side navigation rail. Our BuilderEnhancements observes\n// the legacy `_Sidebar--left` layout, so keep that DOM by applying the\n// legacy plugin once at module load.\nconst PSD_LEGACY_SIDEBAR = legacySideBarPlugin();\n\n// The header override is passed to Puck via `overrides.header`. Puck treats\n// the value as a component type — when its reference changes, React diffs\n// and unmounts the old override before mounting the new one. During that\n// gap Puck briefly paints its default top bar, which flashes a stock\n// Publish button on screen. To avoid that we keep the override component\n// reference stable for the entire lifetime of the editor and pipe live\n// props (branding, savedAt, callbacks, etc.) through a context provider\n// rendered by PageStudio. Live updates flow as normal context updates —\n// the component never gets unmounted.\nconst HeaderPropsContext = createContext(null);\n\nfunction StableHeaderOverride() {\n const getPuck = useGetPuck();\n const live = useContext(HeaderPropsContext);\n if (!live) return null;\n const props = {\n pageKey: live.pageKey,\n pageTitle: live.pageTitle,\n account: live.account,\n livePath: live.livePath,\n homeHref: live.homeHref,\n savedAt: live.savedAt,\n pending: live.pending,\n onPublish: () => live.handlePublish(getPuck().appState.data),\n onSignOut: live.onSignOut,\n onCreatePage: live.onCreatePage,\n branding: live.branding,\n extraActions: live.headerActions,\n LinkComponent: live.LinkComponent,\n };\n if (typeof live.header === 'function') return live.header(props);\n if (live.header) return live.header;\n return <DefaultTopBar {...props} />;\n}\n\n// `overrides.header` is referentially stable so Puck never remounts the\n// override. The internal stack also stabilizes `headerActions: () => null`\n// — that's how we suppress Puck's own actions area.\nconst NO_OP_ACTIONS = () => null;\nfunction buildStableOverrides(hostOverrides) {\n return { ...hostOverrides, header: StableHeaderOverride, headerActions: NO_OP_ACTIONS };\n}\n\n// Apply brand colors via BOTH an inline style on <html> (for the outer\n// editor chrome) and a <style> tag in <head> (so Puck's iframe canvas,\n// which clones head styles into its document, picks them up too). Pure\n// inline styles on document.documentElement do not propagate to the\n// iframe — only <style>/<link> elements do.\n//\n// We write to `--tps-*` because that's what the block library reads. The\n// host page may also paint these vars on a wrapping element (e.g. a\n// brand-switcher's `[data-brand]` div), but that element doesn't exist\n// inside Puck's iframe — so without this :root injection the canvas\n// falls back to the package's default values and ignores the brand.\nfunction setCssVars(branding) {\n if (typeof document === 'undefined' || !branding) return;\n const root = document.documentElement.style;\n if (branding.primaryColor) root.setProperty('--tps-primary', branding.primaryColor);\n if (branding.accentColor) root.setProperty('--tps-accent', branding.accentColor);\n if (branding.inkColor) root.setProperty('--tps-ink', branding.inkColor);\n\n const lines = [];\n if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);\n if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);\n if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);\n if (!lines.length) return;\n\n let tag = document.getElementById('tps-brand-vars');\n if (!tag) {\n tag = document.createElement('style');\n tag.id = 'tps-brand-vars';\n document.head.appendChild(tag);\n }\n const next = `:root { ${lines.join(' ')} }`;\n if (tag.textContent !== next) tag.textContent = next;\n}\n\nexport default function PageStudio({\n pageKey,\n initialData,\n pageTitle,\n account,\n livePath,\n homeHref,\n branding,\n studio,\n adapter = {},\n config,\n blockDefaults,\n overrides = defaultOverrides,\n header,\n headerActions,\n onSignOut,\n sidebarLabels,\n LinkComponent,\n // Forwarded to Puck. Defaults to iframe enabled — Puck v0.20 mounts the\n // @dnd-kit context inside the canvas iframe; with the iframe disabled,\n // strict-mode double-mount and Vite HMR can leave the canvas with no live\n // drop targets, so dropped blocks vanish into an empty content array.\n // Hosts that need the canvas to share the host document (e.g. to inherit\n // global CSS without copying it across) can pass { enabled: false }.\n iframe = { enabled: true },\n}) {\n const { message } = AntdApp.useApp();\n const [pending, startTransition] = useTransition();\n const [savedAt, setSavedAt] = useState(null);\n\n // Resolve the config ONCE on first mount and freeze the reference. Puck\n // treats `config` as a structural prop — passing a new object on every\n // brand switch makes it re-diff its block registry, drop @dnd-kit's\n // collision cache and rebuild a chunk of internal memos. That's the\n // bulk of what makes brand switching feel sluggish.\n //\n // Trade-off: a `blockDefaults` change after mount (e.g. host swapping\n // tenant defaults mid-edit) no longer updates the per-block defaults\n // applied to newly dropped blocks — those will use whatever defaults\n // were active at first mount. CSS vars (color, ink, accent) still\n // update live via setCssVars. For the showcase that's exactly what we\n // want: brand swaps repaint colors instantly without churning Puck.\n // Hosts that genuinely need brand-aware drop defaults should remount\n // PageStudio with a fresh key when they swap tenants.\n const [resolvedConfig] = useState(\n () => config || createPuckConfig({ defaults: blockDefaults }),\n );\n\n const [data, setData] = useState(() =>\n initialData && Array.isArray(initialData.content)\n ? applyConfigDefaults(normalizePuckData(initialData), resolvedConfig)\n : null,\n );\n const [loading, setLoading] = useState(!data && !!adapter.loadPage);\n\n // Apply brand CSS variables synchronously during render — before Puck\n // mounts its iframe and before the first paint. Running this in useEffect\n // makes the canvas paint once with the package-default teal and then\n // repaint when the effect commits, producing a visible colour flash on\n // every load. The function is idempotent (it only writes when the head\n // <style> content actually differs) so repeated calls during strict-mode\n // double-render are a no-op.\n setCssVars(branding);\n\n // Initial load when initialData wasn't server-supplied. Hosts that SSR\n // their CMS read should always pass initialData and skip this code path.\n useEffect(() => {\n if (data || !adapter.loadPage) return;\n let cancelled = false;\n setLoading(true);\n adapter.loadPage(pageKey)\n .then((loaded) => {\n if (cancelled) return;\n setData(\n loaded && Array.isArray(loaded.content)\n ? applyConfigDefaults(normalizePuckData(loaded), resolvedConfig)\n : emptyPuckData,\n );\n })\n .catch((err) => {\n if (cancelled) return;\n message.error(err?.message || 'Could not load page.');\n setData(emptyPuckData);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [pageKey, adapter, data, message]);\n\n const handlePublish = (nextData) => {\n if (!adapter.savePage) {\n message.error('No savePage adapter configured.');\n return;\n }\n startTransition(async () => {\n try {\n await adapter.savePage(pageKey, nextData);\n setSavedAt(new Date().toISOString());\n message.success('Published.');\n } catch (err) {\n message.error(err?.message || 'Save failed.');\n }\n });\n };\n\n // Live values consumed by the stable header override via context. The\n // context VALUE may change every render — that's fine, it triggers a\n // normal re-render of StableHeaderOverride. The override COMPONENT\n // reference (passed to Puck via overrides.header) never changes, so\n // Puck never unmounts it. That's what kills the default-button flash\n // on brand switch.\n const headerLive = useMemo(() => ({\n pageKey,\n pageTitle,\n account,\n livePath,\n homeHref,\n savedAt,\n pending,\n handlePublish,\n onSignOut,\n onCreatePage: adapter.onCreatePage,\n branding,\n headerActions,\n LinkComponent,\n header,\n }), [\n pageKey, pageTitle, account, livePath, homeHref, savedAt, pending,\n handlePublish, onSignOut, adapter.onCreatePage, branding, headerActions,\n LinkComponent, header,\n ]);\n\n // Build the overrides object ONCE per `overrides` prop change. The\n // header slot is the stable component declared at module scope —\n // brand swaps don't invalidate the reference.\n const mergedOverrides = useMemo(\n () => buildStableOverrides(overrides),\n [overrides],\n );\n\n if (loading || !data) {\n return (\n <div className=\"psd-builder-page psd-builder-page--loading\">\n <div className=\"psd-builder-loading\">Loading editor…</div>\n </div>\n );\n }\n\n return (\n <PageStudioProvider value={studio}>\n <HeaderPropsContext.Provider value={headerLive}>\n <div className=\"psd-builder-page\">\n <BuilderEnhancements\n blocksLabel={sidebarLabels?.blocks}\n layersLabel={sidebarLabels?.layers}\n searchPlaceholder={sidebarLabels?.search}\n />\n <Puck\n config={resolvedConfig}\n data={data}\n overrides={mergedOverrides}\n onPublish={handlePublish}\n iframe={iframe}\n plugins={[PSD_LEGACY_SIDEBAR]}\n />\n </div>\n </HeaderPropsContext.Provider>\n </PageStudioProvider>\n );\n}\n","'use client';\n\n// Post-mount DOM enhancements for Puck's left sidebar:\n// 1. Hide Puck's \"Components\" / \"Outline\" section titles\n// 2. Inject a tab bar at the top that toggles between the two sections\n// 3. Inject a search input that filters the block cards by name + summary\n// 4. Append a count badge to each component-category header (visible-only)\n//\n// IMPORTANT: every DOM mutation we make MUST be idempotent (no-op when the\n// target state already matches) AND we disconnect the MutationObserver\n// while applying so our own writes don't feed back into the observer and\n// cause an infinite loop / tab freeze.\n\nimport { useEffect } from 'react';\n\nconst STORAGE_KEY = 'psd.builderTab';\n\nexport default function BuilderEnhancements({\n blocksLabel = 'Blocks',\n layersLabel = 'Layers',\n searchPlaceholder = 'Search blocks',\n} = {}) {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n let observer = null;\n // Search query persists across re-renders even if Puck wipes the sidebar\n // and we have to re-inject the input. Lives in this closure (per mount).\n let query = '';\n\n // Filtering is done via attributes on elements WE own (.tps-block-card\n // and _ComponentList_ groups). CSS in editor/styles.css hides the\n // matching Puck wrapper via :has(). Writing only attributes — not\n // inline styles on Puck's wrappers — keeps us out of a fight with\n // Puck's React reconciler when it re-renders the picker after a drop.\n const matchesQuery = (card, q) => {\n if (!q) return true;\n const name = (card.querySelector('.tps-block-card__name')?.textContent || '').toLowerCase();\n const desc = (card.querySelector('.tps-block-card__desc')?.textContent || '').toLowerCase();\n return name.includes(q) || desc.includes(q);\n };\n\n const setAttrIfChanged = (el, name, value) => {\n const current = el.getAttribute(name);\n if (value == null) {\n if (current !== null) el.removeAttribute(name);\n } else if (current !== value) {\n el.setAttribute(name, value);\n }\n };\n\n const updateCounts = (sidebar) => {\n const headers = sidebar.querySelectorAll('[class*=\"_ComponentList-title_\"]');\n headers.forEach((header) => {\n const parent = header.closest('[class*=\"_ComponentList_\"]');\n if (!parent) return;\n const list = parent.querySelector('[class*=\"_ComponentList-content_\"]');\n if (!list) return;\n const cards = list.querySelectorAll('.tps-block-card');\n const visible = Array.from(cards).filter(\n (c) => c.getAttribute('data-psd-hidden') !== 'true',\n ).length;\n const count = String(visible);\n let badge = header.querySelector('.psd-cat-count');\n if (!badge) {\n badge = document.createElement('span');\n badge.className = 'psd-cat-count';\n badge.textContent = count;\n const chevron = header.querySelector('[class*=\"_ComponentList-titleIcon_\"]');\n if (chevron) {\n header.insertBefore(badge, chevron);\n } else {\n header.appendChild(badge);\n }\n } else if (badge.textContent !== count) {\n badge.textContent = count;\n }\n });\n };\n\n const applyFilter = (sidebar) => {\n const q = query.trim().toLowerCase();\n const cards = sidebar.querySelectorAll('.tps-block-card');\n cards.forEach((card) => {\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(card, 'data-psd-hidden', hidden ? 'true' : null);\n });\n\n // Mark the actual grid cell so it collapses out of layout — not just\n // our inner card. Puck's <DrawerItem> renders an unnamed <div> as the\n // direct child of [data-puck-drawer]; that <div> IS the grid cell.\n // Hiding only the .tps-block-card or any class-bearing inner wrapper\n // leaves the cell empty so visible matches stay in their original\n // grid slots (which is what the user was seeing). We tag the cell\n // directly here so a single CSS attribute selector can collapse it.\n const drawers = sidebar.querySelectorAll('[data-puck-drawer]');\n drawers.forEach((drawer) => {\n Array.from(drawer.children).forEach((cell) => {\n const card = cell.querySelector('.tps-block-card');\n if (!card) {\n setAttrIfChanged(cell, 'data-psd-cell-hidden', null);\n return;\n }\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(cell, 'data-psd-cell-hidden', hidden ? 'true' : null);\n });\n });\n\n // Hide entire category groups when all of their cards are filtered out.\n const groups = sidebar.querySelectorAll('[class*=\"_ComponentList_\"]');\n groups.forEach((g) => {\n const items = g.querySelectorAll('.tps-block-card');\n if (!items.length) {\n setAttrIfChanged(g, 'data-psd-empty', null);\n return;\n }\n const allHidden = Array.from(items).every(\n (c) => c.getAttribute('data-psd-hidden') === 'true',\n );\n setAttrIfChanged(g, 'data-psd-empty', q && allHidden ? 'true' : null);\n });\n\n // Counts must refresh on every filter change AND every drag/drop\n // (which adds/removes cards). Call from here so the search-input\n // handler also gets badge updates without re-running apply().\n updateCounts(sidebar);\n };\n\n const apply = () => {\n const sidebar = document.querySelector('[class*=\"_Sidebar--left\"]');\n if (!sidebar) return;\n\n const sections = sidebar.querySelectorAll('[class*=\"_SidebarSection_\"]');\n if (sections.length < 2) return;\n\n sections.forEach((s) => {\n const isComponents = !!s.querySelector('[class*=\"_ComponentList_\"]');\n const want = isComponents ? 'components' : 'outline';\n if (s.getAttribute('data-psd-section') !== want) {\n s.setAttribute('data-psd-section', want);\n }\n });\n\n sections.forEach((s) => {\n const title = s.querySelector('[class*=\"_SidebarSection-title_\"]');\n if (title && !title.hasAttribute('data-psd-hidden-title')) {\n title.setAttribute('data-psd-hidden-title', 'true');\n }\n });\n\n let tabs = sidebar.querySelector('.psd-sidebar-tabs');\n if (!tabs) {\n tabs = document.createElement('div');\n tabs.className = 'psd-sidebar-tabs';\n tabs.innerHTML = `\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"components\">${blocksLabel}</button>\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"outline\">${layersLabel}</button>\n `;\n sidebar.insertBefore(tabs, sidebar.firstChild);\n\n const setActive = (which) => {\n if (sidebar.getAttribute('data-psd-active-tab') === which) return;\n sidebar.setAttribute('data-psd-active-tab', which);\n try { sessionStorage.setItem(STORAGE_KEY, which); } catch {}\n tabs.querySelectorAll('button').forEach((b) => {\n const active = b.dataset.tab === which;\n if (b.classList.contains('is-active') !== active) {\n b.classList.toggle('is-active', active);\n }\n });\n };\n\n tabs.addEventListener('click', (e) => {\n const btn = e.target.closest('button[data-tab]');\n if (btn) setActive(btn.dataset.tab);\n });\n\n let initial = 'components';\n try { initial = sessionStorage.getItem(STORAGE_KEY) || 'components'; } catch {}\n setActive(initial);\n }\n\n // Search input — visible only when the components tab is active\n // (the [data-psd-active-tab=\"components\"] CSS gate does the toggling).\n let search = sidebar.querySelector('.psd-sidebar-search');\n if (!search) {\n search = document.createElement('div');\n search.className = 'psd-sidebar-search';\n const input = document.createElement('input');\n input.type = 'search';\n input.className = 'psd-sidebar-search__input';\n input.placeholder = searchPlaceholder;\n input.setAttribute('aria-label', searchPlaceholder);\n search.appendChild(input);\n // Place directly after the tab bar.\n if (tabs.nextSibling) sidebar.insertBefore(search, tabs.nextSibling);\n else sidebar.appendChild(search);\n\n input.addEventListener('input', () => {\n query = input.value || '';\n applyFilter(sidebar);\n });\n }\n // Re-sync the input value across Puck re-renders.\n const searchInput = search.querySelector('input');\n if (searchInput && searchInput.value !== query) searchInput.value = query;\n\n applyFilter(sidebar);\n };\n\n const safeApply = () => {\n if (observer) observer.disconnect();\n try { apply(); } catch { /* never break the host page */ }\n if (observer) observer.observe(document.body, { childList: true, subtree: true });\n };\n\n safeApply();\n\n let queued = false;\n observer = new MutationObserver(() => {\n if (queued) return;\n queued = true;\n requestAnimationFrame(() => {\n queued = false;\n safeApply();\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n if (observer) observer.disconnect();\n };\n }, [blocksLabel, layersLabel, searchPlaceholder]);\n\n return null;\n}\n","'use client';\n\n// Default top bar for the editor. Replaceable via the `header` prop on\n// <PageStudio /> — if you need full control, pass a render function.\n//\n// Designed to be brand-agnostic: every visible label/color comes from\n// `branding` or sensible neutrals. Account/home/publish are all optional;\n// hide what you don't need by leaving the corresponding prop unset.\n\nimport { Avatar, Button, ConfigProvider, Dropdown, Space, theme as antdTheme } from 'antd';\nimport {\n ArrowLeftOutlined,\n EyeOutlined,\n HomeOutlined,\n LogoutOutlined,\n PlusOutlined,\n RocketOutlined,\n UserOutlined,\n} from '@ant-design/icons';\n\nfunction DefaultLogo() {\n return (\n <svg width={20} height={20} viewBox=\"0 0 64 64\" aria-hidden>\n <rect width=\"64\" height=\"64\" rx=\"14\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nexport default function BuilderTopBar({\n pageKey,\n pageTitle,\n account,\n livePath,\n savedAt,\n pending,\n onPublish,\n onSignOut,\n onCreatePage,\n homeHref,\n branding = {},\n extraActions,\n LinkComponent = 'a',\n}) {\n const brand = {\n name: 'Page Studio',\n logo: <DefaultLogo />,\n primaryColor: '#0F766E',\n ...branding,\n };\n const Link = LinkComponent;\n\n const accountMenu = account\n ? {\n items: [\n {\n key: 'who',\n disabled: true,\n label: (\n <div style={{ padding: '4px 0', minWidth: 200 }}>\n <div style={{ fontSize: 13, fontWeight: 600, color: '#0f172a' }}>\n {account.name}\n </div>\n {account.email && account.email !== account.name && (\n <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 2 }}>\n {account.email}\n </div>\n )}\n </div>\n ),\n },\n { type: 'divider' },\n homeHref && {\n key: 'home',\n icon: <HomeOutlined />,\n label: <Link href={homeHref}>Admin home</Link>,\n },\n onSignOut && {\n key: 'signout',\n icon: <LogoutOutlined />,\n label: (\n <button\n type=\"button\"\n onClick={onSignOut}\n style={{ all: 'unset', cursor: 'pointer', width: '100%', display: 'block' }}\n >\n Sign out\n </button>\n ),\n },\n ].filter(Boolean),\n }\n : null;\n\n // Locally re-theme AntD so the Publish button + avatar pick up the\n // configured brand colors regardless of the host's ConfigProvider — the\n // top bar should look like the brand, not like the surrounding admin.\n const brandTheme = {\n algorithm: antdTheme.defaultAlgorithm,\n token: {\n colorPrimary: brand.primaryColor,\n colorInfo: brand.primaryColor,\n },\n };\n\n return (\n <ConfigProvider theme={brandTheme}>\n <div className=\"psd-builder-bar\" style={{ color: '#fff' }}>\n {homeHref ? (\n <Link\n href={homeHref}\n className=\"psd-builder-bar__brand\"\n title=\"Back to admin\"\n style={{ color: 'inherit' }}\n >\n <ArrowLeftOutlined style={{ fontSize: 12 }} />\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </Link>\n ) : (\n <div className=\"psd-builder-bar__brand\">\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </div>\n )}\n\n <div className=\"psd-builder-bar__crumbs\">\n <span className=\"psd-builder-bar__current\">{pageTitle || pageKey}</span>\n {pageKey && (\n <span style={{ marginLeft: 8, opacity: 0.5, fontSize: 11 }}>\n <code style={{ fontSize: 10 }}>{pageKey}</code>\n </span>\n )}\n {savedAt && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>\n · Saved {new Date(savedAt).toLocaleTimeString()}\n </span>\n )}\n {pending && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>· Saving…</span>\n )}\n </div>\n\n <Space size={6} className=\"psd-builder-bar__actions\">\n {extraActions}\n {onCreatePage && (\n <Button size=\"small\" icon={<PlusOutlined />} onClick={onCreatePage}>\n New page\n </Button>\n )}\n {livePath && (\n <Link href={livePath} target=\"_blank\" rel=\"noreferrer\">\n <Button size=\"small\" icon={<EyeOutlined />}>\n View live\n </Button>\n </Link>\n )}\n <Button\n type=\"primary\"\n size=\"small\"\n icon={<RocketOutlined />}\n onClick={onPublish}\n loading={pending}\n >\n Publish\n </Button>\n {account && accountMenu && (\n <Dropdown menu={accountMenu} placement=\"bottomRight\">\n <Button type=\"text\" size=\"small\" style={{ color: '#fff' }}>\n <Avatar\n size={22}\n icon={<UserOutlined />}\n style={{ background: brand.primaryColor }}\n />\n </Button>\n </Dropdown>\n )}\n </Space>\n </div>\n </ConfigProvider>\n );\n}\n","export { default as PageStudio } from './PageStudio.jsx';\nexport { default as BuilderEnhancements } from './BuilderEnhancements.jsx';\nexport { default as BuilderTopBar } from './BuilderTopBar.jsx';\n\n// Re-export the blocks package's context so consumers don't need a separate\n// import to wrap their public-site renderer with the same studio config.\nexport {\n PageStudioProvider,\n useStudio,\n createPuckConfig,\n defaultBlocks,\n defaultCategories,\n defaultOverrides,\n emptyPuckData,\n} from '@techrox/page-studio-blocks';\n"],"mappings":"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","names":["useEffect","jsx","jsxs","useEffect","PageStudioProvider","createPuckConfig","defaultOverrides","emptyPuckData"]}
1
+ {"version":3,"sources":["../src/PageStudio.jsx","../src/BuilderEnhancements.jsx","../src/BuilderTopBar.jsx","../src/index.js"],"sourcesContent":["'use client';\n\n// PageStudio — the editor entry point consumers render. Wraps Puck with:\n// - an adapter for persistence (loadPage / savePage / onCreatePage)\n// - a StudioProvider that injects host primitives (Link, services, …)\n// into the block tree\n// - a replaceable top bar (header prop) with sane brand-agnostic defaults\n// - sidebar enhancements (Blocks / Layers tabs, count badges)\n//\n// Everything except `pageKey` is optional. Passing only `pageKey` works —\n// you'll get the default block library, default top bar, and a console\n// warning when Publish is clicked without an adapter wired.\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useState,\n useTransition,\n} from 'react';\nimport { Puck, useGetPuck, legacySideBarPlugin } from '@puckeditor/core';\nimport { App as AntdApp } from 'antd';\n\nimport {\n applyConfigDefaults,\n createPuckConfig,\n defaultOverrides,\n emptyPuckData,\n normalizePuckData,\n PageStudioProvider,\n} from '@techrox/page-studio-blocks';\n\nimport BuilderEnhancements from './BuilderEnhancements.jsx';\nimport DefaultTopBar from './BuilderTopBar.jsx';\n\nimport '@puckeditor/core/puck.css';\n\n// 0.21 ships a new side navigation rail. Our BuilderEnhancements observes\n// the legacy `_Sidebar--left` layout, so keep that DOM by applying the\n// legacy plugin once at module load.\nconst PSD_LEGACY_SIDEBAR = legacySideBarPlugin();\n\n// The header override is passed to Puck via `overrides.header`. Puck treats\n// the value as a component type — when its reference changes, React diffs\n// and unmounts the old override before mounting the new one. During that\n// gap Puck briefly paints its default top bar, which flashes a stock\n// Publish button on screen. To avoid that we keep the override component\n// reference stable for the entire lifetime of the editor and pipe live\n// props (branding, savedAt, callbacks, etc.) through a context provider\n// rendered by PageStudio. Live updates flow as normal context updates —\n// the component never gets unmounted.\nconst HeaderPropsContext = createContext(null);\n\nfunction StableHeaderOverride() {\n const getPuck = useGetPuck();\n const live = useContext(HeaderPropsContext);\n if (!live) return null;\n const props = {\n pageKey: live.pageKey,\n pageTitle: live.pageTitle,\n account: live.account,\n livePath: live.livePath,\n homeHref: live.homeHref,\n savedAt: live.savedAt,\n pending: live.pending,\n onPublish: () => live.handlePublish(getPuck().appState.data),\n onSignOut: live.onSignOut,\n onCreatePage: live.onCreatePage,\n branding: live.branding,\n extraActions: live.headerActions,\n LinkComponent: live.LinkComponent,\n };\n if (typeof live.header === 'function') return live.header(props);\n if (live.header) return live.header;\n return <DefaultTopBar {...props} />;\n}\n\n// `overrides.header` is referentially stable so Puck never remounts the\n// override. The internal stack also stabilizes `headerActions: () => null`\n// — that's how we suppress Puck's own actions area.\nconst NO_OP_ACTIONS = () => null;\nfunction buildStableOverrides(hostOverrides) {\n return { ...hostOverrides, header: StableHeaderOverride, headerActions: NO_OP_ACTIONS };\n}\n\n// Apply brand colors + theme tokens by injecting a single <style id=\"tps-brand-vars\">\n// into <head>. Its `:root { --tps-*: … }` rule themes BOTH surfaces: the outer\n// editor chrome reads it directly, and Puck clones <head> <style>/<link> nodes\n// into its canvas iframe (keeping them in sync via a MutationObserver), so the\n// blocks pick up the same tokens.\n//\n// Do NOT also write these tokens as inline styles on document.documentElement.\n// It looks harmless — inline styles on the outer <html> don't \"leak\" into the\n// iframe through the cascade — but Puck SNAPSHOTS the outer <html>'s style\n// attribute onto the iframe's <html> ONCE at canvas mount and never re-syncs it.\n// On a later theme flip the head <style> updates (and re-clones), but the iframe's\n// inline <html> tokens stay frozen at their mount-time values. Inline styles beat\n// every stylesheet rule, so those stale light tokens override the synced dark\n// :root rule — the chrome goes dark while the canvas stays light. Keeping tokens\n// in the <style> tag only (no inline) lets the synced :root rule win cleanly.\n//\n// We write to `--tps-*` because that's what the block library reads. The host\n// page may also paint these vars on a wrapping element (e.g. a brand-switcher's\n// `[data-brand]` div), but that element doesn't exist inside Puck's iframe — so\n// without this :root injection the canvas falls back to the package defaults.\nfunction setCssVars(branding) {\n if (typeof document === 'undefined' || !branding) return;\n\n const lines = [];\n if (branding.primaryColor) lines.push(`--tps-primary: ${branding.primaryColor};`);\n if (branding.accentColor) lines.push(`--tps-accent: ${branding.accentColor};`);\n if (branding.inkColor) lines.push(`--tps-ink: ${branding.inkColor};`);\n\n // Arbitrary theme tokens — a flat { '--tps-bg': '#141414', … } map. This is\n // how a host flips the whole canvas to a non-default theme (dark, sepia, …):\n // the iframe only inherits <head> :root rules, not a `data-tps-theme`\n // attribute on the host wrapper, so we write the resolved token values\n // straight into :root. The host picks the set; the package owns the values.\n const cssVars = branding.cssVars || {};\n for (const [k, v] of Object.entries(cssVars)) {\n if (v == null) continue;\n lines.push(`${k}: ${v};`);\n }\n if (!lines.length) return;\n\n // Paint the canvas backdrop from the theme's section token when supplied, so\n // plain (transparent) sections sit on the themed surface inside the iframe.\n const bg = cssVars['--tps-bg-section'];\n const canvas = bg ? ` html { background: ${bg}; }` : '';\n const next = `:root { ${lines.join(' ')} }${canvas}`;\n\n const existing = document.getElementById('tps-brand-vars');\n if (existing && existing.textContent === next) return;\n\n // Replace the node — do NOT just edit textContent. Puck clones <head> styles\n // into the canvas iframe and keeps them in sync with a MutationObserver that\n // only reacts to childList changes (nodes added/removed), not characterData.\n // Editing an existing tag's text therefore never reaches the iframe, so the\n // canvas keeps the theme it mounted with while the chrome (which reads\n // data-tps-theme directly, outside the iframe) flips — the exact split where\n // sidebars go dark but the blocks stay light. Removing + re-appending a fresh\n // node fires a childList mutation, so Puck re-clones the new tokens and the\n // canvas flips with the chrome.\n if (existing) existing.remove();\n const tag = document.createElement('style');\n tag.id = 'tps-brand-vars';\n tag.textContent = next;\n document.head.appendChild(tag);\n}\n\nexport default function PageStudio({\n pageKey,\n initialData,\n pageTitle,\n account,\n livePath,\n homeHref,\n branding,\n // Active editor theme — 'light' | 'dark' | any future token-scope name.\n // Stamped as data-tps-theme on the editor root so the whole chrome (top\n // bar, sidebars, field panel, inputs) re-paints through the CSS cascade,\n // alongside the canvas (which is themed via branding.cssVars). Falls back\n // to branding.theme so a host that only passes branding still themes.\n theme,\n studio,\n adapter = {},\n config,\n blockDefaults,\n overrides = defaultOverrides,\n header,\n headerActions,\n onSignOut,\n sidebarLabels,\n LinkComponent,\n // Forwarded to Puck. Defaults to iframe enabled — Puck v0.20 mounts the\n // @dnd-kit context inside the canvas iframe; with the iframe disabled,\n // strict-mode double-mount and Vite HMR can leave the canvas with no live\n // drop targets, so dropped blocks vanish into an empty content array.\n // Hosts that need the canvas to share the host document (e.g. to inherit\n // global CSS without copying it across) can pass { enabled: false }.\n iframe = { enabled: true },\n}) {\n const { message } = AntdApp.useApp();\n const [pending, startTransition] = useTransition();\n const [savedAt, setSavedAt] = useState(null);\n\n // Resolve the config ONCE on first mount and freeze the reference. Puck\n // treats `config` as a structural prop — passing a new object on every\n // brand switch makes it re-diff its block registry, drop @dnd-kit's\n // collision cache and rebuild a chunk of internal memos. That's the\n // bulk of what makes brand switching feel sluggish.\n //\n // Trade-off: a `blockDefaults` change after mount (e.g. host swapping\n // tenant defaults mid-edit) no longer updates the per-block defaults\n // applied to newly dropped blocks — those will use whatever defaults\n // were active at first mount. CSS vars (color, ink, accent) still\n // update live via setCssVars. For the showcase that's exactly what we\n // want: brand swaps repaint colors instantly without churning Puck.\n // Hosts that genuinely need brand-aware drop defaults should remount\n // PageStudio with a fresh key when they swap tenants.\n const [resolvedConfig] = useState(\n () => config || createPuckConfig({ defaults: blockDefaults }),\n );\n\n const [data, setData] = useState(() =>\n initialData && Array.isArray(initialData.content)\n ? applyConfigDefaults(normalizePuckData(initialData), resolvedConfig)\n : null,\n );\n const [loading, setLoading] = useState(!data && !!adapter.loadPage);\n\n // Apply brand CSS variables synchronously during render — before Puck\n // mounts its iframe and before the first paint. Running this in useEffect\n // makes the canvas paint once with the package-default teal and then\n // repaint when the effect commits, producing a visible colour flash on\n // every load. The function is idempotent (it only writes when the head\n // <style> content actually differs) so repeated calls during strict-mode\n // double-render are a no-op.\n setCssVars(branding);\n\n // Single external theme switch for the editor chrome. The canvas gets its\n // theme from branding.cssVars (the iframe can't see this attribute); the\n // chrome — top bar, both sidebars, the field panel and its inputs — gets\n // it from data-tps-theme on the root below. Default 'light'.\n const resolvedTheme = theme || branding?.theme || 'light';\n\n // Initial load when initialData wasn't server-supplied. Hosts that SSR\n // their CMS read should always pass initialData and skip this code path.\n useEffect(() => {\n if (data || !adapter.loadPage) return;\n let cancelled = false;\n setLoading(true);\n adapter.loadPage(pageKey)\n .then((loaded) => {\n if (cancelled) return;\n setData(\n loaded && Array.isArray(loaded.content)\n ? applyConfigDefaults(normalizePuckData(loaded), resolvedConfig)\n : emptyPuckData,\n );\n })\n .catch((err) => {\n if (cancelled) return;\n message.error(err?.message || 'Could not load page.');\n setData(emptyPuckData);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [pageKey, adapter, data, message]);\n\n const handlePublish = (nextData) => {\n if (!adapter.savePage) {\n message.error('No savePage adapter configured.');\n return;\n }\n startTransition(async () => {\n try {\n await adapter.savePage(pageKey, nextData);\n setSavedAt(new Date().toISOString());\n message.success('Published.');\n } catch (err) {\n message.error(err?.message || 'Save failed.');\n }\n });\n };\n\n // Live values consumed by the stable header override via context. The\n // context VALUE may change every render — that's fine, it triggers a\n // normal re-render of StableHeaderOverride. The override COMPONENT\n // reference (passed to Puck via overrides.header) never changes, so\n // Puck never unmounts it. That's what kills the default-button flash\n // on brand switch.\n const headerLive = useMemo(() => ({\n pageKey,\n pageTitle,\n account,\n livePath,\n homeHref,\n savedAt,\n pending,\n handlePublish,\n onSignOut,\n onCreatePage: adapter.onCreatePage,\n branding,\n headerActions,\n LinkComponent,\n header,\n }), [\n pageKey, pageTitle, account, livePath, homeHref, savedAt, pending,\n handlePublish, onSignOut, adapter.onCreatePage, branding, headerActions,\n LinkComponent, header,\n ]);\n\n // Build the overrides object ONCE per `overrides` prop change. The\n // header slot is the stable component declared at module scope —\n // brand swaps don't invalidate the reference.\n const mergedOverrides = useMemo(\n () => buildStableOverrides(overrides),\n [overrides],\n );\n\n if (loading || !data) {\n return (\n <div\n className=\"psd-builder-page psd-builder-page--loading\"\n data-tps-theme={resolvedTheme}\n >\n <div className=\"psd-builder-loading\">Loading editor…</div>\n </div>\n );\n }\n\n return (\n <PageStudioProvider value={studio}>\n <HeaderPropsContext.Provider value={headerLive}>\n <div className=\"psd-builder-page\" data-tps-theme={resolvedTheme}>\n <BuilderEnhancements\n blocksLabel={sidebarLabels?.blocks}\n layersLabel={sidebarLabels?.layers}\n searchPlaceholder={sidebarLabels?.search}\n />\n <Puck\n config={resolvedConfig}\n data={data}\n overrides={mergedOverrides}\n onPublish={handlePublish}\n iframe={iframe}\n plugins={[PSD_LEGACY_SIDEBAR]}\n />\n </div>\n </HeaderPropsContext.Provider>\n </PageStudioProvider>\n );\n}\n","'use client';\n\n// Post-mount DOM enhancements for Puck's left sidebar:\n// 1. Hide Puck's \"Components\" / \"Outline\" section titles\n// 2. Inject a tab bar at the top that toggles between the two sections\n// 3. Inject a search input that filters the block cards by name + summary\n// 4. Append a count badge to each component-category header (visible-only)\n//\n// IMPORTANT: every DOM mutation we make MUST be idempotent (no-op when the\n// target state already matches) AND we disconnect the MutationObserver\n// while applying so our own writes don't feed back into the observer and\n// cause an infinite loop / tab freeze.\n\nimport { useEffect } from 'react';\n\nconst STORAGE_KEY = 'psd.builderTab';\n\nexport default function BuilderEnhancements({\n blocksLabel = 'Blocks',\n layersLabel = 'Layers',\n searchPlaceholder = 'Search blocks',\n} = {}) {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n let observer = null;\n // Search query persists across re-renders even if Puck wipes the sidebar\n // and we have to re-inject the input. Lives in this closure (per mount).\n let query = '';\n\n // Filtering is done via attributes on elements WE own (.tps-block-card\n // and _ComponentList_ groups). CSS in editor/styles.css hides the\n // matching Puck wrapper via :has(). Writing only attributes — not\n // inline styles on Puck's wrappers — keeps us out of a fight with\n // Puck's React reconciler when it re-renders the picker after a drop.\n const matchesQuery = (card, q) => {\n if (!q) return true;\n const name = (card.querySelector('.tps-block-card__name')?.textContent || '').toLowerCase();\n const desc = (card.querySelector('.tps-block-card__desc')?.textContent || '').toLowerCase();\n return name.includes(q) || desc.includes(q);\n };\n\n const setAttrIfChanged = (el, name, value) => {\n const current = el.getAttribute(name);\n if (value == null) {\n if (current !== null) el.removeAttribute(name);\n } else if (current !== value) {\n el.setAttribute(name, value);\n }\n };\n\n const updateCounts = (sidebar) => {\n const headers = sidebar.querySelectorAll('[class*=\"_ComponentList-title_\"]');\n headers.forEach((header) => {\n const parent = header.closest('[class*=\"_ComponentList_\"]');\n if (!parent) return;\n const list = parent.querySelector('[class*=\"_ComponentList-content_\"]');\n if (!list) return;\n const cards = list.querySelectorAll('.tps-block-card');\n const visible = Array.from(cards).filter(\n (c) => c.getAttribute('data-psd-hidden') !== 'true',\n ).length;\n const count = String(visible);\n let badge = header.querySelector('.psd-cat-count');\n if (!badge) {\n badge = document.createElement('span');\n badge.className = 'psd-cat-count';\n badge.textContent = count;\n const chevron = header.querySelector('[class*=\"_ComponentList-titleIcon_\"]');\n if (chevron) {\n header.insertBefore(badge, chevron);\n } else {\n header.appendChild(badge);\n }\n } else if (badge.textContent !== count) {\n badge.textContent = count;\n }\n });\n };\n\n const applyFilter = (sidebar) => {\n const q = query.trim().toLowerCase();\n const cards = sidebar.querySelectorAll('.tps-block-card');\n cards.forEach((card) => {\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(card, 'data-psd-hidden', hidden ? 'true' : null);\n });\n\n // Mark the actual grid cell so it collapses out of layout — not just\n // our inner card. Puck's <DrawerItem> renders an unnamed <div> as the\n // direct child of [data-puck-drawer]; that <div> IS the grid cell.\n // Hiding only the .tps-block-card or any class-bearing inner wrapper\n // leaves the cell empty so visible matches stay in their original\n // grid slots (which is what the user was seeing). We tag the cell\n // directly here so a single CSS attribute selector can collapse it.\n const drawers = sidebar.querySelectorAll('[data-puck-drawer]');\n drawers.forEach((drawer) => {\n Array.from(drawer.children).forEach((cell) => {\n const card = cell.querySelector('.tps-block-card');\n if (!card) {\n setAttrIfChanged(cell, 'data-psd-cell-hidden', null);\n return;\n }\n const hidden = !matchesQuery(card, q);\n setAttrIfChanged(cell, 'data-psd-cell-hidden', hidden ? 'true' : null);\n });\n });\n\n // Hide entire category groups when all of their cards are filtered out.\n const groups = sidebar.querySelectorAll('[class*=\"_ComponentList_\"]');\n groups.forEach((g) => {\n const items = g.querySelectorAll('.tps-block-card');\n if (!items.length) {\n setAttrIfChanged(g, 'data-psd-empty', null);\n return;\n }\n const allHidden = Array.from(items).every(\n (c) => c.getAttribute('data-psd-hidden') === 'true',\n );\n setAttrIfChanged(g, 'data-psd-empty', q && allHidden ? 'true' : null);\n });\n\n // Counts must refresh on every filter change AND every drag/drop\n // (which adds/removes cards). Call from here so the search-input\n // handler also gets badge updates without re-running apply().\n updateCounts(sidebar);\n };\n\n const apply = () => {\n const sidebar = document.querySelector('[class*=\"_Sidebar--left\"]');\n if (!sidebar) return;\n\n const sections = sidebar.querySelectorAll('[class*=\"_SidebarSection_\"]');\n if (sections.length < 2) return;\n\n sections.forEach((s) => {\n const isComponents = !!s.querySelector('[class*=\"_ComponentList_\"]');\n const want = isComponents ? 'components' : 'outline';\n if (s.getAttribute('data-psd-section') !== want) {\n s.setAttribute('data-psd-section', want);\n }\n });\n\n sections.forEach((s) => {\n const title = s.querySelector('[class*=\"_SidebarSection-title_\"]');\n if (title && !title.hasAttribute('data-psd-hidden-title')) {\n title.setAttribute('data-psd-hidden-title', 'true');\n }\n });\n\n let tabs = sidebar.querySelector('.psd-sidebar-tabs');\n if (!tabs) {\n tabs = document.createElement('div');\n tabs.className = 'psd-sidebar-tabs';\n tabs.innerHTML = `\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"components\">${blocksLabel}</button>\n <button type=\"button\" class=\"psd-sidebar-tab\" data-tab=\"outline\">${layersLabel}</button>\n `;\n sidebar.insertBefore(tabs, sidebar.firstChild);\n\n const setActive = (which) => {\n if (sidebar.getAttribute('data-psd-active-tab') === which) return;\n sidebar.setAttribute('data-psd-active-tab', which);\n try { sessionStorage.setItem(STORAGE_KEY, which); } catch {}\n tabs.querySelectorAll('button').forEach((b) => {\n const active = b.dataset.tab === which;\n if (b.classList.contains('is-active') !== active) {\n b.classList.toggle('is-active', active);\n }\n });\n };\n\n tabs.addEventListener('click', (e) => {\n const btn = e.target.closest('button[data-tab]');\n if (btn) setActive(btn.dataset.tab);\n });\n\n let initial = 'components';\n try { initial = sessionStorage.getItem(STORAGE_KEY) || 'components'; } catch {}\n setActive(initial);\n }\n\n // Search input — visible only when the components tab is active\n // (the [data-psd-active-tab=\"components\"] CSS gate does the toggling).\n let search = sidebar.querySelector('.psd-sidebar-search');\n if (!search) {\n search = document.createElement('div');\n search.className = 'psd-sidebar-search';\n const input = document.createElement('input');\n input.type = 'search';\n input.className = 'psd-sidebar-search__input';\n input.placeholder = searchPlaceholder;\n input.setAttribute('aria-label', searchPlaceholder);\n search.appendChild(input);\n // Place directly after the tab bar.\n if (tabs.nextSibling) sidebar.insertBefore(search, tabs.nextSibling);\n else sidebar.appendChild(search);\n\n input.addEventListener('input', () => {\n query = input.value || '';\n applyFilter(sidebar);\n });\n }\n // Re-sync the input value across Puck re-renders.\n const searchInput = search.querySelector('input');\n if (searchInput && searchInput.value !== query) searchInput.value = query;\n\n applyFilter(sidebar);\n };\n\n const safeApply = () => {\n if (observer) observer.disconnect();\n try { apply(); } catch { /* never break the host page */ }\n if (observer) observer.observe(document.body, { childList: true, subtree: true });\n };\n\n safeApply();\n\n let queued = false;\n observer = new MutationObserver(() => {\n if (queued) return;\n queued = true;\n requestAnimationFrame(() => {\n queued = false;\n safeApply();\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n\n return () => {\n if (observer) observer.disconnect();\n };\n }, [blocksLabel, layersLabel, searchPlaceholder]);\n\n return null;\n}\n","'use client';\n\n// Default top bar for the editor. Replaceable via the `header` prop on\n// <PageStudio /> — if you need full control, pass a render function.\n//\n// Designed to be brand-agnostic: every visible label/color comes from\n// `branding` or sensible neutrals. Account/home/publish are all optional;\n// hide what you don't need by leaving the corresponding prop unset.\n\nimport { Avatar, Button, ConfigProvider, Dropdown, Space, theme as antdTheme } from 'antd';\nimport {\n ArrowLeftOutlined,\n EyeOutlined,\n HomeOutlined,\n LogoutOutlined,\n PlusOutlined,\n RocketOutlined,\n UserOutlined,\n} from '@ant-design/icons';\n\nfunction DefaultLogo() {\n return (\n <svg width={20} height={20} viewBox=\"0 0 64 64\" aria-hidden>\n <rect width=\"64\" height=\"64\" rx=\"14\" fill=\"currentColor\" />\n </svg>\n );\n}\n\nexport default function BuilderTopBar({\n pageKey,\n pageTitle,\n account,\n livePath,\n savedAt,\n pending,\n onPublish,\n onSignOut,\n onCreatePage,\n homeHref,\n branding = {},\n extraActions,\n LinkComponent = 'a',\n}) {\n const brand = {\n name: 'Page Studio',\n logo: <DefaultLogo />,\n primaryColor: '#0F766E',\n ...branding,\n };\n const Link = LinkComponent;\n\n const accountMenu = account\n ? {\n items: [\n {\n key: 'who',\n disabled: true,\n label: (\n <div style={{ padding: '4px 0', minWidth: 200 }}>\n <div style={{ fontSize: 13, fontWeight: 600, color: '#0f172a' }}>\n {account.name}\n </div>\n {account.email && account.email !== account.name && (\n <div style={{ fontSize: 11, color: '#94a3b8', marginTop: 2 }}>\n {account.email}\n </div>\n )}\n </div>\n ),\n },\n { type: 'divider' },\n homeHref && {\n key: 'home',\n icon: <HomeOutlined />,\n label: <Link href={homeHref}>Admin home</Link>,\n },\n onSignOut && {\n key: 'signout',\n icon: <LogoutOutlined />,\n label: (\n <button\n type=\"button\"\n onClick={onSignOut}\n style={{ all: 'unset', cursor: 'pointer', width: '100%', display: 'block' }}\n >\n Sign out\n </button>\n ),\n },\n ].filter(Boolean),\n }\n : null;\n\n // Locally re-theme AntD so the Publish button + avatar pick up the\n // configured brand colors regardless of the host's ConfigProvider — the\n // top bar should look like the brand, not like the surrounding admin.\n const brandTheme = {\n algorithm: antdTheme.defaultAlgorithm,\n token: {\n colorPrimary: brand.primaryColor,\n colorInfo: brand.primaryColor,\n },\n };\n\n return (\n <ConfigProvider theme={brandTheme}>\n <div className=\"psd-builder-bar\" style={{ color: '#fff' }}>\n {homeHref ? (\n <Link\n href={homeHref}\n className=\"psd-builder-bar__brand\"\n title=\"Back to admin\"\n style={{ color: 'inherit' }}\n >\n <ArrowLeftOutlined style={{ fontSize: 12 }} />\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </Link>\n ) : (\n <div className=\"psd-builder-bar__brand\">\n <span aria-hidden style={{ color: brand.primaryColor, display: 'inline-flex' }}>\n {brand.logo}\n </span>\n <span>{brand.name}</span>\n </div>\n )}\n\n <div className=\"psd-builder-bar__crumbs\">\n <span className=\"psd-builder-bar__current\">{pageTitle || pageKey}</span>\n {pageKey && (\n <span style={{ marginLeft: 8, opacity: 0.5, fontSize: 11 }}>\n <code style={{ fontSize: 10 }}>{pageKey}</code>\n </span>\n )}\n {savedAt && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>\n · Saved {new Date(savedAt).toLocaleTimeString()}\n </span>\n )}\n {pending && (\n <span style={{ marginLeft: 12, fontSize: 11, opacity: 0.55 }}>· Saving…</span>\n )}\n </div>\n\n <Space size={6} className=\"psd-builder-bar__actions\">\n {extraActions}\n {onCreatePage && (\n <Button size=\"small\" icon={<PlusOutlined />} onClick={onCreatePage}>\n New page\n </Button>\n )}\n {livePath && (\n <Link href={livePath} target=\"_blank\" rel=\"noreferrer\">\n <Button size=\"small\" icon={<EyeOutlined />}>\n View live\n </Button>\n </Link>\n )}\n <Button\n type=\"primary\"\n size=\"small\"\n icon={<RocketOutlined />}\n onClick={onPublish}\n loading={pending}\n >\n Publish\n </Button>\n {account && accountMenu && (\n <Dropdown menu={accountMenu} placement=\"bottomRight\">\n <Button type=\"text\" size=\"small\" style={{ color: '#fff' }}>\n <Avatar\n size={22}\n icon={<UserOutlined />}\n style={{ background: brand.primaryColor }}\n />\n </Button>\n </Dropdown>\n )}\n </Space>\n </div>\n </ConfigProvider>\n );\n}\n","export { default as PageStudio } from './PageStudio.jsx';\nexport { default as BuilderEnhancements } from './BuilderEnhancements.jsx';\nexport { default as BuilderTopBar } from './BuilderTopBar.jsx';\n\n// Re-export the blocks package's context so consumers don't need a separate\n// import to wrap their public-site renderer with the same studio config.\nexport {\n PageStudioProvider,\n useStudio,\n createPuckConfig,\n defaultBlocks,\n defaultCategories,\n defaultOverrides,\n emptyPuckData,\n} from '@techrox/page-studio-blocks';\n"],"mappings":"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","names":["useEffect","jsx","jsxs","useEffect","PageStudioProvider","createPuckConfig","defaultOverrides","emptyPuckData"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techrox/page-studio",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Drag-and-drop visual page builder for React, built on Puck. Adapter-driven persistence, brand theming, sidebar block library, replaceable top bar.",
5
5
  "license": "MIT",
6
6
  "author": "techrox",
@@ -38,7 +38,7 @@
38
38
  "antd": ">=5",
39
39
  "react": ">=18",
40
40
  "react-dom": ">=18",
41
- "@techrox/page-studio-blocks": "1.0.0"
41
+ "@techrox/page-studio-blocks": "1.1.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@ant-design/icons": "^5.4.0",
@@ -46,7 +46,7 @@
46
46
  "antd": "^5.19.0",
47
47
  "react": "^18.3.1",
48
48
  "react-dom": "^18.3.1",
49
- "@techrox/page-studio-blocks": "1.0.0"
49
+ "@techrox/page-studio-blocks": "1.1.1"
50
50
  },
51
51
  "publishConfig": {
52
52
  "access": "public"
package/src/styles.css CHANGED
@@ -39,7 +39,10 @@
39
39
  gap: 16px;
40
40
  min-height: 48px;
41
41
  padding: 6px 16px;
42
- background: var(--tps-ink, #0f172a);
42
+ /* The builder bar stays a dark surface in every theme. It reads a chrome
43
+ * token (not --tps-ink) so it doesn't invert when the block ink flips for
44
+ * dark mode — the dark scope below just retargets --psd-bar-bg. */
45
+ background: var(--psd-bar-bg, var(--tps-ink, #0f172a));
43
46
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
44
47
  font-size: 13px;
45
48
  }
@@ -91,7 +94,7 @@
91
94
  display: flex;
92
95
  gap: 4px;
93
96
  padding: 12px 12px 0;
94
- background: #fff;
97
+ background: var(--tps-bg, #fff);
95
98
  border-bottom: 1px solid var(--tps-line, #e2e8f0);
96
99
  }
97
100
  .psd-sidebar-tab {
@@ -132,7 +135,7 @@
132
135
  top: 41px; /* below the .psd-sidebar-tabs (40px content + 1px border) */
133
136
  z-index: 4;
134
137
  padding: 10px 12px;
135
- background: #fff;
138
+ background: var(--tps-bg, #fff);
136
139
  border-bottom: 1px solid var(--tps-line, #e2e8f0);
137
140
  }
138
141
  [class*='_Sidebar--left'][data-psd-active-tab='outline'] > .psd-sidebar-search {
@@ -153,7 +156,7 @@
153
156
  }
154
157
  .psd-sidebar-search__input::placeholder { color: var(--tps-hint, #94a3b8); }
155
158
  .psd-sidebar-search__input:focus {
156
- background: #fff;
159
+ background: var(--tps-bg, #fff);
157
160
  border-color: var(--tps-primary, #0b60d8);
158
161
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--tps-primary, #0b60d8) 15%, transparent);
159
162
  }
@@ -176,6 +179,71 @@
176
179
  .tps-block-card[data-psd-hidden='true'] { display: none !important; }
177
180
  [class*='_ComponentList_'][data-psd-empty='true'] { display: none !important; }
178
181
 
182
+ /* -------------------------------------------------------------------------
183
+ Themes — editor chrome
184
+ -------------------------------------------------------------------------
185
+ PageStudio stamps `data-tps-theme="<name>"` on `.psd-builder-page` (the
186
+ element that wraps the whole editor). A theme is the same idea here as in
187
+ the block library: a re-scope of tokens, repainting descendants through
188
+ the CSS cascade — no per-component code.
189
+
190
+ Two token families are retargeted under the dark scope:
191
+ 1. --tps-* — our own chrome (top bar, sidebar tabs, search, badges).
192
+ 2. --puck-* — Puck's grey/azure ramp, defined on :root by puck.css.
193
+ Because `.psd-builder-page` sits between :root and every Puck panel,
194
+ redefining the ramp here re-themes Puck's left side nav, blocks
195
+ drawer, layers, the right field panel and ALL its inputs at once.
196
+
197
+ The canvas iframe is a separate document and never inherits this
198
+ attribute — it is themed through branding.cssVars (see PageStudio.jsx).
199
+
200
+ To add a theme (e.g. "sepia"): copy this block, rename the selector, and
201
+ change the values. Nothing in the editor components needs to know. */
202
+ [data-tps-theme="dark"].psd-builder-page,
203
+ [data-tps-theme="dark"] .psd-builder-page {
204
+ /* Chrome neutrals — mirrors @techrox/page-studio-blocks dark tokens so the
205
+ chrome self-themes from the attribute alone, even when a host passes no
206
+ branding.cssVars. */
207
+ --tps-ink: #f1f5f9;
208
+ --tps-ink-2: #cbd5e1;
209
+ --tps-muted: #94a3b8;
210
+ --tps-hint: #64748b;
211
+ --tps-line: #262626;
212
+ --tps-bg: #141414;
213
+ --tps-bg-soft: #1a1a1a;
214
+ --tps-bg-section: #0a0a0a;
215
+
216
+ /* The builder bar stays dark regardless of theme (see .psd-builder-bar). */
217
+ --psd-bar-bg: #0a0a0a;
218
+
219
+ /* Puck ramp — backgrounds (light end) flip to dark surfaces, the text end
220
+ flips to light, mid-greys stay readable on dark. */
221
+ --puck-color-white: #141414;
222
+ --puck-color-grey-12: #1a1a1a;
223
+ --puck-color-grey-11: #1f1f1f;
224
+ --puck-color-grey-10: #262626;
225
+ --puck-color-grey-09: #333333;
226
+ --puck-color-grey-08: #525252;
227
+ --puck-color-grey-07: #6b6b6b;
228
+ --puck-color-grey-06: #8a8a8a;
229
+ --puck-color-grey-05: #a3a3a3;
230
+ --puck-color-grey-04: #b5b5b5;
231
+ --puck-color-grey-03: #cfcfcf;
232
+ --puck-color-grey-02: #e2e2e2;
233
+ --puck-color-grey-01: #f1f1f1;
234
+ --puck-color-black: #f8fafc;
235
+
236
+ /* Azure light tints are used as selected-row backgrounds — keep them dark
237
+ so a selection doesn't flash near-white. The saturated end (01–08, used
238
+ for primary buttons / active accents) reads fine on dark, left as-is. */
239
+ --puck-color-azure-12: #0f1b2d;
240
+ --puck-color-azure-11: #11203a;
241
+ --puck-color-azure-10: #16294a;
242
+ --puck-color-azure-09: #1b3257;
243
+
244
+ background: var(--tps-bg-soft, #1a1a1a);
245
+ }
246
+
179
247
  /* Category count badges */
180
248
  .psd-cat-count {
181
249
  display: inline-flex;