@pro-laico/payload-dev-tools 0.0.0

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.
Files changed (91) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +9 -0
  3. package/dist/components/DevToolbar.d.ts +27 -0
  4. package/dist/components/DevToolbar.d.ts.map +1 -0
  5. package/dist/components/DevToolbar.js +32 -0
  6. package/dist/components/DevToolbar.js.map +1 -0
  7. package/dist/components/DevToolbarClient.d.ts +16 -0
  8. package/dist/components/DevToolbarClient.d.ts.map +1 -0
  9. package/dist/components/DevToolbarClient.js +983 -0
  10. package/dist/components/DevToolbarClient.js.map +1 -0
  11. package/dist/components/resolveDevChrome.d.ts +33 -0
  12. package/dist/components/resolveDevChrome.d.ts.map +1 -0
  13. package/dist/components/resolveDevChrome.js +42 -0
  14. package/dist/components/resolveDevChrome.js.map +1 -0
  15. package/dist/components/resolveDevChrome.spec.d.ts +2 -0
  16. package/dist/components/resolveDevChrome.spec.d.ts.map +1 -0
  17. package/dist/components/resolveDevChrome.spec.js +98 -0
  18. package/dist/components/resolveDevChrome.spec.js.map +1 -0
  19. package/dist/components/styles.d.ts +5 -0
  20. package/dist/components/styles.d.ts.map +1 -0
  21. package/dist/components/styles.js +110 -0
  22. package/dist/components/styles.js.map +1 -0
  23. package/dist/cookies.d.ts +12 -0
  24. package/dist/cookies.d.ts.map +1 -0
  25. package/dist/cookies.js +12 -0
  26. package/dist/cookies.js.map +1 -0
  27. package/dist/endpoints/activateIconSet.d.ts +10 -0
  28. package/dist/endpoints/activateIconSet.d.ts.map +1 -0
  29. package/dist/endpoints/activateIconSet.js +55 -0
  30. package/dist/endpoints/activateIconSet.js.map +1 -0
  31. package/dist/endpoints/dev.d.ts +12 -0
  32. package/dist/endpoints/dev.d.ts.map +1 -0
  33. package/dist/endpoints/dev.js +31 -0
  34. package/dist/endpoints/dev.js.map +1 -0
  35. package/dist/endpoints/stage.d.ts +14 -0
  36. package/dist/endpoints/stage.d.ts.map +1 -0
  37. package/dist/endpoints/stage.js +42 -0
  38. package/dist/endpoints/stage.js.map +1 -0
  39. package/dist/harness.d.ts +40 -0
  40. package/dist/harness.d.ts.map +1 -0
  41. package/dist/harness.js +30 -0
  42. package/dist/harness.js.map +1 -0
  43. package/dist/index.d.ts +7 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +9 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/lib/getPayloadClient.d.ts +15 -0
  48. package/dist/lib/getPayloadClient.d.ts.map +1 -0
  49. package/dist/lib/getPayloadClient.js +29 -0
  50. package/dist/lib/getPayloadClient.js.map +1 -0
  51. package/dist/lib/snapshot.d.ts +89 -0
  52. package/dist/lib/snapshot.d.ts.map +1 -0
  53. package/dist/lib/snapshot.js +183 -0
  54. package/dist/lib/snapshot.js.map +1 -0
  55. package/dist/next/client.d.ts +27 -0
  56. package/dist/next/client.d.ts.map +1 -0
  57. package/dist/next/client.js +275 -0
  58. package/dist/next/client.js.map +1 -0
  59. package/dist/next/createDevPage.d.ts +37 -0
  60. package/dist/next/createDevPage.d.ts.map +1 -0
  61. package/dist/next/createDevPage.js +287 -0
  62. package/dist/next/createDevPage.js.map +1 -0
  63. package/dist/next/pageStyles.d.ts +5 -0
  64. package/dist/next/pageStyles.d.ts.map +1 -0
  65. package/dist/next/pageStyles.js +83 -0
  66. package/dist/next/pageStyles.js.map +1 -0
  67. package/dist/next/specimen.d.ts +24 -0
  68. package/dist/next/specimen.d.ts.map +1 -0
  69. package/dist/next/specimen.js +48 -0
  70. package/dist/next/specimen.js.map +1 -0
  71. package/dist/next/views.d.ts +31 -0
  72. package/dist/next/views.d.ts.map +1 -0
  73. package/dist/next/views.js +431 -0
  74. package/dist/next/views.js.map +1 -0
  75. package/dist/next.d.ts +5 -0
  76. package/dist/next.d.ts.map +1 -0
  77. package/dist/next.js +6 -0
  78. package/dist/next.js.map +1 -0
  79. package/dist/options.d.ts +17 -0
  80. package/dist/options.d.ts.map +1 -0
  81. package/dist/options.js +3 -0
  82. package/dist/options.js.map +1 -0
  83. package/dist/plugin.d.ts +20 -0
  84. package/dist/plugin.d.ts.map +1 -0
  85. package/dist/plugin.js +51 -0
  86. package/dist/plugin.js.map +1 -0
  87. package/dist/toolbar.d.ts +8 -0
  88. package/dist/toolbar.d.ts.map +1 -0
  89. package/dist/toolbar.js +8 -0
  90. package/dist/toolbar.js.map +1 -0
  91. package/package.json +77 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/DevToolbarClient.tsx"],"sourcesContent":["'use client'\n\nimport Link from 'next/link'\nimport { usePathname, useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport { CHROME_COOKIES, type ChromeSlot, STAGE_COOKIE } from '../cookies'\nimport type { TestMeta } from '../harness'\nimport type { DevSnapshot } from '../lib/snapshot'\n\nexport type DevLink = { href: string; title: string }\n\ntype Corner = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'\ntype Size = 'sm' | 'md' | 'lg'\ntype Settings = { corner: Corner; size: Size }\ntype View = 'main' | 'info' | 'seed' | 'pages' | 'tests' | 'settings'\ntype SeedError = { error: string; issues?: string[] }\ntype StageSelection = { testKey: string; versionId: string }\n\nconst STORE_KEY = 'pdt-settings'\nconst HIDE_KEY = 'pdt-hidden'\nconst DEFAULTS: Settings = { corner: 'bottom-right', size: 'md' }\nconst CORNER_CLASS: Record<Corner, string> = { 'bottom-right': 'br', 'bottom-left': 'bl', 'top-right': 'tr', 'top-left': 'tl' }\nconst CORNERS: { value: Corner; arrow: string }[] = [\n { value: 'top-left', arrow: '↖' },\n { value: 'top-right', arrow: '↗' },\n { value: 'bottom-left', arrow: '↙' },\n { value: 'bottom-right', arrow: '↘' },\n]\nconst SIZES: Size[] = ['sm', 'md', 'lg']\nconst VIEW_TITLE: Record<Exclude<View, 'main'>, string> = { info: 'Info', seed: 'Seed', pages: 'Pages', tests: 'Tests', settings: 'Settings' }\n\nconst readSettings = (): Settings => {\n try {\n const parsed: unknown = JSON.parse(localStorage.getItem(STORE_KEY) ?? '{}')\n return parsed && typeof parsed === 'object' ? { ...DEFAULTS, ...parsed } : DEFAULTS\n } catch {\n return DEFAULTS\n }\n}\n\n/** A selection cookie (`testKey:versionId`), read client-side — the server reads the same cookie\n * (test pages for the stage cookie, `resolveDevChrome` for the chrome slots), so the toolbar\n * chips and the rendered result always agree. */\nconst readSelectionCookie = (name: string): StageSelection | null => {\n const raw = document.cookie\n .split('; ')\n .find((c) => c.startsWith(`${name}=`))\n ?.split('=')[1]\n if (!raw) return null\n try {\n const decoded = decodeURIComponent(raw)\n const sep = decoded.indexOf(':')\n return sep > 0 ? { testKey: decoded.slice(0, sep), versionId: decoded.slice(sep + 1) } : null\n } catch {\n return null\n }\n}\n\n/**\n * The toolbar panel — THE controller for the dev experience. Navigation happens through it (the\n * dev pages render content only, no nav of their own), the panel stays open across route changes\n * (it lives in the layout, and rows are client-side `<Link>`s), and the Tests view both opens a\n * test's page and toggles which version that page shows.\n */\nexport function DevToolbarClient({ tests, links }: { tests: TestMeta[]; links: DevLink[] }) {\n const router = useRouter()\n const pathname = usePathname()\n const [mounted, setMounted] = useState(false)\n const [hidden, setHidden] = useState(false)\n const [open, setOpen] = useState(false)\n const [view, setView] = useState<View>('main')\n const [settings, setSettings] = useState<Settings>(DEFAULTS)\n const [selection, setSelection] = useState<StageSelection | null>(null)\n const [chrome, setChrome] = useState<Record<ChromeSlot, StageSelection | null>>({ header: null, footer: null })\n const [snapshot, setSnapshot] = useState<DevSnapshot | null>(null)\n const [snapshotError, setSnapshotError] = useState(false)\n const [seeding, setSeeding] = useState(false)\n const [confirming, setConfirming] = useState(false)\n const [seedError, setSeedError] = useState<SeedError | null>(null)\n const rootRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n setHidden(sessionStorage.getItem(HIDE_KEY) === '1')\n setSettings(readSettings())\n setSelection(readSelectionCookie(STAGE_COOKIE))\n setChrome({ header: readSelectionCookie(CHROME_COOKIES.header), footer: readSelectionCookie(CHROME_COOKIES.footer) })\n setMounted(true)\n }, [])\n\n const fetchSnapshot = useCallback(async () => {\n try {\n const res = await fetch('/api/dev', { headers: { accept: 'application/json' }, credentials: 'include' })\n if (!res.ok) throw new Error(String(res.status))\n setSnapshot((await res.json()) as DevSnapshot)\n setSnapshotError(false)\n } catch {\n setSnapshotError(true)\n }\n }, [])\n\n // Lazy: the snapshot loads on first open, not on page load.\n useEffect(() => {\n if (open && !snapshot && !snapshotError) void fetchSnapshot()\n }, [open, snapshot, snapshotError, fetchSnapshot])\n\n const base = snapshot?.devRoute ?? '/dev'\n\n /** Select a version: write the cookie, then show it — refresh if we're already on the test's\n * page, navigate there if not. The panel stays open either way. */\n const selectVersion = useCallback(\n (testKey: string, versionId: string) => {\n // biome-ignore lint/suspicious/noDocumentCookie: dev-only synchronous cookie write; Cookie Store API is async and not universal\n document.cookie = `${STAGE_COOKIE}=${encodeURIComponent(`${testKey}:${versionId}`)}; path=/; samesite=lax`\n setSelection({ testKey, versionId })\n const testPath = `${base}/tests/${testKey}`\n if (pathname === testPath) router.refresh()\n else router.push(testPath)\n },\n [base, pathname, router],\n )\n\n /** Set/clear a chrome override (header/footer swap): the cookie applies SITE-WIDE in dev —\n * `resolveDevChrome` in the host layout swaps the variant in wherever you browse. */\n const selectChrome = useCallback(\n (slot: ChromeSlot, sel: StageSelection | null) => {\n // biome-ignore lint/suspicious/noDocumentCookie: dev-only synchronous cookie write; Cookie Store API is async and not universal\n document.cookie = sel\n ? `${CHROME_COOKIES[slot]}=${encodeURIComponent(`${sel.testKey}:${sel.versionId}`)}; path=/; samesite=lax`\n : `${CHROME_COOKIES[slot]}=; path=/; max-age=0; samesite=lax`\n setChrome((prev) => ({ ...prev, [slot]: sel }))\n router.refresh()\n },\n [router],\n )\n\n const update = useCallback((patch: Partial<Settings>) => {\n setSettings((prev) => {\n const next = { ...prev, ...patch }\n try {\n localStorage.setItem(STORE_KEY, JSON.stringify(next))\n } catch {}\n return next\n })\n }, [])\n\n const runSeed = async () => {\n setSeeding(true)\n setSeedError(null)\n try {\n const res = await fetch('/api/seed', { method: 'POST', credentials: 'include' })\n if (res.ok) {\n setConfirming(false)\n setSeeding(false)\n await fetchSnapshot()\n router.refresh()\n return\n }\n const body = (await res.json().catch(() => null)) as SeedError | null\n setSeedError(body?.error ? body : { error: `Seed failed (HTTP ${res.status}).` })\n } catch {\n setSeedError({ error: 'Seed request failed — is the dev server running?' })\n }\n setSeeding(false)\n setConfirming(false)\n }\n\n useEffect(() => {\n if (!open) return\n const onDown = (e: MouseEvent) => {\n if (rootRef.current && e.target instanceof Node && !rootRef.current.contains(e.target)) setOpen(false)\n }\n document.addEventListener('mousedown', onDown)\n return () => document.removeEventListener('mousedown', onDown)\n }, [open])\n\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key !== 'Escape' || !open) return\n if (view === 'main') setOpen(false)\n else setView('main')\n }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [open, view])\n\n if (!mounted || hidden) return null\n\n const corner = CORNER_CLASS[settings.corner]\n const seed = snapshot?.seed\n\n const pageLinks: DevLink[] = [\n { href: base, title: 'Overview' },\n ...(snapshot?.icons ? [{ href: `${base}/icons`, title: 'Icons' }] : []),\n ...(snapshot?.fonts ? [{ href: `${base}/fonts`, title: 'Fonts' }] : []),\n ...(snapshot?.images ? [{ href: `${base}/images`, title: 'Images' }] : []),\n ...(snapshot?.mux ? [{ href: `${base}/mux`, title: 'Mux' }] : []),\n ...links,\n { href: snapshot?.adminRoute ?? '/admin', title: 'Payload admin' },\n { href: 'https://payload-plugins.prolaico.com/docs', title: 'Plugin docs ↗' },\n ]\n\n return (\n <div ref={rootRef} className={`pdt-root pdt-corner-${corner}`}>\n {open ? (\n <div className={`pdt-panel pdt-panel-${corner}`}>\n <div className=\"pdt-head\">\n {view !== 'main' ? (\n <button type=\"button\" className=\"pdt-back\" aria-label=\"Back\" onClick={() => setView('main')}>\n ‹\n </button>\n ) : null}\n <span className=\"pdt-head-title\">{view === 'main' ? 'Dev tools' : VIEW_TITLE[view]}</span>\n <span className=\"pdt-head-badge\">dev only</span>\n </div>\n\n <div className=\"pdt-body\">\n {view === 'main' ? (\n <ul className=\"pdt-menu\">\n <MenuRow label=\"Info\" hint={snapshot ? snapshot.env.nodeEnv : undefined} onClick={() => setView('info')} />\n {snapshot?.plugins.seed !== false ? (\n <MenuRow\n label=\"Seed\"\n hint={seed ? (seed.seeded ? `${seed.totalDocs} docs` : 'empty') : undefined}\n onClick={() => setView('seed')}\n />\n ) : null}\n <MenuRow label=\"Pages\" hint={pathname.startsWith(base) ? pathname : undefined} onClick={() => setView('pages')} />\n {tests.length ? <MenuRow label=\"Tests\" hint={`${tests.length}`} onClick={() => setView('tests')} /> : null}\n <MenuRow label=\"Settings\" onClick={() => setView('settings')} />\n </ul>\n ) : null}\n\n {view === 'info' ? (\n <div>\n {snapshotError ? <SnapshotMissing retry={fetchSnapshot} /> : null}\n {snapshot ? (\n <>\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">Environment</span>\n <button type=\"button\" className=\"pdt-kind\" onClick={fetchSnapshot}>\n refresh\n </button>\n </div>\n <p className=\"pdt-small\" style={{ margin: 0 }}>\n <span className=\"pdt-code\">{snapshot.env.nodeEnv}</span> · node {snapshot.env.nodeVersion}\n </p>\n <div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: '3px 12px' }} className=\"pdt-small\">\n {Object.entries(snapshot.plugins).map(([name, on]) => (\n <span key={name}>\n <span className={`pdt-dot ${on ? 'pdt-dot-on' : 'pdt-dot-off'}`} />\n {name}\n </span>\n ))}\n </div>\n </div>\n\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">Collections</span>\n </div>\n <table className=\"pdt-table\">\n <tbody>\n {snapshot.collections.map((c) => (\n <tr key={c.slug}>\n <td>{c.slug}</td>\n <td>{c.count ?? '?'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {snapshot.icons ? (\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">Icons</span>\n <span className=\"pdt-kind\">{snapshot.icons.iconCount ?? '?'}</span>\n </div>\n <p className=\"pdt-small\" style={{ margin: 0 }}>\n Active set: {snapshot.icons.activeSet ?? <span className=\"pdt-warn\">none</span>}\n </p>\n {snapshot.icons.misses.length ? (\n <p className=\"pdt-small pdt-warn\" style={{ margin: '6px 0 0' }}>\n Misses:{' '}\n {snapshot.icons.misses.map((m) => (\n <span key={m.name} className=\"pdt-code\" style={{ marginRight: 5 }}>\n {m.name} ×{m.count}\n </span>\n ))}\n </p>\n ) : null}\n </div>\n ) : null}\n\n {snapshot.fonts ? (\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">Fonts</span>\n <span className=\"pdt-kind\">{snapshot.fonts.fontCount ?? '?'}</span>\n </div>\n <table className=\"pdt-table\">\n <tbody>\n {snapshot.fonts.familyKeys.map((key) => (\n <tr key={key}>\n <td>{key}</td>\n <td>{snapshot.fonts?.slots[key] ?? 'unset'}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : null}\n\n {snapshot.mux ? (\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">Mux</span>\n <span className=\"pdt-kind\">\n {snapshot.mux.total ?? '?'} / {snapshot.mux.ready ?? '?'} ready\n </span>\n </div>\n {!snapshot.mux.credentialed ? (\n <p className=\"pdt-small pdt-warn\" style={{ margin: 0 }}>\n MUX_TOKEN_ID / MUX_TOKEN_SECRET not set\n </p>\n ) : null}\n </div>\n ) : null}\n </>\n ) : !snapshotError ? (\n <p className=\"pdt-note\">Loading…</p>\n ) : null}\n </div>\n ) : null}\n\n {view === 'seed' ? (\n <div>\n {snapshotError ? <SnapshotMissing retry={fetchSnapshot} /> : null}\n {snapshot && !seed ? (\n <p className=\"pdt-note\">\n <span className=\"pdt-code\">@pro-laico/payload-seed</span> isn't installed — add{' '}\n <span className=\"pdt-code\">seedPlugin()</span> to get one-click seeding here.\n </p>\n ) : null}\n {seed ? (\n <div className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">{seed.seeded ? `Seeded — ${seed.totalDocs} docs` : 'Not seeded'}</span>\n {!seed.enabled ? <span className=\"pdt-kind pdt-warn\">locked</span> : null}\n </div>\n {seed.definitions.length ? (\n <table className=\"pdt-table\">\n <tbody>\n {seed.definitions.map((d) => (\n <tr key={d.slug}>\n <td>\n {d.slug}\n {d.disabled ? <span className=\"pdt-warn\"> · skipped</span> : null}\n </td>\n <td>{d.kind === 'global' ? 'global' : (seed.counts[d.slug] ?? 0)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n ) : (\n <p className=\"pdt-note\" style={{ margin: 0 }}>\n No seed definitions registered.\n </p>\n )}\n <div style={{ marginTop: 10, display: 'flex', gap: 6 }}>\n <button\n type=\"button\"\n className={`pdt-btn ${seed.seeded ? 'pdt-btn-danger' : 'pdt-btn-primary'}`}\n disabled={seeding}\n onClick={() => (seed.seeded && !confirming ? setConfirming(true) : void runSeed())}\n >\n {seeding ? 'Seeding…' : confirming ? 'Destructive — click again' : seed.seeded ? 'Reseed' : 'Seed the database'}\n </button>\n {confirming ? (\n <button type=\"button\" className=\"pdt-btn\" onClick={() => setConfirming(false)}>\n Cancel\n </button>\n ) : null}\n </div>\n {!seed.enabled ? (\n <p className=\"pdt-note\">\n Set <span className=\"pdt-code\">ENABLE_SEED=true</span> in <span className=\"pdt-code\">.env.local</span> — the seed wipes\n seeded collections, so it's off by default.\n </p>\n ) : null}\n <p className=\"pdt-note\" style={{ marginBottom: 0 }}>\n Needs a logged-in{' '}\n <a href={snapshot?.adminRoute ?? '/admin'} style={{ textDecoration: 'underline' }}>\n admin\n </a>{' '}\n user · CLI: <span className=\"pdt-code\">ENABLE_SEED=true pnpm seed</span>\n </p>\n {seedError ? (\n <div className=\"pdt-error\">\n {seedError.error}\n {seedError.issues?.length ? (\n <ul>\n {seedError.issues.map((issue) => (\n <li key={issue}>{issue}</li>\n ))}\n </ul>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n </div>\n ) : null}\n\n {view === 'pages' ? (\n <div>\n <ul className=\"pdt-menu\">\n {pageLinks.map((link) =>\n link.href.startsWith('http') ? (\n <li key={link.href}>\n <a className=\"pdt-menu-row\" href={link.href} target=\"_blank\" rel=\"noreferrer\">\n <span>{link.title}</span>\n <span className=\"pdt-menu-hint\">{link.href.replace(/^https?:\\/\\//, '')}</span>\n </a>\n </li>\n ) : (\n <li key={link.href}>\n <Link className={`pdt-menu-row ${pathname === link.href ? 'pdt-current' : ''}`} href={link.href}>\n <span>{link.title}</span>\n <span className=\"pdt-menu-hint\">{link.href}</span>\n </Link>\n </li>\n ),\n )}\n </ul>\n {!snapshot && !snapshotError ? <p className=\"pdt-note\">Loading plugin pages…</p> : null}\n {snapshotError ? <SnapshotMissing retry={fetchSnapshot} /> : null}\n </div>\n ) : null}\n\n {view === 'tests' ? (\n <div>\n {tests.map((t) => {\n // header/footer kinds are chrome OVERRIDES: chips swap the variant into the real\n // layout site-wide (via resolveDevChrome) instead of opening a test page.\n if (t.kind === 'header' || t.kind === 'footer') {\n const slot = t.kind\n const active = chrome[slot]?.testKey === t.key ? chrome[slot] : null\n return (\n <div key={t.key} className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <span className=\"pdt-card-title\">{t.label}</span>\n <span className=\"pdt-kind\">{active ? <span className=\"pdt-viewing\">override on</span> : `${t.kind} override`}</span>\n </div>\n <div className=\"pdt-chips\">\n <button type=\"button\" className={`pdt-chip ${active ? '' : 'pdt-active'}`} onClick={() => selectChrome(slot, null)}>\n Real\n </button>\n {t.versions.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n className={`pdt-chip ${active?.versionId === v.id ? 'pdt-active' : ''}`}\n onClick={() => selectChrome(slot, { testKey: t.key, versionId: v.id })}\n >\n {v.label}\n </button>\n ))}\n </div>\n </div>\n )\n }\n const testPath = `${base}/tests/${t.key}`\n const onPage = pathname === testPath\n // The page defaults to the first version when no cookie targets this test.\n const selectedId = selection?.testKey === t.key ? selection.versionId : t.versions[0]?.id\n return (\n <div key={t.key} className=\"pdt-card\">\n <div className=\"pdt-card-head\">\n <Link href={testPath} className=\"pdt-card-title\" style={{ textDecoration: onPage ? 'none' : undefined }}>\n {t.label} {onPage ? null : <span aria-hidden>›</span>}\n </Link>\n <span className=\"pdt-kind\">{onPage ? <span className=\"pdt-viewing\">viewing</span> : t.kind}</span>\n </div>\n <div className=\"pdt-chips\">\n {t.versions.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n className={`pdt-chip ${selectedId === v.id ? 'pdt-active' : ''}`}\n onClick={() => selectVersion(t.key, v.id)}\n >\n {v.label}\n </button>\n ))}\n </div>\n </div>\n )\n })}\n <p className=\"pdt-note\">\n Page tests open as their own page; header/footer overrides swap into the live layout everywhere until you hit Real. Scriptable\n via <span className=\"pdt-code\">/api/dev/stage?test=…&version=…</span>\n </p>\n </div>\n ) : null}\n\n {view === 'settings' ? (\n <div>\n <div className=\"pdt-settings-row\">\n <span className=\"pdt-small pdt-muted\">Corner</span>\n <div className=\"pdt-corner-grid\">\n {CORNERS.map((c) => (\n <button\n key={c.value}\n type=\"button\"\n aria-label={c.value}\n className={`pdt-corner-btn ${settings.corner === c.value ? 'pdt-active' : ''}`}\n onClick={() => update({ corner: c.value })}\n >\n {c.arrow}\n </button>\n ))}\n </div>\n </div>\n <div className=\"pdt-settings-row\">\n <span className=\"pdt-small pdt-muted\">Size</span>\n <div className=\"pdt-seg\">\n {SIZES.map((s) => (\n <button key={s} type=\"button\" className={settings.size === s ? 'pdt-active' : ''} onClick={() => update({ size: s })}>\n {s}\n </button>\n ))}\n </div>\n </div>\n <div style={{ padding: '6px 4px 2px' }}>\n <button\n type=\"button\"\n className=\"pdt-btn\"\n style={{ width: '100%', justifyContent: 'center' }}\n onClick={() => {\n try {\n sessionStorage.setItem(HIDE_KEY, '1')\n } catch {}\n setHidden(true)\n setOpen(false)\n }}\n >\n Hide for this session\n </button>\n <p className=\"pdt-note\" style={{ textAlign: 'center' }}>\n reopens in a new tab / session\n </p>\n </div>\n </div>\n ) : null}\n </div>\n </div>\n ) : null}\n\n <button\n type=\"button\"\n aria-label=\"Dev tools\"\n title=\"Dev tools\"\n className={`pdt-fab pdt-fab-${settings.size} ${open ? 'pdt-open' : ''}`}\n onClick={() => {\n setOpen((v) => !v)\n setView('main')\n }}\n >\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m5 8 4 4-4 4\" />\n <path d=\"M13 16h6\" />\n </svg>\n </button>\n </div>\n )\n}\n\nconst MenuRow = ({ label, hint, onClick }: { label: string; hint?: string; onClick: () => void }) => (\n <li>\n <button type=\"button\" className=\"pdt-menu-row\" onClick={onClick}>\n <span>{label}</span>\n <span className=\"pdt-menu-hint\">\n {hint ? <span>{hint}</span> : null}\n <span aria-hidden>›</span>\n </span>\n </button>\n </li>\n)\n\nconst SnapshotMissing = ({ retry }: { retry: () => Promise<void> }) => (\n <p className=\"pdt-note\">\n <span className=\"pdt-warn\">Couldn't reach /api/dev.</span> Is <span className=\"pdt-code\">devToolsPlugin()</span> in your Payload plugins\n list?{' '}\n <button type=\"button\" style={{ textDecoration: 'underline' }} onClick={() => void retry()}>\n Retry\n </button>\n </p>\n)\n"],"names":["Link","usePathname","useRouter","useCallback","useEffect","useRef","useState","CHROME_COOKIES","STAGE_COOKIE","STORE_KEY","HIDE_KEY","DEFAULTS","corner","size","CORNER_CLASS","CORNERS","value","arrow","SIZES","VIEW_TITLE","info","seed","pages","tests","settings","readSettings","parsed","JSON","parse","localStorage","getItem","readSelectionCookie","name","raw","document","cookie","split","find","c","startsWith","decoded","decodeURIComponent","sep","indexOf","testKey","slice","versionId","DevToolbarClient","links","router","pathname","mounted","setMounted","hidden","setHidden","open","setOpen","view","setView","setSettings","selection","setSelection","chrome","setChrome","header","footer","snapshot","setSnapshot","snapshotError","setSnapshotError","seeding","setSeeding","confirming","setConfirming","seedError","setSeedError","rootRef","sessionStorage","fetchSnapshot","res","fetch","headers","accept","credentials","ok","Error","String","status","json","base","devRoute","selectVersion","encodeURIComponent","testPath","refresh","push","selectChrome","slot","sel","prev","update","patch","next","setItem","stringify","runSeed","method","body","catch","error","onDown","e","current","target","Node","contains","addEventListener","removeEventListener","onKey","key","pageLinks","href","title","icons","fonts","images","mux","adminRoute","div","ref","className","button","type","aria-label","onClick","span","ul","MenuRow","label","hint","env","nodeEnv","undefined","plugins","seeded","totalDocs","length","SnapshotMissing","retry","p","style","margin","nodeVersion","marginTop","display","flexWrap","gap","Object","entries","map","on","table","tbody","collections","tr","td","slug","count","iconCount","activeSet","misses","m","marginRight","fontCount","familyKeys","slots","total","ready","credentialed","enabled","definitions","d","disabled","kind","counts","marginBottom","a","textDecoration","issues","issue","li","link","rel","replace","t","active","versions","v","id","onPage","selectedId","aria-hidden","s","padding","width","justifyContent","textAlign","svg","viewBox","fill","stroke","strokeWidth","strokeLinecap","strokeLinejoin","path"],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type ChromeSlot } from '../cookies';
