@sudajs/cli 0.18.10 → 0.18.11

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/dist/index.d.ts CHANGED
@@ -1816,7 +1816,9 @@ declare const __testUtils: {
1816
1816
  performUpdateThemeSettings: typeof performUpdateThemeSettings;
1817
1817
  getRemoteThemeSettings: typeof getRemoteThemeSettings;
1818
1818
  renderDevStarterPageHtml: typeof renderDevStarterPageHtml;
1819
+ renderDevEditorPageHtml: typeof renderDevEditorPageHtml;
1819
1820
  writeVitePreviewEntries: typeof writeVitePreviewEntries;
1821
+ resolveDevEditorSlug: typeof resolveDevEditorSlug;
1820
1822
  resolveDevStarterSlug: typeof resolveDevStarterSlug;
1821
1823
  resolveScreenshotOptions: typeof resolveScreenshotOptions;
1822
1824
  slugifyThemeName: typeof slugifyThemeName;
@@ -1834,6 +1836,7 @@ declare const __testUtils: {
1834
1836
  declare function watchTheme(root: string, port: number): Promise<void>;
1835
1837
  declare function writeVitePreviewEntries(root: string): Promise<void>;
1836
1838
  declare function withSerializedVitePreviewLocaleLoad<Result>(server: object, load: () => Promise<Result>): Promise<Result>;
1839
+ declare function resolveDevEditorSlug(pathname: string): string | null | undefined;
1837
1840
  declare function resolveDevStarterSlug(pathname: string): string | null | undefined;
1838
1841
  declare function resolveThemePreviewLocale(params: {
1839
1842
  previewLocales: string[] | undefined;
@@ -1858,7 +1861,8 @@ interface ThemeRenderer {
1858
1861
  renderToString: typeof renderToString;
1859
1862
  }
1860
1863
  declare function loadThemeScopedRenderer(themeRoot: string): Promise<ThemeRenderer>;
1861
- declare function renderDevStarterPageHtml(theme: ValidatedTheme, page: ThemeStarterPage, renderer: ThemeRenderer, locale?: string, preserveLang?: boolean, previewLocaleMessages?: ThemePreviewLocaleMessages): string;
1864
+ declare function renderDevStarterPageHtml(theme: ValidatedTheme, page: ThemeStarterPage, renderer: ThemeRenderer, locale?: string, preserveLang?: boolean, previewLocaleMessages?: ThemePreviewLocaleMessages, searchParams?: URLSearchParams): string;
1865
+ declare function renderDevEditorPageHtml(theme: ValidatedTheme, page: ThemeStarterPage, renderer: ThemeRenderer, locale?: string, searchParams?: URLSearchParams, previewLocaleMessages?: ThemePreviewLocaleMessages): string;
1862
1866
  interface ScreenshotOptions extends ThemeRootOptions {
1863
1867
  device?: string[] | string;
1864
1868
  output?: string;
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import { themeDesignSystemSchema, createThemeDesignCssVariables, createThemeAgen
12
12
  import { themeManifestSchema, runWithThemePreviewLocale } from '@sudajs/theme-engine/server';
13
13
  import { Command } from 'commander';
14
14
  import { build } from 'esbuild';
15
- import { CircleCheck } from 'lucide-react';
15
+ import { CircleCheck, Pencil } from 'lucide-react';
16
16
  import { createElement } from 'react';
17
17
  import { renderToStaticMarkup } from 'react-dom/server';
18
18
  import { z } from 'zod';
@@ -1738,7 +1738,9 @@ var __testUtils = {
1738
1738
  performUpdateThemeSettings,
1739
1739
  getRemoteThemeSettings,
1740
1740
  renderDevStarterPageHtml,
1741
+ renderDevEditorPageHtml,
1741
1742
  writeVitePreviewEntries,
1743
+ resolveDevEditorSlug,
1742
1744
  resolveDevStarterSlug,
1743
1745
  resolveScreenshotOptions,
1744
1746
  slugifyThemeName,
@@ -1819,7 +1821,7 @@ async function startViteDevPreviewServer(root, port) {
1819
1821
  cacheDir: path2.join(root, ".suda-build", "node_modules", ".vite"),
1820
1822
  appType: "custom",
1821
1823
  optimizeDeps: {
1822
- entries: [".suda-build/preview-client.ts"]
1824
+ entries: [".suda-build/preview-client.ts", ".suda-build/editor-client.ts"]
1823
1825
  },
1824
1826
  server: { host, port },
1825
1827
  plugins: [createSudaPreviewVitePlugin(root)]
@@ -1858,6 +1860,28 @@ async function writeVitePreviewEntries(root) {
1858
1860
  ].join("\n"),
1859
1861
  "utf8"
1860
1862
  );
1863
+ await writeFile(
1864
+ path2.join(buildDir, "editor-client.ts"),
1865
+ [
1866
+ 'import "@puckeditor/core/puck.css";',
1867
+ 'import { composeUnifiedThemeEditorData, createUnifiedThemeEditorConfig, installThemePreviewLocale, resolveAssetPath, ThemeEditor } from "@sudajs/theme-engine";',
1868
+ 'import { createElement } from "react";',
1869
+ 'import { createRoot } from "react-dom/client";',
1870
+ 'import runtime from "./runtime.dev";',
1871
+ 'const payloadElement=document.querySelector("#suda-theme-editor-data");',
1872
+ 'if(!payloadElement){throw new Error("Missing Suda theme editor payload.")}',
1873
+ "const payload=JSON.parse(payloadElement.textContent ?? '{}');",
1874
+ "if(payload.previewLocaleMessages){installThemePreviewLocale(payload.previewLocaleMessages)}",
1875
+ 'const resolveAssetUrl=(value: string | undefined)=>resolveAssetPath(value,{legacyResolve:(key)=>key.startsWith("themes/")?`/api/themes/${key.slice("themes/".length)}`:undefined});',
1876
+ 'const config=createUnifiedThemeEditorConfig(runtime.theme,undefined,{labels:{pageContent:"Page content",themeSettings:"Theme settings"}});',
1877
+ "const data=composeUnifiedThemeEditorData(payload.layoutData,payload.pageData,{layoutConfig:runtime.theme.layoutConfig,pageConfig:runtime.theme.pageConfig});",
1878
+ 'const editorContext={assets:[],resolveAssetUrl,currentPageId:payload.currentPageId,backHref:payload.backHref,editorPages:payload.editorPages,editorLocale:payload.locale,chromeMessages:{backToPages:"Back to preview",pageMenuLabel:"Switch starter page",themeSettings:"Theme settings",outline:"Outline",blocks:"Blocks",switchViewport:"Switch viewport"}};',
1879
+ "const metadata={contactForm:payload.contactForm,isEditor:true,resolveAssetUrl};",
1880
+ 'createRoot(document.querySelector("#suda-theme-dev-editor-root")!).render(createElement(ThemeEditor,{config,data,editorContext,metadata,iframe:false,height:"100%",publishActionVisible:false}));',
1881
+ ""
1882
+ ].join("\n"),
1883
+ "utf8"
1884
+ );
1861
1885
  }
1862
1886
  function createClientRuntimeSource(clientHooksImport, useClientDirective) {
1863
1887
  return [
@@ -1894,11 +1918,14 @@ function createSudaPreviewVitePlugin(root) {
1894
1918
  next();
1895
1919
  return;
1896
1920
  }
1897
- const slug = resolveDevStarterSlug(url.pathname);
1898
- if (slug === void 0) {
1921
+ const editorSlug = resolveDevEditorSlug(url.pathname);
1922
+ const previewSlug = resolveDevStarterSlug(url.pathname);
1923
+ if (editorSlug === void 0 && previewSlug === void 0) {
1899
1924
  next();
1900
1925
  return;
1901
1926
  }
1927
+ const isEditor = editorSlug !== void 0;
1928
+ const slug = isEditor ? editorSlug : previewSlug;
1902
1929
  try {
1903
1930
  const acceptLanguageHeader = request.headers["accept-language"];
1904
1931
  const acceptLanguage = Array.isArray(acceptLanguageHeader) ? acceptLanguageHeader.join(",") : acceptLanguageHeader;
@@ -1919,17 +1946,23 @@ function createSudaPreviewVitePlugin(root) {
1919
1946
  );
1920
1947
  return;
1921
1948
  }
1922
- const html = await server.transformIndexHtml(
1923
- url.pathname,
1924
- renderDevStarterPageHtml(
1925
- localized.theme,
1926
- starter,
1927
- renderer,
1928
- localized.locale,
1929
- canonicalLocale(explicitLang) !== null,
1930
- localized.previewLocaleMessages
1931
- )
1949
+ const htmlSource = isEditor ? renderDevEditorPageHtml(
1950
+ localized.theme,
1951
+ starter,
1952
+ renderer,
1953
+ localized.locale,
1954
+ url.searchParams,
1955
+ localized.previewLocaleMessages
1956
+ ) : renderDevStarterPageHtml(
1957
+ localized.theme,
1958
+ starter,
1959
+ renderer,
1960
+ localized.locale,
1961
+ canonicalLocale(explicitLang) !== null,
1962
+ localized.previewLocaleMessages,
1963
+ url.searchParams
1932
1964
  );
1965
+ const html = await server.transformIndexHtml(url.pathname, htmlSource);
1933
1966
  response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
1934
1967
  response.end(html);
1935
1968
  } catch (error2) {
@@ -2009,6 +2042,20 @@ function pickStarterSlug(theme) {
2009
2042
  function findStarterPage(theme, slug) {
2010
2043
  return theme.module.starterPages.find((page) => page.slug === slug) ?? null;
2011
2044
  }
2045
+ var DEV_EDITOR_PATH = "/__suda/editor";
2046
+ function resolveDevEditorSlug(pathname) {
2047
+ if (pathname === DEV_EDITOR_PATH || pathname === `${DEV_EDITOR_PATH}/`) {
2048
+ return null;
2049
+ }
2050
+ if (!pathname.startsWith(`${DEV_EDITOR_PATH}/`)) {
2051
+ return void 0;
2052
+ }
2053
+ const normalized = pathname.slice(DEV_EDITOR_PATH.length + 1).replace(/\/+$/, "");
2054
+ if (!normalized || normalized.includes("/")) {
2055
+ return void 0;
2056
+ }
2057
+ return decodeURIComponent(normalized);
2058
+ }
2012
2059
  function resolveDevStarterSlug(pathname) {
2013
2060
  if (pathname === "/") {
2014
2061
  return null;
@@ -2122,8 +2169,9 @@ async function loadThemeScopedRenderer(themeRoot) {
2122
2169
  renderToString: reactDomServerModule.renderToString
2123
2170
  };
2124
2171
  }
2125
- function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserveLang = false, previewLocaleMessages) {
2126
- const chrome = renderer.extractLayoutChrome(theme.module.defaultLayout);
2172
+ function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserveLang = false, previewLocaleMessages, searchParams = new URLSearchParams()) {
2173
+ const layoutData = createDevPreviewLayout(theme, searchParams);
2174
+ const chrome = renderer.extractLayoutChrome(layoutData);
2127
2175
  const metadata = {
2128
2176
  resolveAssetUrl: createPreviewAssetResolver(renderer.resolveAssetPath),
2129
2177
  contactForm: PREVIEW_CONTACT_FORM
@@ -2132,17 +2180,20 @@ function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserve
2132
2180
  renderer.createElement(renderer.ThemeRender, {
2133
2181
  theme: theme.module,
2134
2182
  pageData: page.data,
2135
- layoutData: theme.module.defaultLayout,
2183
+ layoutData,
2136
2184
  metadata
2137
2185
  })
2138
2186
  );
2139
2187
  const cssVarStyle = Object.entries(chrome.cssVariables).map(([key, value]) => `${key}: ${value};`).join(" ");
2140
2188
  const customHead = chrome.customHeadCode ?? "";
2141
2189
  const customBody = chrome.customBodyCode ?? "";
2142
- const designPalette = renderDevDesignPalette(theme.module.manifest.designSystem);
2190
+ const designPalette = renderDevDesignPalette(
2191
+ theme.module.manifest.designSystem,
2192
+ createDevEditorHref(page.slug, searchParams)
2193
+ );
2143
2194
  const hydrationPayload = serializeInlineScriptValue({
2144
2195
  pageData: page.data,
2145
- layoutData: theme.module.defaultLayout,
2196
+ layoutData,
2146
2197
  contactForm: PREVIEW_CONTACT_FORM,
2147
2198
  previewLocaleMessages
2148
2199
  });
@@ -2173,13 +2224,95 @@ function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserve
2173
2224
  "</html>"
2174
2225
  ].join("");
2175
2226
  }
2227
+ function copyDevPreviewSearchParams(searchParams) {
2228
+ const copied = new URLSearchParams();
2229
+ for (const key of ["lang", "designPreset"]) {
2230
+ const value = searchParams.get(key);
2231
+ if (value) {
2232
+ copied.set(key, value);
2233
+ }
2234
+ }
2235
+ return copied;
2236
+ }
2237
+ function createDevEditorHref(slug, searchParams) {
2238
+ const query = copyDevPreviewSearchParams(searchParams).toString();
2239
+ return `${DEV_EDITOR_PATH}/${encodeURIComponent(slug)}${query ? `?${query}` : ""}`;
2240
+ }
2241
+ function createDevPreviewHref(page, searchParams) {
2242
+ const pathname = page.isHome ? "/" : `/${encodeURIComponent(page.slug)}`;
2243
+ const query = copyDevPreviewSearchParams(searchParams).toString();
2244
+ return `${pathname}${query ? `?${query}` : ""}`;
2245
+ }
2246
+ function createDevPreviewLayout(theme, searchParams) {
2247
+ const requestedPresetId = searchParams.get("designPreset");
2248
+ const requestedPreset = requestedPresetId ? theme.module.manifest.designSystem.presets.find(
2249
+ (preset) => preset.id === requestedPresetId
2250
+ ) : void 0;
2251
+ if (!requestedPreset) {
2252
+ return theme.module.defaultLayout;
2253
+ }
2254
+ const rootFields = theme.module.layoutConfig.root?.fields ?? {};
2255
+ const designFieldName = Object.entries(rootFields).find(
2256
+ ([, field]) => field.metadata?.sudaField === "designSystem"
2257
+ )?.[0];
2258
+ if (!designFieldName) {
2259
+ return theme.module.defaultLayout;
2260
+ }
2261
+ const layoutData = structuredClone(theme.module.defaultLayout);
2262
+ layoutData.root = {
2263
+ ...layoutData.root ?? {},
2264
+ props: {
2265
+ ...layoutData.root?.props ?? {},
2266
+ [designFieldName]: { presetId: requestedPreset.id }
2267
+ }
2268
+ };
2269
+ return layoutData;
2270
+ }
2271
+ function renderDevEditorPageHtml(theme, page, renderer, locale = "en", searchParams = new URLSearchParams(), previewLocaleMessages) {
2272
+ const layoutData = createDevPreviewLayout(theme, searchParams);
2273
+ const chrome = renderer.extractLayoutChrome(layoutData);
2274
+ const cssVariables = { ...chrome.cssVariables };
2275
+ const cssVarStyle = Object.entries(cssVariables).map(([key, value]) => `${key}: ${value};`).join(" ");
2276
+ const editorPages = theme.module.starterPages.map((starterPage) => ({
2277
+ id: starterPage.slug,
2278
+ handle: starterPage.slug,
2279
+ label: starterPage.title,
2280
+ group: starterPage.isHome ? "home" : "static",
2281
+ editorHref: createDevEditorHref(starterPage.slug, searchParams)
2282
+ }));
2283
+ const payload = serializeInlineScriptValue({
2284
+ pageData: page.data,
2285
+ layoutData,
2286
+ contactForm: PREVIEW_CONTACT_FORM,
2287
+ previewLocaleMessages,
2288
+ locale,
2289
+ currentPageId: page.slug,
2290
+ backHref: createDevPreviewHref(page, searchParams),
2291
+ editorPages
2292
+ });
2293
+ return [
2294
+ "<!doctype html>",
2295
+ `<html lang="${escapeHtml(locale)}">`,
2296
+ "<head>",
2297
+ '<meta charset="utf-8" />',
2298
+ '<meta name="viewport" content="width=device-width, initial-scale=1" />',
2299
+ `<title>${escapeHtml(`Edit ${theme.module.manifest.name} \u2014 ${page.title}`)}</title>`,
2300
+ '<link rel="stylesheet" href="/src/styles.css" />',
2301
+ "<style>html,body,#suda-theme-dev-editor-root{height:100%;margin:0}body{overflow:hidden}</style>",
2302
+ "</head>",
2303
+ `<body${cssVarStyle ? ` style="${cssVarStyle}"` : ""}>`,
2304
+ '<div id="suda-theme-dev-editor-root"></div>',
2305
+ `<script id="suda-theme-editor-data" type="application/json">${payload}</script>`,
2306
+ '<script type="module">void import("/.suda-build/editor-client.ts").catch((error)=>console.error("[theme-dev] theme editor failed to load",error))</script>',
2307
+ "</body>",
2308
+ "</html>"
2309
+ ].join("");
2310
+ }
2176
2311
  function serializeInlineScriptValue(value) {
2177
2312
  return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
2178
2313
  }
2179
- function renderDevDesignPalette(designSystem) {
2180
- if (designSystem.presets.length < 2) {
2181
- return { style: "", html: "" };
2182
- }
2314
+ function renderDevDesignPalette(designSystem, editorHref) {
2315
+ const hasPresetSwitcher = designSystem.presets.length >= 2;
2183
2316
  const presets = designSystem.presets.map((preset) => ({
2184
2317
  id: preset.id,
2185
2318
  variables: createThemeDesignCssVariables(preset.tokens)
@@ -2193,20 +2326,28 @@ function renderDevDesignPalette(designSystem) {
2193
2326
  focusable: false
2194
2327
  })
2195
2328
  );
2196
- const buttons = designSystem.presets.map(
2329
+ const editIcon = renderToStaticMarkup(
2330
+ createElement(Pencil, {
2331
+ width: 20,
2332
+ height: 20,
2333
+ "aria-hidden": true,
2334
+ focusable: false
2335
+ })
2336
+ );
2337
+ const buttons = hasPresetSwitcher ? designSystem.presets.map(
2197
2338
  (preset) => `<button type="button" data-suda-dev-preset="${escapeHtml(preset.id)}" title="${escapeHtml(
2198
2339
  preset.description ? `${preset.label} \u2014 ${preset.description}` : preset.label
2199
2340
  )}" aria-label="${escapeHtml(preset.label)}"><span style="background:${escapeHtml(
2200
2341
  preset.tokens.colors.primary
2201
2342
  )}"></span>${selectedIcon}</button>`
2202
- ).join("");
2343
+ ).join("") : "";
2203
2344
  const config = serializeInlineScriptValue({
2204
2345
  defaultPresetId: designSystem.defaultPresetId,
2205
2346
  presets
2206
2347
  });
2207
2348
  return {
2208
- style: '<style data-suda-dev-palette-style>\n#suda-dev-palette{all:initial;position:fixed;z-index:2147483000;top:50%;right:16px;width:64px;max-height:calc(100svh - 32px);padding:8px 9px;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;background:#fff;border:1px solid #d1d5db;border-radius:999px;box-shadow:0 8px 24px rgb(15 23 42 / 12%);transform:translateY(-50%);color-scheme:light;scrollbar-width:none;-ms-overflow-style:none}\n#suda-dev-palette::-webkit-scrollbar{display:none}\n#suda-dev-palette button{all:initial;position:relative;display:grid;width:44px;height:44px;box-sizing:border-box;cursor:pointer;border-radius:999px;place-items:center}\n#suda-dev-palette button span{display:block;width:32px;height:32px;border-radius:999px;transition:transform 120ms ease}\n#suda-dev-palette button:hover span{transform:scale(1.08)}\n#suda-dev-palette button[aria-pressed="true"] span{visibility:hidden}\n#suda-dev-palette .suda-dev-preset-selected-icon{position:absolute;display:block;width:32px;height:32px;color:var(--suda-dev-preset-primary);visibility:hidden;opacity:0;transform:scale(.85);transition:opacity 120ms ease,transform 120ms ease}\n#suda-dev-palette button[aria-pressed="true"] .suda-dev-preset-selected-icon{visibility:visible;opacity:1;transform:scale(1)}\n#suda-dev-palette button:focus-visible{outline:2px solid #94a3b8;outline-offset:2px}\n@media(max-width:640px){#suda-dev-palette{right:8px;max-height:calc(100svh - 16px)}}\n@media(prefers-reduced-motion:reduce){#suda-dev-palette button span,#suda-dev-palette .suda-dev-preset-selected-icon{transition:none}}\n</style>',
2209
- html: `<aside id="suda-dev-palette" aria-label="Design presets">${buttons}</aside><script data-suda-dev-palette-script>(function(){const config=${config};const palette=document.getElementById("suda-dev-palette");if(!palette)return;const byId=new Map(config.presets.map((preset)=>[preset.id,preset]));const params=new URL(location.href).searchParams;const requested=params.get("designPreset");const selected=byId.has(requested)?requested:config.defaultPresetId;function apply(id,updateUrl){const preset=byId.get(id);if(!preset)return;const properties=Object.keys(preset.variables);const targets=[document.documentElement,document.body,...document.querySelectorAll("[style]")].filter((element,index,items)=>items.indexOf(element)===index&&(element===document.documentElement||element===document.body||properties.some((property)=>element.style.getPropertyValue(property)!=="")));for(const target of targets){for(const [property,value] of Object.entries(preset.variables))target.style.setProperty(property,value)}for(const button of palette.querySelectorAll("[data-suda-dev-preset]")){const active=button.dataset.sudaDevPreset===id;button.setAttribute("aria-pressed",String(active));button.style.setProperty("--suda-dev-preset-primary",byId.get(button.dataset.sudaDevPreset).variables["--suda-color-primary"])}if(updateUrl){const url=new URL(location.href);url.searchParams.set("designPreset",id);history.replaceState(history.state,"",url)}}if(requested&&byId.has(requested))apply(selected,false);else for(const button of palette.querySelectorAll("[data-suda-dev-preset]")){button.setAttribute("aria-pressed",String(button.dataset.sudaDevPreset===selected));button.style.setProperty("--suda-dev-preset-primary",byId.get(button.dataset.sudaDevPreset).variables["--suda-color-primary"])}palette.addEventListener("click",(event)=>{const button=event.target.closest("[data-suda-dev-preset]");if(button)apply(button.dataset.sudaDevPreset,true)})})();</script>`
2349
+ style: '<style data-suda-dev-palette-style>\n#suda-dev-palette{all:initial;position:fixed;z-index:2147483000;top:50%;right:16px;width:64px;max-height:calc(100svh - 32px);padding:8px 9px;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;background:#fff;border:1px solid #d1d5db;border-radius:999px;box-shadow:0 8px 24px rgb(15 23 42 / 12%);transform:translateY(-50%);color-scheme:light;scrollbar-width:none;-ms-overflow-style:none}\n#suda-dev-palette::-webkit-scrollbar{display:none}\n#suda-dev-palette button{all:initial;position:relative;display:grid;width:44px;height:44px;box-sizing:border-box;cursor:pointer;border-radius:999px;place-items:center}\n#suda-dev-palette button span{display:block;width:32px;height:32px;border-radius:999px;transition:transform 120ms ease}\n#suda-dev-palette button:hover span{transform:scale(1.08)}\n#suda-dev-palette button[aria-pressed="true"] span{visibility:hidden}\n#suda-dev-palette .suda-dev-preset-selected-icon{position:absolute;display:block;width:32px;height:32px;color:var(--suda-dev-preset-primary);visibility:hidden;opacity:0;transform:scale(.85);transition:opacity 120ms ease,transform 120ms ease}\n#suda-dev-palette button[aria-pressed="true"] .suda-dev-preset-selected-icon{visibility:visible;opacity:1;transform:scale(1)}\n#suda-dev-palette button:focus-visible{outline:2px solid #94a3b8;outline-offset:2px}\n#suda-dev-palette .suda-dev-edit-divider{display:block;width:28px;height:1px;margin:4px auto;background:#e2e8f0}\n#suda-dev-palette .suda-dev-edit-link{all:initial;display:grid;width:44px;height:44px;box-sizing:border-box;color:#334155;cursor:pointer;border-radius:999px;place-items:center;transition:background-color 120ms ease,color 120ms ease}\n#suda-dev-palette .suda-dev-edit-link:hover{color:#0f172a;background:#f1f5f9}\n#suda-dev-palette .suda-dev-edit-link:focus-visible{outline:2px solid #94a3b8;outline-offset:2px}\n@media(max-width:640px){#suda-dev-palette{right:8px;max-height:calc(100svh - 16px)}}\n@media(prefers-reduced-motion:reduce){#suda-dev-palette button span,#suda-dev-palette .suda-dev-preset-selected-icon,#suda-dev-palette .suda-dev-edit-link{transition:none}}\n</style>',
2350
+ html: `<aside id="suda-dev-palette" aria-label="Theme development tools">${buttons}${hasPresetSwitcher ? '<span class="suda-dev-edit-divider" aria-hidden="true"></span>' : ""}<a class="suda-dev-edit-link" data-suda-dev-editor-link="" href="${escapeHtml(editorHref)}" title="Edit starter page" aria-label="Edit starter page">${editIcon}</a></aside>` + (hasPresetSwitcher ? `<script data-suda-dev-palette-script>(function(){const config=${config};const palette=document.getElementById("suda-dev-palette");if(!palette)return;const byId=new Map(config.presets.map((preset)=>[preset.id,preset]));const params=new URL(location.href).searchParams;const requested=params.get("designPreset");const selected=byId.has(requested)?requested:config.defaultPresetId;function apply(id,updateUrl){const preset=byId.get(id);if(!preset)return;const properties=Object.keys(preset.variables);const targets=[document.documentElement,document.body,...document.querySelectorAll("[style]")].filter((element,index,items)=>items.indexOf(element)===index&&(element===document.documentElement||element===document.body||properties.some((property)=>element.style.getPropertyValue(property)!=="")));for(const target of targets){for(const [property,value] of Object.entries(preset.variables))target.style.setProperty(property,value)}for(const button of palette.querySelectorAll("[data-suda-dev-preset]")){const active=button.dataset.sudaDevPreset===id;button.setAttribute("aria-pressed",String(active));button.style.setProperty("--suda-dev-preset-primary",byId.get(button.dataset.sudaDevPreset).variables["--suda-color-primary"])}const editorLink=palette.querySelector("[data-suda-dev-editor-link]");if(editorLink){const editorUrl=new URL(editorLink.href,location.href);editorUrl.searchParams.set("designPreset",id);editorLink.href=editorUrl.pathname+editorUrl.search+editorUrl.hash}if(updateUrl){const url=new URL(location.href);url.searchParams.set("designPreset",id);history.replaceState(history.state,"",url)}}if(requested&&byId.has(requested))apply(selected,false);else for(const button of palette.querySelectorAll("[data-suda-dev-preset]")){button.setAttribute("aria-pressed",String(button.dataset.sudaDevPreset===selected));button.style.setProperty("--suda-dev-preset-primary",byId.get(button.dataset.sudaDevPreset).variables["--suda-color-primary"])}palette.addEventListener("click",(event)=>{const button=event.target.closest("[data-suda-dev-preset]");if(button)apply(button.dataset.sudaDevPreset,true)})})();</script>` : "")
2210
2351
  };
2211
2352
  }
2212
2353
  var SCREENSHOT_DEVICES = ["desktop", "tablet", "mobile"];