Files
VRCX/src/shared/utils/base/ui.js
2026-01-11 06:05:20 +13:00

351 lines
9.8 KiB
JavaScript

import { storeToRefs } from 'pinia';
import { toast } from 'vue-sonner';
import { THEME_CONFIG } from '../../constants';
import { i18n } from '../../../plugin/i18n';
import { router } from '../../../plugin/router';
import { useAppearanceSettingsStore } from '../../../stores';
import configRepository from '../../../service/config.js';
/**
*
* @returns {boolean}
*/
function systemIsDarkMode() {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
/**
*
* @param {boolean}isDark
*/
function changeAppDarkStyle(isDark) {
if (isDark) {
AppApi.ChangeTheme(1);
} else {
AppApi.ChangeTheme(0);
}
}
function applyThemeFonts(themeKey, fontLinks = []) {
document
.querySelectorAll('link[data-theme-font]')
.forEach((linkEl) => linkEl.remove());
if (!fontLinks?.length) {
return;
}
const head = document.head;
fontLinks.forEach((href) => {
if (!href) {
return;
}
const fontLink = document.createElement('link');
fontLink.rel = 'stylesheet';
fontLink.href = href;
fontLink.dataset.themeFont = themeKey;
head.appendChild(fontLink);
});
}
function ensureStylesheetLink(id) {
const linkEl = /** @type {HTMLLinkElement | null} */ (
document.getElementById(id)
);
if (!linkEl) {
const created = document.createElement('link');
created.setAttribute('id', id);
created.rel = 'stylesheet';
document.head.appendChild(created);
return created;
}
return linkEl;
}
function removeStylesheetLink(id) {
const linkEl = document.getElementById(id);
linkEl?.remove();
}
function changeAppThemeStyle(themeMode) {
if (themeMode === 'system') {
themeMode = systemIsDarkMode() ? 'dark' : 'light';
}
let themeConfig = THEME_CONFIG[themeMode];
if (!themeConfig) {
// fallback to system
console.error('Invalid theme mode:', themeMode);
configRepository.setString('VRCX_ThemeMode', 'system');
themeMode = systemIsDarkMode() ? 'dark' : 'light';
themeConfig = THEME_CONFIG[themeMode];
}
const cssFiles = Array.isArray(themeConfig.cssFiles)
? themeConfig.cssFiles.filter(Boolean)
: themeConfig.cssFile
? [themeConfig.cssFile]
: [];
if (cssFiles.length > 0) {
const $appThemeStyle = ensureStylesheetLink('app-theme-style');
$appThemeStyle.href = cssFiles[0];
} else {
removeStylesheetLink('app-theme-style');
}
if (cssFiles.length > 1) {
const $appThemeOverlayStyle = ensureStylesheetLink(
'app-theme-overlay-style'
);
$appThemeOverlayStyle.href = cssFiles[1];
} else {
removeStylesheetLink('app-theme-overlay-style');
}
applyThemeFonts(themeMode, themeConfig.fontLinks);
const shouldUseDarkClass =
typeof themeConfig.useDarkClass === 'boolean'
? themeConfig.useDarkClass
: Boolean(themeConfig.isDark);
if (shouldUseDarkClass) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
changeAppDarkStyle(themeConfig.isDark);
return { isDark: themeConfig.isDark };
// let $appThemeDarkStyle = document.getElementById('app-theme-dark-style');
// const darkThemeCssPath = `${filePathPrefix}theme.dark.css`;
// const shouldApplyDarkBase = themeConfig.isDark;
// if (shouldApplyDarkBase) {
// if (!$appThemeDarkStyle) {
// $appThemeDarkStyle = document.createElement('link');
// $appThemeDarkStyle.setAttribute('id', 'app-theme-dark-style');
// $appThemeDarkStyle.rel = 'stylesheet';
// $appThemeDarkStyle.href = darkThemeCssPath;
// document.head.insertBefore($appThemeDarkStyle, $appThemeStyle);
// } else if ($appThemeDarkStyle.href !== darkThemeCssPath) {
// $appThemeDarkStyle.href = darkThemeCssPath;
// }
// } else {
// $appThemeDarkStyle && $appThemeDarkStyle.remove();
// }
}
/**
*
* @param {object} trustColor
*/
function updateTrustColorClasses(trustColor) {
if (document.getElementById('trustColor') !== null) {
document.getElementById('trustColor').outerHTML = '';
}
const style = document.createElement('style');
style.id = 'trustColor';
style.type = 'text/css';
let newCSS = '';
for (const rank in trustColor) {
newCSS += `.x-tag-${rank} { color: ${trustColor[rank]} !important; border-color: ${trustColor[rank]} !important; } `;
}
style.innerHTML = newCSS;
document.getElementsByTagName('head')[0].appendChild(style);
}
async function refreshCustomCss() {
if (document.contains(document.getElementById('app-custom-style'))) {
document.getElementById('app-custom-style').remove();
}
const customCss = await AppApi.CustomCss();
if (customCss) {
const head = document.head;
const $appCustomStyle = document.createElement('link');
$appCustomStyle.setAttribute('id', 'app-custom-style');
$appCustomStyle.rel = 'stylesheet';
$appCustomStyle.type = 'text/css';
$appCustomStyle.href = URL.createObjectURL(
new Blob([customCss], { type: 'text/css' })
);
head.appendChild($appCustomStyle);
}
}
async function refreshCustomScript() {
if (document.contains(document.getElementById('app-custom-script'))) {
document.getElementById('app-custom-script').remove();
}
const customScript = await AppApi.CustomScript();
if (customScript) {
const head = document.head;
const $appCustomScript = document.createElement('script');
$appCustomScript.setAttribute('id', 'app-custom-script');
$appCustomScript.type = 'text/javascript';
$appCustomScript.textContent = customScript;
head.appendChild($appCustomScript);
}
}
/**
*
* @param {number} hue
* @returns {string}
*/
function HueToHex(hue) {
const appSettingsStore = useAppearanceSettingsStore();
const { isDarkMode } = storeToRefs(appSettingsStore);
// this.HSVtoRGB(hue / 65535, .8, .8);
if (isDarkMode.value) {
return HSVtoRGB(hue / 65535, 0.6, 1);
}
return HSVtoRGB(hue / 65535, 1, 0.7);
}
/**
*
* @param {number} h
* @param {number} s
* @param {number} v
* @returns {string}
*/
function HSVtoRGB(h, s, v) {
let r = 0;
let g = 0;
let b = 0;
if (arguments.length === 1) {
s = h.s;
v = h.v;
h = h.h;
}
const i = Math.floor(h * 6);
const f = h * 6 - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
}
const red = Math.round(r * 255);
const green = Math.round(g * 255);
const blue = Math.round(b * 255);
const decColor = 0x1000000 + blue + 0x100 * green + 0x10000 * red;
return `#${decColor.toString(16).substr(1)}`;
}
function getNextDialogIndex() {
let z = 2000;
document.querySelectorAll('.el-overlay,.el-modal-dialog').forEach((v) => {
if (v.style.display === 'none') {
return;
}
const _z = Number(v.style.zIndex) || 0;
if (_z > z) {
z = _z;
}
});
return z + 1;
}
function changeHtmlLangAttribute(language) {
const htmlElement = document.documentElement;
htmlElement.setAttribute('lang', language);
}
// prevent flicker on login page
function setLoginContainerStyle(isDarkMode) {
const loginContainerStyle = document.createElement('style');
loginContainerStyle.id = 'login-container-style';
loginContainerStyle.type = 'text/css';
const backgroundFallback = isDarkMode ? '#101010' : '#ffffff';
const inputBackgroundFallback = isDarkMode ? '#1f1f1f' : '#ffffff';
const borderFallback = isDarkMode ? '#3b3b3b' : '#DCDFE6';
loginContainerStyle.innerHTML = `
.x-login-container {
background-color: var(--el-bg-color-page, ${backgroundFallback}) !important;
transition: background-color 0.3s ease;
}
.x-login-container .el-input__wrapper {
background-color: var(--el-bg-color, ${inputBackgroundFallback}) !important;
border: 1px solid var(--el-border-color, ${borderFallback}) !important;
transition: background-color 0.3s ease, border-color 0.3s ease;
}
`;
document.head.insertBefore(loginContainerStyle, document.head.firstChild);
}
async function getThemeMode(configRepository) {
const initThemeMode = await configRepository.getString(
'VRCX_ThemeMode',
'system'
);
let isDarkMode;
if (initThemeMode === 'light') {
isDarkMode = false;
} else if (initThemeMode === 'system') {
isDarkMode = systemIsDarkMode();
} else {
isDarkMode = true;
}
return { initThemeMode, isDarkMode };
}
function redirectToToolsTab() {
router.push({ name: 'tools' });
toast(i18n.global.t('view.tools.redirect_message'), { duration: 3000 });
}
export {
systemIsDarkMode,
changeAppDarkStyle,
changeAppThemeStyle,
updateTrustColorClasses,
refreshCustomCss,
refreshCustomScript,
HueToHex,
HSVtoRGB,
getNextDialogIndex,
changeHtmlLangAttribute,
setLoginContainerStyle,
getThemeMode,
redirectToToolsTab
};