3
+ import { type Test } from '../harness';
4
+ export type ResolveDevChromeOptions = {
5
+ /** The same test array the `<DevToolbar>` receives — only `header`/`footer`-kind tests apply. */
6
+ tests: Test[];
7
+ /** The host's REAL `<SiteHeader />` — rendered whenever no header override is selected. */
8
+ header: ReactNode;
9
+ /** The host's REAL `<SiteFooter />`. */
10
+ footer: ReactNode;
11
+ /** Force on/off. Defaults to `NODE_ENV === 'development'` — in production the real chrome is
12
+ * returned untouched, cookies never read. */
13
+ enabled?: boolean;
14
+ };
15
+ /** Resolve one slot: its cookie must name a test of the MATCHING kind (a stale or cross-slot
16
+ * cookie falls back to the real chrome). Exported for the toolbar client to mirror the rule. */
17
+ export declare const chromeTestsFor: (tests: Test[], slot: ChromeSlot) => Test[];
18
+ /**
19
+ * The chrome-swap seam for the test harness — one line in your frontend layout:
20
+ *
21
+ * const { header, footer } = await resolveDevChrome({ tests: devTests, header: <SiteHeader />, footer: <SiteFooter /> })
22
+ *
23
+ * Render `{header}` / `{footer}` where the real ones went. In production (or with no override
24
+ * selected) you get exactly what you passed in; in dev, picking a `header`/`footer`-kind test
25
+ * version in the toolbar's Tests view swaps that variant into the REAL layout, around REAL
26
+ * content, across the whole site — browse every page wearing the candidate chrome, then hit
27
+ * "Real" to reset. Server-only (reads cookies): call it from your layout, not client code.
28
+ */
29
+ export declare function resolveDevChrome({ tests, header, footer, enabled, }: ResolveDevChromeOptions): Promise<{
30
+ header: ReactNode;
31
+ footer: ReactNode;
32
+ }>;
33
+ //# sourceMappingURL=resolveDevChrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveDevChrome.d.ts","sourceRoot":"","sources":["../../src/components/resolveDevChrome.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,MAAM,uBAAuB,GAAG;IACpC,iGAAiG;IACjG,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,2FAA2F;IAC3F,MAAM,EAAE,SAAS,CAAA;IACjB,wCAAwC;IACxC,MAAM,EAAE,SAAS,CAAA;IACjB;kDAC8C;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;iGACiG;AACjG,eAAO,MAAM,cAAc,GAAI,OAAO,IAAI,EAAE,EAAE,MAAM,UAAU,KAAG,IAAI,EAA0C,CAAA;AAE/G;;;;;;;;;;GAUG;AACH,wBAAsB,gBAAgB,CAAC,EACrC,KAAK,EACL,MAAM,EACN,MAAM,EACN,OAAO,GACR,EAAE,uBAAuB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC,CAgB7E"}
@@ -0,0 +1,42 @@
1
+ import { cookies } from "next/headers";
2
+ import { CHROME_COOKIES } from "../cookies.js";
3
+ import { parseStage } from "../harness.js";
4
+ /** Resolve one slot: its cookie must name a test of the MATCHING kind (a stale or cross-slot
5
+ * cookie falls back to the real chrome). Exported for the toolbar client to mirror the rule. */ export const chromeTestsFor = (tests, slot)=>tests.filter((t)=>t.kind === slot);
6
+ /**
7
+ * The chrome-swap seam for the test harness — one line in your frontend layout:
8
+ *
9
+ * const { header, footer } = await resolveDevChrome({ tests: devTests, header: <SiteHeader />, footer: <SiteFooter /> })
10
+ *
11
+ * Render `{header}` / `{footer}` where the real ones went. In production (or with no override
12
+ * selected) you get exactly what you passed in; in dev, picking a `header`/`footer`-kind test
13
+ * version in the toolbar's Tests view swaps that variant into the REAL layout, around REAL
14
+ * content, across the whole site — browse every page wearing the candidate chrome, then hit
15
+ * "Real" to reset. Server-only (reads cookies): call it from your layout, not client code.
16
+ */ export async function resolveDevChrome({ tests, header, footer, enabled }) {
17
+ if (!(enabled ?? process.env.NODE_ENV === 'development')) return {
18
+ header,
19
+ footer
20
+ };
21
+ const jar = await cookies();
22
+ const resolve = async (slot, real)=>{
23
+ const stage = parseStage(jar.get(CHROME_COOKIES[slot])?.value, chromeTestsFor(tests, slot));
24
+ if (!stage) return real;
25
+ try {
26
+ return await stage.version.render();
27
+ } catch {
28
+ return real // a throwing variant must never take the whole site down
29
+ ;
30
+ }
31
+ };
32
+ const [h, f] = await Promise.all([
33
+ resolve('header', header),
34
+ resolve('footer', footer)
35
+ ]);
36
+ return {
37
+ header: h,
38
+ footer: f
39
+ };
40
+ }
41
+
42
+ //# sourceMappingURL=resolveDevChrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/resolveDevChrome.tsx"],"sourcesContent":["import { cookies } from 'next/headers'\nimport type { ReactNode } from 'react'\nimport { CHROME_COOKIES, type ChromeSlot } from '../cookies'\nimport { parseStage, type Test } from '../harness'\n\nexport type ResolveDevChromeOptions = {\n /** The same test array the `<DevToolbar>` receives — only `header`/`footer`-kind tests apply. */\n tests: Test[]\n /** The host's REAL `<SiteHeader />` — rendered whenever no header override is selected. */\n header: ReactNode\n /** The host's REAL `<SiteFooter />`. */\n footer: ReactNode\n /** Force on/off. Defaults to `NODE_ENV === 'development'` — in production the real chrome is\n * returned untouched, cookies never read. */\n enabled?: boolean\n}\n\n/** Resolve one slot: its cookie must name a test of the MATCHING kind (a stale or cross-slot\n * cookie falls back to the real chrome). Exported for the toolbar client to mirror the rule. */\nexport const chromeTestsFor = (tests: Test[], slot: ChromeSlot): Test[] => tests.filter((t) => t.kind === slot)\n\n/**\n * The chrome-swap seam for the test harness — one line in your frontend layout:\n *\n * const { header, footer } = await resolveDevChrome({ tests: devTests, header: <SiteHeader />, footer: <SiteFooter /> })\n *\n * Render `{header}` / `{footer}` where the real ones went. In production (or with no override\n * selected) you get exactly what you passed in; in dev, picking a `header`/`footer`-kind test\n * version in the toolbar's Tests view swaps that variant into the REAL layout, around REAL\n * content, across the whole site — browse every page wearing the candidate chrome, then hit\n * \"Real\" to reset. Server-only (reads cookies): call it from your layout, not client code.\n */\nexport async function resolveDevChrome({\n tests,\n header,\n footer,\n enabled,\n}: ResolveDevChromeOptions): Promise<{ header: ReactNode; footer: ReactNode }> {\n if (!(enabled ?? process.env.NODE_ENV === 'development')) return { header, footer }\n\n const jar = await cookies()\n const resolve = async (slot: ChromeSlot, real: ReactNode): Promise<ReactNode> => {\n const stage = parseStage(jar.get(CHROME_COOKIES[slot])?.value, chromeTestsFor(tests, slot))\n if (!stage) return real\n try {\n return await stage.version.render()\n } catch {\n return real // a throwing variant must never take the whole site down\n }\n }\n\n const [h, f] = await Promise.all([resolve('header', header), resolve('footer', footer)])\n return { header: h, footer: f }\n}\n"],"names":["cookies","CHROME_COOKIES","parseStage","chromeTestsFor","tests","slot","filter","t","kind","resolveDevChrome","header","footer","enabled","process","env","NODE_ENV","jar","resolve","real","stage","get","value","version","render","h","f","Promise","all"],"mappings":"AAAA,SAASA,OAAO,QAAQ,eAAc;AAEtC,SAASC,cAAc,QAAyB,gBAAY;AAC5D,SAASC,UAAU,QAAmB,gBAAY;AAclD;+FAC+F,GAC/F,OAAO,MAAMC,iBAAiB,CAACC,OAAeC,OAA6BD,MAAME,MAAM,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKH,MAAK;AAE/G;;;;;;;;;;CAUC,GACD,OAAO,eAAeI,iBAAiB,EACrCL,KAAK,EACLM,MAAM,EACNC,MAAM,EACNC,OAAO,EACiB;IACxB,IAAI,CAAEA,CAAAA,WAAWC,QAAQC,GAAG,CAACC,QAAQ,KAAK,aAAY,GAAI,OAAO;QAAEL;QAAQC;IAAO;IAElF,MAAMK,MAAM,MAAMhB;IAClB,MAAMiB,UAAU,OAAOZ,MAAkBa;QACvC,MAAMC,QAAQjB,WAAWc,IAAII,GAAG,CAACnB,cAAc,CAACI,KAAK,GAAGgB,OAAOlB,eAAeC,OAAOC;QACrF,IAAI,CAACc,OAAO,OAAOD;QACnB,IAAI;YACF,OAAO,MAAMC,MAAMG,OAAO,CAACC,MAAM;QACnC,EAAE,OAAM;YACN,OAAOL,KAAK,yDAAyD;;QACvE;IACF;IAEA,MAAM,CAACM,GAAGC,EAAE,GAAG,MAAMC,QAAQC,GAAG,CAAC;QAACV,QAAQ,UAAUP;QAASO,QAAQ,UAAUN;KAAQ;IACvF,OAAO;QAAED,QAAQc;QAAGb,QAAQc;IAAE;AAChC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=resolveDevChrome.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveDevChrome.spec.d.ts","sourceRoot":"","sources":["../../src/components/resolveDevChrome.spec.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,98 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { defineTest } from "../harness.js";
3
+ const jar = new Map();
4
+ vi.mock('next/headers', ()=>({
5
+ cookies: async ()=>({
6
+ get: (name)=>jar.has(name) ? {
7
+ value: jar.get(name)
8
+ } : undefined
9
+ })
10
+ }));
11
+ const { resolveDevChrome } = await import("./resolveDevChrome.js");
12
+ const headerTest = defineTest({
13
+ key: 'site-header',
14
+ label: 'Site header',
15
+ kind: 'header',
16
+ versions: [
17
+ {
18
+ id: 'centered',
19
+ label: 'Centered',
20
+ render: ()=>'CENTERED-HEADER'
21
+ },
22
+ {
23
+ id: 'boom',
24
+ label: 'Boom',
25
+ render: ()=>Promise.reject(new Error('variant exploded'))
26
+ }
27
+ ]
28
+ });
29
+ const pageTest = defineTest({
30
+ key: 'hero',
31
+ label: 'Hero',
32
+ kind: 'page',
33
+ versions: [
34
+ {
35
+ id: 'a',
36
+ label: 'A',
37
+ render: ()=>'HERO-A'
38
+ }
39
+ ]
40
+ });
41
+ const tests = [
42
+ headerTest,
43
+ pageTest
44
+ ];
45
+ beforeEach(()=>jar.clear());
46
+ describe('resolveDevChrome', ()=>{
47
+ it('returns the real chrome untouched when disabled (production default under NODE_ENV=test)', async ()=>{
48
+ jar.set('pdt-chrome-header', 'site-header:centered');
49
+ const out = await resolveDevChrome({
50
+ tests,
51
+ header: 'REAL-H',
52
+ footer: 'REAL-F'
53
+ });
54
+ expect(out).toEqual({
55
+ header: 'REAL-H',
56
+ footer: 'REAL-F'
57
+ });
58
+ });
59
+ it('swaps the selected header variant in, leaving the footer real', async ()=>{
60
+ jar.set('pdt-chrome-header', 'site-header:centered');
61
+ const out = await resolveDevChrome({
62
+ tests,
63
+ header: 'REAL-H',
64
+ footer: 'REAL-F',
65
+ enabled: true
66
+ });
67
+ expect(out).toEqual({
68
+ header: 'CENTERED-HEADER',
69
+ footer: 'REAL-F'
70
+ });
71
+ });
72
+ it('ignores stale cookies, cross-kind keys, and unknown versions', async ()=>{
73
+ jar.set('pdt-chrome-header', 'hero:a'); // a page test can never occupy a chrome slot
74
+ jar.set('pdt-chrome-footer', 'site-header:ghost');
75
+ const out = await resolveDevChrome({
76
+ tests,
77
+ header: 'REAL-H',
78
+ footer: 'REAL-F',
79
+ enabled: true
80
+ });
81
+ expect(out).toEqual({
82
+ header: 'REAL-H',
83
+ footer: 'REAL-F'
84
+ });
85
+ });
86
+ it('falls back to the real chrome when a variant throws — never takes the site down', async ()=>{
87
+ jar.set('pdt-chrome-header', 'site-header:boom');
88
+ const out = await resolveDevChrome({
89
+ tests,
90
+ header: 'REAL-H',
91
+ footer: 'REAL-F',
92
+ enabled: true
93
+ });
94
+ expect(out.header).toBe('REAL-H');
95
+ });
96
+ });
97
+
98
+ //# sourceMappingURL=resolveDevChrome.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/resolveDevChrome.spec.tsx"],"sourcesContent":["import { beforeEach, describe, expect, it, vi } from 'vitest'\nimport { defineTest } from '../harness'\n\nconst jar = new Map<string, string>()\nvi.mock('next/headers', () => ({\n cookies: async () => ({ get: (name: string) => (jar.has(name) ? { value: jar.get(name) } : undefined) }),\n}))\n\nconst { resolveDevChrome } = await import('./resolveDevChrome')\n\nconst headerTest = defineTest({\n key: 'site-header',\n label: 'Site header',\n kind: 'header',\n versions: [\n { id: 'centered', label: 'Centered', render: () => 'CENTERED-HEADER' },\n { id: 'boom', label: 'Boom', render: () => Promise.reject(new Error('variant exploded')) },\n ],\n})\nconst pageTest = defineTest({ key: 'hero', label: 'Hero', kind: 'page', versions: [{ id: 'a', label: 'A', render: () => 'HERO-A' }] })\nconst tests = [headerTest, pageTest]\n\nbeforeEach(() => jar.clear())\n\ndescribe('resolveDevChrome', () => {\n it('returns the real chrome untouched when disabled (production default under NODE_ENV=test)', async () => {\n jar.set('pdt-chrome-header', 'site-header:centered')\n const out = await resolveDevChrome({ tests, header: 'REAL-H', footer: 'REAL-F' })\n expect(out).toEqual({ header: 'REAL-H', footer: 'REAL-F' })\n })\n\n it('swaps the selected header variant in, leaving the footer real', async () => {\n jar.set('pdt-chrome-header', 'site-header:centered')\n const out = await resolveDevChrome({ tests, header: 'REAL-H', footer: 'REAL-F', enabled: true })\n expect(out).toEqual({ header: 'CENTERED-HEADER', footer: 'REAL-F' })\n })\n\n it('ignores stale cookies, cross-kind keys, and unknown versions', async () => {\n jar.set('pdt-chrome-header', 'hero:a') // a page test can never occupy a chrome slot\n jar.set('pdt-chrome-footer', 'site-header:ghost')\n const out = await resolveDevChrome({ tests, header: 'REAL-H', footer: 'REAL-F', enabled: true })\n expect(out).toEqual({ header: 'REAL-H', footer: 'REAL-F' })\n })\n\n it('falls back to the real chrome when a variant throws — never takes the site down', async () => {\n jar.set('pdt-chrome-header', 'site-header:boom')\n const out = await resolveDevChrome({ tests, header: 'REAL-H', footer: 'REAL-F', enabled: true })\n expect(out.header).toBe('REAL-H')\n })\n})\n"],"names":["beforeEach","describe","expect","it","vi","defineTest","jar","Map","mock","cookies","get","name","has","value","undefined","resolveDevChrome","headerTest","key","label","kind","versions","id","render","Promise","reject","Error","pageTest","tests","clear","set","out","header","footer","toEqual","enabled","toBe"],"mappings":"AAAA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAC7D,SAASC,UAAU,QAAQ,gBAAY;AAEvC,MAAMC,MAAM,IAAIC;AAChBH,GAAGI,IAAI,CAAC,gBAAgB,IAAO,CAAA;QAC7BC,SAAS,UAAa,CAAA;gBAAEC,KAAK,CAACC,OAAkBL,IAAIM,GAAG,CAACD,QAAQ;wBAAEE,OAAOP,IAAII,GAAG,CAACC;oBAAM,IAAIG;YAAW,CAAA;IACxG,CAAA;AAEA,MAAM,EAAEC,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC;AAE1C,MAAMC,aAAaX,WAAW;IAC5BY,KAAK;IACLC,OAAO;IACPC,MAAM;IACNC,UAAU;QACR;YAAEC,IAAI;YAAYH,OAAO;YAAYI,QAAQ,IAAM;QAAkB;QACrE;YAAED,IAAI;YAAQH,OAAO;YAAQI,QAAQ,IAAMC,QAAQC,MAAM,CAAC,IAAIC,MAAM;QAAqB;KAC1F;AACH;AACA,MAAMC,WAAWrB,WAAW;IAAEY,KAAK;IAAQC,OAAO;IAAQC,MAAM;IAAQC,UAAU;QAAC;YAAEC,IAAI;YAAKH,OAAO;YAAKI,QAAQ,IAAM;QAAS;KAAE;AAAC;AACpI,MAAMK,QAAQ;IAACX;IAAYU;CAAS;AAEpC1B,WAAW,IAAMM,IAAIsB,KAAK;AAE1B3B,SAAS,oBAAoB;IAC3BE,GAAG,4FAA4F;QAC7FG,IAAIuB,GAAG,CAAC,qBAAqB;QAC7B,MAAMC,MAAM,MAAMf,iBAAiB;YAAEY;YAAOI,QAAQ;YAAUC,QAAQ;QAAS;QAC/E9B,OAAO4B,KAAKG,OAAO,CAAC;YAAEF,QAAQ;YAAUC,QAAQ;QAAS;IAC3D;IAEA7B,GAAG,iEAAiE;QAClEG,IAAIuB,GAAG,CAAC,qBAAqB;QAC7B,MAAMC,MAAM,MAAMf,iBAAiB;YAAEY;YAAOI,QAAQ;YAAUC,QAAQ;YAAUE,SAAS;QAAK;QAC9FhC,OAAO4B,KAAKG,OAAO,CAAC;YAAEF,QAAQ;YAAmBC,QAAQ;QAAS;IACpE;IAEA7B,GAAG,gEAAgE;QACjEG,IAAIuB,GAAG,CAAC,qBAAqB,WAAU,6CAA6C;QACpFvB,IAAIuB,GAAG,CAAC,qBAAqB;QAC7B,MAAMC,MAAM,MAAMf,iBAAiB;YAAEY;YAAOI,QAAQ;YAAUC,QAAQ;YAAUE,SAAS;QAAK;QAC9FhC,OAAO4B,KAAKG,OAAO,CAAC;YAAEF,QAAQ;YAAUC,QAAQ;QAAS;IAC3D;IAEA7B,GAAG,mFAAmF;QACpFG,IAAIuB,GAAG,CAAC,qBAAqB;QAC7B,MAAMC,MAAM,MAAMf,iBAAiB;YAAEY;YAAOI,QAAQ;YAAUC,QAAQ;YAAUE,SAAS;QAAK;QAC9FhC,OAAO4B,IAAIC,MAAM,EAAEI,IAAI,CAAC;IAC1B;AACF"}
@@ -0,0 +1,5 @@
1
+ /** The toolbar's entire stylesheet, injected by `<DevToolbar>` as one `<style>` tag so the host
2
+ * app needs no CSS import and no Tailwind. Everything is `.pdt-` prefixed and self-contained
3
+ * (own dark theme); `--pdt-accent` is the override seam. */
4
+ export declare const PDT_CSS = "\n.pdt-root { position: fixed; z-index: 999999; color-scheme: dark;\n --pdt-accent: oklch(0.72 0.16 250); --pdt-bg: oklch(0.185 0 0); --pdt-fg: oklch(0.985 0 0);\n --pdt-muted: oklch(0.708 0 0); --pdt-border: oklch(1 0 0 / 12%); --pdt-hover: oklch(1 0 0 / 7%);\n --pdt-danger: oklch(0.65 0.18 25); --pdt-warn: oklch(0.8 0.14 85);\n font-family: ui-sans-serif, system-ui, sans-serif; font-size: 14px; line-height: 1.45; color: var(--pdt-fg); }\n.pdt-root *, .pdt-root *::before, .pdt-root *::after { box-sizing: border-box; }\n/* :where() keeps this reset at zero specificity so component classes below always win it. */\n:where(.pdt-root) button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }\n.pdt-root a { color: inherit; text-decoration: none; }\n\n.pdt-corner-br { bottom: 16px; right: 16px; } .pdt-corner-bl { bottom: 16px; left: 16px; }\n.pdt-corner-tr { top: 16px; right: 16px; } .pdt-corner-tl { top: 16px; left: 16px; }\n\n/* The launcher: solid black, no shadow, a thin border that breathes toward grey \u2014 the same\n idiom as Next.js's own dev indicator, so the two read as siblings. */\n@keyframes pdt-fab-breathe { 0%, 100% { border-color: oklch(0.25 0 0); } 50% { border-color: oklch(0.52 0 0); } }\n.pdt-fab { display: flex; align-items: center; justify-content: center; border-radius: 999px;\n background: oklch(0 0 0); color: oklch(0.985 0 0); border: 1px solid oklch(0.25 0 0);\n animation: pdt-fab-breathe 4s ease-in-out infinite; transition: border-color 0.2s ease; }\n.pdt-fab:hover { animation: none; border-color: oklch(0.6 0 0); }\n.pdt-fab.pdt-open { animation: none; border-color: var(--pdt-accent); }\n.pdt-fab-sm { width: 30px; height: 30px; } .pdt-fab-sm svg { width: 14px; height: 14px; }\n.pdt-fab-md { width: 38px; height: 38px; } .pdt-fab-md svg { width: 17px; height: 17px; }\n.pdt-fab-lg { width: 46px; height: 46px; } .pdt-fab-lg svg { width: 21px; height: 21px; }\n\n.pdt-panel { position: absolute; width: 330px; max-height: 72vh; display: flex; flex-direction: column;\n overflow: hidden; border-radius: 12px; border: 1px solid var(--pdt-border); background: var(--pdt-bg);\n box-shadow: 0 12px 40px oklch(0 0 0 / 55%); }\n.pdt-panel-br { bottom: calc(100% + 10px); right: 0; } .pdt-panel-bl { bottom: calc(100% + 10px); left: 0; }\n.pdt-panel-tr { top: calc(100% + 10px); right: 0; } .pdt-panel-tl { top: calc(100% + 10px); left: 0; }\n\n.pdt-head { display: flex; align-items: center; gap: 8px; padding: 9px 12px;\n border-bottom: 1px solid var(--pdt-border); background: oklch(0.14 0 0); }\n.pdt-head-title { flex: 1; font-family: ui-monospace, Consolas, monospace; font-size: 11px;\n text-transform: uppercase; letter-spacing: 0.08em; }\n.pdt-head-badge { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;\n color: var(--pdt-muted); border: 1px solid var(--pdt-border); border-radius: 4px; padding: 1px 6px; }\n.pdt-back { color: var(--pdt-muted); padding: 0 4px; font-size: 16px; line-height: 1; }\n.pdt-back:hover { color: var(--pdt-fg); }\n\n/* Minimal scrollbar for the tiny panel: a bare 4px thumb, no track, no arrows. */\n.pdt-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: oklch(0.42 0 0) transparent; }\n.pdt-body::-webkit-scrollbar { width: 4px; }\n.pdt-body::-webkit-scrollbar-track { background: transparent; }\n.pdt-body::-webkit-scrollbar-thumb { background: oklch(0.42 0 0); border-radius: 999px; }\n.pdt-body::-webkit-scrollbar-thumb:hover { background: oklch(0.58 0 0); }\n.pdt-body::-webkit-scrollbar-button { display: none; width: 0; height: 0; }\n.pdt-menu { list-style: none; margin: 0; padding: 0; }\n.pdt-menu-row { display: flex; width: 100%; align-items: center; justify-content: space-between;\n padding: 9px 10px; border-radius: 8px; text-align: left; }\n.pdt-menu-row:hover { background: var(--pdt-hover); }\n.pdt-menu-hint { display: flex; align-items: center; gap: 8px; color: var(--pdt-muted);\n font-family: ui-monospace, Consolas, monospace; font-size: 11px; }\n\n.pdt-card { border: 1px solid var(--pdt-border); border-radius: 10px; padding: 10px; margin: 4px 2px 10px; }\n.pdt-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }\n.pdt-card-title { font-weight: 600; }\n.pdt-kind { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;\n letter-spacing: 0.06em; color: var(--pdt-muted); }\n.pdt-viewing { color: var(--pdt-accent); font-weight: 600; }\n\n.pdt-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.pdt-chip { border: 1px solid var(--pdt-border); border-radius: 999px; padding: 5px 14px; font-size: 12.5px;\n background: oklch(0.24 0 0); transition: border-color 0.15s ease, background 0.15s ease; }\n.pdt-chip:hover { background: oklch(0.29 0 0); border-color: oklch(0.45 0 0); }\n.pdt-chip.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--pdt-border);\n border-radius: 8px; padding: 7px 12px; font-size: 13px; font-weight: 500; }\n.pdt-btn:hover { background: var(--pdt-hover); }\n.pdt-btn:disabled { opacity: 0.55; cursor: default; }\n.pdt-btn-danger { border-color: color-mix(in oklch, var(--pdt-danger), transparent 45%); color: var(--pdt-danger); }\n.pdt-btn-primary { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-muted { color: var(--pdt-muted); } .pdt-warn { color: var(--pdt-warn); } .pdt-danger-text { color: var(--pdt-danger); }\n.pdt-small { font-size: 12px; } .pdt-mono { font-family: ui-monospace, Consolas, monospace; font-size: 0.9em; }\n.pdt-note { margin: 6px 2px 4px; font-size: 11.5px; color: var(--pdt-muted); }\n.pdt-code { font-family: ui-monospace, Consolas, monospace; font-size: 0.85em; background: oklch(0.26 0 0);\n padding: 1px 5px; border-radius: 4px; }\n.pdt-error { margin-top: 8px; border: 1px solid color-mix(in oklch, var(--pdt-danger), transparent 55%);\n border-radius: 8px; padding: 8px 10px; font-size: 12.5px; color: var(--pdt-danger); }\n.pdt-error ul { margin: 6px 0 0; padding-left: 18px; color: var(--pdt-fg); }\n\n.pdt-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }\n.pdt-table td { padding: 4px 8px; border-bottom: 1px solid var(--pdt-border); }\n.pdt-table td:last-child { text-align: right; color: var(--pdt-muted); font-family: ui-monospace, Consolas, monospace; }\n.pdt-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; }\n.pdt-dot-on { background: oklch(0.75 0.15 165); } .pdt-dot-off { background: oklch(0.45 0 0); }\n\n.pdt-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 4px; }\n.pdt-corner-grid { display: grid; grid-template-columns: repeat(2, 24px); gap: 3px; }\n.pdt-corner-btn { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;\n border: 1px solid var(--pdt-border); border-radius: 5px; font-size: 12px; }\n.pdt-corner-btn:hover { background: var(--pdt-hover); }\n.pdt-corner-btn.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); }\n.pdt-seg { display: flex; overflow: hidden; border: 1px solid var(--pdt-border); border-radius: 6px; }\n.pdt-seg button { padding: 4px 10px; font-size: 11px; text-transform: uppercase; }\n.pdt-seg button:hover { background: var(--pdt-hover); }\n.pdt-seg button.pdt-active { background: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-menu-row.pdt-current { background: var(--pdt-hover); }\n.pdt-menu-row.pdt-current > span:first-child { color: var(--pdt-accent); font-weight: 600; }\n\n@media print { .pdt-root { display: none !important; } }\n";
5
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../src/components/styles.ts"],"names":[],"mappings":"AAAA;;6DAE6D;AAC7D,eAAO,MAAM,OAAO,2wPAyGnB,CAAA"}
@@ -0,0 +1,110 @@
1
+ /** The toolbar's entire stylesheet, injected by `<DevToolbar>` as one `<style>` tag so the host
2
+ * app needs no CSS import and no Tailwind. Everything is `.pdt-` prefixed and self-contained
3
+ * (own dark theme); `--pdt-accent` is the override seam. */ export const PDT_CSS = `
4
+ .pdt-root { position: fixed; z-index: 999999; color-scheme: dark;
5
+ --pdt-accent: oklch(0.72 0.16 250); --pdt-bg: oklch(0.185 0 0); --pdt-fg: oklch(0.985 0 0);
6
+ --pdt-muted: oklch(0.708 0 0); --pdt-border: oklch(1 0 0 / 12%); --pdt-hover: oklch(1 0 0 / 7%);
7
+ --pdt-danger: oklch(0.65 0.18 25); --pdt-warn: oklch(0.8 0.14 85);
8
+ font-family: ui-sans-serif, system-ui, sans-serif; font-size: 14px; line-height: 1.45; color: var(--pdt-fg); }
9
+ .pdt-root *, .pdt-root *::before, .pdt-root *::after { box-sizing: border-box; }
10
+ /* :where() keeps this reset at zero specificity so component classes below always win it. */
11
+ :where(.pdt-root) button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
12
+ .pdt-root a { color: inherit; text-decoration: none; }
13
+
14
+ .pdt-corner-br { bottom: 16px; right: 16px; } .pdt-corner-bl { bottom: 16px; left: 16px; }
15
+ .pdt-corner-tr { top: 16px; right: 16px; } .pdt-corner-tl { top: 16px; left: 16px; }
16
+
17
+ /* The launcher: solid black, no shadow, a thin border that breathes toward grey — the same
18
+ idiom as Next.js's own dev indicator, so the two read as siblings. */
19
+ @keyframes pdt-fab-breathe { 0%, 100% { border-color: oklch(0.25 0 0); } 50% { border-color: oklch(0.52 0 0); } }
20
+ .pdt-fab { display: flex; align-items: center; justify-content: center; border-radius: 999px;
21
+ background: oklch(0 0 0); color: oklch(0.985 0 0); border: 1px solid oklch(0.25 0 0);
22
+ animation: pdt-fab-breathe 4s ease-in-out infinite; transition: border-color 0.2s ease; }
23
+ .pdt-fab:hover { animation: none; border-color: oklch(0.6 0 0); }
24
+ .pdt-fab.pdt-open { animation: none; border-color: var(--pdt-accent); }
25
+ .pdt-fab-sm { width: 30px; height: 30px; } .pdt-fab-sm svg { width: 14px; height: 14px; }
26
+ .pdt-fab-md { width: 38px; height: 38px; } .pdt-fab-md svg { width: 17px; height: 17px; }
27
+ .pdt-fab-lg { width: 46px; height: 46px; } .pdt-fab-lg svg { width: 21px; height: 21px; }
28
+
29
+ .pdt-panel { position: absolute; width: 330px; max-height: 72vh; display: flex; flex-direction: column;
30
+ overflow: hidden; border-radius: 12px; border: 1px solid var(--pdt-border); background: var(--pdt-bg);
31
+ box-shadow: 0 12px 40px oklch(0 0 0 / 55%); }
32
+ .pdt-panel-br { bottom: calc(100% + 10px); right: 0; } .pdt-panel-bl { bottom: calc(100% + 10px); left: 0; }
33
+ .pdt-panel-tr { top: calc(100% + 10px); right: 0; } .pdt-panel-tl { top: calc(100% + 10px); left: 0; }
34
+
35
+ .pdt-head { display: flex; align-items: center; gap: 8px; padding: 9px 12px;
36
+ border-bottom: 1px solid var(--pdt-border); background: oklch(0.14 0 0); }
37
+ .pdt-head-title { flex: 1; font-family: ui-monospace, Consolas, monospace; font-size: 11px;
38
+ text-transform: uppercase; letter-spacing: 0.08em; }
39
+ .pdt-head-badge { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;
40
+ color: var(--pdt-muted); border: 1px solid var(--pdt-border); border-radius: 4px; padding: 1px 6px; }
41
+ .pdt-back { color: var(--pdt-muted); padding: 0 4px; font-size: 16px; line-height: 1; }
42
+ .pdt-back:hover { color: var(--pdt-fg); }
43
+
44
+ /* Minimal scrollbar for the tiny panel: a bare 4px thumb, no track, no arrows. */
45
+ .pdt-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: oklch(0.42 0 0) transparent; }
46
+ .pdt-body::-webkit-scrollbar { width: 4px; }
47
+ .pdt-body::-webkit-scrollbar-track { background: transparent; }
48
+ .pdt-body::-webkit-scrollbar-thumb { background: oklch(0.42 0 0); border-radius: 999px; }
49
+ .pdt-body::-webkit-scrollbar-thumb:hover { background: oklch(0.58 0 0); }
50
+ .pdt-body::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
51
+ .pdt-menu { list-style: none; margin: 0; padding: 0; }
52
+ .pdt-menu-row { display: flex; width: 100%; align-items: center; justify-content: space-between;
53
+ padding: 9px 10px; border-radius: 8px; text-align: left; }
54
+ .pdt-menu-row:hover { background: var(--pdt-hover); }
55
+ .pdt-menu-hint { display: flex; align-items: center; gap: 8px; color: var(--pdt-muted);
56
+ font-family: ui-monospace, Consolas, monospace; font-size: 11px; }
57
+
58
+ .pdt-card { border: 1px solid var(--pdt-border); border-radius: 10px; padding: 10px; margin: 4px 2px 10px; }
59
+ .pdt-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
60
+ .pdt-card-title { font-weight: 600; }
61
+ .pdt-kind { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;
62
+ letter-spacing: 0.06em; color: var(--pdt-muted); }
63
+ .pdt-viewing { color: var(--pdt-accent); font-weight: 600; }
64
+
65
+ .pdt-chips { display: flex; flex-wrap: wrap; gap: 6px; }
66
+ .pdt-chip { border: 1px solid var(--pdt-border); border-radius: 999px; padding: 5px 14px; font-size: 12.5px;
67
+ background: oklch(0.24 0 0); transition: border-color 0.15s ease, background 0.15s ease; }
68
+ .pdt-chip:hover { background: oklch(0.29 0 0); border-color: oklch(0.45 0 0); }
69
+ .pdt-chip.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }
70
+
71
+ .pdt-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--pdt-border);
72
+ border-radius: 8px; padding: 7px 12px; font-size: 13px; font-weight: 500; }
73
+ .pdt-btn:hover { background: var(--pdt-hover); }
74
+ .pdt-btn:disabled { opacity: 0.55; cursor: default; }
75
+ .pdt-btn-danger { border-color: color-mix(in oklch, var(--pdt-danger), transparent 45%); color: var(--pdt-danger); }
76
+ .pdt-btn-primary { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }
77
+
78
+ .pdt-muted { color: var(--pdt-muted); } .pdt-warn { color: var(--pdt-warn); } .pdt-danger-text { color: var(--pdt-danger); }
79
+ .pdt-small { font-size: 12px; } .pdt-mono { font-family: ui-monospace, Consolas, monospace; font-size: 0.9em; }
80
+ .pdt-note { margin: 6px 2px 4px; font-size: 11.5px; color: var(--pdt-muted); }
81
+ .pdt-code { font-family: ui-monospace, Consolas, monospace; font-size: 0.85em; background: oklch(0.26 0 0);
82
+ padding: 1px 5px; border-radius: 4px; }
83
+ .pdt-error { margin-top: 8px; border: 1px solid color-mix(in oklch, var(--pdt-danger), transparent 55%);
84
+ border-radius: 8px; padding: 8px 10px; font-size: 12.5px; color: var(--pdt-danger); }
85
+ .pdt-error ul { margin: 6px 0 0; padding-left: 18px; color: var(--pdt-fg); }
86
+
87
+ .pdt-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
88
+ .pdt-table td { padding: 4px 8px; border-bottom: 1px solid var(--pdt-border); }
89
+ .pdt-table td:last-child { text-align: right; color: var(--pdt-muted); font-family: ui-monospace, Consolas, monospace; }
90
+ .pdt-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; }
91
+ .pdt-dot-on { background: oklch(0.75 0.15 165); } .pdt-dot-off { background: oklch(0.45 0 0); }
92
+
93
+ .pdt-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 4px; }
94
+ .pdt-corner-grid { display: grid; grid-template-columns: repeat(2, 24px); gap: 3px; }
95
+ .pdt-corner-btn { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
96
+ border: 1px solid var(--pdt-border); border-radius: 5px; font-size: 12px; }
97
+ .pdt-corner-btn:hover { background: var(--pdt-hover); }
98
+ .pdt-corner-btn.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); }
99
+ .pdt-seg { display: flex; overflow: hidden; border: 1px solid var(--pdt-border); border-radius: 6px; }
100
+ .pdt-seg button { padding: 4px 10px; font-size: 11px; text-transform: uppercase; }
101
+ .pdt-seg button:hover { background: var(--pdt-hover); }
102
+ .pdt-seg button.pdt-active { background: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }
103
+
104
+ .pdt-menu-row.pdt-current { background: var(--pdt-hover); }
105
+ .pdt-menu-row.pdt-current > span:first-child { color: var(--pdt-accent); font-weight: 600; }
106
+
107
+ @media print { .pdt-root { display: none !important; } }
108
+ `;
109
+
110
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/styles.ts"],"sourcesContent":["/** The toolbar's entire stylesheet, injected by `<DevToolbar>` as one `<style>` tag so the host\n * app needs no CSS import and no Tailwind. Everything is `.pdt-` prefixed and self-contained\n * (own dark theme); `--pdt-accent` is the override seam. */\nexport const PDT_CSS = `\n.pdt-root { position: fixed; z-index: 999999; color-scheme: dark;\n --pdt-accent: oklch(0.72 0.16 250); --pdt-bg: oklch(0.185 0 0); --pdt-fg: oklch(0.985 0 0);\n --pdt-muted: oklch(0.708 0 0); --pdt-border: oklch(1 0 0 / 12%); --pdt-hover: oklch(1 0 0 / 7%);\n --pdt-danger: oklch(0.65 0.18 25); --pdt-warn: oklch(0.8 0.14 85);\n font-family: ui-sans-serif, system-ui, sans-serif; font-size: 14px; line-height: 1.45; color: var(--pdt-fg); }\n.pdt-root *, .pdt-root *::before, .pdt-root *::after { box-sizing: border-box; }\n/* :where() keeps this reset at zero specificity so component classes below always win it. */\n:where(.pdt-root) button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }\n.pdt-root a { color: inherit; text-decoration: none; }\n\n.pdt-corner-br { bottom: 16px; right: 16px; } .pdt-corner-bl { bottom: 16px; left: 16px; }\n.pdt-corner-tr { top: 16px; right: 16px; } .pdt-corner-tl { top: 16px; left: 16px; }\n\n/* The launcher: solid black, no shadow, a thin border that breathes toward grey — the same\n idiom as Next.js's own dev indicator, so the two read as siblings. */\n@keyframes pdt-fab-breathe { 0%, 100% { border-color: oklch(0.25 0 0); } 50% { border-color: oklch(0.52 0 0); } }\n.pdt-fab { display: flex; align-items: center; justify-content: center; border-radius: 999px;\n background: oklch(0 0 0); color: oklch(0.985 0 0); border: 1px solid oklch(0.25 0 0);\n animation: pdt-fab-breathe 4s ease-in-out infinite; transition: border-color 0.2s ease; }\n.pdt-fab:hover { animation: none; border-color: oklch(0.6 0 0); }\n.pdt-fab.pdt-open { animation: none; border-color: var(--pdt-accent); }\n.pdt-fab-sm { width: 30px; height: 30px; } .pdt-fab-sm svg { width: 14px; height: 14px; }\n.pdt-fab-md { width: 38px; height: 38px; } .pdt-fab-md svg { width: 17px; height: 17px; }\n.pdt-fab-lg { width: 46px; height: 46px; } .pdt-fab-lg svg { width: 21px; height: 21px; }\n\n.pdt-panel { position: absolute; width: 330px; max-height: 72vh; display: flex; flex-direction: column;\n overflow: hidden; border-radius: 12px; border: 1px solid var(--pdt-border); background: var(--pdt-bg);\n box-shadow: 0 12px 40px oklch(0 0 0 / 55%); }\n.pdt-panel-br { bottom: calc(100% + 10px); right: 0; } .pdt-panel-bl { bottom: calc(100% + 10px); left: 0; }\n.pdt-panel-tr { top: calc(100% + 10px); right: 0; } .pdt-panel-tl { top: calc(100% + 10px); left: 0; }\n\n.pdt-head { display: flex; align-items: center; gap: 8px; padding: 9px 12px;\n border-bottom: 1px solid var(--pdt-border); background: oklch(0.14 0 0); }\n.pdt-head-title { flex: 1; font-family: ui-monospace, Consolas, monospace; font-size: 11px;\n text-transform: uppercase; letter-spacing: 0.08em; }\n.pdt-head-badge { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;\n color: var(--pdt-muted); border: 1px solid var(--pdt-border); border-radius: 4px; padding: 1px 6px; }\n.pdt-back { color: var(--pdt-muted); padding: 0 4px; font-size: 16px; line-height: 1; }\n.pdt-back:hover { color: var(--pdt-fg); }\n\n/* Minimal scrollbar for the tiny panel: a bare 4px thumb, no track, no arrows. */\n.pdt-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: oklch(0.42 0 0) transparent; }\n.pdt-body::-webkit-scrollbar { width: 4px; }\n.pdt-body::-webkit-scrollbar-track { background: transparent; }\n.pdt-body::-webkit-scrollbar-thumb { background: oklch(0.42 0 0); border-radius: 999px; }\n.pdt-body::-webkit-scrollbar-thumb:hover { background: oklch(0.58 0 0); }\n.pdt-body::-webkit-scrollbar-button { display: none; width: 0; height: 0; }\n.pdt-menu { list-style: none; margin: 0; padding: 0; }\n.pdt-menu-row { display: flex; width: 100%; align-items: center; justify-content: space-between;\n padding: 9px 10px; border-radius: 8px; text-align: left; }\n.pdt-menu-row:hover { background: var(--pdt-hover); }\n.pdt-menu-hint { display: flex; align-items: center; gap: 8px; color: var(--pdt-muted);\n font-family: ui-monospace, Consolas, monospace; font-size: 11px; }\n\n.pdt-card { border: 1px solid var(--pdt-border); border-radius: 10px; padding: 10px; margin: 4px 2px 10px; }\n.pdt-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }\n.pdt-card-title { font-weight: 600; }\n.pdt-kind { font-family: ui-monospace, Consolas, monospace; font-size: 10px; text-transform: uppercase;\n letter-spacing: 0.06em; color: var(--pdt-muted); }\n.pdt-viewing { color: var(--pdt-accent); font-weight: 600; }\n\n.pdt-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.pdt-chip { border: 1px solid var(--pdt-border); border-radius: 999px; padding: 5px 14px; font-size: 12.5px;\n background: oklch(0.24 0 0); transition: border-color 0.15s ease, background 0.15s ease; }\n.pdt-chip:hover { background: oklch(0.29 0 0); border-color: oklch(0.45 0 0); }\n.pdt-chip.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--pdt-border);\n border-radius: 8px; padding: 7px 12px; font-size: 13px; font-weight: 500; }\n.pdt-btn:hover { background: var(--pdt-hover); }\n.pdt-btn:disabled { opacity: 0.55; cursor: default; }\n.pdt-btn-danger { border-color: color-mix(in oklch, var(--pdt-danger), transparent 45%); color: var(--pdt-danger); }\n.pdt-btn-primary { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-muted { color: var(--pdt-muted); } .pdt-warn { color: var(--pdt-warn); } .pdt-danger-text { color: var(--pdt-danger); }\n.pdt-small { font-size: 12px; } .pdt-mono { font-family: ui-monospace, Consolas, monospace; font-size: 0.9em; }\n.pdt-note { margin: 6px 2px 4px; font-size: 11.5px; color: var(--pdt-muted); }\n.pdt-code { font-family: ui-monospace, Consolas, monospace; font-size: 0.85em; background: oklch(0.26 0 0);\n padding: 1px 5px; border-radius: 4px; }\n.pdt-error { margin-top: 8px; border: 1px solid color-mix(in oklch, var(--pdt-danger), transparent 55%);\n border-radius: 8px; padding: 8px 10px; font-size: 12.5px; color: var(--pdt-danger); }\n.pdt-error ul { margin: 6px 0 0; padding-left: 18px; color: var(--pdt-fg); }\n\n.pdt-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }\n.pdt-table td { padding: 4px 8px; border-bottom: 1px solid var(--pdt-border); }\n.pdt-table td:last-child { text-align: right; color: var(--pdt-muted); font-family: ui-monospace, Consolas, monospace; }\n.pdt-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px; }\n.pdt-dot-on { background: oklch(0.75 0.15 165); } .pdt-dot-off { background: oklch(0.45 0 0); }\n\n.pdt-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 4px; }\n.pdt-corner-grid { display: grid; grid-template-columns: repeat(2, 24px); gap: 3px; }\n.pdt-corner-btn { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;\n border: 1px solid var(--pdt-border); border-radius: 5px; font-size: 12px; }\n.pdt-corner-btn:hover { background: var(--pdt-hover); }\n.pdt-corner-btn.pdt-active { background: var(--pdt-accent); border-color: var(--pdt-accent); color: oklch(0.13 0 0); }\n.pdt-seg { display: flex; overflow: hidden; border: 1px solid var(--pdt-border); border-radius: 6px; }\n.pdt-seg button { padding: 4px 10px; font-size: 11px; text-transform: uppercase; }\n.pdt-seg button:hover { background: var(--pdt-hover); }\n.pdt-seg button.pdt-active { background: var(--pdt-accent); color: oklch(0.13 0 0); font-weight: 600; }\n\n.pdt-menu-row.pdt-current { background: var(--pdt-hover); }\n.pdt-menu-row.pdt-current > span:first-child { color: var(--pdt-accent); font-weight: 600; }\n\n@media print { .pdt-root { display: none !important; } }\n`\n"],"names":["PDT_CSS"],"mappings":"AAAA;;2DAE2D,GAC3D,OAAO,MAAMA,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyGxB,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ /** Session cookie holding the selected test version as `testKey:versionId`. Set by the toolbar's
2
+ * Tests view (or `GET /api/dev/stage` for scripted selection); read server-side by the test
3
+ * pages (`<devRoute>/tests/<key>`), which render the selected version — the toolbar is the
4
+ * controller, the page is the canvas. */
5
+ export declare const STAGE_COOKIE = "pdt-stage";
6
+ /** The chrome-override slots a `header`/`footer`-kind test can occupy. */
7
+ export type ChromeSlot = 'header' | 'footer';
8
+ /** Session cookies holding a chrome override as `testKey:versionId` — set by the toolbar, read by
9
+ * {@link resolveDevChrome} in the host layout, which swaps the REAL header/footer for the
10
+ * selected variant across the whole site (dev only). */
11
+ export declare const CHROME_COOKIES: Record<ChromeSlot, string>;
12
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../src/cookies.ts"],"names":[],"mappings":"AAAA;;;0CAG0C;AAC1C,eAAO,MAAM,YAAY,cAAc,CAAA;AAEvC,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE5C;;yDAEyD;AACzD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAgE,CAAA"}
@@ -0,0 +1,12 @@
1
+ /** Session cookie holding the selected test version as `testKey:versionId`. Set by the toolbar's
2
+ * Tests view (or `GET /api/dev/stage` for scripted selection); read server-side by the test
3
+ * pages (`<devRoute>/tests/<key>`), which render the selected version — the toolbar is the
4
+ * controller, the page is the canvas. */ export const STAGE_COOKIE = 'pdt-stage';
5
+ /** Session cookies holding a chrome override as `testKey:versionId` — set by the toolbar, read by
6
+ * {@link resolveDevChrome} in the host layout, which swaps the REAL header/footer for the
7
+ * selected variant across the whole site (dev only). */ export const CHROME_COOKIES = {
8
+ header: 'pdt-chrome-header',
9
+ footer: 'pdt-chrome-footer'
10
+ };
11
+
12
+ //# sourceMappingURL=cookies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cookies.ts"],"sourcesContent":["/** Session cookie holding the selected test version as `testKey:versionId`. Set by the toolbar's\n * Tests view (or `GET /api/dev/stage` for scripted selection); read server-side by the test\n * pages (`<devRoute>/tests/<key>`), which render the selected version — the toolbar is the\n * controller, the page is the canvas. */\nexport const STAGE_COOKIE = 'pdt-stage'\n\n/** The chrome-override slots a `header`/`footer`-kind test can occupy. */\nexport type ChromeSlot = 'header' | 'footer'\n\n/** Session cookies holding a chrome override as `testKey:versionId` — set by the toolbar, read by\n * {@link resolveDevChrome} in the host layout, which swaps the REAL header/footer for the\n * selected variant across the whole site (dev only). */\nexport const CHROME_COOKIES: Record<ChromeSlot, string> = { header: 'pdt-chrome-header', footer: 'pdt-chrome-footer' }\n"],"names":["STAGE_COOKIE","CHROME_COOKIES","header","footer"],"mappings":"AAAA;;;wCAGwC,GACxC,OAAO,MAAMA,eAAe,YAAW;AAKvC;;uDAEuD,GACvD,OAAO,MAAMC,iBAA6C;IAAEC,QAAQ;IAAqBC,QAAQ;AAAoB,EAAC"}
@@ -0,0 +1,10 @@
1
+ import type { Endpoint } from 'payload';
2
+ /**
3
+ * `POST /api/dev/icons/activate` (body `{ id }`) — activate an icon set, publishing the change so
4
+ * the frontend re-skins immediately. Powers the set switcher on the `/dev/icons` page: flipping
5
+ * between sets to visually compare them is exactly the kind of one-click dev action this plugin
6
+ * exists for. payload-icons' own single-active hook deactivates the others. Dev-only (404s
7
+ * otherwise); requires payload-icons (discovered via its config marker).
8
+ */
9
+ export declare function createActivateIconSetEndpoint(enabled?: boolean): Endpoint;
10
+ //# sourceMappingURL=activateIconSet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"activateIconSet.d.ts","sourceRoot":"","sources":["../../src/endpoints/activateIconSet.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CA4BzE"}
@@ -0,0 +1,55 @@
1
+ import { devToolsEnabled } from "../options.js";
2
+ /**
3
+ * `POST /api/dev/icons/activate` (body `{ id }`) — activate an icon set, publishing the change so
4
+ * the frontend re-skins immediately. Powers the set switcher on the `/dev/icons` page: flipping
5
+ * between sets to visually compare them is exactly the kind of one-click dev action this plugin
6
+ * exists for. payload-icons' own single-active hook deactivates the others. Dev-only (404s
7
+ * otherwise); requires payload-icons (discovered via its config marker).
8
+ */ export function createActivateIconSetEndpoint(enabled) {
9
+ return {
10
+ path: '/dev/icons/activate',
11
+ method: 'post',
12
+ handler: async (req)=>{
13
+ if (!devToolsEnabled(enabled)) return Response.json({
14
+ error: 'Not found'
15
+ }, {
16
+ status: 404
17
+ });
18
+ const marker = req.payload.config.custom?.payloadIcons;
19
+ if (!marker?.iconSetSlug) return Response.json({
20
+ error: 'payload-icons (with its iconSet collection) is not installed.'
21
+ }, {
22
+ status: 400
23
+ });
24
+ const body = await req.json?.().catch(()=>null);
25
+ if (!body?.id) return Response.json({
26
+ error: 'Missing icon set `id`.'
27
+ }, {
28
+ status: 400
29
+ });
30
+ try {
31
+ await req.payload.update({
32
+ collection: marker.iconSetSlug,
33
+ id: body.id,
34
+ data: {
35
+ active: true,
36
+ _status: 'published'
37
+ },
38
+ draft: false,
39
+ overrideAccess: true
40
+ });
41
+ return Response.json({
42
+ success: true
43
+ });
44
+ } catch (e) {
45
+ return Response.json({
46
+ error: e instanceof Error ? e.message : 'Failed to activate icon set.'
47
+ }, {
48
+ status: 500
49
+ });
50
+ }
51
+ }
52
+ };
53
+ }
54
+
55
+ //# sourceMappingURL=activateIconSet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/endpoints/activateIconSet.ts"],"sourcesContent":["import type { CollectionSlug, Endpoint } from 'payload'\nimport { devToolsEnabled } from '../options'\n\n/**\n * `POST /api/dev/icons/activate` (body `{ id }`) — activate an icon set, publishing the change so\n * the frontend re-skins immediately. Powers the set switcher on the `/dev/icons` page: flipping\n * between sets to visually compare them is exactly the kind of one-click dev action this plugin\n * exists for. payload-icons' own single-active hook deactivates the others. Dev-only (404s\n * otherwise); requires payload-icons (discovered via its config marker).\n */\nexport function createActivateIconSetEndpoint(enabled?: boolean): Endpoint {\n return {\n path: '/dev/icons/activate',\n method: 'post',\n handler: async (req) => {\n if (!devToolsEnabled(enabled)) return Response.json({ error: 'Not found' }, { status: 404 })\n\n const marker = req.payload.config.custom?.payloadIcons as { iconSetSlug?: string | null } | undefined\n if (!marker?.iconSetSlug)\n return Response.json({ error: 'payload-icons (with its iconSet collection) is not installed.' }, { status: 400 })\n\n const body = (await req.json?.().catch(() => null)) as { id?: string | number } | null\n if (!body?.id) return Response.json({ error: 'Missing icon set `id`.' }, { status: 400 })\n\n try {\n await req.payload.update({\n collection: marker.iconSetSlug as CollectionSlug,\n id: body.id,\n data: { active: true, _status: 'published' },\n draft: false,\n overrideAccess: true,\n })\n return Response.json({ success: true })\n } catch (e) {\n return Response.json({ error: e instanceof Error ? e.message : 'Failed to activate icon set.' }, { status: 500 })\n }\n },\n }\n}\n"],"names":["devToolsEnabled","createActivateIconSetEndpoint","enabled","path","method","handler","req","Response","json","error","status","marker","payload","config","custom","payloadIcons","iconSetSlug","body","catch","id","update","collection","data","active","_status","draft","overrideAccess","success","e","Error","message"],"mappings":"AACA,SAASA,eAAe,QAAQ,gBAAY;AAE5C;;;;;;CAMC,GACD,OAAO,SAASC,8BAA8BC,OAAiB;IAC7D,OAAO;QACLC,MAAM;QACNC,QAAQ;QACRC,SAAS,OAAOC;YACd,IAAI,CAACN,gBAAgBE,UAAU,OAAOK,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAAY,GAAG;gBAAEC,QAAQ;YAAI;YAE1F,MAAMC,SAASL,IAAIM,OAAO,CAACC,MAAM,CAACC,MAAM,EAAEC;YAC1C,IAAI,CAACJ,QAAQK,aACX,OAAOT,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAAgE,GAAG;gBAAEC,QAAQ;YAAI;YAEjH,MAAMO,OAAQ,MAAMX,IAAIE,IAAI,KAAKU,MAAM,IAAM;YAC7C,IAAI,CAACD,MAAME,IAAI,OAAOZ,SAASC,IAAI,CAAC;gBAAEC,OAAO;YAAyB,GAAG;gBAAEC,QAAQ;YAAI;YAEvF,IAAI;gBACF,MAAMJ,IAAIM,OAAO,CAACQ,MAAM,CAAC;oBACvBC,YAAYV,OAAOK,WAAW;oBAC9BG,IAAIF,KAAKE,EAAE;oBACXG,MAAM;wBAAEC,QAAQ;wBAAMC,SAAS;oBAAY;oBAC3CC,OAAO;oBACPC,gBAAgB;gBAClB;gBACA,OAAOnB,SAASC,IAAI,CAAC;oBAAEmB,SAAS;gBAAK;YACvC,EAAE,OAAOC,GAAG;gBACV,OAAOrB,SAASC,IAAI,CAAC;oBAAEC,OAAOmB,aAAaC,QAAQD,EAAEE,OAAO,GAAG;gBAA+B,GAAG;oBAAEpB,QAAQ;gBAAI;YACjH;QACF;IACF;AACF"}