improve settings ui

This commit is contained in:
pa
2026-03-16 11:30:11 +09:00
parent 8e3c1e0054
commit dcec53cdc3
9 changed files with 881 additions and 897 deletions
+1 -1
View File
@@ -108,7 +108,7 @@
:value="it.value" :value="it.value"
:class="[ :class="[
'pt-4 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background', 'pt-4 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background',
fill ? 'min-h-0 flex-1' : '' fill ? 'min-h-0 flex-1 overflow-y-auto' : ''
]"> ]">
<slot :name="it.value" /> <slot :name="it.value" />
</TabsContent> </TabsContent>
+4 -4
View File
@@ -1,13 +1,13 @@
<template> <template>
<div class="x-container"> <div class="x-container flex flex-col overflow-hidden!">
<div class="options-container mt-0 p-1.5"> <div class="shrink-0 p-1.5">
<span class="header">{{ t('view.settings.header') }}</span> <span class="text-lg font-semibold text-foreground">{{ t('view.settings.header') }}</span>
</div> </div>
<TabsUnderline <TabsUnderline
default-value="general" default-value="general"
:items="settingsTabs" :items="settingsTabs"
:unmount-on-hide="false" :unmount-on-hide="false"
style="height: calc(100% - 51px)"> fill>
<template #general> <template #general>
<GeneralTab /> <GeneralTab />
</template> </template>
@@ -1,6 +1,11 @@
<template> <template>
<div class="flex flex-col gap-3.5"> <div class="flex flex-col gap-3.5">
<h3 v-if="title" class="text-base font-semibold pl-0.5 text-foreground m-0">{{ title }}</h3> <div v-if="title || $slots.description" class="flex flex-col gap-1.5 pl-0.5">
<h3 v-if="title" class="text-base font-semibold text-foreground m-0">{{ title }}</h3>
<div v-if="$slots.description" class="text-sm text-muted-foreground">
<slot name="description" />
</div>
</div>
<Card class="p-0"> <Card class="p-0">
<CardContent class="flex flex-col gap-1 py-4.5 px-5.5"> <CardContent class="flex flex-col gap-1 py-4.5 px-5.5">
<slot /> <slot />
+210 -304
View File
@@ -1,193 +1,183 @@
<template> <template>
<div> <div class="flex flex-col gap-10 py-2">
<div class="options-container mt-2!"> <SettingsGroup :title="t('view.settings.advanced.advanced.vrchat_settings.header')">
<div class="header">{{ t('view.settings.advanced.advanced.vrchat_settings.header') }}</div> <SettingsItem :label="t('view.settings.advanced.advanced.relaunch_vrchat.header')"
<span class="sub-header">{{ t('view.settings.advanced.advanced.relaunch_vrchat.header') }}</span> :description="t('view.settings.advanced.advanced.relaunch_vrchat.description')">
<simple-switch <Switch :model-value="relaunchVRChatAfterCrash" @update:modelValue="setRelaunchVRChatAfterCrash" />
:label="t('view.settings.advanced.advanced.relaunch_vrchat.description')" </SettingsItem>
:value="relaunchVRChatAfterCrash"
:long-label="true"
@change="setRelaunchVRChatAfterCrash" />
<span class="sub-header">{{ t('view.settings.advanced.advanced.vrchat_quit_fix.header') }}</span> <SettingsItem :label="t('view.settings.advanced.advanced.vrchat_quit_fix.header')"
<simple-switch :description="t('view.settings.advanced.advanced.vrchat_quit_fix.description')">
:label="t('view.settings.advanced.advanced.vrchat_quit_fix.description')" <Switch :model-value="vrcQuitFix" @update:modelValue="setVrcQuitFix" />
:value="vrcQuitFix" </SettingsItem>
:long-label="true"
@change="setVrcQuitFix" />
<span class="sub-header">{{ t('view.settings.advanced.advanced.auto_cache_management.header') }}</span> <SettingsItem :label="t('view.settings.advanced.advanced.auto_cache_management.header')"
<simple-switch :description="t('view.settings.advanced.advanced.auto_cache_management.description')">
:label="t('view.settings.advanced.advanced.auto_cache_management.description')" <Switch :model-value="autoSweepVRChatCache" @update:modelValue="setAutoSweepVRChatCache" />
:value="autoSweepVRChatCache" </SettingsItem>
:long-label="true"
@change="setAutoSweepVRChatCache" />
<span class="sub-header">{{ t('view.settings.advanced.advanced.self_invite.header') }}</span> <SettingsItem :label="t('view.settings.advanced.advanced.self_invite.header')"
<simple-switch :description="t('view.settings.advanced.advanced.self_invite.description')">
:label="t('view.settings.advanced.advanced.self_invite.description')" <Switch :model-value="selfInviteOverride" @update:modelValue="setSelfInviteOverride" />
:value="selfInviteOverride" </SettingsItem>
:long-label="true" </SettingsGroup>
@change="setSelfInviteOverride" />
</div>
<div class="options-container">
<div class="header">{{ t('view.settings.advanced.advanced.vrcx_settings.header') }}</div>
<span class="sub-header">{{ t('view.settings.advanced.advanced.primary_password.header') }}</span>
<simple-switch
:label="t('view.settings.advanced.advanced.primary_password.description')"
:value="enablePrimaryPassword"
:disabled="!enablePrimaryPassword"
:long-label="true"
@change="enablePrimaryPasswordChange" />
<div v-if="branch === 'Nightly'"> <SettingsGroup :title="t('view.settings.advanced.advanced.vrcx_settings.header')">
<span class="sub-header">{{ <SettingsItem :label="t('view.settings.advanced.advanced.primary_password.header')"
t('view.settings.advanced.advanced.anonymous_error_reporting.header') :description="t('view.settings.advanced.advanced.primary_password.description')">
}}</span> <Switch
<simple-switch :model-value="enablePrimaryPassword"
:label="t('view.settings.advanced.advanced.anonymous_error_reporting.description')" :disabled="!enablePrimaryPassword"
:value="sentryErrorReporting" @update:modelValue="enablePrimaryPasswordChange" />
:long-label="true" </SettingsItem>
@change="setSentryErrorReporting()" />
</div>
<span class="sub-header">{{ t('view.settings.general.logging.header') }}</span> <template v-if="branch === 'Nightly'">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.anonymous_error_reporting.header')"
:label="t('view.settings.advanced.advanced.cache_debug.udon_exception_logging')" :description="t('view.settings.advanced.advanced.anonymous_error_reporting.description')">
:value="udonExceptionLogging" <Switch :model-value="sentryErrorReporting" @update:modelValue="setSentryErrorReporting()" />
@change="setUdonExceptionLogging" /> </SettingsItem>
<simple-switch </template>
:label="t('view.settings.general.logging.resource_load')" </SettingsGroup>
:value="logResourceLoad"
@change="setLogResourceLoad" /> <SettingsGroup :title="t('view.settings.general.logging.header')">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.cache_debug.udon_exception_logging')">
:label="t('view.settings.general.logging.empty_avatar')" <Switch :model-value="udonExceptionLogging" @update:modelValue="setUdonExceptionLogging" />
:value="logEmptyAvatars" </SettingsItem>
@change="setLogEmptyAvatars" />
<simple-switch <SettingsItem :label="t('view.settings.general.logging.resource_load')">
:label="t('view.settings.general.logging.auto_login_delay')" <Switch :model-value="logResourceLoad" @update:modelValue="setLogResourceLoad" />
:value="autoLoginDelayEnabled" </SettingsItem>
@change="setAutoLoginDelayEnabled" />
<div v-if="autoLoginDelayEnabled" class="options-container-item"> <SettingsItem :label="t('view.settings.general.logging.empty_avatar')">
<Switch :model-value="logEmptyAvatars" @update:modelValue="setLogEmptyAvatars" />
</SettingsItem>
<SettingsItem :label="t('view.settings.general.logging.auto_login_delay')">
<Switch :model-value="autoLoginDelayEnabled" @update:modelValue="setAutoLoginDelayEnabled" />
</SettingsItem>
<SettingsItem v-if="autoLoginDelayEnabled"
:label="t('view.settings.general.logging.auto_login_delay_button')">
<Button size="sm" variant="outline" @click="promptAutoLoginDelaySeconds"> <Button size="sm" variant="outline" @click="promptAutoLoginDelaySeconds">
{{ t('view.settings.general.logging.auto_login_delay_button') }} {{ t('view.settings.general.logging.auto_login_delay_button') }}
</Button> </Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.profile.game_info.header') }}</span> <SettingsGroup :title="t('view.profile.game_info.header')">
<div class="px-2.5 overflow-y-auto overflow-x-hidden mt-2"> <div class="px-1 py-1">
<div class="box-border flex items-center p-1.5 text-[13px] cursor-pointer"> <div class="flex-1 cursor-pointer" @click="getVisits">
<div class="flex-1 overflow-hidden" @click="getVisits"> <span class="block truncate font-medium text-sm leading-[18px]">{{
<span class="block truncate font-medium leading-[18px]">{{ t('view.profile.game_info.online_users')
t('view.profile.game_info.online_users') }}</span>
}}</span> <span v-if="visits" class="block truncate text-xs text-muted-foreground">{{
<span v-if="visits" class="block truncate text-xs">{{ 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">{{
<span v-else class="block truncate text-xs">{{ t('view.profile.game_info.refresh') }}</span> t('view.profile.game_info.refresh')
</div> }}</span>
</div> </div>
</div> </div>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.settings.advanced.advanced.remote_database.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.remote_database.header')">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.remote_database.enable')">
:label="t('view.settings.advanced.advanced.remote_database.enable')" <Switch
:value="avatarRemoteDatabase" :model-value="avatarRemoteDatabase"
:long-label="true" @update:modelValue="setAvatarRemoteDatabase(!avatarRemoteDatabase)" />
@change="setAvatarRemoteDatabase(!avatarRemoteDatabase)" /> </SettingsItem>
<div class="options-container-item">
<SettingsItem :label="t('view.settings.advanced.advanced.remote_database.avatar_database_provider')">
<Button size="sm" variant="outline" @click="showAvatarProviderDialog">{{ <Button size="sm" variant="outline" @click="showAvatarProviderDialog">{{
t('view.settings.advanced.advanced.remote_database.avatar_database_provider') t('view.settings.advanced.advanced.remote_database.avatar_database_provider')
}}</Button> }}</Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<template v-if="!isLinux"> <template v-if="!isLinux">
<div class="options-container"> <SettingsGroup :title="t('view.settings.advanced.advanced.app_launcher.header')">
<span class="header">{{ t('view.settings.advanced.advanced.app_launcher.header') }}</span> <SettingsItem :label="t('view.settings.advanced.advanced.app_launcher.folder')">
<br /> <Button size="sm" variant="outline" @click="openShortcutFolder()">{{
<Button class="mt-1.5" size="sm" variant="outline" @click="openShortcutFolder()">{{ t('view.settings.advanced.advanced.app_launcher.folder')
t('view.settings.advanced.advanced.app_launcher.folder') }}</Button>
}}</Button> </SettingsItem>
<simple-switch
:label="t('view.settings.advanced.advanced.remote_database.enable')" <SettingsItem :label="t('view.settings.advanced.advanced.remote_database.enable')"
:value="enableAppLauncher" :description="t('view.settings.advanced.advanced.app_launcher.folder_tooltip')">
:tooltip="t('view.settings.advanced.advanced.app_launcher.folder_tooltip')" <Switch :model-value="enableAppLauncher" @update:modelValue="setEnableAppLauncher" />
:long-label="true" </SettingsItem>
@change="setEnableAppLauncher" />
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.app_launcher.auto_close')">
:label="t('view.settings.advanced.advanced.app_launcher.auto_close')" <Switch
:value="enableAppLauncherAutoClose" :model-value="enableAppLauncherAutoClose"
:long-label="true" @update:modelValue="setEnableAppLauncherAutoClose" />
@change="setEnableAppLauncherAutoClose" /> </SettingsItem>
<simple-switch
:label="t('view.settings.advanced.advanced.app_launcher.run_process_once')" <SettingsItem :label="t('view.settings.advanced.advanced.app_launcher.run_process_once')">
:value="enableAppLauncherRunProcessOnce" <Switch
:long-label="true" :model-value="enableAppLauncherRunProcessOnce"
@change="setEnableAppLauncherRunProcessOnce" /> @update:modelValue="setEnableAppLauncherRunProcessOnce" />
</div> </SettingsItem>
</SettingsGroup>
</template> </template>
<div class="options-container">
<span class="header">{{ t('view.settings.advanced.advanced.youtube_api.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.youtube_api.header')">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.enable')"
:label="t('view.settings.advanced.advanced.youtube_api.enable')" :description="t('view.settings.advanced.advanced.youtube_api.enable_tooltip')">
:value="youTubeApi" <Switch :model-value="youTubeApi" @update:modelValue="changeYouTubeApi('VRCX_youtubeAPI')" />
:tooltip="t('view.settings.advanced.advanced.youtube_api.enable_tooltip')" </SettingsItem>
:long-label="true"
@change="changeYouTubeApi('VRCX_youtubeAPI')" /> <SettingsItem :label="t('view.settings.advanced.advanced.youtube_api.youtube_api_key')">
<div class="options-container-item">
<Button size="sm" variant="outline" @click="showYouTubeApiDialog">{{ <Button size="sm" variant="outline" @click="showYouTubeApiDialog">{{
t('view.settings.advanced.advanced.youtube_api.youtube_api_key') t('view.settings.advanced.advanced.youtube_api.youtube_api_key')
}}</Button> }}</Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.settings.advanced.advanced.translation_api.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.translation_api.header')">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.translation_api.enable')"
:label="t('view.settings.advanced.advanced.translation_api.enable')" :description="t('view.settings.advanced.advanced.translation_api.enable_tooltip')">
:value="translationApi" <Switch :model-value="translationApi" @update:modelValue="changeTranslationAPI('VRCX_translationAPI')" />
:tooltip="t('view.settings.advanced.advanced.translation_api.enable_tooltip')" </SettingsItem>
:long-label="true"
@change="changeTranslationAPI('VRCX_translationAPI')" /> <SettingsItem :label="t('view.settings.advanced.advanced.translation_api.translation_api_key')">
<div class="options-container-item">
<Button size="sm" variant="outline" @click="showTranslationApiDialog"> <Button size="sm" variant="outline" @click="showTranslationApiDialog">
<Languages class="h-4 w-4" style="margin-right: 6px" /> <Languages class="h-4 w-4 mr-1.5" />
{{ t('view.settings.advanced.advanced.translation_api.translation_api_key') }} {{ t('view.settings.advanced.advanced.translation_api.translation_api_key') }}
</Button> </Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.settings.advanced.advanced.video_progress_pie.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.video_progress_pie.header')">
<simple-switch <SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.enable')"
:label="t('view.settings.advanced.advanced.video_progress_pie.enable')" :description="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')">
:value="progressPie" <Switch
:disabled="!openVR" :model-value="progressPie"
:tooltip="t('view.settings.advanced.advanced.video_progress_pie.enable_tooltip')" :disabled="!openVR"
:long-label="true" @update:modelValue="changeYouTubeApi('VRCX_progressPie')" />
@change="changeYouTubeApi('VRCX_progressPie')" /> </SettingsItem>
<simple-switch
:label="t('view.settings.advanced.advanced.video_progress_pie.dance_world_only')" <SettingsItem :label="t('view.settings.advanced.advanced.video_progress_pie.dance_world_only')">
:value="progressPieFilter" <Switch
:disabled="!openVR" :model-value="progressPieFilter"
:long-label="true" :disabled="!openVR"
@change="changeYouTubeApi('VRCX_progressPieFilter')" /> @update:modelValue="changeYouTubeApi('VRCX_progressPieFilter')" />
</div> </SettingsItem>
<div class="options-container"> </SettingsGroup>
<span class="header">{{ t('view.settings.advanced.advanced.launch_commands.header') }}</span>
<simple-switch <SettingsGroup :title="t('view.settings.advanced.advanced.launch_commands.header')">
<SettingsItem
:label="t('view.settings.advanced.advanced.launch_commands.show_confirmation_on_switch_avatar_enable')" :label="t('view.settings.advanced.advanced.launch_commands.show_confirmation_on_switch_avatar_enable')"
:value="showConfirmationOnSwitchAvatar" :description="t('view.settings.advanced.advanced.launch_commands.show_confirmation_on_switch_avatar_tooltip')">
:tooltip=" <Switch
t('view.settings.advanced.advanced.launch_commands.show_confirmation_on_switch_avatar_tooltip') :model-value="showConfirmationOnSwitchAvatar"
" @update:modelValue="setShowConfirmationOnSwitchAvatar" />
:long-label="true" </SettingsItem>
@change="setShowConfirmationOnSwitchAvatar" />
<div class="options-container-item"> <div class="flex gap-2">
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
class="mr-2"
@click="openExternalLink('https://github.com/vrcx-team/VRCX/wiki/Launch-parameters-&-VRCX.json')" @click="openExternalLink('https://github.com/vrcx-team/VRCX/wiki/Launch-parameters-&-VRCX.json')"
>{{ t('view.settings.advanced.advanced.launch_commands.docs') }}</Button >{{ t('view.settings.advanced.advanced.launch_commands.docs') }}</Button
> >
@@ -198,15 +188,14 @@
>{{ t('view.settings.advanced.advanced.launch_commands.website_userscript') }}</Button >{{ t('view.settings.advanced.advanced.launch_commands.website_userscript') }}</Button
> >
</div> </div>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.settings.advanced.advanced.cache_debug.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.cache_debug.header')">
<br /> <div class="flex gap-2 flex-wrap">
<div class="options-container-item"> <Button size="sm" variant="outline" @click="clearVRCXCache">{{
<Button size="sm" variant="outline" class="mr-2" @click="clearVRCXCache">{{
t('view.settings.advanced.advanced.cache_debug.clear_cache') t('view.settings.advanced.advanced.cache_debug.clear_cache')
}}</Button> }}</Button>
<Button size="sm" variant="outline" class="mr-2" @click="promptAutoClearVRCXCacheFrequency">{{ <Button size="sm" variant="outline" @click="promptAutoClearVRCXCacheFrequency">{{
t('view.settings.advanced.advanced.cache_debug.auto_clear_cache') t('view.settings.advanced.advanced.cache_debug.auto_clear_cache')
}}</Button> }}</Button>
<Button size="sm" variant="outline" @click="refreshCacheSize">{{ <Button size="sm" variant="outline" @click="refreshCacheSize">{{
@@ -214,146 +203,61 @@
}}</Button> }}</Button>
</div> </div>
<simple-switch <SettingsItem
:label="`${t('view.settings.advanced.advanced.cache_debug.disable_gamelog')} ${t('view.settings.advanced.advanced.cache_debug.disable_gamelog_notice')}`" :label="`${t('view.settings.advanced.advanced.cache_debug.disable_gamelog')} ${t('view.settings.advanced.advanced.cache_debug.disable_gamelog_notice')}`">
:value="gameLogDisabled" <Switch :model-value="gameLogDisabled" @update:modelValue="disableGameLogDialog()" />
:long-label="true" </SettingsItem>
@change="disableGameLogDialog()" />
<div class="options-container-item"> <div class="flex flex-col gap-1 text-sm">
<span class="name"> <span>{{ t('view.settings.advanced.advanced.cache_debug.user_cache') }} <span v-text="cacheSize.cachedUsers"></span></span>
{{ t('view.settings.advanced.advanced.cache_debug.user_cache') }} <span>{{ t('view.settings.advanced.advanced.cache_debug.world_cache') }} <span v-text="cacheSize.cachedWorlds"></span></span>
<span v-text="cacheSize.cachedUsers"></span> <span>{{ t('view.settings.advanced.advanced.cache_debug.avatar_cache') }} <span v-text="cacheSize.cachedAvatars"></span></span>
</span> <span>{{ t('view.settings.advanced.advanced.cache_debug.group_cache') }} <span v-text="cacheSize.cachedGroups"></span></span>
<span>{{ t('view.settings.advanced.advanced.cache_debug.avatar_name_cache') }} <span v-text="cacheSize.cachedAvatarNames"></span></span>
<span>{{ t('view.settings.advanced.advanced.cache_debug.instance_cache') }} <span v-text="cacheSize.cachedInstances"></span></span>
</div> </div>
<div class="options-container-item">
<span class="name"> <SettingsItem :label="t('view.settings.advanced.advanced.cache_debug.show_console')">
{{ t('view.settings.advanced.advanced.cache_debug.world_cache') }}
<span v-text="cacheSize.cachedWorlds"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.cache_debug.avatar_cache') }}
<span v-text="cacheSize.cachedAvatars"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.cache_debug.group_cache') }}
<span v-text="cacheSize.cachedGroups"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.cache_debug.avatar_name_cache') }}
<span v-text="cacheSize.cachedAvatarNames"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.cache_debug.instance_cache') }}
<span v-text="cacheSize.cachedInstances"></span>
</span>
</div>
<div class="options-container-item">
<Button size="sm" variant="outline" @click="showConsole">{{ <Button size="sm" variant="outline" @click="showConsole">{{
t('view.settings.advanced.advanced.cache_debug.show_console') t('view.settings.advanced.advanced.cache_debug.show_console')
}}</Button> }}</Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="sub-header">{{ t('view.settings.advanced.advanced.sqlite_table_size.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.sqlite_table_size.header')">
<div class="options-container-item"> <SettingsItem :label="t('view.settings.advanced.advanced.sqlite_table_size.refresh')">
<Button size="sm" variant="outline" @click="getSqliteTableSizes">{{ <Button size="sm" variant="outline" @click="getSqliteTableSizes">{{
t('view.settings.advanced.advanced.sqlite_table_size.refresh') t('view.settings.advanced.advanced.sqlite_table_size.refresh')
}}</Button> }}</Button>
</div> </SettingsItem>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.gps') }}
<span v-text="sqliteTableSizes.gps"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.status') }}
<span v-text="sqliteTableSizes.status"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.bio') }}
<span v-text="sqliteTableSizes.bio"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.avatar') }}
<span v-text="sqliteTableSizes.avatar"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.online_offline') }}
<span v-text="sqliteTableSizes.onlineOffline"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.friend_log_history') }}
<span v-text="sqliteTableSizes.friendLogHistory"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.notification') }}
<span v-text="sqliteTableSizes.notification"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.location') }}
<span v-text="sqliteTableSizes.location"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.join_leave') }}
<span v-text="sqliteTableSizes.joinLeave"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.portal_spawn') }}
<span v-text="sqliteTableSizes.portalSpawn"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.video_play') }}
<span v-text="sqliteTableSizes.videoPlay"></span>
</span>
</div>
<div class="options-container-item">
<span class="name">
{{ t('view.settings.advanced.advanced.sqlite_table_size.event') }}
<span v-text="sqliteTableSizes.event"></span>
</span>
</div>
</div>
<div class="options-container"> <div class="flex flex-col gap-1 text-sm">
<div class="header-bar"> <span>{{ t('view.settings.advanced.advanced.sqlite_table_size.gps') }} <span v-text="sqliteTableSizes.gps"></span></span>
<span class="header">{{ t('view.profile.config_json') }}</span> <span>{{ t('view.settings.advanced.advanced.sqlite_table_size.status') }} <span v-text="sqliteTableSizes.status"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.bio') }} <span v-text="sqliteTableSizes.bio"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.avatar') }} <span v-text="sqliteTableSizes.avatar"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.online_offline') }} <span v-text="sqliteTableSizes.onlineOffline"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.friend_log_history') }} <span v-text="sqliteTableSizes.friendLogHistory"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.notification') }} <span v-text="sqliteTableSizes.notification"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.location') }} <span v-text="sqliteTableSizes.location"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.join_leave') }} <span v-text="sqliteTableSizes.joinLeave"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.portal_spawn') }} <span v-text="sqliteTableSizes.portalSpawn"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.video_play') }} <span v-text="sqliteTableSizes.videoPlay"></span></span>
<span>{{ t('view.settings.advanced.advanced.sqlite_table_size.event') }} <span v-text="sqliteTableSizes.event"></span></span>
</div>
</SettingsGroup>
<SettingsGroup :title="t('view.profile.config_json')">
<div class="flex items-center gap-2">
<TooltipWrapper side="top" :content="t('view.profile.refresh_tooltip')"> <TooltipWrapper side="top" :content="t('view.profile.refresh_tooltip')">
<Button class="rounded-full mr-2" size="icon-sm" variant="outline" @click="refreshConfigTreeData()"> <Button class="rounded-full" size="icon-sm" variant="outline" @click="refreshConfigTreeData()">
<RefreshCcw /> <RefreshCcw />
</Button> </Button>
</TooltipWrapper> </TooltipWrapper>
<TooltipWrapper side="top" :content="t('view.profile.clear_results_tooltip')"> <TooltipWrapper side="top" :content="t('view.profile.clear_results_tooltip')">
<Button class="rounded-full" size="icon-sm" variant="outline" @click="configTreeData = {}"> <Button class="rounded-full" size="icon-sm" variant="outline" @click="configTreeData = {}">
<Trash2 <Trash2 />
/></Button> </Button>
</TooltipWrapper> </TooltipWrapper>
</div> </div>
<vue-json-pretty <vue-json-pretty
@@ -365,7 +269,7 @@
:dynamic-height="false" :dynamic-height="false"
virtual virtual
show-icon /> show-icon />
</div> </SettingsGroup>
<RegistryBackupDialog /> <RegistryBackupDialog />
<YouTubeApiDialog v-model:isYouTubeApiDialogVisible="isYouTubeApiDialogVisible" /> <YouTubeApiDialog v-model:isYouTubeApiDialogVisible="isYouTubeApiDialogVisible" />
@@ -379,6 +283,7 @@
import { Languages, RefreshCcw, Trash2 } from 'lucide-vue-next'; import { Languages, RefreshCcw, Trash2 } 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 { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -400,18 +305,19 @@
useVRCXUpdaterStore, useVRCXUpdaterStore,
useVrStore, useVrStore,
useWorldStore useWorldStore
} from '../../../../stores'; } from '@/stores';
import { authRequest, queryRequest } from '../../../../api'; import { authRequest, queryRequest } from '@/api';
import { disableGameLogDialog } from '../../../../coordinators/gameLogCoordinator'; import { disableGameLogDialog } from '@/coordinators/gameLogCoordinator';
import { clearVRCXCache } from '../../../../coordinators/vrcxCoordinator'; import { clearVRCXCache } from '@/coordinators/vrcxCoordinator';
import { openExternalLink } from '../../../../shared/utils'; import { openExternalLink } from '@/shared/utils';
import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue'; import AvatarProviderDialog from '../../dialogs/AvatarProviderDialog.vue';
import PhotonSettings from '../PhotonSettings.vue'; import PhotonSettings from '../PhotonSettings.vue';
import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue'; import RegistryBackupDialog from '../../../Tools/dialogs/RegistryBackupDialog.vue';
import SimpleSwitch from '../SimpleSwitch.vue';
import TranslationApiDialog from '../../dialogs/TranslationApiDialog.vue'; import TranslationApiDialog from '../../dialogs/TranslationApiDialog.vue';
import YouTubeApiDialog from '../../dialogs/YouTubeApiDialog.vue'; import YouTubeApiDialog from '../../dialogs/YouTubeApiDialog.vue';
import SettingsGroup from '../SettingsGroup.vue';
import SettingsItem from '../SettingsItem.vue';
const { t } = useI18n(); const { t } = useI18n();
@@ -1,87 +1,111 @@
<template> <template>
<div class="options-container mt-0"> <div class="flex flex-col gap-10 py-2">
<span class="header">{{ t('view.settings.discord_presence.discord_presence.header') }}</span> <SettingsGroup :title="t('view.settings.discord_presence.discord_presence.header')">
<div class="options-container-item"> <template #description>
<span>{{ t('view.settings.discord_presence.discord_presence.description') }}</span> <p class="m-0">{{ t('view.settings.discord_presence.discord_presence.description') }}</p>
</div> <p class="m-0 cursor-pointer hover:text-foreground transition-colors" @click="showVRChatConfig">
<div class="options-container-item" @click="showVRChatConfig" style="cursor: pointer"> {{ t('view.settings.discord_presence.discord_presence.enable_tooltip') }}
<span>{{ t('view.settings.discord_presence.discord_presence.enable_tooltip') }}</span> </p>
</div> </template>
<br />
<simple-switch <SettingsItem :label="t('view.settings.discord_presence.discord_presence.enable')">
:label="t('view.settings.discord_presence.discord_presence.enable')" <Switch
:value="discordActive" :model-value="discordActive"
@change=" @update:modelValue="
setDiscordActive(); setDiscordActive();
saveDiscordOption(); saveDiscordOption();
" /> " />
<simple-switch </SettingsItem>
:label="t('view.settings.discord_presence.discord_presence.world_integration')"
:value="discordWorldIntegration" <SettingsItem
:disabled="!discordActive" :label="t('view.settings.discord_presence.discord_presence.world_integration')"
@change=" :description="t('view.settings.discord_presence.discord_presence.world_integration_tooltip')">
setDiscordWorldIntegration(); <Switch
saveDiscordOption(); :model-value="discordWorldIntegration"
" :disabled="!discordActive"
:tooltip="t('view.settings.discord_presence.discord_presence.world_integration_tooltip')" /> @update:modelValue="
<simple-switch setDiscordWorldIntegration();
:label="t('view.settings.discord_presence.discord_presence.instance_type_player_count')" saveDiscordOption();
:value="discordInstance" " />
:disabled="!discordActive" </SettingsItem>
@change="
setDiscordInstance(); <SettingsItem
saveDiscordOption(); :label="t('view.settings.discord_presence.discord_presence.instance_type_player_count')">
" /> <Switch
<simple-switch :model-value="discordInstance"
:label="t('view.settings.discord_presence.discord_presence.show_current_platform')" :disabled="!discordActive"
:value="discordShowPlatform" @update:modelValue="
:disabled="!discordActive || !discordInstance" setDiscordInstance();
@change=" saveDiscordOption();
setDiscordShowPlatform(); " />
saveDiscordOption(); </SettingsItem>
" />
<simple-switch <SettingsItem :label="t('view.settings.discord_presence.discord_presence.show_current_platform')">
:label="t('view.settings.discord_presence.discord_presence.show_details_in_private')" <Switch
:value="!discordHideInvite" :model-value="discordShowPlatform"
:disabled="!discordActive" :disabled="!discordActive || !discordInstance"
@change=" @update:modelValue="
setDiscordHideInvite(); setDiscordShowPlatform();
saveDiscordOption(); saveDiscordOption();
" /> " />
<simple-switch </SettingsItem>
:label="t('view.settings.discord_presence.discord_presence.join_button')"
:value="discordJoinButton" <SettingsItem
:disabled="!discordActive" :label="t('view.settings.discord_presence.discord_presence.show_details_in_private')">
@change=" <Switch
setDiscordJoinButton(); :model-value="!discordHideInvite"
saveDiscordOption(); :disabled="!discordActive"
" /> @update:modelValue="
<simple-switch setDiscordHideInvite();
:label="t('view.settings.discord_presence.discord_presence.show_images')" saveDiscordOption();
:value="!discordHideImage" " />
:disabled="!discordActive" </SettingsItem>
@change="
setDiscordHideImage(); <SettingsItem :label="t('view.settings.discord_presence.discord_presence.join_button')">
saveDiscordOption(); <Switch
" /> :model-value="discordJoinButton"
<simple-switch :disabled="!discordActive"
:label="t('view.settings.discord_presence.discord_presence.display_world_name_as_discord_status')" @update:modelValue="
:value="discordWorldNameAsDiscordStatus" setDiscordJoinButton();
:disabled="!discordActive" saveDiscordOption();
@change=" " />
setDiscordWorldNameAsDiscordStatus(); </SettingsItem>
saveDiscordOption();
" /> <SettingsItem :label="t('view.settings.discord_presence.discord_presence.show_images')">
<Switch
:model-value="!discordHideImage"
:disabled="!discordActive"
@update:modelValue="
setDiscordHideImage();
saveDiscordOption();
" />
</SettingsItem>
<SettingsItem
:label="
t('view.settings.discord_presence.discord_presence.display_world_name_as_discord_status')
">
<Switch
:model-value="discordWorldNameAsDiscordStatus"
:disabled="!discordActive"
@update:modelValue="
setDiscordWorldNameAsDiscordStatus();
saveDiscordOption();
" />
</SettingsItem>
</SettingsGroup>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Switch } from '@/components/ui/switch';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useAdvancedSettingsStore, useDiscordPresenceSettingsStore } from '../../../../stores'; import { useAdvancedSettingsStore, useDiscordPresenceSettingsStore } from '@/stores';
import SimpleSwitch from '../SimpleSwitch.vue'; import SettingsGroup from '../SettingsGroup.vue';
import SettingsItem from '../SettingsItem.vue';
const { t } = useI18n(); const { t } = useI18n();
+141 -135
View File
@@ -1,138 +1,141 @@
<template> <template>
<div> <div class="flex flex-col gap-10 py-2">
<div class="options-container mt-0"> <SettingsGroup :title="t('view.settings.general.general.header')">
<span class="header">{{ t('view.settings.general.general.header') }}</span> <div class="flex flex-col gap-0.5 px-1 py-1">
<div class="px-2.5 overflow-y-auto overflow-x-hidden mt-2"> <div class="flex-1">
<div class="box-border flex items-center p-1.5 text-[13px] cursor-default"> <span class="block truncate font-medium text-sm leading-[18px]">{{
<div class="flex-1 overflow-hidden"> t('view.settings.general.general.version')
<span class="block truncate font-medium leading-[18px]">{{ }}</span>
t('view.settings.general.general.version') <span class="block truncate text-xs text-muted-foreground" v-text="appVersion"></span>
}}</span>
<span class="block truncate text-xs" v-text="appVersion"></span>
</div>
</div>
<div class="box-border flex items-center p-1.5 text-[13px] cursor-pointer" @click="checkForVRCXUpdate">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('view.settings.general.general.latest_app_version')
}}</span>
<span v-if="latestAppVersion" class="block truncate text-xs" v-text="latestAppVersion"></span>
<span v-else class="block truncate text-xs">{{
t('view.settings.general.general.latest_app_version_refresh')
}}</span>
</div>
</div>
<div
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer"
@click="openExternalLink(links.github)">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('view.settings.general.general.repository_url')
}}</span>
<span v-once class="block truncate text-xs">{{ links.github }}</span>
</div>
</div>
<div
class="box-border flex items-center p-1.5 text-[13px] cursor-pointer"
@click="openExternalLink(links.discord)">
<div class="flex-1 overflow-hidden">
<span class="block truncate font-medium leading-[18px]">{{
t('view.settings.general.general.support')
}}</span>
<span v-once class="block truncate text-xs">{{ links.discord }}</span>
</div>
</div> </div>
</div> </div>
</div>
<div class="options-container"> <div class="flex flex-col gap-0.5 px-1 py-1 cursor-pointer" @click="checkForVRCXUpdate">
<span class="header">{{ t('view.settings.general.vrcx_updater.header') }}</span> <div class="flex-1">
<div class="options-container-item"> <span class="block truncate font-medium text-sm leading-[18px]">{{
<Button size="sm" variant="outline" class="mr-2" @click="showChangeLogDialog">{{ t('view.settings.general.general.latest_app_version')
}}</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">{{
t('view.settings.general.general.latest_app_version_refresh')
}}</span>
</div>
</div>
<div class="flex flex-col gap-0.5 px-1 py-1 cursor-pointer" @click="openExternalLink(links.github)">
<div class="flex-1">
<span class="block truncate font-medium text-sm leading-[18px]">{{
t('view.settings.general.general.repository_url')
}}</span>
<span v-once class="block truncate text-xs text-muted-foreground">{{ links.github }}</span>
</div>
</div>
<div class="flex flex-col gap-0.5 px-1 py-1 cursor-pointer" @click="openExternalLink(links.discord)">
<div class="flex-1">
<span class="block truncate font-medium text-sm leading-[18px]">{{
t('view.settings.general.general.support')
}}</span>
<span v-once class="block truncate text-xs text-muted-foreground">{{ links.discord }}</span>
</div>
</div>
</SettingsGroup>
<SettingsGroup :title="t('view.settings.general.vrcx_updater.header')">
<div class="flex gap-2">
<Button size="sm" variant="outline" @click="showChangeLogDialog">{{
t('view.settings.general.vrcx_updater.change_log') t('view.settings.general.vrcx_updater.change_log')
}}</Button> }}</Button>
<Button size="sm" variant="outline" v-if="!noUpdater" @click="showVRCXUpdateDialog()">{{ <Button v-if="!noUpdater" size="sm" variant="outline" @click="showVRCXUpdateDialog()">{{
t('view.settings.general.vrcx_updater.change_build') t('view.settings.general.vrcx_updater.change_build')
}}</Button> }}</Button>
</div> </div>
<div v-if="!noUpdater" class="text-sm mt-2 flex flex-col align-baseline">
<span class="name">{{ t('view.settings.general.vrcx_updater.update_action') }}</span> <template v-if="!noUpdater">
<ToggleGroup <SettingsItem :label="t('view.settings.general.vrcx_updater.update_action')">
class="mt-1.5" <ToggleGroup
type="single" type="single"
required required
variant="outline" variant="outline"
size="sm" size="sm"
:model-value="autoUpdateVRCX" :model-value="autoUpdateVRCX"
@update:model-value="setAutoUpdateVRCX"> @update:model-value="setAutoUpdateVRCX">
<ToggleGroupItem value="Off">{{ <ToggleGroupItem value="Off">{{
t('view.settings.general.vrcx_updater.auto_update_off') t('view.settings.general.vrcx_updater.auto_update_off')
}}</ToggleGroupItem> }}</ToggleGroupItem>
<ToggleGroupItem value="Notify">{{ <ToggleGroupItem value="Notify">{{
t('view.settings.general.vrcx_updater.auto_update_notify') t('view.settings.general.vrcx_updater.auto_update_notify')
}}</ToggleGroupItem> }}</ToggleGroupItem>
<ToggleGroupItem value="Auto Download">{{ <ToggleGroupItem value="Auto Download">{{
t('view.settings.general.vrcx_updater.auto_update_download') t('view.settings.general.vrcx_updater.auto_update_download')
}}</ToggleGroupItem> }}</ToggleGroupItem>
</ToggleGroup> </ToggleGroup>
</SettingsItem>
</template>
<div v-else class="text-sm text-muted-foreground">
{{ t('view.settings.general.vrcx_updater.updater_disabled') }}
</div> </div>
<div v-else class="options-container-item"> </SettingsGroup>
<span>{{ t('view.settings.general.vrcx_updater.updater_disabled') }}</span>
</div> <SettingsGroup :title="t('view.settings.general.application.header')">
</div> <SettingsItem v-if="!isLinux" :label="t('view.settings.general.application.startup')">
<div class="options-container"> <Switch :model-value="isStartAtWindowsStartup" @update:modelValue="setIsStartAtWindowsStartup" />
<span class="header">{{ t('view.settings.general.application.header') }}</span> </SettingsItem>
<simple-switch
<SettingsItem
v-if="!isLinux" v-if="!isLinux"
:label="t('view.settings.general.application.startup')" :label="t('view.settings.general.application.minimized')">
:value="isStartAtWindowsStartup" <Switch :model-value="isStartAsMinimizedState" @update:modelValue="setIsStartAsMinimizedState" />
@change="setIsStartAtWindowsStartup" /> </SettingsItem>
<simple-switch <SettingsItem
v-if="!isLinux"
:label="t('view.settings.general.application.minimized')"
:value="isStartAsMinimizedState"
@change="setIsStartAsMinimizedState" />
<simple-switch
v-else v-else
:label="t('view.settings.general.application.minimized')" :label="t('view.settings.general.application.minimized')"
:value="isStartAsMinimizedState" :description="t('view.settings.general.application.startup_linux')">
:tooltip="t('view.settings.general.application.startup_linux')" <Switch :model-value="isStartAsMinimizedState" @update:modelValue="setIsStartAsMinimizedState" />
@change="setIsStartAsMinimizedState" /> </SettingsItem>
<simple-switch
v-if="!isMacOS" <SettingsItem v-if="!isMacOS" :label="t('view.settings.general.application.tray')">
:label="t('view.settings.general.application.tray')" <Switch :model-value="isCloseToTray" @update:modelValue="setIsCloseToTray" />
:value="isCloseToTray" </SettingsItem>
@change="setIsCloseToTray" />
<simple-switch <SettingsItem
v-if="!isLinux" v-if="!isLinux"
:label="t('view.settings.general.application.disable_gpu_acceleration')" :label="t('view.settings.general.application.disable_gpu_acceleration')"
:value="disableGpuAcceleration" :description="t('view.settings.general.application.disable_gpu_acceleration_tooltip')">
:tooltip="t('view.settings.general.application.disable_gpu_acceleration_tooltip')" <Switch :model-value="disableGpuAcceleration" @update:modelValue="setDisableGpuAcceleration" />
@change="setDisableGpuAcceleration" /> </SettingsItem>
<simple-switch
<SettingsItem
v-if="!isLinux" v-if="!isLinux"
:label="t('view.settings.general.application.disable_vr_overlay_gpu_acceleration')" :label="t('view.settings.general.application.disable_vr_overlay_gpu_acceleration')"
:value="disableVrOverlayGpuAcceleration" :description="t('view.settings.general.application.disable_gpu_acceleration_tooltip')">
:tooltip="t('view.settings.general.application.disable_gpu_acceleration_tooltip')" <Switch
@change="setDisableVrOverlayGpuAcceleration" /> :model-value="disableVrOverlayGpuAcceleration"
<div class="options-container-item"> @update:modelValue="setDisableVrOverlayGpuAcceleration" />
</SettingsItem>
<SettingsItem :label="t('view.settings.general.application.proxy')">
<Button size="sm" variant="outline" @click="promptProxySettings">{{ <Button size="sm" variant="outline" @click="promptProxySettings">{{
t('view.settings.general.application.proxy') t('view.settings.general.application.proxy')
}}</Button> }}</Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="header inline-flex items-center" <SettingsGroup>
>{{ t('view.settings.general.favorites.header') }} <template #description>
<TooltipWrapper side="top" :content="t('view.settings.general.favorites.header_tooltip')"> <div class="flex items-center gap-1.5">
<Info class="ml-1" style="width: 12px; height: 12px; vertical-align: middle; cursor: help" /> <span class="text-base font-semibold text-foreground">{{ t('view.settings.general.favorites.header') }}</span>
</TooltipWrapper> <TooltipWrapper side="top" :content="t('view.settings.general.favorites.header_tooltip')">
</span> <Info class="size-3 text-muted-foreground cursor-help" />
<br /> </TooltipWrapper>
</div>
</template>
<Select <Select
:model-value="localFavoriteFriendsGroups" :model-value="localFavoriteFriendsGroups"
multiple multiple
@update:modelValue="setLocalFavoriteFriendsGroups"> @update:modelValue="setLocalFavoriteFriendsGroups">
<SelectTrigger class="mt-2"> <SelectTrigger>
<SelectValue :placeholder="t('view.settings.general.favorites.group_placeholder')" /> <SelectValue :placeholder="t('view.settings.general.favorites.group_placeholder')" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -154,37 +157,39 @@
</template> </template>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </SettingsGroup>
<div class="options-container">
<span class="header">{{ t('view.settings.general.contributors.header') }}</span> <SettingsGroup :title="t('view.settings.general.contributors.header')">
<div class="options-container-item"> <div>
<img <img
src="https://contrib.rocks/image?repo=vrcx-team/VRCX" src="https://contrib.rocks/image?repo=vrcx-team/VRCX"
alt="Contributors" alt="Contributors"
style="cursor: pointer" class="cursor-pointer"
@click="openExternalLink('https://github.com/vrcx-team/VRCX/graphs/contributors')" /> @click="openExternalLink('https://github.com/vrcx-team/VRCX/graphs/contributors')" />
</div> </div>
</div> </SettingsGroup>
<div class="options-container border-t border-border" style="margin-top: 45px; padding-top: 30px">
<span class="header">{{ t('view.settings.general.legal_notice.header') }}</span> <SettingsGroup :title="t('view.settings.general.legal_notice.header')">
<div class="options-container-item" style="display: block"> <div class="flex flex-col gap-2 text-sm text-muted-foreground">
<p> <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;
<a class="cursor-pointer" @click="openExternalLink('https://github.com/Natsumi-sama')">Natsumi</a> <a class="cursor-pointer" @click="openExternalLink('https://github.com/Natsumi-sama')">Natsumi</a>
&amp; &amp;
<a class="cursor-pointer" @click="openExternalLink('https://github.com/Map1en')">Map1en</a> <a class="cursor-pointer" @click="openExternalLink('https://github.com/Map1en')">Map1en</a>
</p> </p>
<p>{{ t('view.settings.general.legal_notice.info') }}</p> <p class="m-0">{{ t('view.settings.general.legal_notice.info') }}</p>
<p>{{ t('view.settings.general.legal_notice.disclaimer1') }}</p> <p class="m-0">{{ t('view.settings.general.legal_notice.disclaimer1') }}</p>
<p>{{ t('view.settings.general.legal_notice.disclaimer2') }}</p> <p class="m-0">{{ t('view.settings.general.legal_notice.disclaimer2') }}</p>
</div> </div>
<div class="options-container-item">
<SettingsItem :label="t('view.settings.general.legal_notice.open_source_software_notice')">
<Button size="sm" variant="outline" @click="openOSSDialog">{{ <Button size="sm" variant="outline" @click="openOSSDialog">{{
t('view.settings.general.legal_notice.open_source_software_notice') t('view.settings.general.legal_notice.open_source_software_notice')
}}</Button> }}</Button>
</div> </SettingsItem>
</div> </SettingsGroup>
<OpenSourceSoftwareNoticeDialog v-if="ossDialog" v-model:ossDialog="ossDialog" /> <OpenSourceSoftwareNoticeDialog v-if="ossDialog" v-model:ossDialog="ossDialog" />
</div> </div>
</template> </template>
@@ -192,6 +197,7 @@
<script setup> <script setup>
import { computed, defineAsyncComponent, ref } from 'vue'; import { computed, defineAsyncComponent, ref } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Info } from 'lucide-vue-next'; import { Info } from 'lucide-vue-next';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@@ -204,14 +210,14 @@
SelectSeparator, SelectSeparator,
SelectTrigger, SelectTrigger,
SelectValue SelectValue
} from '../../../../components/ui/select'; } from '@/components/ui/select';
import { useFavoriteStore, useGeneralSettingsStore, useVRCXUpdaterStore } from '../../../../stores'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '../../../../components/ui/toggle-group'; import { useFavoriteStore, useGeneralSettingsStore, useVRCXUpdaterStore } from '@/stores';
import { links } from '../../../../shared/constants'; import { links } from '@/shared/constants';
import { openExternalLink } from '../../../../shared/utils'; import { openExternalLink } from '@/shared/utils';
import SimpleSwitch from '../SimpleSwitch.vue'; import SettingsGroup from '../SettingsGroup.vue';
import TooltipWrapper from '../../../../components/ui/tooltip/TooltipWrapper.vue'; import SettingsItem from '../SettingsItem.vue';
const { t } = useI18n(); const { t } = useI18n();
@@ -1,29 +1,23 @@
<template> <template>
<div> <div class="flex flex-col gap-10 py-2">
<div class="options-container mt-0"> <SettingsGroup :title="t('view.settings.notifications.notifications.header')">
<span class="header">{{ t('view.settings.notifications.notifications.header') }}</span> <SettingsItem :label="t('view.settings.notifications.notifications.notification_filter')">
<div class="options-container-item">
<Button size="sm" variant="outline" @click="showNotyFeedFiltersDialog">{{ <Button size="sm" variant="outline" @click="showNotyFeedFiltersDialog">{{
t('view.settings.notifications.notifications.notification_filter') t('view.settings.notifications.notifications.notification_filter')
}}</Button> }}</Button>
</div> </SettingsItem>
<div class="options-container-item">
<SettingsItem :label="t('view.settings.notifications.notifications.test_notification')">
<Button size="sm" variant="outline" @click="testNotification" <Button size="sm" variant="outline" @click="testNotification"
><Play />{{ t('view.settings.notifications.notifications.test_notification') }}</Button ><Play />{{ t('view.settings.notifications.notifications.test_notification') }}</Button
> >
</div> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="sub-header">{{ <SettingsGroup :title="t('view.settings.notifications.notifications.steamvr_notifications.header')">
t('view.settings.notifications.notifications.steamvr_notifications.header') <SettingsItem
}}</span> :label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
<div class="options-container-item">
<span class="name">{{
t('view.settings.notifications.notifications.desktop_notifications.when_to_display')
}}</span>
<br />
<ToggleGroup <ToggleGroup
class="mt-1.5"
type="single" type="single"
required required
variant="outline" variant="outline"
@@ -52,24 +46,36 @@
t('view.settings.notifications.notifications.conditions.always') t('view.settings.notifications.notifications.conditions.always')
}}</ToggleGroupItem> }}</ToggleGroupItem>
</ToggleGroup> </ToggleGroup>
</div> </SettingsItem>
<simple-switch
:label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')" <SettingsItem
:value="openVR" :label="t('view.settings.notifications.notifications.steamvr_notifications.steamvr_overlay')">
@change=" <Switch
setOpenVR(); :model-value="openVR"
saveOpenVROption(); @update:modelValue="
" /> setOpenVR();
<template v-if="openVR">
<simple-switch
:label="t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')"
:value="overlayNotifications"
:disabled="!openVR"
@change="
setOverlayNotifications();
saveOpenVROption(); saveOpenVROption();
" /> " />
<div class="options-container-item"> </SettingsItem>
<template v-if="openVR">
<SettingsItem
:label="
t('view.settings.notifications.notifications.steamvr_notifications.overlay_notifications')
">
<Switch
:model-value="overlayNotifications"
:disabled="!openVR"
@update:modelValue="
setOverlayNotifications();
saveOpenVROption();
" />
</SettingsItem>
<SettingsItem
:label="
t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
">
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
@@ -79,17 +85,22 @@
t('view.settings.notifications.notifications.steamvr_notifications.notification_position') t('view.settings.notifications.notifications.steamvr_notifications.notification_position')
}}</Button }}</Button
> >
</div> </SettingsItem>
</template> </template>
<div class="options-container-item">
<span class="name" style="vertical-align: top; padding-top: 8px">{{ <SettingsItem
:label="
t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity') t('view.settings.notifications.notifications.steamvr_notifications.notification_opacity')
}}</span> ">
<div style="flex: 0 0 300px; width: 300px; max-width: 100%; padding-top: 16px"> <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>
</div> </SettingsItem>
<div class="options-container-item">
<SettingsItem
:label="
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
">
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
@@ -99,75 +110,85 @@
t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout') t('view.settings.notifications.notifications.steamvr_notifications.notification_timeout')
}}</Button }}</Button
> >
</div> </SettingsItem>
<simple-switch
:label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')" <SettingsItem
:value="imageNotifications" :label="t('view.settings.notifications.notifications.steamvr_notifications.user_images')">
@change=" <Switch
setImageNotifications(); :model-value="imageNotifications"
saveOpenVROption(); @update:modelValue="
" /> setImageNotifications();
saveOpenVROption();
" />
</SettingsItem>
<template v-if="!isLinux"> <template v-if="!isLinux">
<simple-switch <SettingsItem
:label=" :label="
t('view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications') t('view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications')
" ">
:value="xsNotifications" <Switch
@change=" :model-value="xsNotifications"
setXsNotifications(); @update:modelValue="
saveOpenVROption(); setXsNotifications();
" /> saveOpenVROption();
" />
</SettingsItem>
</template> </template>
<template v-else> <template v-else>
<simple-switch <SettingsItem
:label="t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')" :label="
:value="xsNotifications" t('view.settings.notifications.notifications.steamvr_notifications.wayvr_notifications')
@change=" ">
setXsNotifications(); <Switch
saveOpenVROption(); :model-value="xsNotifications"
" /> @update:modelValue="
setXsNotifications();
saveOpenVROption();
" />
</SettingsItem>
</template> </template>
<template v-if="!isLinux"> <template v-if="!isLinux">
<simple-switch <SettingsItem
:label=" :label="
t( t(
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications' 'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications'
) )
" ">
:value="ovrtHudNotifications" <Switch
@change=" :model-value="ovrtHudNotifications"
setOvrtHudNotifications(); @update:modelValue="
saveOpenVROption(); setOvrtHudNotifications();
" /> saveOpenVROption();
<simple-switch " />
</SettingsItem>
<SettingsItem
:label=" :label="
t( t(
'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications' 'view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications'
) )
" ">
:value="ovrtWristNotifications" <Switch
@change=" :model-value="ovrtWristNotifications"
setOvrtWristNotifications(); @update:modelValue="
saveOpenVROption(); setOvrtWristNotifications();
" /> saveOpenVROption();
" />
</SettingsItem>
</template> </template>
</div> </SettingsGroup>
<div class="options-container">
<span class="sub-header">{{ <SettingsGroup :title="t('view.settings.notifications.notifications.desktop_notifications.header')">
t('view.settings.notifications.notifications.desktop_notifications.header') <SettingsItem
}}</span> :label="t('view.settings.notifications.notifications.desktop_notifications.when_to_display')">
<div class="options-container-item">
<span class="name">{{
t('view.settings.notifications.notifications.desktop_notifications.when_to_display')
}}</span>
<br />
<ToggleGroup <ToggleGroup
type="single" type="single"
required required
variant="outline" variant="outline"
size="sm" size="sm"
:model-value="desktopToast" :model-value="desktopToast"
style="margin-top: 6px"
@update:model-value="setDesktopToast(String($event))"> @update:model-value="setDesktopToast(String($event))">
<ToggleGroupItem value="Never">{{ <ToggleGroupItem value="Never">{{
t('view.settings.notifications.notifications.conditions.never') t('view.settings.notifications.notifications.conditions.never')
@@ -191,28 +212,25 @@
t('view.settings.notifications.notifications.conditions.always') t('view.settings.notifications.notifications.conditions.always')
}}</ToggleGroupItem> }}</ToggleGroupItem>
</ToggleGroup> </ToggleGroup>
</div> </SettingsItem>
<simple-switch
<SettingsItem
:label=" :label="
t('view.settings.notifications.notifications.desktop_notifications.desktop_notification_while_afk') t('view.settings.notifications.notifications.desktop_notifications.desktop_notification_while_afk')
" ">
:value="afkDesktopToast" <Switch :model-value="afkDesktopToast" @update:modelValue="setAfkDesktopToast" />
@change="setAfkDesktopToast" /> </SettingsItem>
</div> </SettingsGroup>
<div class="options-container">
<span class="sub-header">{{ t('view.settings.notifications.notifications.text_to_speech.header') }}</span> <SettingsGroup :title="t('view.settings.notifications.notifications.text_to_speech.header')">
<div class="options-container-item"> <SettingsItem
<span class="name">{{ :label="t('view.settings.notifications.notifications.text_to_speech.when_to_play')">
t('view.settings.notifications.notifications.text_to_speech.when_to_play')
}}</span>
<br />
<ToggleGroup <ToggleGroup
type="single" type="single"
required required
variant="outline" variant="outline"
size="sm" size="sm"
:model-value="notificationTTS" :model-value="notificationTTS"
style="margin-top: 6px"
@update:model-value="saveNotificationTTS"> @update:model-value="saveNotificationTTS">
<ToggleGroupItem value="Never">{{ <ToggleGroupItem value="Never">{{
t('view.settings.notifications.notifications.conditions.never') t('view.settings.notifications.notifications.conditions.never')
@@ -230,9 +248,9 @@
t('view.settings.notifications.notifications.conditions.always') t('view.settings.notifications.notifications.conditions.always')
}}</ToggleGroupItem> }}</ToggleGroupItem>
</ToggleGroup> </ToggleGroup>
</div> </SettingsItem>
<div class="options-container-item">
<span class="name">{{ t('view.settings.notifications.notifications.text_to_speech.tts_voice') }}</span> <SettingsItem :label="t('view.settings.notifications.notifications.text_to_speech.tts_voice')">
<Select <Select
:model-value="ttsVoiceIndex" :model-value="ttsVoiceIndex"
:disabled="notificationTTS === 'Never'" :disabled="notificationTTS === 'Never'"
@@ -248,28 +266,34 @@
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </SettingsItem>
<simple-switch
:label="t('view.settings.notifications.notifications.text_to_speech.use_memo_nicknames')" <SettingsItem
:value="notificationTTSNickName" :label="t('view.settings.notifications.notifications.text_to_speech.use_memo_nicknames')">
:disabled="notificationTTS === 'Never'" <Switch
@change="setNotificationTTSNickName" /> :model-value="notificationTTSNickName"
<simple-switch :disabled="notificationTTS === 'Never'"
:label="t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')" @update:modelValue="setNotificationTTSNickName" />
:value="isTestTTSVisible" </SettingsItem>
@change="isTestTTSVisible = !isTestTTSVisible" />
<div v-if="isTestTTSVisible" style="margin-top: 6px"> <SettingsItem
:label="t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')">
<Switch :model-value="isTestTTSVisible" @update:modelValue="isTestTTSVisible = !isTestTTSVisible" />
</SettingsItem>
<div v-if="isTestTTSVisible" class="flex items-center gap-2 mt-1">
<InputGroupTextareaField <InputGroupTextareaField
v-model="notificationTTSTest" v-model="notificationTTSTest"
:placeholder="t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')" :placeholder="t('view.settings.notifications.notifications.text_to_speech.tts_test_placeholder')"
:rows="1" :rows="1"
style="width: 175px; display: inline-block" class="w-44"
input-class="resize-none min-h-0" /> input-class="resize-none min-h-0" />
<Button size="sm" variant="outline" @click="testNotificationTTS">{{ <Button size="sm" variant="outline" @click="testNotificationTTS">{{
t('view.settings.notifications.notifications.text_to_speech.play') t('view.settings.notifications.notifications.text_to_speech.play')
}}</Button> }}</Button>
</div> </div>
</div> </SettingsGroup>
<NotificationPositionDialog v-model:isNotificationPositionDialogVisible="isNotificationPositionDialogVisible" /> <NotificationPositionDialog v-model:isNotificationPositionDialogVisible="isNotificationPositionDialogVisible" />
<FeedFiltersDialog v-model:feedFiltersDialogMode="feedFiltersDialogMode" /> <FeedFiltersDialog v-model:feedFiltersDialogMode="feedFiltersDialogMode" />
</div> </div>
@@ -277,6 +301,9 @@
<script setup> <script setup>
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import { Switch } from '@/components/ui/switch';
import { Slider } from '@/components/ui/slider';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { InputGroupTextareaField } from '@/components/ui/input-group'; import { InputGroupTextareaField } from '@/components/ui/input-group';
@@ -289,13 +316,12 @@
useNotificationStore, useNotificationStore,
useNotificationsSettingsStore, useNotificationsSettingsStore,
useVrStore useVrStore
} from '../../../../stores'; } from '@/stores';
import { ToggleGroup, ToggleGroupItem } from '../../../../components/ui/toggle-group';
import { Slider } from '../../../../components/ui/slider';
import FeedFiltersDialog from '../../dialogs/FeedFiltersDialog.vue'; import FeedFiltersDialog from '../../dialogs/FeedFiltersDialog.vue';
import NotificationPositionDialog from '../../dialogs/NotificationPositionDialog.vue'; import NotificationPositionDialog from '../../dialogs/NotificationPositionDialog.vue';
import SimpleSwitch from '../SimpleSwitch.vue'; import SettingsGroup from '../SettingsGroup.vue';
import SettingsItem from '../SettingsItem.vue';
const { t } = useI18n(); const { t } = useI18n();
@@ -1,117 +1,110 @@
<template> <template>
<!--//- Pictures | Screenshot Helper--> <!--//- Pictures | Screenshot Helper-->
<div class="options-container mt-0"> <div class="flex flex-col gap-10 py-2">
<span class="header">{{ t('view.settings.advanced.advanced.screenshot_helper.header') }}</span> <SettingsGroup :title="t('view.settings.advanced.advanced.screenshot_helper.header')">
<div class="options-container-item"> <template #description>
<span class="name">{{ t('view.settings.advanced.advanced.screenshot_helper.description') }}</span> {{ t('view.settings.advanced.advanced.screenshot_helper.description') }}
</div> </template>
<simple-switch
:label="t('view.settings.advanced.advanced.screenshot_helper.enable')"
:value="screenshotHelper"
@change="setScreenshotHelper()"
:tooltip="t('view.settings.advanced.advanced.screenshot_helper.description_tooltip')"
:long-label="true" />
<simple-switch
:label="t('view.settings.advanced.advanced.screenshot_helper.modify_filename')"
:value="screenshotHelperModifyFilename"
@change="setScreenshotHelperModifyFilename()"
:disabled="!screenshotHelper"
:tooltip="t('view.settings.advanced.advanced.screenshot_helper.modify_filename_tooltip')"
:long-label="true" />
<simple-switch
:label="t('view.settings.advanced.advanced.screenshot_helper.copy_to_clipboard')"
:value="screenshotHelperCopyToClipboard"
@change="setScreenshotHelperCopyToClipboard()"
:long-label="true" />
<Button size="sm" variant="outline" class="mt-2" @click="askDeleteAllScreenshotMetadata()">{{
t('view.settings.advanced.advanced.delete_all_screenshot_metadata.button')
}}</Button>
</div>
<div class="options-container"> <SettingsItem
<span class="header">{{ t('view.settings.pictures.pictures.auto_delete_old_prints') }}</span> :label="t('view.settings.advanced.advanced.screenshot_helper.enable')"
<simple-switch :description="t('view.settings.advanced.advanced.screenshot_helper.description_tooltip')">
:label="t('view.settings.pictures.pictures.auto_delete_prints_from_vrc')" <Switch :model-value="screenshotHelper" @update:modelValue="setScreenshotHelper()" />
:value="autoDeleteOldPrints" </SettingsItem>
@change="setAutoDeleteOldPrints()"
:long-label="true" />
</div>
<!-- //- Pictures | User Generated Content --> <SettingsItem
<div class="options-container"> :label="t('view.settings.advanced.advanced.screenshot_helper.modify_filename')"
<span class="header">{{ t('view.settings.advanced.advanced.user_generated_content.header') }}</span> :description="t('view.settings.advanced.advanced.screenshot_helper.modify_filename_tooltip')">
<br /> <Switch
<div class="options-container-item mb-1.5"> :model-value="screenshotHelperModifyFilename"
<span class="name" style="min-width: 300px">{{ :disabled="!screenshotHelper"
t('view.settings.advanced.advanced.user_generated_content.description') @update:modelValue="setScreenshotHelperModifyFilename()" />
}}</span> </SettingsItem>
</div>
<div class="flex gap-2 mt-2">
<Button size="sm" variant="outline" @click="openUGCFolder()">{{
t('view.settings.advanced.advanced.user_generated_content.folder')
}}</Button>
<Button size="sm" variant="outline" @click="openUGCFolderSelector()">{{
t('view.settings.advanced.advanced.user_generated_content.set_folder')
}}</Button>
<Button size="sm" variant="outline" @click="resetUGCFolder()" v-if="ugcFolderPath">{{
t('view.settings.advanced.advanced.user_generated_content.reset_override')
}}</Button>
</div>
<br /> <SettingsItem :label="t('view.settings.advanced.advanced.screenshot_helper.copy_to_clipboard')">
<br /> <Switch
<br /> :model-value="screenshotHelperCopyToClipboard"
<span class="sub-header mr-1.5">{{ @update:modelValue="setScreenshotHelperCopyToClipboard()" />
t('view.settings.advanced.advanced.save_instance_prints_to_file.header') </SettingsItem>
}}</span>
<TooltipWrapper <SettingsItem :label="t('view.settings.advanced.advanced.delete_all_screenshot_metadata.button')">
side="top" <Button size="sm" variant="outline" @click="askDeleteAllScreenshotMetadata()">{{
:content="t('view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip')"> t('view.settings.advanced.advanced.delete_all_screenshot_metadata.button')
<Info class="inline-block" /> }}</Button>
</TooltipWrapper> </SettingsItem>
<simple-switch </SettingsGroup>
:label="t('view.settings.advanced.advanced.save_instance_prints_to_file.description')"
:value="saveInstancePrints" <SettingsGroup :title="t('view.settings.pictures.pictures.auto_delete_old_prints')">
@change="setSaveInstancePrints()" <SettingsItem :label="t('view.settings.pictures.pictures.auto_delete_prints_from_vrc')">
:long-label="true" /> <Switch :model-value="autoDeleteOldPrints" @update:modelValue="setAutoDeleteOldPrints()" />
<simple-switch </SettingsItem>
:label="t('view.settings.advanced.advanced.save_instance_prints_to_file.crop')" </SettingsGroup>
:value="cropInstancePrints"
@change="setCropInstancePrints()" <!-- //- Pictures | User Generated Content -->
:long-label="true" /> <SettingsGroup :title="t('view.settings.advanced.advanced.user_generated_content.header')">
<br /> <template #description>
<span class="sub-header">{{ t('view.settings.advanced.advanced.save_instance_stickers_to_file.header') }}</span> {{ t('view.settings.advanced.advanced.user_generated_content.description') }}
<simple-switch </template>
:label="t('view.settings.advanced.advanced.save_instance_stickers_to_file.description')"
:value="saveInstanceStickers" <div class="flex gap-2">
@change="setSaveInstanceStickers()" <Button size="sm" variant="outline" @click="openUGCFolder()">{{
:long-label="true" /> t('view.settings.advanced.advanced.user_generated_content.folder')
<br /> }}</Button>
<span class="sub-header mr-1.5" <Button size="sm" variant="outline" @click="openUGCFolderSelector()">{{
>{{ t('view.settings.advanced.advanced.save_instance_emoji_to_file.header') }} t('view.settings.advanced.advanced.user_generated_content.set_folder')
</span> }}</Button>
<TooltipWrapper <Button v-if="ugcFolderPath" size="sm" variant="outline" @click="resetUGCFolder()">{{
side="top" t('view.settings.advanced.advanced.user_generated_content.reset_override')
:content="t('view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip')"> }}</Button>
<Info class="inline-block" /> </div>
</TooltipWrapper> </SettingsGroup>
<simple-switch
:label="t('view.settings.advanced.advanced.save_instance_emoji_to_file.description')" <SettingsGroup :title="t('view.settings.advanced.advanced.save_instance_prints_to_file.header')">
:value="saveInstanceEmoji" <template #description>
@change="setSaveInstanceEmoji()" {{ t('view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip') }}
:long-label="true" /> </template>
<SettingsItem
:label="t('view.settings.advanced.advanced.save_instance_prints_to_file.description')">
<Switch :model-value="saveInstancePrints" @update:modelValue="setSaveInstancePrints()" />
</SettingsItem>
<SettingsItem :label="t('view.settings.advanced.advanced.save_instance_prints_to_file.crop')">
<Switch :model-value="cropInstancePrints" @update:modelValue="setCropInstancePrints()" />
</SettingsItem>
</SettingsGroup>
<SettingsGroup
:title="t('view.settings.advanced.advanced.save_instance_stickers_to_file.header')">
<SettingsItem
:label="t('view.settings.advanced.advanced.save_instance_stickers_to_file.description')">
<Switch :model-value="saveInstanceStickers" @update:modelValue="setSaveInstanceStickers()" />
</SettingsItem>
</SettingsGroup>
<SettingsGroup :title="t('view.settings.advanced.advanced.save_instance_emoji_to_file.header')">
<template #description>
{{ t('view.settings.advanced.advanced.save_instance_prints_to_file.header_tooltip') }}
</template>
<SettingsItem
:label="t('view.settings.advanced.advanced.save_instance_emoji_to_file.description')">
<Switch :model-value="saveInstanceEmoji" @update:modelValue="setSaveInstanceEmoji()" />
</SettingsItem>
</SettingsGroup>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Info } from 'lucide-vue-next'; import { Switch } from '@/components/ui/switch';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useAdvancedSettingsStore } from '../../../../stores'; import { useAdvancedSettingsStore } from '@/stores';
import SimpleSwitch from '../SimpleSwitch.vue'; import SettingsGroup from '../SettingsGroup.vue';
import SettingsItem from '../SettingsItem.vue';
const { t } = useI18n(); const { t } = useI18n();
@@ -1,168 +1,192 @@
<template> <template>
<div class="options-container mt-0"> <div class="flex flex-col gap-10 py-2">
<span class="header">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.header') }}</span> <SettingsGroup :title="t('view.settings.wrist_overlay.steamvr_wrist_overlay.header')">
<div class="options-container-item"> <template #description>
<Button <p class="m-0">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.description') }}</p>
size="sm" <p class="m-0">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.grip') }}</p>
variant="outline" <p class="m-0">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.menu') }}</p>
:disabled="!openVR || !overlayWrist" </template>
@click="emit('open-feed-filters')"
>{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_filters') }}</Button <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_filters')">
> <Button
</div> size="sm"
<div class="options-container-item"> variant="outline"
<span>{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.description') }}</span> :disabled="!openVR || !overlayWrist"
</div> @click="emit('open-feed-filters')"
<div class="options-container-item"> >{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_filters') }}</Button
<span>{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.grip') }}</span> >
</div> </SettingsItem>
<div class="options-container-item">
<span>{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.menu') }}</span> <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.steamvr_overlay')">
</div> <Switch
<simple-switch :model-value="openVR"
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.steamvr_overlay')" @update:modelValue="
:value="openVR" setOpenVR();
@change=" saveOpenVROption();
setOpenVR(); " />
saveOpenVROption(); </SettingsItem>
" />
<simple-switch <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_overlay')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.wrist_feed_overlay')" <Switch
:value="overlayWrist" :model-value="overlayWrist"
:disabled="!openVR" :disabled="!openVR"
@change=" @update:modelValue="
setOverlayWrist(); setOverlayWrist();
saveOpenVROption(); saveOpenVROption();
"></simple-switch> " />
<simple-switch </SettingsItem>
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_private_worlds')"
:value="hidePrivateFromFeed" <SettingsItem
@change=" :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_private_worlds')">
setHidePrivateFromFeed(); <Switch
saveOpenVROption(); :model-value="hidePrivateFromFeed"
" /> @update:modelValue="
<div class="options-container-item" style="min-width: 118px"> setHidePrivateFromFeed();
<span class="name">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with') }}</span> saveOpenVROption();
<RadioGroup " />
:model-value="openVRAlways ? 'true' : 'false'" </SettingsItem>
:disabled="!openVR"
class="gap-2 flex mt-2" <SettingsItem
@update:modelValue="handleOpenVRAlwaysRadio"> :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.start_overlay_with')">
<div class="flex items-center space-x-2"> <RadioGroup
<RadioGroupItem id="openVRAlways-false" value="false" /> :model-value="openVRAlways ? 'true' : 'false'"
<label for="openVRAlways-false">{{ 'VRChat' }}</label> :disabled="!openVR"
</div> class="gap-2 flex"
<div class="flex items-center space-x-2"> @update:modelValue="handleOpenVRAlwaysRadio">
<RadioGroupItem id="openVRAlways-true" value="true" /> <div class="flex items-center space-x-2">
<label for="openVRAlways-true">{{ 'SteamVR' }}</label> <RadioGroupItem id="openVRAlways-false" value="false" />
</div> <label for="openVRAlways-false">{{ 'VRChat' }}</label>
</RadioGroup> </div>
</div> <div class="flex items-center space-x-2">
<div class="options-container-item"> <RadioGroupItem id="openVRAlways-true" value="true" />
<span class="name">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button') }}</span> <label for="openVRAlways-true">{{ 'SteamVR' }}</label>
<RadioGroup </div>
:model-value="overlaybutton ? 'true' : 'false'" </RadioGroup>
:disabled="!openVR || !overlayWrist" </SettingsItem>
class="gap-2 flex mt-2"
@update:modelValue="handleOverlayButtonRadio"> <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button')">
<div class="flex items-center space-x-2"> <RadioGroup
<RadioGroupItem id="overlaybutton-false" value="false" /> :model-value="overlaybutton ? 'true' : 'false'"
<label for="overlaybutton-false">{{ :disabled="!openVR || !overlayWrist"
t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_grip') class="gap-2 flex"
}}</label> @update:modelValue="handleOverlayButtonRadio">
</div> <div class="flex items-center space-x-2">
<div class="flex items-center space-x-2"> <RadioGroupItem id="overlaybutton-false" value="false" />
<RadioGroupItem id="overlaybutton-true" value="true" /> <label for="overlaybutton-false">{{
<label for="overlaybutton-true">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_grip')
t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_menu') }}</label>
}}</label> </div>
</div> <div class="flex items-center space-x-2">
</RadioGroup> <RadioGroupItem id="overlaybutton-true" value="true" />
</div> <label for="overlaybutton-true">{{
<div class="options-container-item"> t('view.settings.wrist_overlay.steamvr_wrist_overlay.overlay_button_menu')
<span class="name">{{ t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on') }}</span> }}</label>
<ToggleGroup </div>
type="single" </RadioGroup>
required </SettingsItem>
variant="outline"
size="sm" <SettingsItem
:model-value="overlayHand" :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on')">
@update:model-value=" <ToggleGroup
setOverlayHand($event); type="single"
saveOpenVROption(); required
"> variant="outline"
<ToggleGroupItem value="1">{{ size="sm"
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left') :model-value="overlayHand"
}}</ToggleGroupItem> @update:model-value="
<ToggleGroupItem value="2">{{ setOverlayHand($event);
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right') saveOpenVROption();
}}</ToggleGroupItem> ">
<ToggleGroupItem value="0">{{ <ToggleGroupItem value="1">{{
t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both') t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_left')
}}</ToggleGroupItem> }}</ToggleGroupItem>
</ToggleGroup> <ToggleGroupItem value="2">{{
</div> t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_right')
<simple-switch }}</ToggleGroupItem>
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background')" <ToggleGroupItem value="0">{{
:value="vrBackgroundEnabled" t('view.settings.wrist_overlay.steamvr_wrist_overlay.display_overlay_on_both')
:disabled="!openVR || !overlayWrist" }}</ToggleGroupItem>
@change=" </ToggleGroup>
setVrBackgroundEnabled(); </SettingsItem>
saveOpenVROption();
" /> <SettingsItem
<simple-switch :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.grey_background')">
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.minimal_feed_icons')" <Switch
:value="minimalFeed" :model-value="vrBackgroundEnabled"
:disabled="!openVR || !overlayWrist" :disabled="!openVR || !overlayWrist"
@change=" @update:modelValue="
setMinimalFeed(); setVrBackgroundEnabled();
saveOpenVROption(); saveOpenVROption();
" /> " />
<simple-switch </SettingsItem>
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_vr_devices')"
:value="!hideDevicesFromFeed" <SettingsItem
:disabled="!openVR || !overlayWrist" :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.minimal_feed_icons')">
@change=" <Switch
setHideDevicesFromFeed(); :model-value="minimalFeed"
saveOpenVROption(); :disabled="!openVR || !overlayWrist"
" /> @update:modelValue="
<simple-switch setMinimalFeed();
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_cpu_usage')" saveOpenVROption();
:value="vrOverlayCpuUsage" " />
:disabled="!openVR || !overlayWrist" </SettingsItem>
@change="
setVrOverlayCpuUsage(); <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_vr_devices')">
saveOpenVROption(); <Switch
" /> :model-value="!hideDevicesFromFeed"
<simple-switch :disabled="!openVR || !overlayWrist"
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_game_uptime')" @update:modelValue="
:value="!hideUptimeFromFeed" setHideDevicesFromFeed();
:disabled="!openVR || !overlayWrist" saveOpenVROption();
@change=" " />
setHideUptimeFromFeed(); </SettingsItem>
saveOpenVROption();
" /> <SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_cpu_usage')">
<simple-switch <Switch
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_pc_uptime')" :model-value="vrOverlayCpuUsage"
:value="pcUptimeOnFeed" :disabled="!openVR || !overlayWrist"
:disabled="!openVR || !overlayWrist" @update:modelValue="
@change=" setVrOverlayCpuUsage();
setPcUptimeOnFeed(); saveOpenVROption();
saveOpenVROption(); " />
"></simple-switch> </SettingsItem>
<SettingsItem
:label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_game_uptime')">
<Switch
:model-value="!hideUptimeFromFeed"
:disabled="!openVR || !overlayWrist"
@update:modelValue="
setHideUptimeFromFeed();
saveOpenVROption();
" />
</SettingsItem>
<SettingsItem :label="t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_pc_uptime')">
<Switch
:model-value="pcUptimeOnFeed"
:disabled="!openVR || !overlayWrist"
@update:modelValue="
setPcUptimeOnFeed();
saveOpenVROption();
" />
</SettingsItem>
</SettingsGroup>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-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';
import { useNotificationsSettingsStore, useVrStore, useWristOverlaySettingsStore } from '../../../stores'; import { useNotificationsSettingsStore, useVrStore, useWristOverlaySettingsStore } from '@/stores';
import { RadioGroup, RadioGroupItem } from '../../../components/ui/radio-group';
import { ToggleGroup, ToggleGroupItem } from '../../../components/ui/toggle-group';
import SimpleSwitch from './SimpleSwitch.vue'; import SettingsGroup from './SettingsGroup.vue';
import SettingsItem from './SettingsItem.vue';
const emit = defineEmits(['open-feed-filters']); const emit = defineEmits(['open-feed-filters']);
const { t } = useI18n(); const { t } = useI18n();