summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages/settings
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-03-13 17:39:53 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-03-13 17:39:53 +0900
commit5d228fb0f32aca9337c8b8a9ea9544f28d981f34 (patch)
tree13ddf5553353770446c8277971f22b2c959e3b16 /packages/frontend/src/pages/settings
parentenhance(frontend): improve emoji picker settings (diff)
downloadmisskey-5d228fb0f32aca9337c8b8a9ea9544f28d981f34.tar.gz
misskey-5d228fb0f32aca9337c8b8a9ea9544f28d981f34.tar.bz2
misskey-5d228fb0f32aca9337c8b8a9ea9544f28d981f34.zip
enhance(frontend): re-organize settings page
Diffstat (limited to 'packages/frontend/src/pages/settings')
-rw-r--r--packages/frontend/src/pages/settings/accessibility.vue51
-rw-r--r--packages/frontend/src/pages/settings/appearance.vue325
-rw-r--r--packages/frontend/src/pages/settings/index.vue5
-rw-r--r--packages/frontend/src/pages/settings/preferences.vue614
4 files changed, 463 insertions, 532 deletions
diff --git a/packages/frontend/src/pages/settings/accessibility.vue b/packages/frontend/src/pages/settings/accessibility.vue
index 3dbb039a17..f7b1e7d2a0 100644
--- a/packages/frontend/src/pages/settings/accessibility.vue
+++ b/packages/frontend/src/pages/settings/accessibility.vue
@@ -60,6 +60,17 @@ SPDX-License-Identifier: AGPL-3.0-only
</SearchMarker>
</div>
+ <SearchMarker :keywords="['menu', 'style', 'popup', 'drawer']">
+ <MkPreferenceContainer k="menuStyle">
+ <MkSelect v-model="menuStyle">
+ <template #label><SearchLabel>{{ i18n.ts.menuStyle }}</SearchLabel></template>
+ <option value="auto">{{ i18n.ts.auto }}</option>
+ <option value="popup">{{ i18n.ts.popup }}</option>
+ <option value="drawer">{{ i18n.ts.drawer }}</option>
+ </MkSelect>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
<SearchMarker :keywords="['contextmenu', 'system', 'native']">
<MkPreferenceContainer k="contextMenu">
<MkSelect v-model="contextMenu">
@@ -70,6 +81,22 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSelect>
</MkPreferenceContainer>
</SearchMarker>
+
+ <SearchMarker :keywords="['font', 'size']">
+ <MkRadios v-model="fontSize">
+ <template #label><SearchLabel>{{ i18n.ts.fontSize }}</SearchLabel></template>
+ <option :value="null"><span style="font-size: 14px;">Aa</span></option>
+ <option value="1"><span style="font-size: 15px;">Aa</span></option>
+ <option value="2"><span style="font-size: 16px;">Aa</span></option>
+ <option value="3"><span style="font-size: 17px;">Aa</span></option>
+ </MkRadios>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['font', 'system', 'native']">
+ <MkSwitch v-model="useSystemFont">
+ <template #label><SearchLabel>{{ i18n.ts.useSystemFont }}</SearchLabel></template>
+ </MkSwitch>
+ </SearchMarker>
</div>
</SearchMarker>
</template>
@@ -84,6 +111,8 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
+import { miLocalStorage } from '@/local-storage.js';
+import MkRadios from '@/components/MkRadios.vue';
const reduceAnimation = prefer.model('animation', v => !v, v => !v);
const animatedMfm = prefer.model('animatedMfm');
@@ -92,10 +121,32 @@ const keepScreenOn = prefer.model('keepScreenOn');
const enableHorizontalSwipe = prefer.model('enableHorizontalSwipe');
const useNativeUiForVideoAudioPlayer = prefer.model('useNativeUiForVideoAudioPlayer');
const contextMenu = prefer.model('contextMenu');
+const menuStyle = prefer.model('menuStyle');
+
+const fontSize = ref(miLocalStorage.getItem('fontSize'));
+const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
+
+watch(fontSize, () => {
+ if (fontSize.value == null) {
+ miLocalStorage.removeItem('fontSize');
+ } else {
+ miLocalStorage.setItem('fontSize', fontSize.value);
+ }
+});
+
+watch(useSystemFont, () => {
+ if (useSystemFont.value) {
+ miLocalStorage.setItem('useSystemFont', 't');
+ } else {
+ miLocalStorage.removeItem('useSystemFont');
+ }
+});
watch([
keepScreenOn,
contextMenu,
+ fontSize,
+ useSystemFont,
], async () => {
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
diff --git a/packages/frontend/src/pages/settings/appearance.vue b/packages/frontend/src/pages/settings/appearance.vue
deleted file mode 100644
index 3fda5bc4c8..0000000000
--- a/packages/frontend/src/pages/settings/appearance.vue
+++ /dev/null
@@ -1,325 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and misskey-project
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<SearchMarker path="/settings/appearance" :label="i18n.ts.appearance" :keywords="['appearance']" icon="ti ti-device-desktop">
- <div class="_gaps_m">
- <MkFeatureBanner icon="/client-assets/desktop_computer_3d.png" color="#eaff00">
- <SearchKeyword>{{ i18n.ts._settings.appearanceBanner }}</SearchKeyword>
- </MkFeatureBanner>
-
- <FormSection first>
- <div class="_gaps_m">
- <div class="_gaps_s">
- <SearchMarker :keywords="['blur']">
- <MkPreferenceContainer k="useBlurEffect">
- <MkSwitch v-model="useBlurEffect">
- <template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['blur', 'modal']">
- <MkPreferenceContainer k="useBlurEffectForModal">
- <MkSwitch v-model="useBlurEffectForModal">
- <template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail']">
- <MkPreferenceContainer k="highlightSensitiveMedia">
- <MkSwitch v-model="highlightSensitiveMedia">
- <template #label><SearchLabel>{{ i18n.ts.highlightSensitiveMedia }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['avatar', 'icon', 'square']">
- <MkPreferenceContainer k="squareAvatars">
- <MkSwitch v-model="squareAvatars">
- <template #label><SearchLabel>{{ i18n.ts.squareAvatars }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']">
- <MkPreferenceContainer k="showAvatarDecorations">
- <MkSwitch v-model="showAvatarDecorations">
- <template #label><SearchLabel>{{ i18n.ts.showAvatarDecorations }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['note', 'timeline', 'gap']">
- <MkPreferenceContainer k="showGapBetweenNotesInTimeline">
- <MkSwitch v-model="showGapBetweenNotesInTimeline">
- <template #label><SearchLabel>{{ i18n.ts.showGapBetweenNotesInTimeline }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['effect', 'show']">
- <MkPreferenceContainer k="enableSeasonalScreenEffect">
- <MkSwitch v-model="enableSeasonalScreenEffect">
- <template #label><SearchLabel>{{ i18n.ts.seasonalScreenEffect }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
- </div>
-
- <SearchMarker :keywords="['menu', 'style', 'popup', 'drawer']">
- <MkPreferenceContainer k="menuStyle">
- <MkSelect v-model="menuStyle">
- <template #label><SearchLabel>{{ i18n.ts.menuStyle }}</SearchLabel></template>
- <option value="auto">{{ i18n.ts.auto }}</option>
- <option value="popup">{{ i18n.ts.popup }}</option>
- <option value="drawer">{{ i18n.ts.drawer }}</option>
- </MkSelect>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']">
- <MkPreferenceContainer k="emojiStyle">
- <div>
- <MkRadios v-model="emojiStyle">
- <template #label><SearchLabel>{{ i18n.ts.emojiStyle }}</SearchLabel></template>
- <option value="native">{{ i18n.ts.native }}</option>
- <option value="fluentEmoji">Fluent Emoji</option>
- <option value="twemoji">Twemoji</option>
- </MkRadios>
- <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
- </div>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['font', 'size']">
- <MkRadios v-model="fontSize">
- <template #label><SearchLabel>{{ i18n.ts.fontSize }}</SearchLabel></template>
- <option :value="null"><span style="font-size: 14px;">Aa</span></option>
- <option value="1"><span style="font-size: 15px;">Aa</span></option>
- <option value="2"><span style="font-size: 16px;">Aa</span></option>
- <option value="3"><span style="font-size: 17px;">Aa</span></option>
- </MkRadios>
- </SearchMarker>
-
- <SearchMarker :keywords="['font', 'system', 'native']">
- <MkSwitch v-model="useSystemFont">
- <template #label><SearchLabel>{{ i18n.ts.useSystemFont }}</SearchLabel></template>
- </MkSwitch>
- </SearchMarker>
- </div>
- </FormSection>
-
- <SearchMarker :keywords="['note', 'display']">
- <FormSection>
- <template #label><SearchLabel>{{ i18n.ts.displayOfNote }}</SearchLabel></template>
-
- <div class="_gaps_m">
- <SearchMarker :keywords="['reaction', 'size', 'scale', 'display']">
- <MkPreferenceContainer k="reactionsDisplaySize">
- <MkRadios v-model="reactionsDisplaySize">
- <template #label><SearchLabel>{{ i18n.ts.reactionsDisplaySize }}</SearchLabel></template>
- <option value="small">{{ i18n.ts.small }}</option>
- <option value="medium">{{ i18n.ts.medium }}</option>
- <option value="large">{{ i18n.ts.large }}</option>
- </MkRadios>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['reaction', 'size', 'scale', 'display', 'width', 'limit']">
- <MkPreferenceContainer k="limitWidthOfReaction">
- <MkSwitch v-model="limitWidthOfReaction">
- <template #label><SearchLabel>{{ i18n.ts.limitWidthOfReaction }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height']">
- <MkPreferenceContainer k="mediaListWithOneImageAppearance">
- <MkRadios v-model="mediaListWithOneImageAppearance">
- <template #label><SearchLabel>{{ i18n.ts.mediaListWithOneImageAppearance }}</SearchLabel></template>
- <option value="expand">{{ i18n.ts.default }}</option>
- <option value="16_9">{{ i18n.tsx.limitTo({ x: '16:9' }) }}</option>
- <option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option>
- <option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option>
- </MkRadios>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation']">
- <MkPreferenceContainer k="instanceTicker">
- <MkSelect v-if="instance.federation !== 'none'" v-model="instanceTicker">
- <template #label><SearchLabel>{{ i18n.ts.instanceTicker }}</SearchLabel></template>
- <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
- <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option>
- <option value="always">{{ i18n.ts._instanceTicker.always }}</option>
- </MkSelect>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility']">
- <MkPreferenceContainer k="nsfw">
- <MkSelect v-model="nsfw">
- <template #label><SearchLabel>{{ i18n.ts.displayOfSensitiveMedia }}</SearchLabel></template>
- <option value="respect">{{ i18n.ts._displayOfSensitiveMedia.respect }}</option>
- <option value="ignore">{{ i18n.ts._displayOfSensitiveMedia.ignore }}</option>
- <option value="force">{{ i18n.ts._displayOfSensitiveMedia.force }}</option>
- </MkSelect>
- </MkPreferenceContainer>
- </SearchMarker>
- </div>
- </FormSection>
- </SearchMarker>
-
- <SearchMarker :keywords="['notification', 'display']">
- <FormSection>
- <template #label><SearchLabel>{{ i18n.ts.notificationDisplay }}</SearchLabel></template>
-
- <div class="_gaps_m">
- <SearchMarker :keywords="['position']">
- <MkPreferenceContainer k="notificationPosition">
- <MkRadios v-model="notificationPosition">
- <template #label><SearchLabel>{{ i18n.ts.position }}</SearchLabel></template>
- <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
- <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
- <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
- <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
- </MkRadios>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <SearchMarker :keywords="['stack', 'axis', 'direction']">
- <MkPreferenceContainer k="notificationStackAxis">
- <MkRadios v-model="notificationStackAxis">
- <template #label><SearchLabel>{{ i18n.ts.stackAxis }}</SearchLabel></template>
- <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
- <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
- </MkRadios>
- </MkPreferenceContainer>
- </SearchMarker>
-
- <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
- </div>
- </FormSection>
- </SearchMarker>
-
- <FormSection>
- <FormLink to="/settings/custom-css"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink>
- </FormSection>
- </div>
-</SearchMarker>
-</template>
-
-<script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
-import * as Misskey from 'misskey-js';
-import MkSwitch from '@/components/MkSwitch.vue';
-import MkSelect from '@/components/MkSelect.vue';
-import MkRadios from '@/components/MkRadios.vue';
-import { prefer } from '@/preferences.js';
-import { reloadAsk } from '@/utility/reload-ask.js';
-import { i18n } from '@/i18n.js';
-import { definePage } from '@/page.js';
-import { miLocalStorage } from '@/local-storage.js';
-import FormLink from '@/components/form/link.vue';
-import { globalEvents } from '@/events.js';
-import { claimAchievement } from '@/utility/achievements.js';
-import MkButton from '@/components/MkButton.vue';
-import FormSection from '@/components/form/section.vue';
-import { instance } from '@/instance.js';
-import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
-import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
-
-const fontSize = ref(miLocalStorage.getItem('fontSize'));
-const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
-
-const showAvatarDecorations = prefer.model('showAvatarDecorations');
-const emojiStyle = prefer.model('emojiStyle');
-const menuStyle = prefer.model('menuStyle');
-const useBlurEffectForModal = prefer.model('useBlurEffectForModal');
-const useBlurEffect = prefer.model('useBlurEffect');
-const highlightSensitiveMedia = prefer.model('highlightSensitiveMedia');
-const squareAvatars = prefer.model('squareAvatars');
-const enableSeasonalScreenEffect = prefer.model('enableSeasonalScreenEffect');
-const showGapBetweenNotesInTimeline = prefer.model('showGapBetweenNotesInTimeline');
-const mediaListWithOneImageAppearance = prefer.model('mediaListWithOneImageAppearance');
-const reactionsDisplaySize = prefer.model('reactionsDisplaySize');
-const limitWidthOfReaction = prefer.model('limitWidthOfReaction');
-const notificationPosition = prefer.model('notificationPosition');
-const notificationStackAxis = prefer.model('notificationStackAxis');
-const nsfw = prefer.model('nsfw');
-const instanceTicker = prefer.model('instanceTicker');
-
-watch(fontSize, () => {
- if (fontSize.value == null) {
- miLocalStorage.removeItem('fontSize');
- } else {
- miLocalStorage.setItem('fontSize', fontSize.value);
- }
-});
-
-watch(useSystemFont, () => {
- if (useSystemFont.value) {
- miLocalStorage.setItem('useSystemFont', 't');
- } else {
- miLocalStorage.removeItem('useSystemFont');
- }
-});
-
-watch([
- fontSize,
- useSystemFont,
- squareAvatars,
- highlightSensitiveMedia,
- enableSeasonalScreenEffect,
- showGapBetweenNotesInTimeline,
- mediaListWithOneImageAppearance,
- reactionsDisplaySize,
- limitWidthOfReaction,
- mediaListWithOneImageAppearance,
- reactionsDisplaySize,
- limitWidthOfReaction,
- instanceTicker,
-], async () => {
- await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
-});
-
-let smashCount = 0;
-let smashTimer: number | null = null;
-
-function testNotification(): void {
- const notification: Misskey.entities.Notification = {
- id: Math.random().toString(),
- createdAt: new Date().toUTCString(),
- isRead: false,
- type: 'test',
- };
-
- globalEvents.emit('clientNotification', notification);
-
- // セルフ通知破壊 実績関連
- smashCount++;
- if (smashCount >= 10) {
- claimAchievement('smashTestNotificationButton');
- smashCount = 0;
- }
- if (smashTimer) {
- clearTimeout(smashTimer);
- }
- smashTimer = window.setTimeout(() => {
- smashCount = 0;
- }, 300);
-}
-
-const headerActions = computed(() => []);
-
-const headerTabs = computed(() => []);
-
-definePage(() => ({
- title: i18n.ts.appearance,
- icon: 'ti ti-device-desktop',
-}));
-</script>
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index debcd4bd3e..3b7c44fbfe 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -119,11 +119,6 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
to: '/settings/emoji-palette',
active: currentPage.value?.route.name === 'emoji-palette',
}, {
- icon: 'ti ti-device-desktop',
- text: i18n.ts.appearance,
- to: '/settings/appearance',
- active: currentPage.value?.route.name === 'appearance',
- }, {
icon: 'ti ti-music',
text: i18n.ts.sounds,
to: '/settings/sounds',
diff --git a/packages/frontend/src/pages/settings/preferences.vue b/packages/frontend/src/pages/settings/preferences.vue
index 374477c510..b9a596067c 100644
--- a/packages/frontend/src/pages/settings/preferences.vue
+++ b/packages/frontend/src/pages/settings/preferences.vue
@@ -10,121 +10,174 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchKeyword>{{ i18n.ts._settings.preferencesBanner }}</SearchKeyword>
</MkFeatureBanner>
- <SearchMarker :keywords="['language']">
- <MkSelect v-model="lang">
- <template #label><SearchLabel>{{ i18n.ts.uiLanguage }}</SearchLabel></template>
- <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
- <template #caption>
- <I18n :src="i18n.ts.i18nInfo" tag="span">
- <template #link>
- <MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink>
- </template>
- </I18n>
- </template>
- </MkSelect>
- </SearchMarker>
+ <SearchMarker :keywords="['general']">
+ <MkFolder :defaultOpen="true">
+ <template #label><SearchLabel>{{ i18n.ts.general }}</SearchLabel></template>
- <SearchMarker :keywords="['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop']">
- <MkRadios v-model="overridedDeviceKind">
- <template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template>
- <option :value="null">{{ i18n.ts.auto }}</option>
- <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option>
- <option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option>
- <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option>
- </MkRadios>
- </SearchMarker>
+ <div class="_gaps_m">
+ <SearchMarker :keywords="['language']">
+ <MkSelect v-model="lang">
+ <template #label><SearchLabel>{{ i18n.ts.uiLanguage }}</SearchLabel></template>
+ <option v-for="x in langs" :key="x[0]" :value="x[0]">{{ x[1] }}</option>
+ <template #caption>
+ <I18n :src="i18n.ts.i18nInfo" tag="span">
+ <template #link>
+ <MkLink url="https://crowdin.com/project/misskey">Crowdin</MkLink>
+ </template>
+ </I18n>
+ </template>
+ </MkSelect>
+ </SearchMarker>
- <FormSection>
- <div class="_gaps_s">
- <SearchMarker :keywords="['post', 'form', 'timeline']">
- <MkPreferenceContainer k="showFixedPostForm">
- <MkSwitch v-model="showFixedPostForm">
- <template #label><SearchLabel>{{ i18n.ts.showFixedPostForm }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
+ <SearchMarker :keywords="['device', 'type', 'kind', 'smartphone', 'tablet', 'desktop']">
+ <MkRadios v-model="overridedDeviceKind">
+ <template #label><SearchLabel>{{ i18n.ts.overridedDeviceKind }}</SearchLabel></template>
+ <option :value="null">{{ i18n.ts.auto }}</option>
+ <option value="smartphone"><i class="ti ti-device-mobile"/> {{ i18n.ts.smartphone }}</option>
+ <option value="tablet"><i class="ti ti-device-tablet"/> {{ i18n.ts.tablet }}</option>
+ <option value="desktop"><i class="ti ti-device-desktop"/> {{ i18n.ts.desktop }}</option>
+ </MkRadios>
+ </SearchMarker>
- <SearchMarker :keywords="['post', 'form', 'timeline', 'channel']">
- <MkPreferenceContainer k="showFixedPostFormInChannel">
- <MkSwitch v-model="showFixedPostFormInChannel">
- <template #label><SearchLabel>{{ i18n.ts.showFixedPostFormInChannel }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
+ <div class="_gaps_s">
+ <SearchMarker :keywords="['blur']">
+ <MkPreferenceContainer k="useBlurEffect">
+ <MkSwitch v-model="useBlurEffect">
+ <template #label><SearchLabel>{{ i18n.ts.useBlurEffect }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <SearchMarker :keywords="['pinned', 'list']">
- <MkFolder>
- <template #label><SearchLabel>{{ i18n.ts.pinnedList }}</SearchLabel></template>
- <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
- <MkButton v-if="prefer.r.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton>
- <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
- </MkFolder>
- </SearchMarker>
+ <SearchMarker :keywords="['blur', 'modal']">
+ <MkPreferenceContainer k="useBlurEffectForModal">
+ <MkSwitch v-model="useBlurEffectForModal">
+ <template #label><SearchLabel>{{ i18n.ts.useBlurEffectForModal }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <SearchMarker :keywords="['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn']">
- <MkPreferenceContainer k="enableQuickAddMfmFunction">
- <MkSwitch v-model="enableQuickAddMfmFunction">
- <template #label><SearchLabel>{{ i18n.ts.enableQuickAddMfmFunction }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
- </div>
- </FormSection>
+ <SearchMarker :keywords="['avatar', 'icon', 'decoration', 'show']">
+ <MkPreferenceContainer k="showAvatarDecorations">
+ <MkSwitch v-model="showAvatarDecorations">
+ <template #label><SearchLabel>{{ i18n.ts.showAvatarDecorations }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <FormSection>
- <div class="_gaps_m">
- <SearchMarker :keywords="['remember', 'keep', 'note', 'visibility']">
- <MkPreferenceContainer k="rememberNoteVisibility">
- <MkSwitch v-model="rememberNoteVisibility">
- <template #label><SearchLabel>{{ i18n.ts.rememberNoteVisibility }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
+ <SearchMarker :keywords="['follow', 'confirm', 'always']">
+ <MkPreferenceContainer k="alwaysConfirmFollow">
+ <MkSwitch v-model="alwaysConfirmFollow">
+ <template #label><SearchLabel>{{ i18n.ts.alwaysConfirmFollow }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <SearchMarker :keywords="['default', 'note', 'visibility']">
- <MkDisableSection :disabled="rememberNoteVisibility">
- <MkFolder>
- <template #label><SearchLabel>{{ i18n.ts.defaultNoteVisibility }}</SearchLabel></template>
- <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template>
- <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template>
- <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template>
- <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template>
+ <SearchMarker :keywords="['highlight', 'sensitive', 'nsfw', 'image', 'photo', 'picture', 'media', 'thumbnail']">
+ <MkPreferenceContainer k="highlightSensitiveMedia">
+ <MkSwitch v-model="highlightSensitiveMedia">
+ <template #label><SearchLabel>{{ i18n.ts.highlightSensitiveMedia }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <div class="_gaps_m">
- <MkPreferenceContainer k="defaultNoteVisibility">
- <MkSelect v-model="defaultNoteVisibility">
- <option value="public">{{ i18n.ts._visibility.public }}</option>
- <option value="home">{{ i18n.ts._visibility.home }}</option>
- <option value="followers">{{ i18n.ts._visibility.followers }}</option>
- <option value="specified">{{ i18n.ts._visibility.specified }}</option>
- </MkSelect>
- </MkPreferenceContainer>
+ <SearchMarker :keywords="['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm']">
+ <MkPreferenceContainer k="confirmWhenRevealingSensitiveMedia">
+ <MkSwitch v-model="confirmWhenRevealingSensitiveMedia">
+ <template #label><SearchLabel>{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+ </div>
- <MkPreferenceContainer k="defaultNoteLocalOnly">
- <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch>
- </MkPreferenceContainer>
+ <SearchMarker :keywords="['emoji', 'style', 'native', 'system', 'fluent', 'twemoji']">
+ <MkPreferenceContainer k="emojiStyle">
+ <div>
+ <MkRadios v-model="emojiStyle">
+ <template #label><SearchLabel>{{ i18n.ts.emojiStyle }}</SearchLabel></template>
+ <option value="native">{{ i18n.ts.native }}</option>
+ <option value="fluentEmoji">Fluent Emoji</option>
+ <option value="twemoji">Twemoji</option>
+ </MkRadios>
+ <div style="margin: 8px 0 0 0; font-size: 1.5em;"><Mfm :key="emojiStyle" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
</div>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['pinned', 'list']">
+ <MkFolder>
+ <template #label><SearchLabel>{{ i18n.ts.pinnedList }}</SearchLabel></template>
+ <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
+ <MkButton v-if="prefer.r.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton>
+ <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton>
</MkFolder>
- </MkDisableSection>
- </SearchMarker>
- </div>
- </FormSection>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['timeline']">
+ <MkFolder :defaultOpen="true">
+ <template #label><SearchLabel>{{ i18n.ts.timeline }}</SearchLabel></template>
+
+ <div class="_gaps_s">
+ <SearchMarker :keywords="['post', 'form', 'timeline']">
+ <MkPreferenceContainer k="showFixedPostForm">
+ <MkSwitch v-model="showFixedPostForm">
+ <template #label><SearchLabel>{{ i18n.ts.showFixedPostForm }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['post', 'form', 'timeline', 'channel']">
+ <MkPreferenceContainer k="showFixedPostFormInChannel">
+ <MkSwitch v-model="showFixedPostFormInChannel">
+ <template #label><SearchLabel>{{ i18n.ts.showFixedPostFormInChannel }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['renote']">
+ <MkPreferenceContainer k="collapseRenotes">
+ <MkSwitch v-model="collapseRenotes">
+ <template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template>
+ <template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['note', 'timeline', 'gap']">
+ <MkPreferenceContainer k="showGapBetweenNotesInTimeline">
+ <MkSwitch v-model="showGapBetweenNotesInTimeline">
+ <template #label><SearchLabel>{{ i18n.ts.showGapBetweenNotesInTimeline }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['load', 'auto', 'more']">
+ <MkPreferenceContainer k="enableInfiniteScroll">
+ <MkSwitch v-model="enableInfiniteScroll">
+ <template #label><SearchLabel>{{ i18n.ts.enableInfiniteScroll }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['disable', 'streaming', 'timeline']">
+ <MkPreferenceContainer k="disableStreamingTimeline">
+ <MkSwitch v-model="disableStreamingTimeline">
+ <template #label><SearchLabel>{{ i18n.ts.disableStreamingTimeline }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
<SearchMarker :keywords="['note']">
- <FormSection>
+ <MkFolder :defaultOpen="true">
<template #label><SearchLabel>{{ i18n.ts.note }}</SearchLabel></template>
<div class="_gaps_m">
<div class="_gaps_s">
- <SearchMarker :keywords="['renote']">
- <MkPreferenceContainer k="collapseRenotes">
- <MkSwitch v-model="collapseRenotes">
- <template #label><SearchLabel>{{ i18n.ts.collapseRenotes }}</SearchLabel></template>
- <template #caption><SearchKeyword>{{ i18n.ts.collapseRenotesDescription }}</SearchKeyword></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
-
<SearchMarker :keywords="['hover', 'show', 'footer', 'action']">
<MkPreferenceContainer k="showNoteActionsOnlyHover">
<MkSwitch v-model="showNoteActionsOnlyHover">
@@ -157,6 +210,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPreferenceContainer>
</SearchMarker>
+ <SearchMarker :keywords="['reaction', 'confirm']">
+ <MkPreferenceContainer k="confirmOnReact">
+ <MkSwitch v-model="confirmOnReact">
+ <template #label><SearchLabel>{{ i18n.ts.confirmOnReact }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
<SearchMarker :keywords="['image', 'photo', 'picture', 'media', 'thumbnail', 'quality', 'raw', 'attachment']">
<MkPreferenceContainer k="loadRawImages">
<MkSwitch v-model="loadRawImages">
@@ -164,93 +225,199 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
+
+ <SearchMarker :keywords="['reaction', 'picker', 'contextmenu', 'open']">
+ <MkPreferenceContainer k="useReactionPickerForContextMenu">
+ <MkSwitch v-model="useReactionPickerForContextMenu">
+ <template #label><SearchLabel>{{ i18n.ts.useReactionPickerForContextMenu }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
</div>
- </div>
- </FormSection>
- </SearchMarker>
- <SearchMarker :keywords="['notification']">
- <FormSection>
- <template #label><SearchLabel>{{ i18n.ts.notifications }}</SearchLabel></template>
+ <SearchMarker :keywords="['reaction', 'size', 'scale', 'display']">
+ <MkPreferenceContainer k="reactionsDisplaySize">
+ <MkRadios v-model="reactionsDisplaySize">
+ <template #label><SearchLabel>{{ i18n.ts.reactionsDisplaySize }}</SearchLabel></template>
+ <option value="small">{{ i18n.ts.small }}</option>
+ <option value="medium">{{ i18n.ts.medium }}</option>
+ <option value="large">{{ i18n.ts.large }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
- <div class="_gaps_m">
- <SearchMarker :keywords="['group']">
- <MkPreferenceContainer k="useGroupedNotifications">
- <MkSwitch v-model="useGroupedNotifications">
- <template #label><SearchLabel>{{ i18n.ts.useGroupedNotifications }}</SearchLabel></template>
+ <SearchMarker :keywords="['reaction', 'size', 'scale', 'display', 'width', 'limit']">
+ <MkPreferenceContainer k="limitWidthOfReaction">
+ <MkSwitch v-model="limitWidthOfReaction">
+ <template #label><SearchLabel>{{ i18n.ts.limitWidthOfReaction }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
+
+ <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'list', 'size', 'height']">
+ <MkPreferenceContainer k="mediaListWithOneImageAppearance">
+ <MkRadios v-model="mediaListWithOneImageAppearance">
+ <template #label><SearchLabel>{{ i18n.ts.mediaListWithOneImageAppearance }}</SearchLabel></template>
+ <option value="expand">{{ i18n.ts.default }}</option>
+ <option value="16_9">{{ i18n.tsx.limitTo({ x: '16:9' }) }}</option>
+ <option value="1_1">{{ i18n.tsx.limitTo({ x: '1:1' }) }}</option>
+ <option value="2_3">{{ i18n.tsx.limitTo({ x: '2:3' }) }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['ticker', 'information', 'label', 'instance', 'server', 'host', 'federation']">
+ <MkPreferenceContainer k="instanceTicker">
+ <MkSelect v-if="instance.federation !== 'none'" v-model="instanceTicker">
+ <template #label><SearchLabel>{{ i18n.ts.instanceTicker }}</SearchLabel></template>
+ <option value="none">{{ i18n.ts._instanceTicker.none }}</option>
+ <option value="remote">{{ i18n.ts._instanceTicker.remote }}</option>
+ <option value="always">{{ i18n.ts._instanceTicker.always }}</option>
+ </MkSelect>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['attachment', 'image', 'photo', 'picture', 'media', 'thumbnail', 'nsfw', 'sensitive', 'display', 'show', 'hide', 'visibility']">
+ <MkPreferenceContainer k="nsfw">
+ <MkSelect v-model="nsfw">
+ <template #label><SearchLabel>{{ i18n.ts.displayOfSensitiveMedia }}</SearchLabel></template>
+ <option value="respect">{{ i18n.ts._displayOfSensitiveMedia.respect }}</option>
+ <option value="ignore">{{ i18n.ts._displayOfSensitiveMedia.ignore }}</option>
+ <option value="force">{{ i18n.ts._displayOfSensitiveMedia.force }}</option>
+ </MkSelect>
+ </MkPreferenceContainer>
+ </SearchMarker>
</div>
- </FormSection>
+ </MkFolder>
</SearchMarker>
- <SearchMarker :keywords="['behavior']">
- <FormSection>
- <template #label><SearchLabel>{{ i18n.ts.behavior }}</SearchLabel></template>
+ <SearchMarker :keywords="['post', 'form']">
+ <MkFolder :defaultOpen="true">
+ <template #label><SearchLabel>{{ i18n.ts.postForm }}</SearchLabel></template>
<div class="_gaps_m">
<div class="_gaps_s">
- <SearchMarker :keywords="['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab']">
- <MkPreferenceContainer k="imageNewTab">
- <MkSwitch v-model="imageNewTab">
- <template #label><SearchLabel>{{ i18n.ts.openImageInNewTab }}</SearchLabel></template>
+ <SearchMarker :keywords="['remember', 'keep', 'note', 'cw']">
+ <MkPreferenceContainer k="keepCw">
+ <MkSwitch v-model="keepCw">
+ <template #label><SearchLabel>{{ i18n.ts.keepCw }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
- <SearchMarker :keywords="['reaction', 'picker', 'contextmenu', 'open']">
- <MkPreferenceContainer k="useReactionPickerForContextMenu">
- <MkSwitch v-model="useReactionPickerForContextMenu">
- <template #label><SearchLabel>{{ i18n.ts.useReactionPickerForContextMenu }}</SearchLabel></template>
+ <SearchMarker :keywords="['remember', 'keep', 'note', 'visibility']">
+ <MkPreferenceContainer k="rememberNoteVisibility">
+ <MkSwitch v-model="rememberNoteVisibility">
+ <template #label><SearchLabel>{{ i18n.ts.rememberNoteVisibility }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
- <SearchMarker :keywords="['load', 'auto', 'more']">
- <MkPreferenceContainer k="enableInfiniteScroll">
- <MkSwitch v-model="enableInfiniteScroll">
- <template #label><SearchLabel>{{ i18n.ts.enableInfiniteScroll }}</SearchLabel></template>
+ <SearchMarker :keywords="['mfm', 'enable', 'show', 'advanced', 'picker', 'form', 'function', 'fn']">
+ <MkPreferenceContainer k="enableQuickAddMfmFunction">
+ <MkSwitch v-model="enableQuickAddMfmFunction">
+ <template #label><SearchLabel>{{ i18n.ts.enableQuickAddMfmFunction }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
+ </div>
- <SearchMarker :keywords="['disable', 'streaming', 'timeline']">
- <MkPreferenceContainer k="disableStreamingTimeline">
- <MkSwitch v-model="disableStreamingTimeline">
- <template #label><SearchLabel>{{ i18n.ts.disableStreamingTimeline }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
+ <SearchMarker :keywords="['default', 'note', 'visibility']">
+ <MkDisableSection :disabled="rememberNoteVisibility">
+ <MkFolder>
+ <template #label><SearchLabel>{{ i18n.ts.defaultNoteVisibility }}</SearchLabel></template>
+ <template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template>
+ <template v-else-if="defaultNoteVisibility === 'home'" #suffix>{{ i18n.ts._visibility.home }}</template>
+ <template v-else-if="defaultNoteVisibility === 'followers'" #suffix>{{ i18n.ts._visibility.followers }}</template>
+ <template v-else-if="defaultNoteVisibility === 'specified'" #suffix>{{ i18n.ts._visibility.specified }}</template>
- <SearchMarker :keywords="['follow', 'confirm', 'always']">
- <MkPreferenceContainer k="alwaysConfirmFollow">
- <MkSwitch v-model="alwaysConfirmFollow">
- <template #label><SearchLabel>{{ i18n.ts.alwaysConfirmFollow }}</SearchLabel></template>
- </MkSwitch>
- </MkPreferenceContainer>
- </SearchMarker>
+ <div class="_gaps_m">
+ <MkPreferenceContainer k="defaultNoteVisibility">
+ <MkSelect v-model="defaultNoteVisibility">
+ <option value="public">{{ i18n.ts._visibility.public }}</option>
+ <option value="home">{{ i18n.ts._visibility.home }}</option>
+ <option value="followers">{{ i18n.ts._visibility.followers }}</option>
+ <option value="specified">{{ i18n.ts._visibility.specified }}</option>
+ </MkSelect>
+ </MkPreferenceContainer>
- <SearchMarker :keywords="['sensitive', 'nsfw', 'media', 'image', 'photo', 'picture', 'attachment', 'confirm']">
- <MkPreferenceContainer k="confirmWhenRevealingSensitiveMedia">
- <MkSwitch v-model="confirmWhenRevealingSensitiveMedia">
- <template #label><SearchLabel>{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}</SearchLabel></template>
+ <MkPreferenceContainer k="defaultNoteLocalOnly">
+ <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch>
+ </MkPreferenceContainer>
+ </div>
+ </MkFolder>
+ </MkDisableSection>
+ </SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['notification']">
+ <MkFolder :defaultOpen="true">
+ <template #label><SearchLabel>{{ i18n.ts.notifications }}</SearchLabel></template>
+
+ <div class="_gaps_m">
+ <SearchMarker :keywords="['group']">
+ <MkPreferenceContainer k="useGroupedNotifications">
+ <MkSwitch v-model="useGroupedNotifications">
+ <template #label><SearchLabel>{{ i18n.ts.useGroupedNotifications }}</SearchLabel></template>
+ </MkSwitch>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['position']">
+ <MkPreferenceContainer k="notificationPosition">
+ <MkRadios v-model="notificationPosition">
+ <template #label><SearchLabel>{{ i18n.ts.position }}</SearchLabel></template>
+ <option value="leftTop"><i class="ti ti-align-box-left-top"></i> {{ i18n.ts.leftTop }}</option>
+ <option value="rightTop"><i class="ti ti-align-box-right-top"></i> {{ i18n.ts.rightTop }}</option>
+ <option value="leftBottom"><i class="ti ti-align-box-left-bottom"></i> {{ i18n.ts.leftBottom }}</option>
+ <option value="rightBottom"><i class="ti ti-align-box-right-bottom"></i> {{ i18n.ts.rightBottom }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['stack', 'axis', 'direction']">
+ <MkPreferenceContainer k="notificationStackAxis">
+ <MkRadios v-model="notificationStackAxis">
+ <template #label><SearchLabel>{{ i18n.ts.stackAxis }}</SearchLabel></template>
+ <option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
+ <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
+ </MkRadios>
+ </MkPreferenceContainer>
+ </SearchMarker>
+
+ <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['other']">
+ <MkFolder :defaultOpen="true">
+ <template #label><SearchLabel>{{ i18n.ts.other }}</SearchLabel></template>
+
+ <div class="_gaps_m">
+ <div class="_gaps_s">
+ <SearchMarker :keywords="['avatar', 'icon', 'square']">
+ <MkPreferenceContainer k="squareAvatars">
+ <MkSwitch v-model="squareAvatars">
+ <template #label><SearchLabel>{{ i18n.ts.squareAvatars }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
- <SearchMarker :keywords="['reaction', 'confirm']">
- <MkPreferenceContainer k="confirmOnReact">
- <MkSwitch v-model="confirmOnReact">
- <template #label><SearchLabel>{{ i18n.ts.confirmOnReact }}</SearchLabel></template>
+ <SearchMarker :keywords="['effect', 'show']">
+ <MkPreferenceContainer k="enableSeasonalScreenEffect">
+ <MkSwitch v-model="enableSeasonalScreenEffect">
+ <template #label><SearchLabel>{{ i18n.ts.seasonalScreenEffect }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
- <SearchMarker :keywords="['remember', 'keep', 'note', 'cw']">
- <MkPreferenceContainer k="keepCw">
- <MkSwitch v-model="keepCw">
- <template #label><SearchLabel>{{ i18n.ts.keepCw }}</SearchLabel></template>
+ <SearchMarker :keywords="['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab']">
+ <MkPreferenceContainer k="imageNewTab">
+ <MkSwitch v-model="imageNewTab">
+ <template #label><SearchLabel>{{ i18n.ts.openImageInNewTab }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
@@ -276,47 +443,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPreferenceContainer>
</SearchMarker>
- <SearchMarker :label="i18n.ts.dataSaver" :keywords="['datasaver']">
- <MkFolder>
- <template #label><SearchLabel>{{ i18n.ts.dataSaver }}</SearchLabel></template>
-
- <div class="_gaps_m">
- <MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
-
- <div class="_buttons">
- <MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
- <MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
- </div>
- <div class="_gaps_m">
- <MkSwitch v-model="dataSaver.media">
- {{ i18n.ts._dataSaver._media.title }}
- <template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
- </MkSwitch>
- <MkSwitch v-model="dataSaver.avatar">
- {{ i18n.ts._dataSaver._avatar.title }}
- <template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
- </MkSwitch>
- <MkSwitch v-model="dataSaver.urlPreview">
- {{ i18n.ts._dataSaver._urlPreview.title }}
- <template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
- </MkSwitch>
- <MkSwitch v-model="dataSaver.code">
- {{ i18n.ts._dataSaver._code.title }}
- <template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
- </MkSwitch>
- </div>
- </div>
- </MkFolder>
- </SearchMarker>
- </div>
- </FormSection>
- </SearchMarker>
-
- <SearchMarker>
- <FormSection>
- <template #label><SearchLabel>{{ i18n.ts.other }}</SearchLabel></template>
-
- <div class="_gaps">
<SearchMarker :keywords="['ad', 'show']">
<MkPreferenceContainer k="forceShowAds">
<MkSwitch v-model="forceShowAds">
@@ -347,18 +473,47 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</MkFolder>
</SearchMarker>
+ </div>
+ </MkFolder>
+ </SearchMarker>
+
+ <SearchMarker :keywords="['datasaver']">
+ <MkFolder>
+ <template #label><SearchLabel>{{ i18n.ts.dataSaver }}</SearchLabel></template>
+
+ <div class="_gaps_m">
+ <MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
- <FormLink to="/settings/navbar">{{ i18n.ts.navbar }}</FormLink>
- <FormLink to="/settings/statusbar">{{ i18n.ts.statusbar }}</FormLink>
+ <div class="_buttons">
+ <MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
+ <MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
+ </div>
+ <div class="_gaps_m">
+ <MkSwitch v-model="dataSaver.media">
+ {{ i18n.ts._dataSaver._media.title }}
+ <template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="dataSaver.avatar">
+ {{ i18n.ts._dataSaver._avatar.title }}
+ <template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="dataSaver.urlPreview">
+ {{ i18n.ts._dataSaver._urlPreview.title }}
+ <template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
+ </MkSwitch>
+ <MkSwitch v-model="dataSaver.code">
+ {{ i18n.ts._dataSaver._code.title }}
+ <template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
+ </MkSwitch>
+ </div>
</div>
- </FormSection>
+ </MkFolder>
</SearchMarker>
- <FormSection>
- <div class="_gaps">
- <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink>
- </div>
- </FormSection>
+ <FormLink to="/settings/navbar">{{ i18n.ts.navbar }}</FormLink>
+ <FormLink to="/settings/statusbar">{{ i18n.ts.statusbar }}</FormLink>
+ <FormLink to="/settings/deck">{{ i18n.ts.deck }}</FormLink>
+ <FormLink to="/settings/custom-css"><template #icon><i class="ti ti-code"></i></template>{{ i18n.ts.customCss }}</FormLink>
</div>
</SearchMarker>
</template>
@@ -366,6 +521,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { langs } from '@@/js/config.js';
+import * as Misskey from 'misskey-js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkRadios from '@/components/MkRadios.vue';
@@ -386,6 +542,9 @@ import { miLocalStorage } from '@/local-storage.js';
import { prefer } from '@/preferences.js';
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
+import { globalEvents } from '@/events.js';
+import { claimAchievement } from '@/utility/achievements.js';
+import { instance } from '@/instance.js';
const lang = ref(miLocalStorage.getItem('lang'));
const dataSaver = ref(prefer.s.dataSaver);
@@ -413,10 +572,24 @@ const useGroupedNotifications = prefer.model('useGroupedNotifications');
const alwaysConfirmFollow = prefer.model('alwaysConfirmFollow');
const confirmWhenRevealingSensitiveMedia = prefer.model('confirmWhenRevealingSensitiveMedia');
const confirmOnReact = prefer.model('confirmOnReact');
-const contextMenu = prefer.model('contextMenu');
const defaultNoteVisibility = prefer.model('defaultNoteVisibility');
const defaultNoteLocalOnly = prefer.model('defaultNoteLocalOnly');
const rememberNoteVisibility = prefer.model('rememberNoteVisibility');
+const showGapBetweenNotesInTimeline = prefer.model('showGapBetweenNotesInTimeline');
+const notificationPosition = prefer.model('notificationPosition');
+const notificationStackAxis = prefer.model('notificationStackAxis');
+const instanceTicker = prefer.model('instanceTicker');
+const highlightSensitiveMedia = prefer.model('highlightSensitiveMedia');
+const mediaListWithOneImageAppearance = prefer.model('mediaListWithOneImageAppearance');
+const reactionsDisplaySize = prefer.model('reactionsDisplaySize');
+const limitWidthOfReaction = prefer.model('limitWidthOfReaction');
+const squareAvatars = prefer.model('squareAvatars');
+const enableSeasonalScreenEffect = prefer.model('enableSeasonalScreenEffect');
+const showAvatarDecorations = prefer.model('showAvatarDecorations');
+const nsfw = prefer.model('nsfw');
+const emojiStyle = prefer.model('emojiStyle');
+const useBlurEffectForModal = prefer.model('useBlurEffectForModal');
+const useBlurEffect = prefer.model('useBlurEffect');
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
@@ -433,7 +606,17 @@ watch([
disableStreamingTimeline,
alwaysConfirmFollow,
confirmWhenRevealingSensitiveMedia,
- contextMenu,
+ showGapBetweenNotesInTimeline,
+ mediaListWithOneImageAppearance,
+ reactionsDisplaySize,
+ limitWidthOfReaction,
+ mediaListWithOneImageAppearance,
+ reactionsDisplaySize,
+ limitWidthOfReaction,
+ instanceTicker,
+ squareAvatars,
+ highlightSensitiveMedia,
+ enableSeasonalScreenEffect,
], async () => {
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
@@ -522,6 +705,33 @@ watch(dataSaver, (to) => {
deep: true,
});
+let smashCount = 0;
+let smashTimer: number | null = null;
+
+function testNotification(): void {
+ const notification: Misskey.entities.Notification = {
+ id: Math.random().toString(),
+ createdAt: new Date().toUTCString(),
+ isRead: false,
+ type: 'test',
+ };
+
+ globalEvents.emit('clientNotification', notification);
+
+ // セルフ通知破壊 実績関連
+ smashCount++;
+ if (smashCount >= 10) {
+ claimAchievement('smashTestNotificationButton');
+ smashCount = 0;
+ }
+ if (smashTimer) {
+ clearTimeout(smashTimer);
+ }
+ smashTimer = window.setTimeout(() => {
+ smashCount = 0;
+ }, 300);
+}
+
const headerActions = computed(() => []);
const headerTabs = computed(() => []);