@styleglide/theme-editor
Version:
Add the StyleGlide theme editor to any shadcn/ui app
629 lines (618 loc) • 22.1 kB
JavaScript
;
// src/lib/origin.ts
var STYLEGLIDE_ORIGIN = "https://www.styleglide.ai";
// src/lib/default-options.ts
var defaultOptions = {
origin: STYLEGLIDE_ORIGIN,
cssColorFormat: "colorSpace",
onChangeMode: () => {
},
resolvedMode: "light",
enabled: true
};
// src/lib/hotkeys.ts
var HOTKEYS = ["mod+z", "mod+shift+z", "t", "s", "e", "r", "g"];
var currentListeners = [];
function initHotkeys({ origin }) {
if (typeof window === "undefined") {
return;
}
cleanupHotkeys();
function handleKeyDown(event) {
if (isInputEvent(event)) {
return;
}
const keyCombo = buildKeyCombo(event);
if (HOTKEYS.includes(keyCombo)) {
event.preventDefault();
const message = {
action: "hotkey",
payload: { keys: keyCombo }
};
window.parent.postMessage(message, origin);
}
}
window.addEventListener("keydown", handleKeyDown);
currentListeners.push(
() => window.removeEventListener("keydown", handleKeyDown)
);
}
function cleanupHotkeys() {
currentListeners.forEach((cleanup) => cleanup());
currentListeners = [];
}
function buildKeyCombo(event) {
const parts = [];
if (event.ctrlKey || event.metaKey) {
parts.push("mod");
}
if (event.shiftKey) {
parts.push("shift");
}
const key = event.key.toLowerCase();
if (key !== "control" && key !== "meta" && key !== "shift") {
parts.push(key);
}
return parts.join("+");
}
function isInputEvent(event) {
const element = event.target;
return element.matches("input, select, textarea, [contenteditable]");
}
// src/lib/logo.ts
function createLogo(mode) {
const container = document.createElement("div");
container.innerHTML = `
<svg
xmlns="http://www.w3.org/2000/svg"
id="Layer_1"
viewBox="0 0 922.18 197.54"
shape-rendering="geometricPrecision"
width="120"
height="24"
>
<path d="m65.46 79.59-9.38 5.45-24.8-58.47c2.74-2.56 5.63-4.97 8.64-7.21l25.54 60.23Z" fill="#419ef9" />
<path d="m75.34 68.31-10.75 1.44-.53-63.53c3.49-1.31 7.07-2.43 10.73-3.34l.55 65.43Z" fill="#21b2f9" />
<path d="M113.3 1.01 88.77 61.66l-10.48-2.79L102.1 0c3.79.12 7.53.46 11.2 1.01Z" fill="#00c7f9" />
<path d="m149.62 14.01-45.88 46.65-8.62-6.58 44.54-45.3c3.43 1.56 6.75 3.31 9.96 5.23Z" fill="#2ab3f7" />
<path d="m178.18 39.91-60.23 25.55-5.44-9.38 58.47-24.8c2.56 2.74 4.96 5.61 7.21 8.63h-.01Z" fill="#52a0f5" />
<path d="m194.67 74.78-65.44.55-1.44-10.75 63.54-.53c1.31 3.49 2.43 7.07 3.34 10.73Z" fill="#7b8cf3" />
<path d="M197.54 102.1c-.12 3.8-.46 7.53-1.01 11.21l-60.65-24.53 2.78-10.49 58.88 23.81Z" fill="#a579f0" />
<path d="M188.77 139.66a98.137 98.137 0 0 1-5.24 9.95l-46.65-45.88 6.59-8.62 45.3 44.54Z" fill="#ce65ee" />
<path d="M166.26 170.97a99.38 99.38 0 0 1-8.64 7.21l-25.54-60.23 9.38-5.45 24.79 58.47h.01Z" fill="#f851ec" />
<path d="M133.49 191.32a97.424 97.424 0 0 1-10.73 3.34l-.55-65.43 10.75-1.45.53 63.54Z" fill="#e754ef" />
<path d="m119.25 138.66-23.81 58.88c-3.8-.12-7.53-.46-11.21-1.01l24.53-60.65 10.48 2.78Z" fill="#d657f1" />
<path d="m102.42 143.46-44.54 45.3c-3.42-1.55-6.75-3.3-9.94-5.23l45.87-46.65 8.62 6.59Z" fill="#c35bf4" />
<path d="m85.04 141.46-58.47 24.8c-2.56-2.74-4.96-5.62-7.21-8.64l60.23-25.55 5.44 9.38Z" fill="#b25ef6" />
<path d="m69.75 132.96-63.54.53a97.424 97.424 0 0 1-3.34-10.73l65.44-.55 1.44 10.75Z" fill="#a161f9" />
<path d="m61.66 108.76-2.78 10.49L0 95.44c.12-3.8.46-7.53 1.01-11.21l60.65 24.53Z" fill="#8175f9" />
<path d="m60.66 93.8-6.59 8.62-45.3-44.54c1.56-3.43 3.31-6.75 5.23-9.95L60.66 93.8Z" fill="#6289f9" />
<path
d="M282.6 156.35c-9.78 0-17.97-1.51-24.57-4.54-6.6-3.02-11.57-7.26-14.89-12.7s-5.04-11.64-5.14-18.6h20.41c.1 4.13 1.03 7.71 2.8 10.73 1.76 3.02 4.46 5.39 8.09 7.11s8.32 2.57 14.06 2.57c6.85 0 12.35-1.28 16.48-3.85 4.13-2.57 6.2-6.37 6.2-11.41 0-2.92-.45-5.37-1.36-7.33-.91-1.97-2.42-3.6-4.54-4.91s-4.91-2.42-8.39-3.33c-3.48-.91-7.84-1.81-13.08-2.72-6.25-1.01-11.77-2.34-16.56-4.01s-8.79-3.7-12.02-6.12c-3.23-2.42-5.65-5.37-7.26-8.84-1.61-3.48-2.42-7.64-2.42-12.47 0-6.65 1.69-12.4 5.06-17.24s8.19-8.59 14.44-11.26 13.71-4.01 22.38-4.01 15.9 1.36 22 4.08c6.1 2.72 10.81 6.53 14.14 11.41 3.33 4.89 4.99 10.56 4.99 17.01h-20.11c-.2-3.93-1.26-7.18-3.17-9.75-1.92-2.57-4.46-4.46-7.64-5.67-3.17-1.21-6.83-1.81-10.96-1.81s-7.89.6-10.96 1.81c-3.08 1.21-5.42 2.9-7.03 5.06-1.61 2.17-2.42 4.76-2.42 7.79 0 3.73.93 6.58 2.8 8.54 1.86 1.96 4.89 3.5 9.07 4.61s9.6 2.22 16.25 3.33c4.74.81 9.35 1.81 13.83 3.02s8.52 3 12.1 5.37 6.43 5.59 8.54 9.68c2.12 4.08 3.17 9.35 3.17 15.8s-1.66 12.15-4.99 17.08c-3.33 4.94-8.27 8.77-14.82 11.49s-14.72 4.08-24.49 4.08h.01ZM362.88 154.98c-4.94 0-8.79-.73-11.57-2.19-2.77-1.46-4.71-3.55-5.82-6.27-1.11-2.72-1.66-6-1.66-9.83V89.82h-14.82V74.55h14.82V51.87l18.75-1.97v24.64h17.08v15.27h-17.08v43.39c0 2.52.55 4.21 1.66 5.06 1.11.86 2.82 1.29 5.14 1.29h10.28v15.42h-16.78ZM386.61 180.23v-15.57h8.92c2.42 0 4.43-.3 6.05-.91 1.61-.6 2.97-1.56 4.08-2.87 1.11-1.31 2.02-2.97 2.72-4.99l4.38-11.34v8.47l-32.81-78.47h20.71l16.48 45.96 3.02 9.07h.76l2.72-9.07 15.57-45.96h20.11l-32.51 85.72c-1.81 5.14-4.06 9.17-6.73 12.1-2.67 2.92-5.75 4.96-9.22 6.12-3.48 1.16-7.44 1.74-11.87 1.74h-12.4.02ZM463.87 154.98V44.76h18.9v110.22h-18.9ZM532.36 156.35c-8.27 0-15.32-1.71-21.17-5.14s-10.36-8.21-13.53-14.36c-3.18-6.15-4.76-13.35-4.76-21.62s1.56-16 4.69-22.3c3.12-6.3 7.61-11.19 13.46-14.67 5.84-3.48 12.7-5.22 20.56-5.22 9.17 0 16.68 2.02 22.53 6.05 5.84 4.03 10.05 9.48 12.62 16.33 2.57 6.86 3.4 14.67 2.49 23.43H511.5c0 4.84.88 8.97 2.65 12.4 1.76 3.43 4.21 6.05 7.33 7.86s6.75 2.72 10.88 2.72c4.84 0 8.92-1.08 12.25-3.25s5.44-5.07 6.35-8.69h17.99c-.71 5.24-2.72 9.85-6.05 13.83-3.33 3.98-7.61 7.08-12.85 9.3s-11.14 3.33-17.69 3.33Zm-20.86-47.78-1.66-2.42h42.79l-1.81 2.42c.2-4.84-.53-8.79-2.19-11.87-1.66-3.07-3.98-5.37-6.96-6.88-2.97-1.51-6.33-2.27-10.05-2.27-4.03 0-7.54.86-10.51 2.57-2.97 1.72-5.29 4.13-6.95 7.26-1.66 3.13-2.55 6.86-2.65 11.19ZM629.27 156.35c-8.47 0-16.05-1.34-22.75-4.01s-12.4-6.5-17.08-11.49-8.27-10.96-10.73-17.92c-2.47-6.96-3.7-14.67-3.7-23.13s1.33-16.76 4.01-23.66c2.67-6.9 6.43-12.8 11.26-17.69 4.84-4.89 10.63-8.62 17.39-11.19 6.75-2.57 14.26-3.86 22.53-3.86 9.57 0 17.99 1.59 25.25 4.76s13.08 7.61 17.46 13.3c4.38 5.7 7.03 12.37 7.94 20.03h-20.56c-.91-4.43-2.75-8.19-5.52-11.26s-6.25-5.44-10.43-7.11c-4.18-1.66-8.9-2.49-14.14-2.49-6.96 0-13.05 1.49-18.29 4.46-5.24 2.97-9.27 7.38-12.09 13.23s-4.23 13-4.23 21.47c0 6.35.78 12.02 2.34 17.01s3.86 9.15 6.88 12.47 6.63 5.87 10.81 7.64c4.18 1.76 8.84 2.65 13.98 2.65 6.75 0 12.6-1.23 17.54-3.7 4.94-2.47 8.77-6.05 11.49-10.73 2.72-4.69 4.08-10.25 4.08-16.71v-3.93l5.9 6.96H629.9V96.94h53.07v9.22c0 7.66-1.29 14.59-3.86 20.79-2.57 6.2-6.2 11.47-10.89 15.8s-10.33 7.69-16.93 10.05c-6.6 2.37-13.94 3.55-22 3.55h-.02ZM693.52 154.98V44.76h18.9v110.22h-18.9ZM727.54 63.21V44.77h18.9v18.44h-18.9Zm0 91.77V74.55h18.9v80.43h-18.9ZM788.92 156.35c-4.84 0-9.25-.88-13.23-2.65-3.98-1.76-7.41-4.41-10.28-7.94s-5.06-7.89-6.58-13.08c-1.51-5.19-2.27-11.16-2.27-17.92 0-9.17 1.33-16.86 4.01-23.06 2.67-6.2 6.45-10.86 11.34-13.99 4.89-3.12 10.56-4.69 17.01-4.69 4.43 0 8.39.68 11.87 2.04s6.48 3.25 9 5.67 4.43 5.29 5.75 8.62h1.06v-44.6h18.9l-.15 110.22h-18.14l-.6-14.97h-1.06c-1.92 4.94-5.19 8.9-9.83 11.87s-10.23 4.46-16.78 4.46l-.02.02Zm7.11-15.42c4.54 0 8.29-1.06 11.26-3.17 2.97-2.12 5.22-4.86 6.73-8.24s2.27-6.93 2.27-10.66v-8.32c0-2.92-.43-5.69-1.29-8.32-.86-2.62-2.12-4.96-3.78-7.03-1.66-2.06-3.78-3.7-6.35-4.91s-5.52-1.81-8.84-1.81c-4.03 0-7.56.96-10.58 2.87-3.02 1.92-5.39 4.81-7.11 8.69-1.71 3.88-2.57 8.79-2.57 14.74s.86 10.58 2.57 14.51 4.08 6.86 7.11 8.77c3.02 1.92 6.55 2.87 10.58 2.87ZM884.93 156.35c-8.27 0-15.32-1.71-21.17-5.14-5.85-3.43-10.36-8.21-13.53-14.36-3.17-6.15-4.76-13.35-4.76-21.62s1.56-16 4.69-22.3c3.12-6.3 7.61-11.19 13.46-14.67 5.84-3.48 12.7-5.22 20.56-5.22 9.17 0 16.68 2.02 22.53 6.05 5.84 4.03 10.05 9.48 12.62 16.33 2.57 6.86 3.4 14.67 2.49 23.43h-57.75c0 4.84.88 8.97 2.65 12.4 1.76 3.43 4.21 6.05 7.33 7.86s6.75 2.72 10.89 2.72c4.84 0 8.92-1.08 12.25-3.25s5.44-5.07 6.35-8.69h17.99c-.71 5.24-2.72 9.85-6.05 13.83-3.33 3.98-7.61 7.08-12.85 9.3s-11.14 3.33-17.69 3.33Zm-20.86-47.78-1.66-2.42h42.79l-1.81 2.42c.2-4.84-.53-8.79-2.19-11.87-1.66-3.07-3.98-5.37-6.96-6.88-2.97-1.51-6.33-2.27-10.05-2.27-4.03 0-7.54.86-10.51 2.57-2.97 1.72-5.29 4.13-6.95 7.26-1.66 3.13-2.55 6.86-2.65 11.19h-.01Z"
fill="${mode === "dark" ? "#fff" : "#000"}"
/>
</svg>
`;
return container.firstElementChild;
}
// src/lib/utils.ts
function isDesktop() {
return typeof window !== "undefined" && window.innerWidth >= 1024;
}
function cleanupElement(element) {
if (element) {
element.remove();
}
element = null;
}
function setStyles(element, ...styles) {
Object.assign(element.style, ...styles);
}
// src/lib/buttons.ts
function createOpenButton() {
const button = document.createElement("button");
setStyles(button, style.base, style.hidden, style.open);
setTimeout(() => {
setStyles(button, style.visible);
}, 2e3);
return button;
}
function createCloseButton(resolvedMode) {
const button = document.createElement("button");
button.innerHTML = "\u2715";
setStyles(
button,
style.base,
style.hidden,
style[resolvedMode],
isDesktop() ? style.closeDesktop : style.close
);
return button;
}
function updateOpenButton(button, resolvedMode) {
if (!button)
return;
setStyles(button, style[resolvedMode]);
updateOpenButtonLogo(button, resolvedMode);
}
function updateCloseButton(button, resolvedMode) {
if (!button)
return;
setStyles(button, style[resolvedMode]);
}
function updateOpenButtonLogo(button, resolvedMode) {
const logo = createLogo(resolvedMode);
if (logo) {
button.innerHTML = "";
button.appendChild(logo);
}
}
function showOpenButton(button) {
if (!button)
return;
setTimeout(() => {
setStyles(button, style.visible, style.open);
}, 400);
}
function hideOpenButton(button) {
if (!button)
return;
setStyles(button, style.hidden);
}
function showCloseButton(button) {
if (!button)
return;
setStyles(
button,
style.visible,
isDesktop() ? style.closeDesktop : style.close
);
}
function hideCloseButton(button) {
if (!button)
return;
setStyles(button, style.hidden);
}
function onClick(button, handler) {
button.addEventListener("click", handler);
}
function findCustomOpenButton() {
return document.querySelector("[data-theme-editor-open]");
}
var style = {
base: {
position: "fixed",
bottom: "1.825rem",
left: "50%",
padding: "0.5rem 0.875rem",
borderWidth: "1px",
borderStyle: "solid",
borderRadius: "1.5rem",
cursor: "pointer",
zIndex: "2147483647",
boxShadow: "0 4px 20px rgba(0,0,0,0.15)",
userSelect: "none"
},
light: {
backgroundColor: "#fff",
borderColor: "#E4E4E7"
},
dark: {
backgroundColor: "#17191A",
borderColor: "#27272A"
},
hidden: {
opacity: "0",
pointerEvents: "none",
transition: "auto"
},
visible: {
opacity: "1",
pointerEvents: "auto",
transition: "opacity 0.3s ease"
},
open: {
transform: "translateX(-50%)"
},
close: {
transform: "translateX(-50%) translateY(-6.25rem)"
},
closeDesktop: {
transform: "translateX(calc(-50% + 18rem))"
}
};
// src/lib/iframe.ts
var currentIframe = null;
var currentCloseButton = null;
var currentOpenButton = null;
var currentResizeListener = null;
var initialOverflow = null;
var isCustomOpenButton = false;
var open = false;
function initIframe({
origin,
cssColorFormat,
resolvedMode
}) {
const isClient = typeof window !== "undefined";
const isEmbedded = isClient && window.location.search.includes("_embedded=1");
if (!isClient || isEmbedded) {
return;
}
cleanupIframe();
const iframe = document.createElement("iframe");
const currentUrl = new URL(window.location.href);
currentUrl.searchParams.set("_embedded", "1");
const iframeUrl = new URL(`${origin}/themes`);
iframeUrl.searchParams.set("url", currentUrl.toString());
iframeUrl.searchParams.set("format", cssColorFormat);
iframe.src = iframeUrl.toString();
iframe.allow = "clipboard-write";
iframe.addEventListener(
"load",
() => sendInitialMode(iframe, origin, resolvedMode)
);
setStyles(iframe, style2.base, style2.closed);
document.body.appendChild(iframe);
const customOpenButton = findCustomOpenButton();
isCustomOpenButton = !!customOpenButton;
const openButton = customOpenButton || createOpenButton();
onClick(openButton, () => openIframe(origin));
if (!isCustomOpenButton) {
updateOpenButton(openButton, resolvedMode);
document.body.appendChild(openButton);
}
const closeButton = createCloseButton(resolvedMode);
onClick(closeButton, () => closeIframe(origin));
document.body.appendChild(closeButton);
currentIframe = iframe;
currentOpenButton = openButton;
currentCloseButton = closeButton;
hideCloseButton(closeButton);
initResizeListener();
}
function updateIframe({
resolvedMode
}) {
if (!isCustomOpenButton) {
updateOpenButton(currentOpenButton, resolvedMode);
}
updateCloseButton(currentCloseButton, resolvedMode);
}
function cleanupIframe() {
cleanupElement(currentIframe);
cleanupElement(currentCloseButton);
cleanupResizeListener();
restoreOverflow();
if (!isCustomOpenButton) {
cleanupElement(currentOpenButton);
}
isCustomOpenButton = false;
open = false;
}
function openIframe(origin) {
if (!currentIframe)
return;
initialOverflow = document.body.style.overflow || null;
setStyles(currentIframe, style2.open);
setStyles(document.body, { overflow: "hidden" });
showCloseButton(currentCloseButton);
if (!isCustomOpenButton) {
hideOpenButton(currentOpenButton);
}
postMessage(currentIframe, origin, {
action: "iframeEditorOpened"
});
open = true;
}
function closeIframe(origin) {
if (!currentIframe)
return;
postMessage(currentIframe, origin, {
action: "iframeEditorClosed"
});
if (!isCustomOpenButton) {
showOpenButton(currentOpenButton);
}
hideCloseButton(currentCloseButton);
open = false;
const timeout = isDesktop() ? 350 : 0;
setTimeout(() => {
if (!currentIframe)
return;
setStyles(currentIframe, style2.closed);
restoreOverflow();
}, timeout);
}
function sendInitialMode(iframe, origin, resolvedMode) {
const message = {
action: "modeToggled",
payload: resolvedMode
};
setTimeout(() => postMessage(iframe, origin, message), 100);
}
function postMessage(iframe, origin, message) {
iframe.contentWindow?.postMessage(message, origin);
}
function initResizeListener() {
currentResizeListener = () => {
if (open) {
showCloseButton(currentCloseButton);
}
};
window.addEventListener("resize", currentResizeListener);
}
function cleanupResizeListener() {
if (!currentResizeListener)
return;
window.removeEventListener("resize", currentResizeListener);
currentResizeListener = null;
}
function restoreOverflow() {
if (initialOverflow) {
setStyles(document.body, { overflow: initialOverflow });
} else {
document.body.style.removeProperty("overflow");
}
}
var style2 = {
base: {
position: "fixed",
top: "0",
left: "0",
width: "100vw",
height: "100vh",
border: "none"
},
closed: {
zIndex: "-1",
opacity: "0",
pointerEvents: "none"
},
open: {
zIndex: "2147483646",
opacity: "1",
pointerEvents: "auto"
}
};
// src/lib/theme-listener.ts
var currentStylesheet = null;
var currentResolvedMode = null;
var currentOnChangeMode = null;
var currentMessageListener = null;
function initThemeListener({
origin,
resolvedMode,
onChangeMode
}) {
if (typeof window === "undefined") {
return () => {
};
}
currentResolvedMode = resolvedMode;
currentOnChangeMode = onChangeMode;
cleanupThemeListener();
const themeListener = (event) => {
if (event.origin !== origin) {
return;
}
if (event.data.action === "styleKitUpdated") {
const { css, displayFontUrl, textFontUrl, mode } = event.data?.payload || {};
loadThemeCss(css);
loadFontLink(displayFontUrl);
loadFontLink(textFontUrl);
handleFontStyles();
if (mode !== currentResolvedMode && currentOnChangeMode) {
currentOnChangeMode(mode);
const root = document.documentElement;
root.style.colorScheme = mode;
}
}
};
currentMessageListener = themeListener;
window.addEventListener("message", themeListener);
}
function updateThemeListener({
resolvedMode,
onChangeMode
}) {
currentResolvedMode = resolvedMode;
currentOnChangeMode = onChangeMode;
}
function cleanupThemeListener() {
if (!currentMessageListener)
return;
window.removeEventListener("message", currentMessageListener);
currentMessageListener = null;
}
function loadFontLink(url) {
if (!url || isFontLinkLoaded(url))
return;
const link = document.createElement("link");
link.href = url;
link.rel = "stylesheet";
document.head.appendChild(link);
}
function isFontLinkLoaded(url) {
return Array.from(document.head.getElementsByTagName("link")).some(
(link) => link.href === url
);
}
function loadThemeCss(css) {
if (!css)
return;
if (!currentStylesheet) {
currentStylesheet = document.createElement("style");
currentStylesheet.id = "styleglide-theme";
document.head.appendChild(currentStylesheet);
}
currentStylesheet.textContent = css;
}
function hasClassNames(classNames) {
return document.getElementsByClassName(classNames).length > 0;
}
function handleFontStyles() {
if (!hasClassNames("font-text")) {
const body = document.body;
body.style.setProperty("font-family", "var(--text-family)", "important");
body.style.setProperty("font-weight", "var(--text-weight)", "important");
}
if (!hasClassNames("font-display")) {
const headings = document.querySelectorAll(
"h1, h2, h3, h4, h5, h6"
);
headings.forEach((heading) => {
heading.style.setProperty(
"font-family",
"var(--display-family)",
"important"
);
heading.style.setProperty(
"font-weight",
"var(--display-weight)",
"important"
);
});
}
}
// src/lib/theme-editor.ts
var prevProps = null;
var isInitialized = false;
var warnedOptions = /* @__PURE__ */ new Set();
function themeEditor(options = {}) {
const props = setProps(options);
const { enabled } = props;
if (!enabled && prevProps?.enabled && isInitialized) {
cleanupAll();
isInitialized = false;
}
if (!enabled) {
return save(props);
}
if (!prevProps || !isInitialized) {
initAll(props);
isInitialized = true;
return save(props);
}
function updated(...keys) {
return keys.some((key) => prevProps?.[key] !== props[key]);
}
if (updated("origin")) {
initAll(props);
return save(props);
}
if (updated("resolvedMode", "onChangeMode")) {
updateThemeListener(props);
}
if (updated("resolvedMode")) {
updateIframe(props);
}
if (updated("cssColorFormat")) {
cleanupIframe();
initIframe(props);
}
return save(props);
}
function initAll(props) {
initHotkeys(props);
initIframe(props);
initThemeListener(props);
}
function cleanupAll() {
cleanupHotkeys();
cleanupIframe();
cleanupThemeListener();
}
function save(props) {
prevProps = props;
}
function setProps(options = {}) {
const validatedOptions = validateOptions(options);
return { ...defaultOptions, ...validatedOptions };
}
function validateOptions(options) {
const validatedOptions = {};
for (const [key, value] of Object.entries(options)) {
switch (key) {
case "origin":
if (typeof value === "string") {
try {
new URL(value);
validatedOptions.origin = value;
} catch {
warnInvalidOption({
key,
expected: "valid string URL",
received: value
});
}
} else {
warnInvalidOption({
key,
expected: "valid string URL",
received: value
});
}
break;
case "cssColorFormat":
if (value === "channels" || value === "colorSpace") {
validatedOptions.cssColorFormat = value;
} else {
warnInvalidOption({
key,
expected: "'channels' or 'colorSpace'",
received: value
});
}
break;
case "onChangeMode":
if (typeof value === "function") {
validatedOptions.onChangeMode = value;
} else {
warnInvalidOption({ key, expected: "function", received: value });
}
break;
case "resolvedMode":
if (value === "light" || value === "dark") {
validatedOptions.resolvedMode = value;
} else {
warnInvalidOption({
key,
expected: "'light' or 'dark'",
received: value
});
}
break;
case "enabled":
if (typeof value === "boolean") {
validatedOptions.enabled = value;
} else {
warnInvalidOption({ key, expected: "boolean", received: value });
}
break;
default:
console.warn(
`[@styleglide/theme-editor]: Unknown option '${key}' provided. Ignoring.`
);
}
}
return validatedOptions;
}
function warnInvalidOption({
key,
expected,
received
}) {
const isClient = typeof window !== "undefined";
const isEmbedded = isClient && window.location.search.includes("_embedded=1");
if (isEmbedded) {
return;
}
if (!warnedOptions.has(key)) {
warnedOptions.add(key);
console.warn(
`[@styleglide/theme-editor]: Invalid option '${key}': expected ${expected}, got ${typeof received === "string" ? `'${received}'` : typeof received}. Reverting to default.`
);
}
}
exports.themeEditor = themeEditor;