mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-15 21:03:52 +02:00
305 lines
8.2 KiB
JavaScript
305 lines
8.2 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 { textToHex } from './string';
|
|
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 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];
|
|
}
|
|
|
|
applyThemeFonts(themeMode, themeConfig.fontLinks);
|
|
|
|
document.documentElement.setAttribute('data-theme', themeMode);
|
|
|
|
const shouldUseDarkClass = 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) {
|
|
// @ts-ignore
|
|
s = h.s;
|
|
// @ts-ignore
|
|
v = h.v;
|
|
// @ts-ignore
|
|
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 formatJsonVars(ref) {
|
|
// remove all object keys that start with $
|
|
const newRef = { ...ref };
|
|
for (const key in newRef) {
|
|
if (key.startsWith('$')) {
|
|
delete newRef[key];
|
|
}
|
|
}
|
|
// sort keys alphabetically
|
|
const sortedKeys = Object.keys(newRef).sort();
|
|
const sortedRef = {};
|
|
sortedKeys.forEach((key) => {
|
|
sortedRef[key] = newRef[key];
|
|
});
|
|
if ('displayName' in sortedRef) {
|
|
// add _hexDisplayName to top
|
|
return {
|
|
// @ts-ignore
|
|
_hexDisplayName: textToHex(sortedRef.displayName),
|
|
...sortedRef
|
|
};
|
|
}
|
|
if ('name' in sortedRef) {
|
|
// add _hexName to top
|
|
return {
|
|
// @ts-ignore
|
|
_hexName: textToHex(sortedRef.name),
|
|
...sortedRef
|
|
};
|
|
}
|
|
return sortedRef;
|
|
}
|
|
|
|
function changeHtmlLangAttribute(language) {
|
|
const htmlElement = document.documentElement;
|
|
htmlElement.setAttribute('lang', language);
|
|
}
|
|
|
|
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,
|
|
formatJsonVars,
|
|
changeHtmlLangAttribute,
|
|
getThemeMode,
|
|
redirectToToolsTab
|
|
};
|