improve settings ui

This commit is contained in:
pa
2026-03-23 20:20:59 +09:00
parent e6ec7e6150
commit 296e254718
7 changed files with 271 additions and 264 deletions

View File

@@ -15,7 +15,9 @@
"delete": "Delete", "delete": "Delete",
"remove": "Remove", "remove": "Remove",
"reset": "Reset", "reset": "Reset",
"view_details": "View Details" "view_details": "View Details",
"configure": "Configure",
"refresh": "Refresh"
}, },
"sort_by": "Sort by:", "sort_by": "Sort by:",
"time_units": { "time_units": {
@@ -920,7 +922,7 @@
"vrcx_memos_description": "Shows notes saved locally in VRCX", "vrcx_memos_description": "Shows notes saved locally in VRCX",
"recent_action_cooldown": "Recent Action Indicator", "recent_action_cooldown": "Recent Action Indicator",
"recent_action_cooldown_description": "Show a clock icon on invite/request actions that were recently performed", "recent_action_cooldown_description": "Show a clock icon on invite/request actions that were recently performed",
"recent_action_cooldown_minutes": "Cooldown (minutes)" "recent_action_cooldown_minutes": "Cooldown (Minutes)"
}, },
"user_colors": { "user_colors": {
"header": "User Colors", "header": "User Colors",
@@ -974,7 +976,7 @@
}, },
"text_to_speech": { "text_to_speech": {
"header": "Text-To-Speech", "header": "Text-To-Speech",
"when_to_play": "Notification TTS. When to Play", "when_to_play": "Notification TTS",
"tts_voice": "TTS Voice", "tts_voice": "TTS Voice",
"use_memo_nicknames": "Use Memo Nicknames", "use_memo_nicknames": "Use Memo Nicknames",
"play": "Play", "play": "Play",

View File

@@ -410,6 +410,20 @@ export const useNotificationsSettingsStore = defineStore(
speechSynthesis.speak(tts); speechSynthesis.speak(tts);
} }
/**
* @param {number} seconds - timeout in seconds
*/
function setNotificationTimeout(seconds) {
const ms = Math.trunc(Number(seconds) * 1000);
if (isNaN(ms) || ms < 0) return;
notificationTimeout.value = ms;
configRepository.setString(
'VRCX_notificationTimeout',
ms.toString()
);
vrStore.updateVRConfigVars();
}
function promptNotificationTimeout() { function promptNotificationTimeout() {
modalStore modalStore
.prompt({ .prompt({
@@ -474,6 +488,7 @@ export const useNotificationsSettingsStore = defineStore(
testNotificationTTS, testNotificationTTS,
speak, speak,
changeNotificationPosition, changeNotificationPosition,
setNotificationTimeout,
promptNotificationTimeout promptNotificationTimeout
}; };
} }

View File

@@ -227,7 +227,9 @@
<span v-text="sqliteTableSizes.event"></span <span v-text="sqliteTableSizes.event"></span
></span> ></span>
</div> </div>
</SettingsGroup>
<SettingsGroup :title="t('view.settings.advanced.advanced.database_cleanup.header')">
<SettingsItem <SettingsItem
:label="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup')" :label="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup')"
:description="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup_description')"> :description="t('view.settings.advanced.advanced.database_cleanup.auto_cleanup_description')">
@@ -329,35 +331,29 @@
</Dialog> </Dialog>
<SettingsGroup :title="t('view.settings.advanced_groups.diagnostics.header')"> <SettingsGroup :title="t('view.settings.advanced_groups.diagnostics.header')">
<SettingsGroup :title="t('view.profile.game_info.header')"> <SettingsItem :label="t('view.profile.game_info.online_users')">
<div class="px-1 py-1"> <div class="flex items-center gap-2">
<div class="flex-1 cursor-pointer" @click="getVisits"> <span v-if="visits !== null" class="text-sm text-muted-foreground">{{
<span class="block truncate font-medium text-sm leading-[18px]">{{
t('view.profile.game_info.online_users')
}}</span>
<span v-if="visits" class="block truncate text-xs text-muted-foreground">{{
t('view.profile.game_info.user_online', { count: visits }) t('view.profile.game_info.user_online', { count: visits })
}}</span> }}</span>
<span v-else class="block truncate text-xs text-muted-foreground">{{ <Button size="sm" variant="outline" @click="getVisits">{{ t('common.actions.refresh') }}</Button>
t('view.profile.game_info.refresh')
}}</span>
</div> </div>
</div> </SettingsItem>
</SettingsGroup>
<SettingsGroup :title="t('view.profile.config_json')"> <SettingsItem :label="t('view.profile.config_json')">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<TooltipWrapper side="top" :content="t('view.profile.refresh_tooltip')"> <Button size="sm" variant="outline" @click="refreshConfigTreeData()">{{
<Button class="rounded-full" size="icon-sm" variant="outline" @click="refreshConfigTreeData()"> t('common.actions.refresh')
<RefreshCcw /> }}</Button>
</Button> <Button
</TooltipWrapper> v-if="Object.keys(configTreeData).length > 0"
<TooltipWrapper side="top" :content="t('view.profile.clear_results_tooltip')"> size="sm"
<Button class="rounded-full" size="icon-sm" variant="outline" @click="configTreeData = {}"> variant="outline"
<Trash2 /> @click="configTreeData = {}"
</Button> >{{ t('common.actions.clear') }}</Button
</TooltipWrapper> >
</div> </div>
</SettingsItem>
<vue-json-pretty <vue-json-pretty
v-if="Object.keys(configTreeData).length > 0" v-if="Object.keys(configTreeData).length > 0"
:data="configTreeData" :data="configTreeData"
@@ -368,7 +364,6 @@
virtual virtual
show-icon /> show-icon />
</SettingsGroup> </SettingsGroup>
</SettingsGroup>
<template v-if="branch === 'Nightly'"> <template v-if="branch === 'Nightly'">
<SettingsGroup :title="t('view.settings.advanced_groups.nightly.header')"> <SettingsGroup :title="t('view.settings.advanced_groups.nightly.header')">
@@ -386,7 +381,7 @@
</template> </template>
<script setup> <script setup>
import { RefreshCcw, Trash2, TriangleAlert } from 'lucide-vue-next'; import { Trash2, TriangleAlert } from 'lucide-vue-next';
import { computed, reactive, ref } from 'vue'; import { computed, reactive, ref } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
@@ -421,7 +416,6 @@
import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue'; import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue';
import SettingsGroup from '../SettingsGroup.vue'; import SettingsGroup from '../SettingsGroup.vue';
import SettingsItem from '../SettingsItem.vue'; import SettingsItem from '../SettingsItem.vue';
import { TooltipWrapper } from '@/components/ui/tooltip';
const { t } = useI18n(); const { t } = useI18n();
@@ -486,7 +480,7 @@
} = advancedSettingsStore; } = advancedSettingsStore;
const configTreeData = ref({}); const configTreeData = ref({});
const visits = ref(0); const visits = ref(null);
const selectedPurgePeriod = ref('180'); const selectedPurgePeriod = ref('180');
const isPurgeDialogVisible = ref(false); const isPurgeDialogVisible = ref(false);

View File

@@ -156,51 +156,61 @@
<SettingsGroup :title="t('view.settings.interface.lists_tables.header')"> <SettingsGroup :title="t('view.settings.interface.lists_tables.header')">
<SettingsItem :label="t('view.settings.appearance.appearance.sort_favorite_by')"> <SettingsItem :label="t('view.settings.appearance.appearance.sort_favorite_by')">
<RadioGroup <ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="sortFavorites ? 'true' : 'false'" :model-value="sortFavorites ? 'true' : 'false'"
class="gap-2 flex" @update:model-value="handleSortFavoritesRadio">
@update:modelValue="handleSortFavoritesRadio"> <ToggleGroupItem value="false">{{
<div class="flex items-center space-x-2"> t('view.settings.appearance.appearance.sort_favorite_by_name')
<RadioGroupItem id="sortFavorites-false" value="false" /> }}</ToggleGroupItem>
<label for="sortFavorites-false"> <ToggleGroupItem value="true">{{
{{ t('view.settings.appearance.appearance.sort_favorite_by_name') }} t('view.settings.appearance.appearance.sort_favorite_by_date')
</label> }}</ToggleGroupItem>
</div> </ToggleGroup>
<div class="flex items-center space-x-2">
<RadioGroupItem id="sortFavorites-true" value="true" />
<label for="sortFavorites-true">
{{ t('view.settings.appearance.appearance.sort_favorite_by_date') }}
</label>
</div>
</RadioGroup>
</SettingsItem> </SettingsItem>
<SettingsItem :label="t('view.settings.appearance.appearance.sort_instance_users_by')"> <SettingsItem :label="t('view.settings.appearance.appearance.sort_instance_users_by')">
<RadioGroup <ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="instanceUsersSortAlphabetical ? 'true' : 'false'" :model-value="instanceUsersSortAlphabetical ? 'true' : 'false'"
class="gap-2 flex" @update:model-value="handleInstanceUsersSortAlphabeticalRadio">
@update:modelValue="handleInstanceUsersSortAlphabeticalRadio"> <ToggleGroupItem value="false">{{
<div class="flex items-center space-x-2"> t('view.settings.appearance.appearance.sort_instance_users_by_time')
<RadioGroupItem id="instanceUsersSortAlphabetical-false" value="false" /> }}</ToggleGroupItem>
<label for="instanceUsersSortAlphabetical-false"> <ToggleGroupItem value="true">{{
{{ t('view.settings.appearance.appearance.sort_instance_users_by_time') }} t('view.settings.appearance.appearance.sort_instance_users_by_alphabet')
</label> }}</ToggleGroupItem>
</div> </ToggleGroup>
<div class="flex items-center space-x-2">
<RadioGroupItem id="instanceUsersSortAlphabetical-true" value="true" />
<label for="instanceUsersSortAlphabetical-true">
{{ t('view.settings.appearance.appearance.sort_instance_users_by_alphabet') }}
</label>
</div>
</RadioGroup>
</SettingsItem> </SettingsItem>
<SettingsItem :label="t('view.settings.appearance.appearance.table_page_sizes')"> <SettingsItem :label="t('view.settings.appearance.appearance.table_page_sizes')">
<Button size="sm" variant="outline" @click="tablePageSizesDialogOpen = true">{{
t('common.actions.configure')
}}</Button>
<Dialog v-model:open="tablePageSizesDialogOpen">
<DialogContent class="sm:max-w-md">
<DialogHeader>
<DialogTitle>{{ t('view.settings.appearance.appearance.table_page_sizes') }}</DialogTitle>
</DialogHeader>
<Popover v-model:open="tablePageSizesOpen"> <Popover v-model:open="tablePageSizesOpen">
<ListboxRoot v-model="tablePageSizesModel" highlight-on-hover multiple> <ListboxRoot v-model="tablePageSizesModel" highlight-on-hover multiple>
<PopoverAnchor class="inline-flex w-75"> <PopoverAnchor class="inline-flex w-full">
<TagsInput v-slot="{ modelValue: tags }" v-model="tablePageSizesModel" class="w-full"> <TagsInput
<TagsInputItem v-for="item in tags" :key="item.toString()" :value="item.toString()"> v-slot="{ modelValue: tags }"
v-model="tablePageSizesModel"
class="w-full">
<TagsInputItem
v-for="item in tags"
:key="item.toString()"
:value="item.toString()">
<TagsInputItemText /> <TagsInputItemText />
<TagsInputItemDelete /> <TagsInputItemDelete />
</TagsInputItem> </TagsInputItem>
@@ -208,21 +218,25 @@
<ListboxFilter v-model="tablePageSizesSearchTerm" as-child> <ListboxFilter v-model="tablePageSizesSearchTerm" as-child>
<TagsInputInput <TagsInputInput
:placeholder="t('view.settings.appearance.appearance.table_page_sizes')" :placeholder="t('view.settings.appearance.appearance.table_page_sizes')"
@keydown.enter.prevent="addTablePageSizeFromInput"
@keydown.down="tablePageSizesOpen = true" /> @keydown.down="tablePageSizesOpen = true" />
</ListboxFilter> </ListboxFilter>
<PopoverTrigger as-child> <PopoverTrigger as-child>
<Button size="icon-sm" variant="ghost" class="order-last self-start ml-auto"> <Button
size="icon-sm"
variant="ghost"
class="order-last ml-auto self-start">
<ChevronDown class="size-3.5" /> <ChevronDown class="size-3.5" />
</Button> </Button>
</PopoverTrigger> </PopoverTrigger>
</TagsInput> </TagsInput>
</PopoverAnchor> </PopoverAnchor>
<PopoverContent class="p-1" @open-auto-focus.prevent> <PopoverContent
class="w-[var(--reka-popover-trigger-width)] p-1"
@open-auto-focus.prevent>
<ListboxContent <ListboxContent
class="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto empty:after:content-['No_options'] empty:p-1 empty:after:block" class="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto empty:after:block empty:after:content-['No_options'] empty:p-1"
tabindex="0"> tabindex="0">
<ListboxItem <ListboxItem
v-for="size in filteredTablePageSizeOptions" v-for="size in filteredTablePageSizeOptions"
@@ -232,7 +246,8 @@
@select="tablePageSizesSearchTerm = ''"> @select="tablePageSizesSearchTerm = ''">
<span>{{ size }}</span> <span>{{ size }}</span>
<ListboxItemIndicator class="ml-auto inline-flex items-center justify-center"> <ListboxItemIndicator
class="ml-auto inline-flex items-center justify-center">
<CheckIcon /> <CheckIcon />
</ListboxItemIndicator> </ListboxItemIndicator>
</ListboxItem> </ListboxItem>
@@ -240,13 +255,21 @@
</PopoverContent> </PopoverContent>
</ListboxRoot> </ListboxRoot>
</Popover> </Popover>
<DialogFooter>
<Button variant="outline" @click="tablePageSizesDialogOpen = false">{{
t('dialog.alertdialog.ok')
}}</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
:label="t('view.settings.appearance.appearance.table_entries_settings')" :label="t('view.settings.appearance.appearance.table_entries_settings')"
:description="t('view.settings.appearance.appearance.table_entries_settings_description')"> :description="t('view.settings.appearance.appearance.table_entries_settings_description')">
<Button size="sm" variant="outline" @click="showTableLimitsDialog">{{ <Button size="sm" variant="outline" @click="showTableLimitsDialog">{{
t('view.settings.appearance.appearance.table_entries_settings') t('common.actions.configure')
}}</Button> }}</Button>
</SettingsItem> </SettingsItem>
</SettingsGroup> </SettingsGroup>
@@ -254,23 +277,20 @@
<SettingsGroup :title="t('view.settings.appearance.timedate.header')"> <SettingsGroup :title="t('view.settings.appearance.timedate.header')">
<SettingsItem :label="t('view.settings.appearance.timedate.time_format')"> <SettingsItem :label="t('view.settings.appearance.timedate.time_format')">
<RadioGroup <ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="dtHour12 ? 'true' : 'false'" :model-value="dtHour12 ? 'true' : 'false'"
class="gap-2 flex" @update:model-value="handleDtHour12Radio">
@update:modelValue="handleDtHour12Radio"> <ToggleGroupItem value="true">{{
<div class="flex items-center space-x-2"> t('view.settings.appearance.timedate.time_format_12')
<RadioGroupItem id="dtHour12-true" value="true" /> }}</ToggleGroupItem>
<label for="dtHour12-true"> <ToggleGroupItem value="false">{{
{{ t('view.settings.appearance.timedate.time_format_12') }} t('view.settings.appearance.timedate.time_format_24')
</label> }}</ToggleGroupItem>
</div> </ToggleGroup>
<div class="flex items-center space-x-2">
<RadioGroupItem id="dtHour12-false" value="false" />
<label for="dtHour12-false">
{{ t('view.settings.appearance.timedate.time_format_24') }}
</label>
</div>
</RadioGroup>
</SettingsItem> </SettingsItem>
<SettingsItem :label="t('view.settings.appearance.timedate.force_iso_date_format')"> <SettingsItem :label="t('view.settings.appearance.timedate.force_iso_date_format')">
@@ -366,11 +386,13 @@
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { computed, onBeforeUnmount, ref, watch } from 'vue'; import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { CheckIcon, ChevronDown } from 'lucide-vue-next'; import { CheckIcon, ChevronDown } from 'lucide-vue-next';
import { useAppearanceSettingsStore, useFavoriteStore, useVrStore } from '@/stores'; import { useAppearanceSettingsStore, useVrStore } from '@/stores';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { getLanguageName, languageCodes } from '@/localization'; import { getLanguageName, languageCodes } from '@/localization';
import { APP_CJK_FONT_PACKS, APP_FONT_CONFIG, APP_FONT_DEFAULT_KEY, APP_FONT_FAMILIES } from '@/shared/constants'; import { APP_CJK_FONT_PACKS, APP_FONT_CONFIG, APP_FONT_DEFAULT_KEY, APP_FONT_FAMILIES } from '@/shared/constants';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
@@ -626,6 +648,7 @@
const TABLE_PAGE_SIZE_SUGGESTIONS = Object.freeze([5, 10, 15, 20, 25, 30, 50, 75, 100, 150, 200, 250, 500, 1000]); const TABLE_PAGE_SIZE_SUGGESTIONS = Object.freeze([5, 10, 15, 20, 25, 30, 50, 75, 100, 150, 200, 250, 500, 1000]);
const tablePageSizesDialogOpen = ref(false);
const tablePageSizesOpen = ref(false); const tablePageSizesOpen = ref(false);
const tablePageSizesSearchTerm = ref(''); const tablePageSizesSearchTerm = ref('');
@@ -650,22 +673,6 @@
} }
}); });
/**
*
*/
function addTablePageSizeFromInput() {
const raw = String(tablePageSizesSearchTerm.value ?? '').trim();
if (!raw) {
return;
}
if (!Array.isArray(tablePageSizesModel.value)) {
tablePageSizesModel.value = [raw];
} else if (!tablePageSizesModel.value.includes(raw)) {
tablePageSizesModel.value = [...tablePageSizesModel.value, raw];
}
tablePageSizesSearchTerm.value = '';
}
/** /**
* *
*/ */

