mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-06 06:46:04 +02:00
theme
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div @click="confirm" class="cursor-pointer w-fit align-top">
|
||||
<span v-if="avatarType" :class="color" class="mr-2">
|
||||
<div @click="confirm" class="cursor-pointer w-fit align-top flex items-center">
|
||||
<span v-if="avatarType" :class="color" class="mr-1.5">
|
||||
<Lock v-if="avatarType === '(own)'" class="h-4 w-4" />
|
||||
<Unlock v-else-if="avatarType === '(public)'" class="h-4 w-4" />
|
||||
</span>
|
||||
|
||||
+65
-91
@@ -128,7 +128,7 @@
|
||||
<span v-show="!isCollapsed">{{ t('nav_tooltip.manage') }}</span>
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent side="right" align="start" class="w-64">
|
||||
<DropdownMenuContent side="right" align="start" class="w-54">
|
||||
<div class="flex items-center gap-2 px-2 py-1.5">
|
||||
<img class="h-6 w-6 cursor-pointer" :src="vrcxLogo" alt="VRCX" @click="openGithub" />
|
||||
<div class="flex min-w-0 flex-col">
|
||||
@@ -145,23 +145,20 @@
|
||||
<DropdownMenuItem @click="handleSettingsClick">
|
||||
<span>{{ t('nav_tooltip.settings') }}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="handleOpenCustomNavDialog">
|
||||
<span>{{ t('nav_menu.custom_nav.header') }}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<span>{{ t('view.settings.appearance.appearance.theme_mode') }}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent side="right" align="start" class="w-72">
|
||||
<DropdownMenuItem
|
||||
<DropdownMenuSubContent side="right" align="start" class="w-54">
|
||||
<DropdownMenuCheckboxItem
|
||||
v-for="theme in themes"
|
||||
:key="theme"
|
||||
:class="themeMode === theme ? 'font-medium' : undefined"
|
||||
@click="handleThemeSelect(theme)">
|
||||
<span class="flex-1">{{ themeDisplayName(theme) }}</span>
|
||||
<span v-if="themeMode === theme" class="text-muted-foreground">✓</span>
|
||||
</DropdownMenuItem>
|
||||
:model-value="themeMode === theme"
|
||||
indicator-position="right"
|
||||
@select="handleThemeSelect(theme)">
|
||||
<span>{{ themeDisplayName(theme) }}</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuSub>
|
||||
@@ -171,27 +168,36 @@
|
||||
<DropdownMenuSubContent
|
||||
side="right"
|
||||
align="start"
|
||||
class="w-72 max-h-80 overflow-auto">
|
||||
<DropdownMenuItem
|
||||
v-for="color in colorFamilies"
|
||||
:key="color.name"
|
||||
:disabled="isApplyingPrimaryColor"
|
||||
@click="handleThemeColorSelect(color)">
|
||||
class="w-54 max-h-80 overflow-auto">
|
||||
<DropdownMenuCheckboxItem
|
||||
v-for="theme in themeColors"
|
||||
:key="theme.key"
|
||||
:model-value="currentThemeColor === theme.key"
|
||||
:disabled="isApplyingThemeColor"
|
||||
indicator-position="right"
|
||||
@select="handleThemeColorSelect(theme)">
|
||||
<span class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<span
|
||||
class="h-3 w-3 shrink-0 rounded-sm"
|
||||
:style="{ backgroundColor: color.base }" />
|
||||
<span class="truncate">{{ color.name }}</span>
|
||||
:style="{ backgroundColor: theme.swatch }" />
|
||||
<span class="truncate">{{ theme.label }}</span>
|
||||
</span>
|
||||
<span v-if="currentPrimary === color.base" class="text-muted-foreground"
|
||||
>✓</span
|
||||
>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuCheckboxItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
|
||||
<DropdownMenuCheckboxItem
|
||||
:model-value="compactTableMode"
|
||||
indicator-position="right"
|
||||
@update:modelValue="handleCompactModeToggle">
|
||||
<span>{{ t('view.settings.appearance.appearance.compact_table_mode') }}</span>
|
||||
</DropdownMenuCheckboxItem>
|
||||
<DropdownMenuItem @click="handleOpenCustomNavDialog">
|
||||
<span>{{ t('nav_menu.custom_nav.header') }}</span>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem variant="destructive" @click="handleLogoutClick">
|
||||
<span>{{ t('dialog.user.actions.logout') }}</span>
|
||||
@@ -200,6 +206,15 @@
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton :tooltip="t('nav_tooltip.toggle_theme')" @click="handleThemeToggle">
|
||||
<i
|
||||
:class="currentThemeIsDark ? 'ri-moon-line' : 'ri-sun-line'"
|
||||
class="inline-flex size-6 items-center justify-center text-[19px]" />
|
||||
<span v-show="!isCollapsed">{{ t('nav_tooltip.toggle_theme') }}</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
:tooltip="isCollapsed ? t('nav_tooltip.expand_menu') : t('nav_tooltip.collapse_menu')"
|
||||
@@ -238,6 +253,7 @@
|
||||
} from '@/components/ui/sidebar';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
@@ -253,6 +269,7 @@
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useThemeColor } from '@/shared/utils/base/ui';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
@@ -307,15 +324,19 @@
|
||||
const DEFAULT_FOLDER_ICON = 'ri-menu-fold-line';
|
||||
|
||||
const VRCXUpdaterStore = useVRCXUpdaterStore();
|
||||
const { pendingVRCXUpdate, pendingVRCXInstall, updateInProgress, updateProgress, branch, appVersion } =
|
||||
storeToRefs(VRCXUpdaterStore);
|
||||
const { showVRCXUpdateDialog, updateProgressText, showChangeLogDialog } = VRCXUpdaterStore;
|
||||
const { pendingVRCXUpdate, pendingVRCXInstall, appVersion } = storeToRefs(VRCXUpdaterStore);
|
||||
const { showVRCXUpdateDialog, showChangeLogDialog } = VRCXUpdaterStore;
|
||||
const uiStore = useUiStore();
|
||||
const { notifiedMenus } = storeToRefs(uiStore);
|
||||
const { directAccessPaste } = useSearchStore();
|
||||
const { logout } = useAuthStore();
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
const { themeMode, isNavCollapsed: isCollapsed } = storeToRefs(appearanceSettingsStore);
|
||||
const {
|
||||
themeMode,
|
||||
compactTableMode,
|
||||
isDarkMode,
|
||||
isNavCollapsed: isCollapsed
|
||||
} = storeToRefs(appearanceSettingsStore);
|
||||
const navLayout = ref([]);
|
||||
const navLayoutReady = ref(false);
|
||||
|
||||
@@ -394,15 +415,8 @@
|
||||
const vrcxLogo = new URL('../../images/VRCX.png', import.meta.url).href;
|
||||
|
||||
const themes = computed(() => Object.keys(THEME_CONFIG));
|
||||
|
||||
// const {
|
||||
// currentPrimary,
|
||||
// isApplying: isApplyingPrimaryColor,
|
||||
// applyCustomPrimaryColor,
|
||||
// initPrimaryColor,
|
||||
// colorFamilies,
|
||||
// selectPaletteColor
|
||||
// } = useThemePrimaryColor();
|
||||
const { themeColors, currentThemeColor, isApplyingThemeColor, applyThemeColor, initThemeColor } = useThemeColor();
|
||||
const currentThemeIsDark = computed(() => isDarkMode.value);
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
@@ -502,9 +516,20 @@
|
||||
appearanceSettingsStore.setThemeMode(theme);
|
||||
};
|
||||
|
||||
// const handleThemeColorSelect = async (colorFamily) => {
|
||||
// await selectPaletteColor(colorFamily);
|
||||
// };
|
||||
const handleThemeToggle = () => {
|
||||
appearanceSettingsStore.setThemeMode(isDarkMode.value ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const handleCompactModeToggle = () => {
|
||||
appearanceSettingsStore.setCompactTableMode();
|
||||
};
|
||||
|
||||
const handleThemeColorSelect = async (theme) => {
|
||||
if (!theme) {
|
||||
return;
|
||||
}
|
||||
await applyThemeColor(theme.key);
|
||||
};
|
||||
|
||||
const openGithub = () => {
|
||||
openExternalLink('https://github.com/vrcx-team/VRCX');
|
||||
@@ -687,63 +712,12 @@
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
// await initPrimaryColor();
|
||||
await initThemeColor();
|
||||
await loadNavMenuConfig();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nav-menu-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 64px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
|
||||
.nav-menu-theme__label--swatch {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.nav-menu-theme__label-text {
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.nav-menu-theme__swatch {
|
||||
inline-size: 14px;
|
||||
block-size: 14px;
|
||||
border-radius: 4px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.nav-menu-theme__custom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.nav-menu-theme__custom-label {
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-menu-theme--colors {
|
||||
max-height: 360px;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.notify::after {
|
||||
position: absolute;
|
||||
top: 45%;
|
||||
|
||||
@@ -271,6 +271,7 @@
|
||||
const meta = columnDef?.meta ?? {};
|
||||
const pinned = getPinnedState(cell?.column);
|
||||
return joinClasses(
|
||||
'py-1.5',
|
||||
pinned && 'sticky bg-background z-20',
|
||||
isSpacer(cell.column) && 'p-0',
|
||||
resolveClassValue(meta.class, cell?.getContext?.()),
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80',
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/40 backdrop-blur-xs',
|
||||
props.class
|
||||
)
|
||||
">
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
textValue: { type: String, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
class: { type: null, required: false },
|
||||
indicatorPosition: { type: String, required: false, default: 'left' }
|
||||
});
|
||||
const emits = defineEmits(['select', 'update:modelValue']);
|
||||
|
||||
const delegatedProps = reactiveOmit(props, 'class');
|
||||
const delegatedProps = reactiveOmit(props, 'class', 'indicatorPosition');
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
||||
</script>
|
||||
@@ -26,10 +27,16 @@
|
||||
:class="
|
||||
cn(
|
||||
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\'size-\'])]:size-4',
|
||||
props.indicatorPosition === 'right' ? 'pr-8 pl-2' : undefined,
|
||||
props.class
|
||||
)
|
||||
">
|
||||
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<span
|
||||
:class="
|
||||
props.indicatorPosition === 'right'
|
||||
? 'pointer-events-none absolute right-2 flex size-3.5 items-center justify-center'
|
||||
: 'pointer-events-none absolute left-2 flex size-3.5 items-center justify-center'
|
||||
">
|
||||
<DropdownMenuItemIndicator>
|
||||
<slot name="indicator-icon">
|
||||
<Check class="size-4" />
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
data-slot="table-cell"
|
||||
:class="
|
||||
cn(
|
||||
'p-2 align-middle whitespace-nowrap in-[.is-compact-table]:py-1! in-[.is-compact-table]:px-2.5! [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-0.5',
|
||||
'p-2 align-middle whitespace-nowrap in-[.is-compact-table]:py-1! [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-0.5',
|
||||
props.class
|
||||
)
|
||||
">
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
import colors from 'tailwindcss/colors';
|
||||
|
||||
import configRepository from '../service/config';
|
||||
|
||||
// Tailwind indigo-500 in OKLCH
|
||||
const DEFAULT_PRIMARY_COLOR = 'oklch(58.5% 0.233 277.117)';
|
||||
const DARK_WEIGHT = 0.2;
|
||||
const CONFIG_KEY = 'VRCX_elPrimaryColor';
|
||||
const STYLE_ID = 'el-dynamic-theme';
|
||||
|
||||
let elementThemeInstance = null;
|
||||
|
||||
const INVALID_TAILWIND_COLOR_KEYS = new Set([
|
||||
'inherit',
|
||||
'current',
|
||||
'transparent',
|
||||
'black',
|
||||
'white',
|
||||
'lightBlue',
|
||||
'warmGray',
|
||||
'trueGray',
|
||||
'coolGray',
|
||||
'blueGray'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Normalize a theme color and prevent CSS injection.
|
||||
*/
|
||||
function toPrimaryColor(color, fallback = DEFAULT_PRIMARY_COLOR) {
|
||||
if (typeof color !== 'string') {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
const normalized = color.trim();
|
||||
if (!normalized) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (!CSS?.supports?.('color', normalized)) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Element Plus CSS variables based on a primary color.
|
||||
* Light colors use Tailwind palette directly; only dark-2 is calculated.
|
||||
* Dark mode overrides light-9 with a softer tint for better contrast.
|
||||
* @param {string} primary
|
||||
* @param {object|null} palette
|
||||
*/
|
||||
function setElementPlusColors(primary, palette = null) {
|
||||
let styleEl = document.getElementById(STYLE_ID);
|
||||
if (!styleEl) {
|
||||
styleEl = document.createElement('style');
|
||||
styleEl.id = STYLE_ID;
|
||||
document.head.appendChild(styleEl);
|
||||
}
|
||||
|
||||
// Derive Element Plus light steps either from a palette or by mixing with white.
|
||||
const lightValues = palette
|
||||
? ['400', '300', '200', '100', '50', '50', '50', '50', '50'].map(
|
||||
(key) => palette[key] || primary
|
||||
)
|
||||
: Array.from({ length: 9 }, (_, idx) => {
|
||||
const whitePercent = (idx + 1) * 10;
|
||||
const primaryPercent = 100 - whitePercent;
|
||||
return `color-mix(in oklch, ${primary} ${primaryPercent}%, white ${whitePercent}%)`;
|
||||
});
|
||||
|
||||
const lights = lightValues
|
||||
.map(
|
||||
(value, index) =>
|
||||
` --el-color-primary-light-${index + 1}: ${value};`
|
||||
)
|
||||
.join('\n');
|
||||
|
||||
const darkPercent = DARK_WEIGHT * 100;
|
||||
const primaryPercent = 100 - darkPercent;
|
||||
const darkValue = `color-mix(in oklch, ${primary} ${primaryPercent}%, black ${darkPercent}%)`;
|
||||
const darkLight9 = `color-mix(in oklch, ${primary} 18%, transparent)`;
|
||||
|
||||
const baseSelector =
|
||||
":root, html.dark, :root.dark, :root[data-theme='dark']";
|
||||
const darkSelector = "html.dark, :root.dark, :root[data-theme='dark']";
|
||||
styleEl.textContent =
|
||||
`${baseSelector} {\n --el-color-primary: ${primary};\n${lights}\n --el-color-primary-dark-2: ${darkValue};\n}\n` +
|
||||
`${darkSelector} {\n --el-color-primary-light-9: ${darkLight9};\n}`;
|
||||
}
|
||||
|
||||
const TAILWIND_COLOR_FAMILIES = Object.entries(colors)
|
||||
.filter(([name, palette]) => {
|
||||
return (
|
||||
!INVALID_TAILWIND_COLOR_KEYS.has(name) &&
|
||||
palette &&
|
||||
typeof palette === 'object' &&
|
||||
palette['500']
|
||||
);
|
||||
})
|
||||
.map(([name, palette]) => ({
|
||||
name,
|
||||
base: palette['500'],
|
||||
palette
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
/**
|
||||
* Shared Element Plus theme controller.
|
||||
* @param {string} defaultColor
|
||||
*/
|
||||
export function useElementTheme(defaultColor = DEFAULT_PRIMARY_COLOR) {
|
||||
if (elementThemeInstance) {
|
||||
return elementThemeInstance;
|
||||
}
|
||||
|
||||
const currentPrimary = ref(defaultColor);
|
||||
const isApplying = ref(false);
|
||||
let initialized = false;
|
||||
|
||||
const applyPrimaryColor = async (color, palette = null) => {
|
||||
const nextColor = toPrimaryColor(color, currentPrimary.value);
|
||||
const effectivePalette = palette || null;
|
||||
isApplying.value = true;
|
||||
setElementPlusColors(nextColor, effectivePalette);
|
||||
currentPrimary.value = nextColor;
|
||||
try {
|
||||
await configRepository.setString(CONFIG_KEY, nextColor);
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist theme color', error);
|
||||
} finally {
|
||||
isApplying.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const initPrimaryColor = async (fallbackColor = currentPrimary.value) => {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
const storedColor =
|
||||
(await configRepository.getString(CONFIG_KEY)) ||
|
||||
fallbackColor ||
|
||||
DEFAULT_PRIMARY_COLOR;
|
||||
await applyPrimaryColor(storedColor);
|
||||
};
|
||||
|
||||
elementThemeInstance = {
|
||||
currentPrimary,
|
||||
isApplying,
|
||||
applyPrimaryColor,
|
||||
initPrimaryColor
|
||||
};
|
||||
|
||||
return elementThemeInstance;
|
||||
}
|
||||
|
||||
export function useThemePrimaryColor() {
|
||||
const { currentPrimary, isApplying, applyPrimaryColor, initPrimaryColor } =
|
||||
useElementTheme(DEFAULT_PRIMARY_COLOR);
|
||||
const colorFamilies = TAILWIND_COLOR_FAMILIES;
|
||||
|
||||
const selectPaletteColor = async (colorFamily) => {
|
||||
if (!colorFamily) {
|
||||
return;
|
||||
}
|
||||
await applyPrimaryColor(colorFamily.base, colorFamily.palette);
|
||||
};
|
||||
|
||||
const applyCustomPrimaryColor = async (color) => {
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
await applyPrimaryColor(color);
|
||||
};
|
||||
|
||||
return {
|
||||
currentPrimary,
|
||||
isApplying,
|
||||
initPrimaryColor,
|
||||
applyCustomPrimaryColor,
|
||||
colorFamilies,
|
||||
selectPaletteColor
|
||||
};
|
||||
}
|
||||
@@ -24,7 +24,8 @@
|
||||
"manage": "Manage",
|
||||
"help_support": "Help & Support",
|
||||
"expand_menu": "Expand Menu",
|
||||
"collapse_menu": "Collapse Menu"
|
||||
"collapse_menu": "Collapse Menu",
|
||||
"toggle_theme": "Toggle Theme"
|
||||
},
|
||||
"nav_menu": {
|
||||
"resources": "RESOURCES",
|
||||
@@ -578,7 +579,7 @@
|
||||
"table_page_sizes": "Table Page Sizes",
|
||||
"table_page_sizes_error": "Page size must be a number between 1 and 1000",
|
||||
"show_notification_icon_dot": "Show Tray Notification Dot",
|
||||
"compact_table_mode": "Compact Table Mode"
|
||||
"compact_table_mode": "Compact Mode"
|
||||
},
|
||||
"timedate": {
|
||||
"header": "Time/Date",
|
||||
|
||||
@@ -573,7 +573,7 @@
|
||||
"table_page_sizes": "显示记录大小",
|
||||
"table_page_sizes_error": "记录大小必须是 1 到 1000 之间的数字",
|
||||
"show_notification_icon_dot": "显示托盘通知",
|
||||
"compact_table_mode": "紧凑表格模式"
|
||||
"compact_table_mode": "紧凑模式"
|
||||
},
|
||||
"timedate": {
|
||||
"header": "时间 / 日期",
|
||||
|
||||
@@ -574,7 +574,7 @@
|
||||
"table_page_sizes": "表格每頁筆數",
|
||||
"table_page_sizes_error": "每頁筆數必須是 1 到 1000 之間的數字",
|
||||
"show_notification_icon_dot": "顯示通知圖示提示點",
|
||||
"compact_table_mode": "精簡表格模式"
|
||||
"compact_table_mode": "精簡模式"
|
||||
},
|
||||
"timedate": {
|
||||
"header": "時間 / 日期",
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
changeAppThemeStyle,
|
||||
changeHtmlLangAttribute,
|
||||
getThemeMode,
|
||||
initThemeColor,
|
||||
refreshCustomCss
|
||||
// setLoginContainerStyle
|
||||
} from '../shared/utils/base/ui';
|
||||
@@ -25,6 +26,7 @@ export async function initUi() {
|
||||
await getThemeMode(configRepository);
|
||||
// setLoginContainerStyle(isDarkMode);
|
||||
changeAppThemeStyle(initThemeMode);
|
||||
await initThemeColor();
|
||||
} catch (error) {
|
||||
console.error('Error initializing locale and theme:', error);
|
||||
}
|
||||
|
||||
@@ -16,3 +16,54 @@ export const THEME_CONFIG = {
|
||||
// name: 'Midnight'
|
||||
// }
|
||||
};
|
||||
|
||||
export const THEME_COLORS = [
|
||||
{
|
||||
key: 'default',
|
||||
label: 'Default',
|
||||
swatch: 'oklch(0.205 0 0)',
|
||||
file: null
|
||||
},
|
||||
{
|
||||
key: 'blue',
|
||||
label: 'Blue',
|
||||
swatch: 'oklch(0.488 0.243 264.376)',
|
||||
file: 'blue.css'
|
||||
},
|
||||
{
|
||||
key: 'green',
|
||||
label: 'Green',
|
||||
swatch: 'oklch(0.648 0.2 131.684)',
|
||||
file: 'green.css'
|
||||
},
|
||||
{
|
||||
key: 'orange',
|
||||
label: 'Orange',
|
||||
swatch: 'oklch(0.646 0.222 41.116)',
|
||||
file: 'orange.css'
|
||||
},
|
||||
{
|
||||
key: 'red',
|
||||
label: 'Red',
|
||||
swatch: 'oklch(0.577 0.245 27.325)',
|
||||
file: 'red.css'
|
||||
},
|
||||
{
|
||||
key: 'rose',
|
||||
label: 'Rose',
|
||||
swatch: 'oklch(0.586 0.253 17.585)',
|
||||
file: 'rose.css'
|
||||
},
|
||||
{
|
||||
key: 'violet',
|
||||
label: 'Violet',
|
||||
swatch: 'oklch(0.541 0.281 293.009)',
|
||||
file: 'violet.css'
|
||||
},
|
||||
{
|
||||
key: 'yellow',
|
||||
label: 'Yellow',
|
||||
swatch: 'oklch(0.852 0.199 91.936)',
|
||||
file: 'yellow.css'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { toast } from 'vue-sonner';
|
||||
|
||||
import { THEME_CONFIG } from '../../constants';
|
||||
import { THEME_COLORS, THEME_CONFIG } from '../../constants';
|
||||
import { i18n } from '../../../plugin/i18n';
|
||||
import { router } from '../../../plugin/router';
|
||||
import { textToHex } from './string';
|
||||
@@ -9,6 +10,86 @@ import { useAppearanceSettingsStore } from '../../../stores';
|
||||
|
||||
import configRepository from '../../../service/config.js';
|
||||
|
||||
const THEME_COLOR_STORAGE_KEY = 'VRCX_themeColor';
|
||||
const THEME_COLOR_STYLE_ID = 'app-theme-color-style';
|
||||
const DEFAULT_THEME_COLOR_KEY = 'default';
|
||||
|
||||
const themeColors = THEME_COLORS.map((theme) => ({
|
||||
...theme,
|
||||
href: theme.file
|
||||
? new URL(`../../../styles/themes/${theme.file}`, import.meta.url).href
|
||||
: null
|
||||
}));
|
||||
|
||||
const currentThemeColor = ref(DEFAULT_THEME_COLOR_KEY);
|
||||
const isApplyingThemeColor = ref(false);
|
||||
|
||||
function resolveThemeColor(themeKey) {
|
||||
const normalized = String(themeKey).trim().toLowerCase();
|
||||
return (
|
||||
themeColors.find((theme) => theme.key === normalized) ||
|
||||
themeColors.find((theme) => theme.key === DEFAULT_THEME_COLOR_KEY)
|
||||
);
|
||||
}
|
||||
|
||||
function applyThemeColorStyle(theme) {
|
||||
const root = document.documentElement;
|
||||
root.setAttribute('data-theme-color', theme.key);
|
||||
|
||||
let styleEl = document.getElementById(THEME_COLOR_STYLE_ID);
|
||||
if (!theme.href) {
|
||||
styleEl?.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!styleEl) {
|
||||
styleEl = document.createElement('link');
|
||||
styleEl.id = THEME_COLOR_STYLE_ID;
|
||||
styleEl.rel = 'stylesheet';
|
||||
document.head.appendChild(styleEl);
|
||||
}
|
||||
|
||||
if (styleEl.getAttribute('href') !== theme.href) {
|
||||
styleEl.setAttribute('href', theme.href);
|
||||
}
|
||||
}
|
||||
|
||||
async function applyThemeColor(themeKey, { persist = true } = {}) {
|
||||
const resolved = resolveThemeColor(themeKey);
|
||||
isApplyingThemeColor.value = true;
|
||||
applyThemeColorStyle(resolved);
|
||||
currentThemeColor.value = resolved.key;
|
||||
if (persist) {
|
||||
try {
|
||||
await configRepository.setString(
|
||||
THEME_COLOR_STORAGE_KEY,
|
||||
resolved.key
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist theme color', error);
|
||||
}
|
||||
}
|
||||
isApplyingThemeColor.value = false;
|
||||
return resolved;
|
||||
}
|
||||
|
||||
async function initThemeColor() {
|
||||
const storedKey = await configRepository.getString(THEME_COLOR_STORAGE_KEY);
|
||||
const resolved = resolveThemeColor(storedKey || DEFAULT_THEME_COLOR_KEY);
|
||||
applyThemeColorStyle(resolved);
|
||||
currentThemeColor.value = resolved.key;
|
||||
}
|
||||
|
||||
function useThemeColor() {
|
||||
return {
|
||||
themeColors,
|
||||
currentThemeColor,
|
||||
isApplyingThemeColor,
|
||||
applyThemeColor,
|
||||
initThemeColor
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {boolean}
|
||||
@@ -292,6 +373,9 @@ export {
|
||||
systemIsDarkMode,
|
||||
changeAppDarkStyle,
|
||||
changeAppThemeStyle,
|
||||
useThemeColor,
|
||||
applyThemeColor,
|
||||
initThemeColor,
|
||||
updateTrustColorClasses,
|
||||
refreshCustomCss,
|
||||
refreshCustomScript,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
HueToHex,
|
||||
changeAppThemeStyle,
|
||||
changeHtmlLangAttribute,
|
||||
getThemeMode,
|
||||
updateTrustColorClasses
|
||||
} from '../../shared/utils/base/ui';
|
||||
import { database } from '../../service/database';
|
||||
@@ -41,7 +42,6 @@ export const useAppearanceSettingsStore = defineStore(
|
||||
|
||||
const MAX_TABLE_PAGE_SIZE = 1000;
|
||||
const DEFAULT_TABLE_PAGE_SIZES = [10, 15, 20, 25, 50, 100];
|
||||
// const { initPrimaryColor } = useElementTheme();
|
||||
|
||||
const appLanguage = ref('en');
|
||||
const themeMode = ref('');
|
||||
@@ -102,9 +102,10 @@ export const useAppearanceSettingsStore = defineStore(
|
||||
};
|
||||
|
||||
async function initAppearanceSettings() {
|
||||
const { initThemeMode, isDarkMode: initDarkMode } =
|
||||
await getThemeMode(configRepository);
|
||||
const [
|
||||
appLanguageConfig,
|
||||
themeModeConfig,
|
||||
displayVRCPlusIconsAsAvatarConfig,
|
||||
hideNicknamesConfig,
|
||||
showInstanceIdInLocationConfig,
|
||||
@@ -131,7 +132,6 @@ export const useAppearanceSettingsStore = defineStore(
|
||||
navIsCollapsedConfig
|
||||
] = await Promise.all([
|
||||
configRepository.getString('VRCX_appLanguage'),
|
||||
configRepository.getString('VRCX_ThemeMode', 'system'),
|
||||
configRepository.getBool('displayVRCPlusIconsAsAvatar', true),
|
||||
configRepository.getBool('VRCX_hideNicknames', false),
|
||||
configRepository.getBool(
|
||||
@@ -201,9 +201,8 @@ export const useAppearanceSettingsStore = defineStore(
|
||||
await changeAppLanguage(appLanguageConfig);
|
||||
}
|
||||
|
||||
themeMode.value = themeModeConfig;
|
||||
setThemeMode(themeModeConfig);
|
||||
// await initPrimaryColor();
|
||||
themeMode.value = initThemeMode;
|
||||
isDarkMode.value = initDarkMode;
|
||||
|
||||
displayVRCPlusIconsAsAvatar.value =
|
||||
displayVRCPlusIconsAsAvatarConfig;
|
||||
|
||||
@@ -142,10 +142,12 @@
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
/* -2 */
|
||||
--radius-sm: calc(var(--radius) - 6px);
|
||||
--radius-md: calc(var(--radius) - 4px);
|
||||
--radius-lg: calc(var(--radius) - 2px);
|
||||
--radius-xl: calc(var(--radius) + 2px);
|
||||
/* */
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.488 0.243 264.376);
|
||||
--primary-foreground: oklch(0.97 0.014 254.604);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.809 0.105 251.813);
|
||||
--chart-2: oklch(0.623 0.214 259.815);
|
||||
--chart-3: oklch(0.546 0.245 262.881);
|
||||
--chart-4: oklch(0.488 0.243 264.376);
|
||||
--chart-5: oklch(0.424 0.199 265.638);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.546 0.245 262.881);
|
||||
--sidebar-primary-foreground: oklch(0.97 0.014 254.604);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.488 0.243 264.376);
|
||||
--primary-foreground: oklch(0.97 0.014 254.604);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.556 0 0);
|
||||
--chart-1: oklch(0.809 0.105 251.813);
|
||||
--chart-2: oklch(0.623 0.214 259.815);
|
||||
--chart-3: oklch(0.546 0.245 262.881);
|
||||
--chart-4: oklch(0.488 0.243 264.376);
|
||||
--chart-5: oklch(0.424 0.199 265.638);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.623 0.214 259.815);
|
||||
--sidebar-primary-foreground: oklch(0.97 0.014 254.604);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.648 0.2 131.684);
|
||||
--primary-foreground: oklch(0.986 0.031 120.757);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.841 0.238 128.85);
|
||||
--chart-1: oklch(0.871 0.15 154.449);
|
||||
--chart-2: oklch(0.723 0.219 149.579);
|
||||
--chart-3: oklch(0.627 0.194 149.214);
|
||||
--chart-4: oklch(0.527 0.154 150.069);
|
||||
--chart-5: oklch(0.448 0.119 151.328);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.648 0.2 131.684);
|
||||
--sidebar-primary-foreground: oklch(0.986 0.031 120.757);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.841 0.238 128.85);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.648 0.2 131.684);
|
||||
--primary-foreground: oklch(0.986 0.031 120.757);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.405 0.101 131.063);
|
||||
--chart-1: oklch(0.871 0.15 154.449);
|
||||
--chart-2: oklch(0.723 0.219 149.579);
|
||||
--chart-3: oklch(0.627 0.194 149.214);
|
||||
--chart-4: oklch(0.527 0.154 150.069);
|
||||
--chart-5: oklch(0.448 0.119 151.328);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.768 0.233 130.85);
|
||||
--sidebar-primary-foreground: oklch(0.986 0.031 120.757);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.405 0.101 131.063);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.646 0.222 41.116);
|
||||
--primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.75 0.183 55.934);
|
||||
--chart-1: oklch(0.837 0.128 66.29);
|
||||
--chart-2: oklch(0.705 0.213 47.604);
|
||||
--chart-3: oklch(0.646 0.222 41.116);
|
||||
--chart-4: oklch(0.553 0.195 38.402);
|
||||
--chart-5: oklch(0.47 0.157 37.304);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.646 0.222 41.116);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.75 0.183 55.934);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.705 0.213 47.604);
|
||||
--primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.408 0.123 38.172);
|
||||
--chart-1: oklch(0.837 0.128 66.29);
|
||||
--chart-2: oklch(0.705 0.213 47.604);
|
||||
--chart-3: oklch(0.646 0.222 41.116);
|
||||
--chart-4: oklch(0.553 0.195 38.402);
|
||||
--chart-5: oklch(0.47 0.157 37.304);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.705 0.213 47.604);
|
||||
--sidebar-primary-foreground: oklch(0.98 0.016 73.684);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.408 0.123 38.172);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.577 0.245 27.325);
|
||||
--primary-foreground: oklch(0.971 0.013 17.38);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.704 0.191 22.216);
|
||||
--chart-1: oklch(0.808 0.114 19.571);
|
||||
--chart-2: oklch(0.637 0.237 25.331);
|
||||
--chart-3: oklch(0.577 0.245 27.325);
|
||||
--chart-4: oklch(0.505 0.213 27.518);
|
||||
--chart-5: oklch(0.444 0.177 26.899);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.577 0.245 27.325);
|
||||
--sidebar-primary-foreground: oklch(0.971 0.013 17.38);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.704 0.191 22.216);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.637 0.237 25.331);
|
||||
--primary-foreground: oklch(0.971 0.013 17.38);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.396 0.141 25.723);
|
||||
--chart-1: oklch(0.808 0.114 19.571);
|
||||
--chart-2: oklch(0.637 0.237 25.331);
|
||||
--chart-3: oklch(0.577 0.245 27.325);
|
||||
--chart-4: oklch(0.505 0.213 27.518);
|
||||
--chart-5: oklch(0.444 0.177 26.899);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.637 0.237 25.331);
|
||||
--sidebar-primary-foreground: oklch(0.971 0.013 17.38);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.396 0.141 25.723);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.586 0.253 17.585);
|
||||
--primary-foreground: oklch(0.969 0.015 12.422);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.712 0.194 13.428);
|
||||
--chart-1: oklch(0.81 0.117 11.638);
|
||||
--chart-2: oklch(0.645 0.246 16.439);
|
||||
--chart-3: oklch(0.586 0.253 17.585);
|
||||
--chart-4: oklch(0.514 0.222 16.935);
|
||||
--chart-5: oklch(0.455 0.188 13.697);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.586 0.253 17.585);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.015 12.422);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.712 0.194 13.428);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.645 0.246 16.439);
|
||||
--primary-foreground: oklch(0.969 0.015 12.422);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.41 0.159 10.272);
|
||||
--chart-1: oklch(0.81 0.117 11.638);
|
||||
--chart-2: oklch(0.645 0.246 16.439);
|
||||
--chart-3: oklch(0.586 0.253 17.585);
|
||||
--chart-4: oklch(0.514 0.222 16.935);
|
||||
--chart-5: oklch(0.455 0.188 13.697);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.645 0.246 16.439);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.015 12.422);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.41 0.159 10.272);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.541 0.281 293.009);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.702 0.183 293.541);
|
||||
--chart-1: oklch(0.811 0.111 293.571);
|
||||
--chart-2: oklch(0.606 0.25 292.717);
|
||||
--chart-3: oklch(0.541 0.281 293.009);
|
||||
--chart-4: oklch(0.491 0.27 292.581);
|
||||
--chart-5: oklch(0.432 0.232 292.759);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.541 0.281 293.009);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.702 0.183 293.541);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.606 0.25 292.717);
|
||||
--primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.38 0.189 293.745);
|
||||
--chart-1: oklch(0.811 0.111 293.571);
|
||||
--chart-2: oklch(0.606 0.25 292.717);
|
||||
--chart-3: oklch(0.541 0.281 293.009);
|
||||
--chart-4: oklch(0.491 0.27 292.581);
|
||||
--chart-5: oklch(0.432 0.232 292.759);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.606 0.25 292.717);
|
||||
--sidebar-primary-foreground: oklch(0.969 0.016 293.756);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.38 0.189 293.745);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
:root {
|
||||
--radius: 0.65rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.141 0.005 285.823);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.141 0.005 285.823);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.141 0.005 285.823);
|
||||
--primary: oklch(0.852 0.199 91.936);
|
||||
--primary-foreground: oklch(0.421 0.095 57.708);
|
||||
--secondary: oklch(0.967 0.001 286.375);
|
||||
--secondary-foreground: oklch(0.21 0.006 285.885);
|
||||
--muted: oklch(0.967 0.001 286.375);
|
||||
--muted-foreground: oklch(0.552 0.016 285.938);
|
||||
--accent: oklch(0.967 0.001 286.375);
|
||||
--accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.92 0.004 286.32);
|
||||
--input: oklch(0.92 0.004 286.32);
|
||||
--ring: oklch(0.852 0.199 91.936);
|
||||
--chart-1: oklch(0.905 0.182 98.111);
|
||||
--chart-2: oklch(0.795 0.184 86.047);
|
||||
--chart-3: oklch(0.681 0.162 75.834);
|
||||
--chart-4: oklch(0.554 0.135 66.442);
|
||||
--chart-5: oklch(0.476 0.114 61.907);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.141 0.005 285.823);
|
||||
--sidebar-primary: oklch(0.681 0.162 75.834);
|
||||
--sidebar-primary-foreground: oklch(0.987 0.026 102.212);
|
||||
--sidebar-accent: oklch(0.967 0.001 286.375);
|
||||
--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
|
||||
--sidebar-border: oklch(0.92 0.004 286.32);
|
||||
--sidebar-ring: oklch(0.852 0.199 91.936);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.141 0.005 285.823);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.21 0.006 285.885);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.21 0.006 285.885);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.795 0.184 86.047);
|
||||
--primary-foreground: oklch(0.421 0.095 57.708);
|
||||
--secondary: oklch(0.274 0.006 286.033);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.274 0.006 286.033);
|
||||
--muted-foreground: oklch(0.705 0.015 286.067);
|
||||
--accent: oklch(0.274 0.006 286.033);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.421 0.095 57.708);
|
||||
--chart-1: oklch(0.905 0.182 98.111);
|
||||
--chart-2: oklch(0.795 0.184 86.047);
|
||||
--chart-3: oklch(0.681 0.162 75.834);
|
||||
--chart-4: oklch(0.554 0.135 66.442);
|
||||
--chart-5: oklch(0.476 0.114 61.907);
|
||||
--sidebar: oklch(0.21 0.006 285.885);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.795 0.184 86.047);
|
||||
--sidebar-primary-foreground: oklch(0.987 0.026 102.212);
|
||||
--sidebar-accent: oklch(0.274 0.006 286.033);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.421 0.095 57.708);
|
||||
}
|
||||
@@ -189,7 +189,6 @@
|
||||
|
||||
import InstanceActivityDetail from './InstanceActivityDetail.vue';
|
||||
|
||||
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const appearanceSettingsStore = useAppearanceSettingsStore();
|
||||
|
||||
@@ -166,7 +166,6 @@
|
||||
import { database } from '../../../service/database';
|
||||
import { userRequest } from '../../../api';
|
||||
|
||||
|
||||
import configRepository from '../../../service/config';
|
||||
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
import { TabsUnderline } from '@/components/ui/tabs';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
import AdvancedTab from './components/Tabs/AdvancedTab.vue';
|
||||
import AppearanceTab from './components/Tabs/AppearanceTab.vue';
|
||||
import DiscordPresenceTab from './components/Tabs/DiscordPresenceTab.vue';
|
||||
|
||||
@@ -166,10 +166,6 @@
|
||||
</ListboxRoot>
|
||||
</Popover>
|
||||
</div>
|
||||
<simple-switch
|
||||
:label="t('view.settings.appearance.appearance.compact_table_mode')"
|
||||
:value="compactTableMode"
|
||||
@change="setCompactTableMode" />
|
||||
<div class="options-container-item">
|
||||
<Button size="sm" variant="outline" @click="promptMaxTableSizeDialog">{{
|
||||
t('view.settings.appearance.appearance.table_max_size')
|
||||
@@ -467,8 +463,7 @@
|
||||
randomUserColours,
|
||||
trustColor,
|
||||
notificationIconDot,
|
||||
tablePageSizes,
|
||||
compactTableMode
|
||||
tablePageSizes
|
||||
} = storeToRefs(appearanceSettingsStore);
|
||||
|
||||
const appLanguageDisplayName = computed(() => getLanguageName(String(appLanguage.value)));
|
||||
@@ -497,8 +492,7 @@
|
||||
changeAppLanguage,
|
||||
promptMaxTableSizeDialog,
|
||||
setNotificationIconDot,
|
||||
setTablePageSizes,
|
||||
setCompactTableMode
|
||||
setTablePageSizes
|
||||
} = appearanceSettingsStore;
|
||||
|
||||
const zoomLevel = ref(100);
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
>{{ friend.ref.displayName }}{{ isGroupByInstance && friend.isVIP ? ' ⭐' : '' }}</span
|
||||
>
|
||||
|
||||
<span v-if="isFriendActiveOrOffline" class="block truncate text-xs">{{ friend.ref.statusDescription }}</span>
|
||||
<span v-if="isFriendActiveOrOffline" class="block truncate text-xs">{{
|
||||
friend.ref.statusDescription
|
||||
}}</span>
|
||||
<template v-else>
|
||||
<div v-if="friend.pendingOffline" class="text-xs">
|
||||
<AlertTriangle class="inline-block" /> {{ t('side_panel.pending_offline') }}
|
||||
@@ -30,7 +32,12 @@
|
||||
" />
|
||||
</div>
|
||||
</template>
|
||||
<Location v-else class="text-xs" :location="locationProp" :traveling="travelingProp" :link="false" />
|
||||
<Location
|
||||
v-else
|
||||
class="text-xs"
|
||||
:location="locationProp"
|
||||
:traveling="travelingProp"
|
||||
:link="false" />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -220,7 +220,6 @@
|
||||
import { toast } from 'vue-sonner';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
||||
import { useFriendStore, useGalleryStore } from '../../stores';
|
||||
|
||||
const GroupCalendarDialog = defineAsyncComponent(() => import('./dialogs/GroupCalendarDialog.vue'));
|
||||
|
||||
Reference in New Issue
Block a user