|
function hexToRgb(color) { |
|
let hex = color[0] === "#" ? color.slice(1) : color; |
|
let c; |
|
|
|
|
|
if (hex.length !== 6) { |
|
hex = (() => { |
|
const result = []; |
|
for (c of Array.from(hex)) { |
|
result.push(`${c}${c}`); |
|
} |
|
return result; |
|
})().join(""); |
|
} |
|
const colorStr = hex.match(/#?(.{2})(.{2})(.{2})/).slice(1); |
|
const rgb = colorStr.map((col) => parseInt(col, 16)); |
|
rgb.push(1); |
|
return rgb; |
|
} |
|
|
|
function rgbToHsl(rgb) { |
|
const r = rgb[0] / 255; |
|
const g = rgb[1] / 255; |
|
const b = rgb[2] / 255; |
|
|
|
const max = Math.max(r, g, b); |
|
const min = Math.min(r, g, b); |
|
const diff = max - min; |
|
const add = max + min; |
|
|
|
const hue = |
|
min === max |
|
? 0 |
|
: r === max |
|
? ((60 * (g - b)) / diff + 360) % 360 |
|
: g === max |
|
? (60 * (b - r)) / diff + 120 |
|
: (60 * (r - g)) / diff + 240; |
|
|
|
const lum = 0.5 * add; |
|
|
|
const sat = |
|
lum === 0 ? 0 : lum === 1 ? 1 : lum <= 0.5 ? diff / add : diff / (2 - add); |
|
|
|
const h = Math.round(hue); |
|
const s = Math.round(sat * 100); |
|
const l = Math.round(lum * 100); |
|
const a = rgb[3] || 1; |
|
|
|
return [h, s, l, a]; |
|
} |
|
|
|
function hexToHsl(color) { |
|
const rgb = hexToRgb(color); |
|
const hsl = rgbToHsl(rgb); |
|
return "hsl(" + hsl[0] + "deg " + hsl[1] + "% " + hsl[2] + "%)"; |
|
} |
|
|
|
function hslToHex(h, s, l) { |
|
l /= 100; |
|
const a = (s * Math.min(l, 1 - l)) / 100; |
|
const f = (n) => { |
|
const k = (n + h / 30) % 12; |
|
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); |
|
return Math.round(255 * Math.max(0, Math.min(color, 1))) |
|
.toString(16) |
|
.padStart(2, "0"); |
|
}; |
|
return `#${f(0)}${f(8)}${f(4)}`; |
|
} |
|
|
|
function hsl2rgb(h, s, l) { |
|
let a = s * Math.min(l, 1 - l); |
|
let f = (n, k = (n + h / 30) % 12) => |
|
l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); |
|
return [f(0), f(8), f(4)]; |
|
} |
|
|
|
function invertColor(hex) { |
|
if (hex.indexOf("#") === 0) { |
|
hex = hex.slice(1); |
|
} |
|
|
|
if (hex.length === 3) { |
|
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; |
|
} |
|
if (hex.length !== 6) { |
|
throw new Error("Invalid HEX color."); |
|
} |
|
|
|
var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16), |
|
g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16), |
|
b = (255 - parseInt(hex.slice(4, 6), 16)).toString(16); |
|
|
|
return "#" + padZero(r) + padZero(g) + padZero(b); |
|
} |
|
|
|
function padZero(str, len) { |
|
len = len || 2; |
|
var zeros = new Array(len).join("0"); |
|
return (zeros + str).slice(-len); |
|
} |
|
|
|
function getValsWrappedIn(str, c1, c2) { |
|
var rg = new RegExp("(?<=\\" + c1 + ")(.*?)(?=\\" + c2 + ")", "g"); |
|
return str.match(rg); |
|
} |
|
|
|
let styleobj = {}; |
|
let hslobj = {}; |
|
let isColorsInv; |
|
|
|
const toHSLArray = (hslStr) => hslStr.match(/\d+/g).map(Number); |
|
|
|
function offsetColorsHSV(ohsl) { |
|
let inner_styles = ""; |
|
|
|
for (const key in styleobj) { |
|
let keyVal = styleobj[key]; |
|
|
|
if (keyVal.indexOf("#") != -1 || keyVal.indexOf("hsl") != -1) { |
|
let colcomp = gradioApp().querySelector("#" + key + " input"); |
|
if (colcomp) { |
|
let hsl; |
|
|
|
if (keyVal.indexOf("#") != -1) { |
|
keyVal = keyVal.replace(/\s+/g, ""); |
|
|
|
if (isColorsInv) { |
|
keyVal = invertColor(keyVal); |
|
styleobj[key] = keyVal; |
|
} |
|
hsl = rgbToHsl(hexToRgb(keyVal)); |
|
} else { |
|
if (isColorsInv) { |
|
let c = toHSLArray(keyVal); |
|
let hex = hslToHex(c[0], c[1], c[2]); |
|
keyVal = invertColor(hex); |
|
styleobj[key] = keyVal; |
|
hsl = rgbToHsl(hexToRgb(keyVal)); |
|
} else { |
|
hsl = toHSLArray(keyVal); |
|
} |
|
} |
|
|
|
let h = (parseInt(hsl[0]) + parseInt(ohsl[0])) % 360; |
|
let s = parseInt(hsl[1]) + parseInt(ohsl[1]); |
|
let l = parseInt(hsl[2]) + parseInt(ohsl[2]); |
|
|
|
let hex = hslToHex( |
|
h, |
|
Math.min(Math.max(s, 0), 100), |
|
Math.min(Math.max(l, 0), 100) |
|
); |
|
|
|
colcomp.value = hex; |
|
|
|
hslobj[key] = "hsl(" + h + "deg " + s + "% " + l + "%)"; |
|
inner_styles += key + ":" + hslobj[key] + ";"; |
|
} |
|
} else { |
|
inner_styles += key + ":" + styleobj[key] + ";"; |
|
} |
|
} |
|
|
|
isColorsInv = false; |
|
|
|
const preview_styles = gradioApp().querySelector("#preview-styles"); |
|
preview_styles.innerHTML = ":root {" + inner_styles + "}"; |
|
preview_styles.innerHTML += |
|
"@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; |
|
|
|
const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); |
|
vars_textarea.value = inner_styles; |
|
|
|
const inputEvent = new Event("input"); |
|
Object.defineProperty(inputEvent, "target", { value: vars_textarea }); |
|
vars_textarea.dispatchEvent(inputEvent); |
|
} |
|
|
|
function updateTheme(vars) { |
|
let inner_styles = ""; |
|
|
|
for (let i = 0; i < vars.length - 1; i++) { |
|
let key = vars[i].split(":"); |
|
let id = key[0].replace(/\s+/g, ""); |
|
let val = key[1].trim(); |
|
|
|
styleobj[id] = val; |
|
inner_styles += id + ":" + val + ";"; |
|
|
|
gradioApp() |
|
.querySelectorAll("#" + id + " input") |
|
.forEach((elem) => { |
|
if (val.indexOf("hsl") != -1) { |
|
let hsl = toHSLArray(val); |
|
let hex = hslToHex(hsl[0], hsl[1], hsl[2]); |
|
elem.value = hex; |
|
} else { |
|
elem.value = val.split("px")[0]; |
|
} |
|
}); |
|
} |
|
|
|
const preview_styles = gradioApp().querySelector("#preview-styles"); |
|
|
|
if (preview_styles) { |
|
preview_styles.innerHTML = ":root {" + inner_styles + "}"; |
|
preview_styles.innerHTML += |
|
"@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; |
|
} else { |
|
const r = gradioApp(); |
|
const style = document.createElement("style"); |
|
style.id = "preview-styles"; |
|
style.innerHTML = ":root {" + inner_styles + "}"; |
|
style.innerHTML += |
|
"@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; |
|
r.appendChild(style); |
|
} |
|
|
|
const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); |
|
const css_textarea = gradioApp().querySelector("#theme_css textarea"); |
|
|
|
vars_textarea.value = inner_styles; |
|
css_textarea.value = css_textarea.value; |
|
|
|
|
|
|
|
const vEvent = new Event("input"); |
|
const cEvent = new Event("input"); |
|
Object.defineProperty(vEvent, "target", { value: vars_textarea }); |
|
Object.defineProperty(cEvent, "target", { value: css_textarea }); |
|
vars_textarea.dispatchEvent(vEvent); |
|
css_textarea.dispatchEvent(cEvent); |
|
} |
|
|
|
function applyTheme() { |
|
console.log("apply"); |
|
} |
|
|
|
function initTheme() { |
|
const current_style = gradioApp().querySelector(".gradio-container > style"); |
|
|
|
|
|
|
|
|
|
const css_styles = current_style.innerHTML.split( |
|
"/*BREAKPOINT_CSS_CONTENT*/" |
|
); |
|
let init_css_vars = css_styles[0].split("}")[0].split("{")[1]; |
|
init_css_vars = init_css_vars.replace(/\n|\r/g, ""); |
|
|
|
let init_vars = init_css_vars.split(";"); |
|
let vars = init_vars; |
|
|
|
|
|
|
|
const vars_textarea = gradioApp().querySelector("#theme_vars textarea"); |
|
const css_textarea = gradioApp().querySelector("#theme_css textarea"); |
|
|
|
vars_textarea.value = init_css_vars; |
|
css_textarea.value = |
|
"/*BREAKPOINT_CSS_CONTENT*/" + css_styles[1] + "/*BREAKPOINT_CSS_CONTENT*/"; |
|
|
|
updateTheme(vars); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const preview_styles = gradioApp().querySelector("#preview-styles"); |
|
let intervalChange; |
|
|
|
gradioApp() |
|
.querySelectorAll("#ui_theme_settings input") |
|
.forEach((elem) => { |
|
elem.addEventListener("input", function (e) { |
|
let celem = e.currentTarget; |
|
let val = e.currentTarget.value; |
|
let curr_val; |
|
|
|
switch (e.currentTarget.type) { |
|
case "range": |
|
celem = celem.parentElement; |
|
val = e.currentTarget.value + "px"; |
|
break; |
|
case "color": |
|
celem = celem.parentElement.parentElement; |
|
val = e.currentTarget.value; |
|
break; |
|
case "number": |
|
celem = celem.parentElement.parentElement.parentElement; |
|
val = e.currentTarget.value + "px"; |
|
break; |
|
} |
|
|
|
styleobj[celem.id] = val; |
|
|
|
|
|
|
|
if (intervalChange != null) clearInterval(intervalChange); |
|
intervalChange = setTimeout(() => { |
|
let inner_styles = ""; |
|
|
|
for (const key in styleobj) { |
|
inner_styles += key + ":" + styleobj[key] + ";"; |
|
} |
|
|
|
vars = inner_styles.split(";"); |
|
preview_styles.innerHTML = ":root {" + inner_styles + "}"; |
|
preview_styles.innerHTML += |
|
"@media only screen and (max-width: 860px) {:root{--ae-outside-gap-size: var(--ae-mobile-outside-gap-size);--ae-inside-padding-size: var(--ae-mobile-inside-padding-size);}}"; |
|
|
|
vars_textarea.value = inner_styles; |
|
const vEvent = new Event("input"); |
|
Object.defineProperty(vEvent, "target", { value: vars_textarea }); |
|
vars_textarea.dispatchEvent(vEvent); |
|
|
|
offsetColorsHSV(hsloffset); |
|
}, 1000); |
|
}); |
|
}); |
|
|
|
const reset_btn = gradioApp().getElementById("theme_reset_btn"); |
|
reset_btn.addEventListener("click", function (e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
gradioApp() |
|
.querySelectorAll("#ui_theme_hsv input") |
|
.forEach((elem) => { |
|
elem.value = 0; |
|
}); |
|
hsloffset = [0, 0, 0]; |
|
updateTheme(init_vars); |
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let intervalCheck; |
|
function dropDownOnChange() { |
|
if (init_css_vars != vars_textarea.value) { |
|
clearInterval(intervalCheck); |
|
init_css_vars = vars_textarea.value.replace(/\n|\r/g, ""); |
|
vars_textarea.value = init_css_vars; |
|
init_vars = init_css_vars.split(";"); |
|
vars = init_vars; |
|
updateTheme(vars); |
|
} |
|
} |
|
|
|
const drop_down = gradioApp().querySelector("#themes_drop_down"); |
|
drop_down.addEventListener("click", function (e) { |
|
if (intervalCheck != null) clearInterval(intervalCheck); |
|
intervalCheck = setInterval(dropDownOnChange, 100); |
|
|
|
}); |
|
|
|
let hsloffset = [0, 0, 0]; |
|
|
|
const hue = gradioApp() |
|
.querySelectorAll("#theme_hue input") |
|
.forEach((elem) => { |
|
elem.addEventListener("change", function (e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
hsloffset[0] = e.currentTarget.value; |
|
offsetColorsHSV(hsloffset); |
|
}); |
|
}); |
|
|
|
const sat = gradioApp() |
|
.querySelectorAll("#theme_sat input") |
|
.forEach((elem) => { |
|
elem.addEventListener("change", function (e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
hsloffset[1] = e.currentTarget.value; |
|
offsetColorsHSV(hsloffset); |
|
}); |
|
}); |
|
|
|
const brt = gradioApp() |
|
.querySelectorAll("#theme_brt input") |
|
.forEach((elem) => { |
|
elem.addEventListener("change", function (e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
hsloffset[2] = e.currentTarget.value; |
|
offsetColorsHSV(hsloffset); |
|
}); |
|
}); |
|
|
|
const inv_btn = gradioApp().getElementById("theme_invert_btn"); |
|
inv_btn.addEventListener("click", function (e) { |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
isColorsInv = !isColorsInv; |
|
offsetColorsHSV(hsloffset); |
|
}); |
|
} |
|
|
|
function observeGradioApp() { |
|
const observer = new MutationObserver(() => { |
|
const block = gradioApp().getElementById("tab_ui_theme"); |
|
if (block) { |
|
observer.disconnect(); |
|
|
|
setTimeout(() => { |
|
initTheme(); |
|
}, "500"); |
|
} |
|
}); |
|
observer.observe(gradioApp(), { childList: true, subtree: true }); |
|
} |
|
|
|
document.addEventListener("DOMContentLoaded", () => { |
|
observeGradioApp(); |
|
}); |
|
|