View File

@@ -15,7 +15,10 @@
<span class="block truncate font-medium text-sm leading-[18px]">{{ <span class="block truncate font-medium text-sm leading-[18px]">{{
t('view.settings.general.general.latest_app_version') t('view.settings.general.general.latest_app_version')
}}</span> }}</span>
<span v-if="latestAppVersion" class="block truncate text-xs text-muted-foreground" v-text="latestAppVersion"></span> <span
v-if="latestAppVersion"
class="block truncate text-xs text-muted-foreground"
v-text="latestAppVersion"></span>
<span v-else class="block truncate text-xs text-muted-foreground">{{ <span v-else class="block truncate text-xs text-muted-foreground">{{
t('view.settings.general.general.latest_app_version_refresh') t('view.settings.general.general.latest_app_version_refresh')
}}</span> }}</span>
@@ -53,23 +56,22 @@
<template v-if="!noUpdater"> <template v-if="!noUpdater">
<SettingsItem :label="t('view.settings.general.vrcx_updater.update_action')"> <SettingsItem :label="t('view.settings.general.vrcx_updater.update_action')">
<ToggleGroup <Select :model-value="autoUpdateVRCX" @update:model-value="setAutoUpdateVRCX">
type="single" <SelectTrigger size="sm">
required <SelectValue />
variant="outline" </SelectTrigger>
size="sm" <SelectContent>
:model-value="autoUpdateVRCX" <SelectItem value="Off">{{
@update:model-value="setAutoUpdateVRCX">
<ToggleGroupItem value="Off">{{
t('view.settings.general.vrcx_updater.auto_update_off') t('view.settings.general.vrcx_updater.auto_update_off')
}}</ToggleGroupItem> }}</SelectItem>
<ToggleGroupItem value="Notify">{{ <SelectItem value="Notify">{{
t('view.settings.general.vrcx_updater.auto_update_notify') t('view.settings.general.vrcx_updater.auto_update_notify')
}}</ToggleGroupItem> }}</SelectItem>
<ToggleGroupItem value="Auto Download">{{ <SelectItem value="Auto Download">{{
t('view.settings.general.vrcx_updater.auto_update_download') t('view.settings.general.vrcx_updater.auto_update_download')
}}</ToggleGroupItem> }}</SelectItem>
</ToggleGroup> </SelectContent>
</Select>
</SettingsItem> </SettingsItem>
</template> </template>
<div v-else class="text-sm text-muted-foreground"> <div v-else class="text-sm text-muted-foreground">
@@ -82,9 +84,7 @@
<Switch :model-value="isStartAtWindowsStartup" @update:modelValue="setIsStartAtWindowsStartup" /> <Switch :model-value="isStartAtWindowsStartup" @update:modelValue="setIsStartAtWindowsStartup" />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem v-if="!isLinux" :label="t('view.settings.general.application.minimized')">
v-if="!isLinux"
:label="t('view.settings.general.application.minimized')">
<Switch :model-value="isStartAsMinimizedState" @update:modelValue="setIsStartAsMinimizedState" /> <Switch :model-value="isStartAsMinimizedState" @update:modelValue="setIsStartAsMinimizedState" />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
@@ -132,7 +132,7 @@
</SettingsGroup> </SettingsGroup>
<SettingsGroup :title="t('view.settings.general.legal_notice.header')"> <SettingsGroup :title="t('view.settings.general.legal_notice.header')">
<div class="flex flex-col gap-2 text-sm text-muted-foreground"> <div class="flex flex-col gap-2 text-sm text-muted-foreground mb-2">
<p class="m-0"> <p class="m-0">
&copy; 2019-2026 &copy; 2019-2026
<a class="cursor-pointer" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a> &amp; <a class="cursor-pointer" @click="openExternalLink('https://github.com/pypy-vrc')">pypy</a> &amp;
@@ -163,7 +163,7 @@
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { useGeneralSettingsStore, useVRCXUpdaterStore } from '@/stores'; import { useGeneralSettingsStore, useVRCXUpdaterStore } from '@/stores';
import { links } from '@/shared/constants'; import { links } from '@/shared/constants';
import { openExternalLink } from '@/shared/utils'; import { openExternalLink } from '@/shared/utils';

