Files

423 lines
16 KiB
JavaScript

/**
* Clean, native dark design system and UI helpers for ComfyUI-Majoor-ProjectSettings.
* Aligned with ComfyUI v0.34+ and modern dark studio interface.
* Unobtrusive, sober, low-contrast, no flashy glows or saturated gradients.
*/
export const THEME = {
bgPanel: "var(--comfy-menu-bg, var(--bg-color, #202020))",
bgCard: "var(--bg-color, #1a1a1a)",
bgCardHover: "var(--comfy-input-bg, #2a2a2a)",
bgInput: "var(--comfy-input-bg, #1a1a1a)",
borderColor: "var(--border-color, #404040)",
borderActive: "var(--p-button-primary-background, #3b82f6)",
primaryColor: "var(--fg-color, #ffffff)",
textColor: "var(--fg-color, #ffffff)",
textMuted: "var(--descrip-text, #999999)",
textDim: "var(--descrip-text, #999999)",
accentPrimary: "var(--p-button-primary-background, #3b82f6)",
accentPrimaryBg: "var(--comfy-input-bg, rgba(59, 130, 246, 0.1))",
accentPrimaryBorder: "var(--p-button-primary-background, #3b82f6)",
accentSuccess: "var(--success-text, #22c55e)",
accentSuccessBg: "var(--comfy-input-bg, rgba(34, 197, 94, 0.1))",
accentSuccessBorder: "var(--success-text, #22c55e)",
accentWarning: "var(--warning-text, #eab308)",
accentWarningBg: "var(--comfy-input-bg, rgba(234, 179, 8, 0.1))",
accentWarningBorder: "var(--warning-text, #eab308)",
accentError: "var(--error-text, #ef4444)",
accentErrorBg: "var(--comfy-input-bg, rgba(239, 68, 68, 0.1))",
accentErrorBorder: "var(--error-text, #ef4444)",
accentCyan: "var(--p-button-primary-background, #3b82f6)",
accentCyanBg: "var(--comfy-input-bg, rgba(59, 130, 246, 0.1))",
accentCyanBorder: "var(--p-button-primary-background, #3b82f6)",
radiusSm: "2px",
radiusMd: "4px",
radiusLg: "6px",
fontFamily: "var(--font-family, sans-serif)",
gap: "8px",
};
export function createCard(title, badgeText, badgeColor) {
const card = document.createElement("div");
card.style.display = "flex";
card.style.flexDirection = "column";
card.style.gap = THEME.gap;
card.style.padding = "8px 10px";
card.style.borderRadius = THEME.radiusMd;
card.style.background = THEME.bgCard;
card.style.border = `1px solid ${THEME.borderColor}`;
card.style.boxSizing = "border-box";
card.style.transition = "border-color 0.15s ease, box-shadow 0.15s ease";
if (title) {
const header = document.createElement("div");
header.style.display = "flex";
header.style.alignItems = "center";
header.style.justifyContent = "space-between";
header.style.paddingBottom = "4px";
header.style.borderBottom = `1px solid ${THEME.borderColor}`;
const titleEl = document.createElement("div");
titleEl.textContent = title;
titleEl.style.fontWeight = "600";
titleEl.style.fontSize = "11px";
titleEl.style.letterSpacing = "0.3px";
titleEl.style.color = THEME.textColor;
header.appendChild(titleEl);
if (badgeText) {
const badgeEl = createBadge(badgeText, badgeColor);
header.appendChild(badgeEl);
}
card.appendChild(header);
}
return card;
}
export function createBadge(text, color = THEME.textMuted) {
const badge = document.createElement("span");
badge.textContent = text;
badge.style.display = "inline-flex";
badge.style.alignItems = "center";
badge.style.padding = "2px 6px";
badge.style.borderRadius = THEME.radiusSm;
badge.style.fontSize = "10px";
badge.style.fontWeight = "500";
badge.style.lineHeight = "1";
badge.style.color = color;
badge.style.background = "rgba(255, 255, 255, 0.05)";
badge.style.border = `1px solid ${THEME.borderColor}`;
return badge;
}
export function createButton(text, variant = "secondary", icon) {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "mjr-ps-btn";
btn.style.display = "inline-flex";
btn.style.alignItems = "center";
btn.style.justifyContent = "center";
btn.style.gap = "5px";
btn.style.padding = "6px 10px";
btn.style.borderRadius = THEME.radiusSm;
btn.style.fontSize = "11px";
btn.style.fontWeight = "500";
btn.style.cursor = "pointer";
btn.style.lineHeight = "1.2";
btn.style.whiteSpace = "nowrap";
btn.style.transition = "all 0.15s ease";
btn.style.boxSizing = "border-box";
if (icon) {
const iconEl = document.createElement("i");
iconEl.className = icon;
iconEl.style.fontSize = "11px";
btn.appendChild(iconEl);
}
const textNode = document.createTextNode(text);
btn.appendChild(textNode);
if (variant === "primary") {
btn.style.background = "linear-gradient(135deg, rgba(99, 102, 241, 0.25) 0%, rgba(59, 130, 246, 0.18) 100%)";
btn.style.border = "1px solid rgba(129, 140, 248, 0.45)";
btn.style.color = "#ffffff";
btn.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.25)";
}
else if (variant === "danger") {
btn.style.background = "rgba(239, 68, 68, 0.12)";
btn.style.border = "1px solid rgba(239, 68, 68, 0.3)";
btn.style.color = "#fca5a5";
}
else if (variant === "ghost") {
btn.style.background = "transparent";
btn.style.border = "1px solid transparent";
btn.style.color = THEME.textMuted;
}
else {
// secondary
btn.style.background = "rgba(255, 255, 255, 0.04)";
btn.style.border = `1px solid ${THEME.borderColor}`;
btn.style.color = THEME.textColor;
}
btn.addEventListener("mouseenter", () => {
if (btn.disabled)
return;
if (variant === "primary") {
btn.style.background = "linear-gradient(135deg, rgba(99, 102, 241, 0.35) 0%, rgba(59, 130, 246, 0.28) 100%)";
btn.style.borderColor = "rgba(165, 180, 252, 0.65)";
btn.style.boxShadow = "0 2px 6px rgba(99, 102, 241, 0.25)";
}
else if (variant === "danger") {
btn.style.background = "rgba(239, 68, 68, 0.2)";
btn.style.borderColor = "rgba(239, 68, 68, 0.45)";
}
else if (variant === "ghost") {
btn.style.background = "rgba(255, 255, 255, 0.05)";
btn.style.color = "#ffffff";
}
else {
btn.style.background = "rgba(255, 255, 255, 0.07)";
btn.style.borderColor = "rgba(255, 255, 255, 0.18)";
}
});
btn.addEventListener("mouseleave", () => {
if (variant === "primary") {
btn.style.background = "linear-gradient(135deg, rgba(99, 102, 241, 0.25) 0%, rgba(59, 130, 246, 0.18) 100%)";
btn.style.borderColor = "rgba(129, 140, 248, 0.45)";
btn.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.25)";
}
else if (variant === "danger") {
btn.style.background = "rgba(239, 68, 68, 0.12)";
btn.style.borderColor = "rgba(239, 68, 68, 0.3)";
}
else if (variant === "ghost") {
btn.style.background = "transparent";
btn.style.borderColor = "transparent";
btn.style.color = THEME.textMuted;
}
else {
btn.style.background = "rgba(255, 255, 255, 0.04)";
btn.style.borderColor = THEME.borderColor;
}
});
return btn;
}
export function createInput(placeholder = "", value = "") {
const input = document.createElement("input");
input.type = "text";
input.placeholder = placeholder;
input.value = value;
input.style.width = "100%";
input.style.boxSizing = "border-box";
input.style.padding = "6px 8px";
input.style.borderRadius = THEME.radiusSm;
input.style.border = `1px solid ${THEME.borderColor}`;
input.style.background = THEME.bgInput;
input.style.color = THEME.textColor;
input.style.fontSize = "12px";
input.style.outline = "none";
input.style.transition = "border-color 0.15s ease";
input.addEventListener("focus", () => {
input.style.borderColor = THEME.borderActive;
input.style.boxShadow = "0 0 0 2px rgba(99, 102, 241, 0.2)";
});
input.addEventListener("blur", () => {
input.style.borderColor = THEME.borderColor;
input.style.boxShadow = "none";
});
return input;
}
export function createSelect(options = []) {
const sel = document.createElement("select");
sel.className = "mjr-ps-select";
sel.style.width = "100%";
sel.style.boxSizing = "border-box";
sel.style.padding = "6px 8px";
sel.style.borderRadius = THEME.radiusSm;
sel.style.border = `1px solid ${THEME.borderColor}`;
sel.style.background = THEME.bgInput;
sel.style.color = THEME.textColor;
sel.style.fontSize = "12px";
sel.style.outline = "none";
sel.style.cursor = "pointer";
for (const opt of options) {
const el = document.createElement("option");
el.value = opt.value;
el.textContent = opt.label;
sel.appendChild(el);
}
return sel;
}
export function createRow(labelText, contentEl, tooltip) {
const row = document.createElement("div");
row.style.display = "flex";
row.style.flexDirection = "column";
row.style.gap = "3px";
const label = document.createElement("label");
label.textContent = labelText;
label.style.fontSize = "11px";
label.style.fontWeight = "500";
label.style.color = THEME.textMuted;
if (tooltip)
label.title = tooltip;
row.appendChild(label);
row.appendChild(contentEl);
return row;
}
export function createSwitch(labelText, checked, onChange) {
const wrap = document.createElement("label");
wrap.style.display = "inline-flex";
wrap.style.alignItems = "center";
wrap.style.gap = "8px";
wrap.style.cursor = "pointer";
wrap.style.fontSize = "11px";
wrap.style.color = THEME.textColor;
wrap.style.userSelect = "none";
const cb = document.createElement("input");
cb.type = "checkbox";
cb.checked = checked;
cb.style.accentColor = "var(--border-color, #71717a)";
cb.style.cursor = "pointer";
const span = document.createElement("span");
span.textContent = labelText;
span.style.opacity = "0.9";
cb.addEventListener("change", () => {
onChange?.(cb.checked);
});
wrap.appendChild(cb);
wrap.appendChild(span);
return wrap;
}
export function createDetails(titleText, defaultOpen = false) {
const details = document.createElement("details");
details.open = defaultOpen;
details.style.display = "flex";
details.style.flexDirection = "column";
details.style.gap = THEME.gap;
details.style.padding = "7px 9px";
details.style.borderRadius = THEME.radiusMd;
details.style.background = THEME.bgCard;
details.style.border = `1px solid ${THEME.borderColor}`;
details.style.transition = "border-color 0.15s ease";
const summary = document.createElement("summary");
summary.textContent = titleText;
summary.style.cursor = "pointer";
summary.style.fontSize = "11px";
summary.style.fontWeight = "600";
summary.style.letterSpacing = "0.3px";
summary.style.color = THEME.textColor;
summary.style.outline = "none";
summary.style.userSelect = "none";
details.appendChild(summary);
details._summary = summary;
return details;
}
export function createSegmentedControl(options, initialValue, onChange) {
const container = document.createElement("div");
container.style.display = "grid";
container.style.gridTemplateColumns = `repeat(${options.length}, 1fr)`;
container.style.gap = "2px";
container.style.padding = "2px";
container.style.borderRadius = THEME.radiusSm;
container.style.background = "rgba(255, 255, 255, 0.04)";
container.style.border = `1px solid ${THEME.borderColor}`;
container.style.boxSizing = "border-box";
let currentValue = initialValue;
const buttons = new Map();
const updateVisuals = () => {
for (const [val, btn] of buttons.entries()) {
const isSelected = val === currentValue;
btn.style.background = isSelected
? "linear-gradient(180deg, rgba(99, 102, 241, 0.22) 0%, rgba(99, 102, 241, 0.1) 100%)"
: "transparent";
btn.style.borderColor = isSelected ? "rgba(129, 140, 248, 0.42)" : "transparent";
btn.style.color = isSelected ? "#ffffff" : THEME.textMuted;
btn.style.fontWeight = isSelected ? "600" : "400";
btn.style.boxShadow = isSelected ? "0 1px 3px rgba(0, 0, 0, 0.25)" : "none";
}
};
for (const opt of options) {
const btn = document.createElement("button");
btn.type = "button";
btn.className = "mjr-ps-btn";
btn.style.display = "inline-flex";
btn.style.alignItems = "center";
btn.style.justifyContent = "center";
btn.style.gap = "5px";
btn.style.padding = "5px 8px";
btn.style.borderRadius = "3px";
btn.style.border = "1px solid transparent";
btn.style.fontSize = "11px";
btn.style.cursor = "pointer";
btn.style.transition = "all 0.15s ease";
btn.style.boxSizing = "border-box";
if (opt.icon) {
const iconEl = document.createElement("i");
iconEl.className = opt.icon;
iconEl.style.fontSize = "10px";
btn.appendChild(iconEl);
}
const textNode = document.createTextNode(opt.label);
btn.appendChild(textNode);
btn.addEventListener("click", () => {
if (currentValue !== opt.value) {
currentValue = opt.value;
updateVisuals();
onChange(opt.value);
}
});
buttons.set(opt.value, btn);
container.appendChild(btn);
}
updateVisuals();
return {
element: container,
setValue: (val) => {
currentValue = val;
updateVisuals();
},
getValue: () => currentValue,
};
}
export function createStatusTile(label, initialValue, icon, initialColor = THEME.textColor) {
const tile = document.createElement("div");
tile.style.display = "flex";
tile.style.flexDirection = "column";
tile.style.gap = "2px";
tile.style.padding = "6px 8px";
tile.style.borderRadius = THEME.radiusSm;
tile.style.background = "rgba(255, 255, 255, 0.02)";
tile.style.border = `1px solid ${THEME.borderColor}`;
tile.style.boxSizing = "border-box";
tile.style.minWidth = "0";
tile.style.transition = "background 0.15s ease, border-color 0.15s ease";
const topRow = document.createElement("div");
topRow.style.display = "flex";
topRow.style.alignItems = "center";
topRow.style.gap = "4px";
topRow.style.fontSize = "10px";
topRow.style.color = THEME.textMuted;
topRow.style.fontWeight = "500";
topRow.style.textTransform = "uppercase";
topRow.style.letterSpacing = "0.3px";
if (icon) {
const iconEl = document.createElement("i");
iconEl.className = icon;
iconEl.style.fontSize = "9px";
topRow.appendChild(iconEl);
}
const labelEl = document.createElement("span");
labelEl.textContent = label;
topRow.appendChild(labelEl);
const valueEl = document.createElement("div");
valueEl.style.fontSize = "11px";
valueEl.style.fontWeight = "600";
valueEl.style.color = initialColor;
valueEl.style.whiteSpace = "nowrap";
valueEl.style.overflow = "hidden";
valueEl.style.textOverflow = "ellipsis";
valueEl.textContent = initialValue;
tile.appendChild(topRow);
tile.appendChild(valueEl);
return {
element: tile,
update: (value, color = THEME.textColor, subtitle) => {
valueEl.textContent = value;
valueEl.style.color = color;
if (color === THEME.accentSuccess) {
tile.style.background = "rgba(74, 222, 128, 0.05)";
tile.style.borderColor = "rgba(74, 222, 128, 0.25)";
}
else if (color === THEME.accentWarning) {
tile.style.background = "rgba(251, 191, 36, 0.05)";
tile.style.borderColor = "rgba(251, 191, 36, 0.25)";
}
else if (color === THEME.accentError) {
tile.style.background = "rgba(248, 113, 113, 0.05)";
tile.style.borderColor = "rgba(248, 113, 113, 0.25)";
}
else {
tile.style.background = "rgba(255, 255, 255, 0.02)";
tile.style.borderColor = THEME.borderColor;
}
if (subtitle) {
tile.title = `${label}: ${value} (${subtitle})`;
}
else {
tile.title = `${label}: ${value}`;
}
},
};
}
//# sourceMappingURL=theme.js.map