@sudajs/cli 0.18.8 → 0.18.10
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 +4 -2
- package/dist/index.js +101 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/templates/theme/AGENTS.md +183 -1182
- package/templates/theme/docs/agent-guides/component-authoring.md +270 -0
- package/templates/theme/docs/agent-guides/design-and-runtime.md +170 -0
- package/templates/theme/docs/agent-guides/editor-compatibility.md +231 -0
- package/templates/theme/docs/agent-guides/templates-and-locales.md +206 -0
- package/templates/theme/src/layout.tsx +7 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { ThemeModule, ThemeStarterPage } from '@sudajs/theme-engine';
|
|
3
|
-
import { ThemeRender, extractLayoutChrome, resolveAssetPath } from '@sudajs/theme-engine/server';
|
|
3
|
+
import { ThemeRender, extractLayoutChrome, resolveAssetPath, ThemePreviewLocaleMessages } from '@sudajs/theme-engine/server';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
5
|
import { createElement } from 'react';
|
|
6
6
|
import { renderToString } from 'react-dom/server';
|
|
@@ -1816,6 +1816,7 @@ declare const __testUtils: {
|
|
|
1816
1816
|
performUpdateThemeSettings: typeof performUpdateThemeSettings;
|
|
1817
1817
|
getRemoteThemeSettings: typeof getRemoteThemeSettings;
|
|
1818
1818
|
renderDevStarterPageHtml: typeof renderDevStarterPageHtml;
|
|
1819
|
+
writeVitePreviewEntries: typeof writeVitePreviewEntries;
|
|
1819
1820
|
resolveDevStarterSlug: typeof resolveDevStarterSlug;
|
|
1820
1821
|
resolveScreenshotOptions: typeof resolveScreenshotOptions;
|
|
1821
1822
|
slugifyThemeName: typeof slugifyThemeName;
|
|
@@ -1831,6 +1832,7 @@ declare const __testUtils: {
|
|
|
1831
1832
|
updateRemotePageDraft: typeof updateRemotePageDraft;
|
|
1832
1833
|
};
|
|
1833
1834
|
declare function watchTheme(root: string, port: number): Promise<void>;
|
|
1835
|
+
declare function writeVitePreviewEntries(root: string): Promise<void>;
|
|
1834
1836
|
declare function withSerializedVitePreviewLocaleLoad<Result>(server: object, load: () => Promise<Result>): Promise<Result>;
|
|
1835
1837
|
declare function resolveDevStarterSlug(pathname: string): string | null | undefined;
|
|
1836
1838
|
declare function resolveThemePreviewLocale(params: {
|
|
@@ -1856,7 +1858,7 @@ interface ThemeRenderer {
|
|
|
1856
1858
|
renderToString: typeof renderToString;
|
|
1857
1859
|
}
|
|
1858
1860
|
declare function loadThemeScopedRenderer(themeRoot: string): Promise<ThemeRenderer>;
|
|
1859
|
-
declare function renderDevStarterPageHtml(theme: ValidatedTheme, page: ThemeStarterPage, renderer: ThemeRenderer, locale?: string, preserveLang?: boolean): string;
|
|
1861
|
+
declare function renderDevStarterPageHtml(theme: ValidatedTheme, page: ThemeStarterPage, renderer: ThemeRenderer, locale?: string, preserveLang?: boolean, previewLocaleMessages?: ThemePreviewLocaleMessages): string;
|
|
1860
1862
|
interface ScreenshotOptions extends ThemeRootOptions {
|
|
1861
1863
|
device?: string[] | string;
|
|
1862
1864
|
output?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from 'crypto';
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
|
-
import fs, { readFile, writeFile, stat, readdir,
|
|
4
|
+
import fs, { readFile, mkdir, writeFile, stat, readdir, copyFile, rm } from 'fs/promises';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
6
|
import path2 from 'path';
|
|
7
7
|
import { createInterface } from 'readline/promises';
|
|
@@ -12,6 +12,9 @@ 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';
|
|
16
|
+
import { createElement } from 'react';
|
|
17
|
+
import { renderToStaticMarkup } from 'react-dom/server';
|
|
15
18
|
import { z } from 'zod';
|
|
16
19
|
import os from 'os';
|
|
17
20
|
import pc from 'picocolors';
|
|
@@ -911,6 +914,7 @@ export const {
|
|
|
911
914
|
SUDA_LOCAL_BLOCK_TYPE_PREFIX,
|
|
912
915
|
cmsPaginationUrl,
|
|
913
916
|
getIcpRecord,
|
|
917
|
+
getPublicSecurityRecord,
|
|
914
918
|
getCmsContent,
|
|
915
919
|
getContactForm,
|
|
916
920
|
getPostResource,
|
|
@@ -1519,26 +1523,7 @@ async function buildClientRuntime(root, minify) {
|
|
|
1519
1523
|
await mkdir(path2.dirname(generatedEntry), { recursive: true });
|
|
1520
1524
|
await writeFile(
|
|
1521
1525
|
generatedEntry,
|
|
1522
|
-
|
|
1523
|
-
'"use client";',
|
|
1524
|
-
'import type { ThemeClientHooks, ThemeClientRuntime, ThemeRenderModule } from "@sudajs/theme-engine/runtime";',
|
|
1525
|
-
'import * as themeConfig from "../src/config.js";',
|
|
1526
|
-
clientHooksImport,
|
|
1527
|
-
"const theme: ThemeRenderModule = {",
|
|
1528
|
-
" pageConfig: themeConfig.pageConfig,",
|
|
1529
|
-
" layoutConfig: themeConfig.layoutConfig,",
|
|
1530
|
-
"};",
|
|
1531
|
-
'if ("rendering" in themeConfig && themeConfig.rendering !== undefined) {',
|
|
1532
|
-
" theme.rendering = themeConfig.rendering;",
|
|
1533
|
-
"}",
|
|
1534
|
-
"const hooks: ThemeClientHooks = clientHooks;",
|
|
1535
|
-
"const runtime: ThemeClientRuntime = { theme };",
|
|
1536
|
-
"if (hooks.initialize !== undefined) {",
|
|
1537
|
-
" runtime.initialize = hooks.initialize;",
|
|
1538
|
-
"}",
|
|
1539
|
-
"export default runtime;",
|
|
1540
|
-
""
|
|
1541
|
-
].join("\n"),
|
|
1526
|
+
createClientRuntimeSource(clientHooksImport, true),
|
|
1542
1527
|
"utf8"
|
|
1543
1528
|
);
|
|
1544
1529
|
await mkdir(path2.join(root, "dist"), { recursive: true });
|
|
@@ -1753,6 +1738,7 @@ var __testUtils = {
|
|
|
1753
1738
|
performUpdateThemeSettings,
|
|
1754
1739
|
getRemoteThemeSettings,
|
|
1755
1740
|
renderDevStarterPageHtml,
|
|
1741
|
+
writeVitePreviewEntries,
|
|
1756
1742
|
resolveDevStarterSlug,
|
|
1757
1743
|
resolveScreenshotOptions,
|
|
1758
1744
|
slugifyThemeName,
|
|
@@ -1824,13 +1810,17 @@ async function watchTheme(root, port) {
|
|
|
1824
1810
|
async function startViteDevPreviewServer(root, port) {
|
|
1825
1811
|
const viteConfig = await findViteConfig(root);
|
|
1826
1812
|
const vite = await loadThemeVite(root);
|
|
1813
|
+
await writeVitePreviewEntries(root);
|
|
1827
1814
|
const host = "127.0.0.1";
|
|
1828
1815
|
const server = await vite.createServer({
|
|
1829
1816
|
root,
|
|
1830
1817
|
configFile: viteConfig,
|
|
1831
1818
|
configLoader: "runner",
|
|
1832
|
-
cacheDir: path2.join(root, ".suda-build", "vite
|
|
1819
|
+
cacheDir: path2.join(root, ".suda-build", "node_modules", ".vite"),
|
|
1833
1820
|
appType: "custom",
|
|
1821
|
+
optimizeDeps: {
|
|
1822
|
+
entries: [".suda-build/preview-client.ts"]
|
|
1823
|
+
},
|
|
1834
1824
|
server: { host, port },
|
|
1835
1825
|
plugins: [createSudaPreviewVitePlugin(root)]
|
|
1836
1826
|
});
|
|
@@ -1842,6 +1832,55 @@ async function startViteDevPreviewServer(root, port) {
|
|
|
1842
1832
|
close: () => server.close()
|
|
1843
1833
|
};
|
|
1844
1834
|
}
|
|
1835
|
+
async function writeVitePreviewEntries(root) {
|
|
1836
|
+
const clientHooksPath = path2.join(root, "src", "client.ts");
|
|
1837
|
+
const clientHooksImport = existsSync(clientHooksPath) ? 'import { clientHooks } from "../src/client.js";' : "const clientHooks = {};";
|
|
1838
|
+
const buildDir = path2.join(root, ".suda-build");
|
|
1839
|
+
await mkdir(buildDir, { recursive: true });
|
|
1840
|
+
await writeFile(
|
|
1841
|
+
path2.join(buildDir, "runtime.dev.ts"),
|
|
1842
|
+
createClientRuntimeSource(clientHooksImport, false),
|
|
1843
|
+
"utf8"
|
|
1844
|
+
);
|
|
1845
|
+
await writeFile(
|
|
1846
|
+
path2.join(buildDir, "preview-client.ts"),
|
|
1847
|
+
[
|
|
1848
|
+
'import { hydrateThemePage, loadThemeRuntime } from "@sudajs/theme-engine/client";',
|
|
1849
|
+
'import { installThemePreviewLocale, resolveAssetPath } from "@sudajs/theme-engine/site-runtime";',
|
|
1850
|
+
'const payloadElement=document.querySelector("#suda-theme-hydration-data");',
|
|
1851
|
+
'if(!payloadElement){throw new Error("Missing Suda theme hydration payload.")}',
|
|
1852
|
+
"const payload=JSON.parse(payloadElement.textContent ?? '{}');",
|
|
1853
|
+
"if(payload.previewLocaleMessages){installThemePreviewLocale(payload.previewLocaleMessages)}",
|
|
1854
|
+
'const runtime=(await loadThemeRuntime("/.suda-build/runtime.dev.ts")).default;',
|
|
1855
|
+
'const metadata={contactForm:payload.contactForm,resolveAssetUrl:(value: string | undefined)=>resolveAssetPath(value,{legacyResolve:(key)=>key.startsWith("themes/")?`/api/themes/${key.slice("themes/".length)}`:undefined})};',
|
|
1856
|
+
'await hydrateThemePage({container:document.querySelector("[data-theme-hydration-root]")!,runtime,pageData:payload.pageData,layoutData:payload.layoutData,metadata,onRecoverableError:(error)=>console.error("[theme-dev] theme hydration recovered from an error",error)});',
|
|
1857
|
+
""
|
|
1858
|
+
].join("\n"),
|
|
1859
|
+
"utf8"
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
function createClientRuntimeSource(clientHooksImport, useClientDirective) {
|
|
1863
|
+
return [
|
|
1864
|
+
...useClientDirective ? ['"use client";'] : [],
|
|
1865
|
+
'import type { ThemeClientHooks, ThemeClientRuntime, ThemeRenderModule } from "@sudajs/theme-engine/runtime";',
|
|
1866
|
+
'import * as themeConfig from "../src/config.js";',
|
|
1867
|
+
clientHooksImport,
|
|
1868
|
+
"const theme: ThemeRenderModule = {",
|
|
1869
|
+
" pageConfig: themeConfig.pageConfig,",
|
|
1870
|
+
" layoutConfig: themeConfig.layoutConfig,",
|
|
1871
|
+
"};",
|
|
1872
|
+
'if ("rendering" in themeConfig && themeConfig.rendering !== undefined) {',
|
|
1873
|
+
" theme.rendering = themeConfig.rendering;",
|
|
1874
|
+
"}",
|
|
1875
|
+
"const hooks: ThemeClientHooks = clientHooks;",
|
|
1876
|
+
"const runtime: ThemeClientRuntime = { theme };",
|
|
1877
|
+
"if (hooks.initialize !== undefined) {",
|
|
1878
|
+
" runtime.initialize = hooks.initialize;",
|
|
1879
|
+
"}",
|
|
1880
|
+
"export default runtime;",
|
|
1881
|
+
""
|
|
1882
|
+
].join("\n");
|
|
1883
|
+
}
|
|
1845
1884
|
function createSudaPreviewVitePlugin(root) {
|
|
1846
1885
|
return {
|
|
1847
1886
|
name: "suda-theme-preview",
|
|
@@ -1864,54 +1903,31 @@ function createSudaPreviewVitePlugin(root) {
|
|
|
1864
1903
|
const acceptLanguageHeader = request.headers["accept-language"];
|
|
1865
1904
|
const acceptLanguage = Array.isArray(acceptLanguageHeader) ? acceptLanguageHeader.join(",") : acceptLanguageHeader;
|
|
1866
1905
|
const explicitLang = url.searchParams.get("lang");
|
|
1867
|
-
const
|
|
1906
|
+
const localized = await loadLocalizedViteDevTheme(
|
|
1868
1907
|
root,
|
|
1869
1908
|
server,
|
|
1870
1909
|
explicitLang,
|
|
1871
1910
|
acceptLanguage
|
|
1872
1911
|
);
|
|
1873
1912
|
const renderer = await loadThemeScopedRenderer(root);
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
if (!homeSlug) {
|
|
1877
|
-
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1878
|
-
response.end("No starter pages declared by this theme.");
|
|
1879
|
-
return;
|
|
1880
|
-
}
|
|
1881
|
-
const starter2 = findStarterPage(theme, homeSlug);
|
|
1882
|
-
if (!starter2) {
|
|
1883
|
-
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1884
|
-
response.end(`Unknown starter page: ${homeSlug}`);
|
|
1885
|
-
return;
|
|
1886
|
-
}
|
|
1887
|
-
const html2 = await server.transformIndexHtml(
|
|
1888
|
-
url.pathname,
|
|
1889
|
-
renderDevStarterPageHtml(
|
|
1890
|
-
theme,
|
|
1891
|
-
starter2,
|
|
1892
|
-
renderer,
|
|
1893
|
-
locale,
|
|
1894
|
-
canonicalLocale(explicitLang) !== null
|
|
1895
|
-
)
|
|
1896
|
-
);
|
|
1897
|
-
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1898
|
-
response.end(html2);
|
|
1899
|
-
return;
|
|
1900
|
-
}
|
|
1901
|
-
const starter = findStarterPage(theme, slug);
|
|
1913
|
+
const starterSlug = slug ?? pickStarterSlug(localized.theme);
|
|
1914
|
+
const starter = starterSlug ? findStarterPage(localized.theme, starterSlug) : null;
|
|
1902
1915
|
if (!starter) {
|
|
1903
1916
|
response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1904
|
-
response.end(
|
|
1917
|
+
response.end(
|
|
1918
|
+
starterSlug ? `Unknown starter page: ${starterSlug}` : "No starter pages declared by this theme."
|
|
1919
|
+
);
|
|
1905
1920
|
return;
|
|
1906
1921
|
}
|
|
1907
1922
|
const html = await server.transformIndexHtml(
|
|
1908
1923
|
url.pathname,
|
|
1909
1924
|
renderDevStarterPageHtml(
|
|
1910
|
-
theme,
|
|
1925
|
+
localized.theme,
|
|
1911
1926
|
starter,
|
|
1912
1927
|
renderer,
|
|
1913
|
-
locale,
|
|
1914
|
-
canonicalLocale(explicitLang) !== null
|
|
1928
|
+
localized.locale,
|
|
1929
|
+
canonicalLocale(explicitLang) !== null,
|
|
1930
|
+
localized.previewLocaleMessages
|
|
1915
1931
|
)
|
|
1916
1932
|
);
|
|
1917
1933
|
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
@@ -1939,13 +1955,13 @@ async function loadLocalizedViteDevTheme(root, server, explicitLang, acceptLangu
|
|
|
1939
1955
|
if (!baseTheme.module.manifest.previewLocales) {
|
|
1940
1956
|
return { theme: baseTheme, locale };
|
|
1941
1957
|
}
|
|
1942
|
-
const
|
|
1958
|
+
const previewLocaleMessages = await readDevPreviewLocaleMessages(root, locale);
|
|
1943
1959
|
server.moduleGraph.invalidateAll();
|
|
1944
1960
|
const theme = await runWithThemePreviewLocale(
|
|
1945
|
-
|
|
1961
|
+
previewLocaleMessages,
|
|
1946
1962
|
() => loadViteDevTheme(root, server)
|
|
1947
1963
|
);
|
|
1948
|
-
return { theme, locale };
|
|
1964
|
+
return { theme, locale, previewLocaleMessages };
|
|
1949
1965
|
});
|
|
1950
1966
|
}
|
|
1951
1967
|
var vitePreviewLocaleLoadTails = /* @__PURE__ */ new WeakMap();
|
|
@@ -1997,6 +2013,9 @@ function resolveDevStarterSlug(pathname) {
|
|
|
1997
2013
|
if (pathname === "/") {
|
|
1998
2014
|
return null;
|
|
1999
2015
|
}
|
|
2016
|
+
if (pathname.startsWith("/@")) {
|
|
2017
|
+
return void 0;
|
|
2018
|
+
}
|
|
2000
2019
|
const normalized = pathname.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
2001
2020
|
if (!normalized || normalized.includes("/")) {
|
|
2002
2021
|
return void 0;
|
|
@@ -2103,7 +2122,7 @@ async function loadThemeScopedRenderer(themeRoot) {
|
|
|
2103
2122
|
renderToString: reactDomServerModule.renderToString
|
|
2104
2123
|
};
|
|
2105
2124
|
}
|
|
2106
|
-
function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserveLang = false) {
|
|
2125
|
+
function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserveLang = false, previewLocaleMessages) {
|
|
2107
2126
|
const chrome = renderer.extractLayoutChrome(theme.module.defaultLayout);
|
|
2108
2127
|
const metadata = {
|
|
2109
2128
|
resolveAssetUrl: createPreviewAssetResolver(renderer.resolveAssetPath),
|
|
@@ -2121,6 +2140,16 @@ function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserve
|
|
|
2121
2140
|
const customHead = chrome.customHeadCode ?? "";
|
|
2122
2141
|
const customBody = chrome.customBodyCode ?? "";
|
|
2123
2142
|
const designPalette = renderDevDesignPalette(theme.module.manifest.designSystem);
|
|
2143
|
+
const hydrationPayload = serializeInlineScriptValue({
|
|
2144
|
+
pageData: page.data,
|
|
2145
|
+
layoutData: theme.module.defaultLayout,
|
|
2146
|
+
contactForm: PREVIEW_CONTACT_FORM,
|
|
2147
|
+
previewLocaleMessages
|
|
2148
|
+
});
|
|
2149
|
+
const hydrationScript = [
|
|
2150
|
+
`<script id="suda-theme-hydration-data" type="application/json">${hydrationPayload}</script>`,
|
|
2151
|
+
'<script type="module">void import("/.suda-build/preview-client.ts").catch((error)=>console.error("[theme-dev] theme client failed to load",error))</script>'
|
|
2152
|
+
].join("");
|
|
2124
2153
|
return [
|
|
2125
2154
|
"<!doctype html>",
|
|
2126
2155
|
`<html lang="${escapeHtml(locale)}">`,
|
|
@@ -2133,9 +2162,10 @@ function renderDevStarterPageHtml(theme, page, renderer, locale = "en", preserve
|
|
|
2133
2162
|
customHead,
|
|
2134
2163
|
"</head>",
|
|
2135
2164
|
`<body${cssVarStyle ? ` style="${cssVarStyle}"` : ""}>`,
|
|
2136
|
-
body
|
|
2165
|
+
`<div data-theme-hydration-root="" style="display:contents">${body}</div>`,
|
|
2137
2166
|
designPalette.html,
|
|
2138
2167
|
customBody,
|
|
2168
|
+
hydrationScript,
|
|
2139
2169
|
...preserveLang ? [
|
|
2140
2170
|
'<script>for(const a of document.querySelectorAll("a[href]")){try{const u=new URL(a.href,location.href);if(u.origin===location.origin){u.searchParams.set("lang",new URL(location.href).searchParams.get("lang"));a.href=u.pathname+u.search+u.hash}}catch{}}</script>'
|
|
2141
2171
|
] : [],
|
|
@@ -2154,19 +2184,28 @@ function renderDevDesignPalette(designSystem) {
|
|
|
2154
2184
|
id: preset.id,
|
|
2155
2185
|
variables: createThemeDesignCssVariables(preset.tokens)
|
|
2156
2186
|
}));
|
|
2187
|
+
const selectedIcon = renderToStaticMarkup(
|
|
2188
|
+
createElement(CircleCheck, {
|
|
2189
|
+
className: "suda-dev-preset-selected-icon",
|
|
2190
|
+
width: 32,
|
|
2191
|
+
height: 32,
|
|
2192
|
+
"aria-hidden": true,
|
|
2193
|
+
focusable: false
|
|
2194
|
+
})
|
|
2195
|
+
);
|
|
2157
2196
|
const buttons = designSystem.presets.map(
|
|
2158
2197
|
(preset) => `<button type="button" data-suda-dev-preset="${escapeHtml(preset.id)}" title="${escapeHtml(
|
|
2159
2198
|
preset.description ? `${preset.label} \u2014 ${preset.description}` : preset.label
|
|
2160
2199
|
)}" aria-label="${escapeHtml(preset.label)}"><span style="background:${escapeHtml(
|
|
2161
2200
|
preset.tokens.colors.primary
|
|
2162
|
-
)}"></span
|
|
2201
|
+
)}"></span>${selectedIcon}</button>`
|
|
2163
2202
|
).join("");
|
|
2164
2203
|
const config = serializeInlineScriptValue({
|
|
2165
2204
|
defaultPresetId: designSystem.defaultPresetId,
|
|
2166
2205
|
presets
|
|
2167
2206
|
});
|
|
2168
2207
|
return {
|
|
2169
|
-
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
|
|
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>',
|
|
2170
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>`
|
|
2171
2210
|
};
|
|
2172
2211
|
}
|