View File

@@ -2,8 +2,7 @@
<div class="flex flex-col gap-10 py-2"> <div class="flex flex-col gap-10 py-2">
<!-- VR Core --> <!-- VR Core -->
<SettingsGroup :title="t('view.settings.vr.vr_core.header')"> <SettingsGroup :title="t('view.settings.vr.vr_core.header')">
<SettingsItem <SettingsItem :label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')">
:label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')">
<Switch <Switch
:model-value="openVR" :model-value="openVR"
@update:modelValue=" @update:modelValue="
@@ -12,22 +11,19 @@
" /> " />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with')"> <Select
<RadioGroup
:model-value="openVRAlways ? 'true' : 'false'" :model-value="openVRAlways ? 'true' : 'false'"
:disabled="!openVR" :disabled="!openVR"
class="gap-2 flex"
@update:modelValue="handleOpenVRAlwaysRadio"> @update:modelValue="handleOpenVRAlwaysRadio">
<div class="flex items-center space-x-2"> <SelectTrigger size="sm">
<RadioGroupItem id="openVRAlways-false" value="false" /> <SelectValue />
<label for="openVRAlways-false">{{ 'VRChat' }}</label> </SelectTrigger>
</div> <SelectContent>
<div class="flex items-center space-x-2"> <SelectItem value="false">{{ 'VRChat' }}</SelectItem>
<RadioGroupItem id="openVRAlways-true" value="true" /> <SelectItem value="true">{{ 'SteamVR' }}</SelectItem>
<label for="openVRAlways-true">{{ 'SteamVR' }}</label> </SelectContent>
</div> </Select>
</RadioGroup>
</SettingsItem> </SettingsItem>
<template v-if="!isLinux"> <template v-if="!isLinux">
@@ -45,9 +41,7 @@
</template> </template>
<template v-else> <template v-else>
<SettingsItem <SettingsItem
:label=" :label="t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')">
t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')
">
<Switch <Switch
:model-value="xsNotifications" :model-value="xsNotifications"
@update:modelValue=" @update:modelValue="
@@ -90,8 +84,7 @@
<!-- VR Notifications --> <!-- VR Notifications -->
<SettingsGroup :title="t('view.settings.vr.vr_notifications.header')"> <SettingsGroup :title="t('view.settings.vr.vr_notifications.header')">
<SettingsItem <SettingsItem :label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
:label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
<ToggleGroup <ToggleGroup
type="single" type="single"
required required
@@ -124,9 +117,7 @@
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
:label=" :label="t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')">
t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')
">
<Switch <Switch
:model-value="overlayNotifications" :model-value="overlayNotifications"
:disabled="!openVR" :disabled="!openVR"
@@ -137,9 +128,7 @@
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
:label=" :label="t('view.settings.notifications.notifications.steamvr_notifications.notification_position')">
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
">
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
@@ -152,31 +141,31 @@
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
:label=" :label="t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity')">
t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity')
">
<div class="w-75 max-w-full pt-1"> <div class="w-75 max-w-full pt-1">
<Slider v-model="notificationOpacityValue" :min="0" :max="100" /> <Slider v-model="notificationOpacityValue" :min="0" :max="100" />
</div> </div>
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem
:label=" :label="t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')">
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout') <NumberField
"> :model-value="notificationTimeoutSeconds"
<Button :min="0"
size="sm" :step="1"
variant="outline" :format-options="{ maximumFractionDigits: 0 }"
:disabled="(!overlayNotifications || !openVR) && !xsNotifications" :disabled="(!overlayNotifications || !openVR) && !xsNotifications"
@click="promptNotificationTimeout" class="w-32"
>{{ @update:modelValue="setNotificationTimeout">
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout') <NumberFieldContent>
}}</Button <NumberFieldDecrement />
> <NumberFieldInput />
<NumberFieldIncrement />
</NumberFieldContent>
</NumberField>
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')">
:label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')">
<Switch <Switch
:model-value="imageNotifications" :model-value="imageNotifications"
@update:modelValue=" @update:modelValue="
@@ -191,7 +180,8 @@
<!-- VR Extras --> <!-- VR Extras -->
<SettingsGroup :title="t('view.settings.vr.vr_extras.header')"> <SettingsGroup :title="t('view.settings.vr.vr_extras.header')">
<SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.header')" <SettingsItem
:label="t('view.settings.advanced.advanced.video_progress_pie.header')"
:description="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')"> :description="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')">
<Switch <Switch
:model-value="progressPie" :model-value="progressPie"
@@ -214,10 +204,17 @@
<script setup> <script setup>
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import {
NumberField,
NumberFieldContent,
NumberFieldDecrement,
NumberFieldIncrement,
NumberFieldInput
} from '@/components/ui/number-field';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider'; import { Slider } from '@/components/ui/slider';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -250,7 +247,8 @@
xsNotifications, xsNotifications,
ovrtHudNotifications, ovrtHudNotifications,
ovrtWristNotifications, ovrtWristNotifications,
imageNotifications imageNotifications,
notificationTimeout
} = storeToRefs(notificationsSettingsStore); } = storeToRefs(notificationsSettingsStore);
const { notificationOpacity } = storeToRefs(advancedSettingsStore); const { notificationOpacity } = storeToRefs(advancedSettingsStore);
@@ -258,10 +256,7 @@
const { openVRAlways } = storeToRefs(wristOverlaySettingsStore); const { openVRAlways } = storeToRefs(wristOverlaySettingsStore);
const { setOpenVRAlways } = wristOverlaySettingsStore; const { setOpenVRAlways } = wristOverlaySettingsStore;
const { const { progressPie, progressPieFilter } = storeToRefs(advancedSettingsStore);
progressPie,
progressPieFilter
} = storeToRefs(advancedSettingsStore);
const { const {
setOverlayToast, setOverlayToast,
@@ -271,9 +266,11 @@
setOvrtHudNotifications, setOvrtHudNotifications,
setOvrtWristNotifications, setOvrtWristNotifications,
setImageNotifications, setImageNotifications,
promptNotificationTimeout setNotificationTimeout
} = notificationsSettingsStore; } = notificationsSettingsStore;
const notificationTimeoutSeconds = computed(() => notificationTimeout.value / 1000);
const { setNotificationOpacity } = advancedSettingsStore; const { setNotificationOpacity } = advancedSettingsStore;
const isNotificationPositionDialogVisible = ref(false); const isNotificationPositionDialogVisible = ref(false);

View File

@@ -27,8 +27,7 @@
" /> " />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_private_worlds')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_private_worlds')">
<Switch <Switch
:model-value="hidePrivateFromFeed" :model-value="hidePrivateFromFeed"
@update:modelValue=" @update:modelValue="
@@ -38,52 +37,49 @@
</SettingsItem> </SettingsItem>
<SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button')"> <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button')">
<RadioGroup <Select
:model-value="overlaybutton ? 'true' : 'false'" :model-value="overlaybutton ? 'true' : 'false'"
:disabled="!openVR || !overlayWrist" :disabled="!openVR || !overlayWrist"
class="gap-2 flex"
@update:modelValue="handleOverlayButtonRadio"> @update:modelValue="handleOverlayButtonRadio">
<div class="flex items-center space-x-2"> <SelectTrigger size="sm">
<RadioGroupItem id="overlaybutton-false" value="false" /> <SelectValue />
<label for="overlaybutton-false">{{ </SelectTrigger>
<SelectContent>
<SelectItem value="false">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_grip') t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_grip')
}}</label> }}</SelectItem>
</div> <SelectItem value="true">{{
<div class="flex items-center space-x-2">
<RadioGroupItem id="overlaybutton-true" value="true" />
<label for="overlaybutton-true">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_menu') t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_menu')
}}</label> }}</SelectItem>
</div> </SelectContent>
</RadioGroup> </Select>
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on')"> <Select
<ToggleGroup
type="single"
required
variant="outline"
size="sm"
:model-value="overlayHand" :model-value="overlayHand"
@update:model-value=" @update:model-value="
setOverlayHand($event); setOverlayHand($event);
saveOpenVROption(); saveOpenVROption();
"> ">
<ToggleGroupItem value="1">{{ <SelectTrigger size="sm">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left') t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left')
}}</ToggleGroupItem> }}</SelectItem>
<ToggleGroupItem value="2">{{ <SelectItem value="2">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right') t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right')
}}</ToggleGroupItem> }}</SelectItem>
<ToggleGroupItem value="0">{{ <SelectItem value="0">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both') t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both')
}}</ToggleGroupItem> }}</SelectItem>
</ToggleGroup> </SelectContent>
</Select>
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background')">
<Switch <Switch
:model-value="vrBackgroundEnabled" :model-value="vrBackgroundEnabled"
:disabled="!openVR || !overlayWrist" :disabled="!openVR || !overlayWrist"
@@ -93,8 +89,7 @@
" /> " />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.minimal_feed_icons')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.minimal_feed_icons')">
<Switch <Switch
:model-value="minimalFeed" :model-value="minimalFeed"
:disabled="!openVR || !overlayWrist" :disabled="!openVR || !overlayWrist"
@@ -124,8 +119,7 @@
" /> " />
</SettingsItem> </SettingsItem>
<SettingsItem <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_game_uptime')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_game_uptime')">
<Switch <Switch
:model-value="!hideUptimeFromFeed" :model-value="!hideUptimeFromFeed"
:disabled="!openVR || !overlayWrist" :disabled="!openVR || !overlayWrist"
@@ -151,8 +145,7 @@
<script setup> <script setup>
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch'; import { Switch } from '@/components/ui/switch';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -169,7 +162,6 @@
const { openVR } = storeToRefs(notificationsSettingsStore); const { openVR } = storeToRefs(notificationsSettingsStore);
const { const {
overlayWrist, overlayWrist,
hidePrivateFromFeed, hidePrivateFromFeed,