Compare commits

..

9 Commits

Author SHA1 Message Date
syuilo 8e9fb4df6e New translations ja-jp.yml (English) 2025-03-30 04:38:27 +09:00
syuilo b905c98e54 New translations ja-jp.yml (Chinese Simplified) 2025-03-30 02:19:27 +09:00
syuilo 6cbacf7ed5 New translations ja-jp.yml (German) 2025-03-30 02:19:25 +09:00
syuilo 6e193d988a New translations ja-jp.yml (German) 2025-03-30 01:04:54 +09:00
syuilo 22995e81e3 New translations ja-jp.yml (German) 2025-03-29 23:40:29 +09:00
syuilo f57e2f76cb New translations ja-jp.yml (German) 2025-03-29 22:39:21 +09:00
syuilo 97ad0daf58 New translations ja-jp.yml (Chinese Traditional) 2025-03-29 22:39:20 +09:00
syuilo e8f695c8d9 New translations ja-jp.yml (Catalan) 2025-03-29 22:39:18 +09:00
syuilo 3e2be115b0 New translations ja-jp.yml (German) 2025-03-29 21:22:01 +09:00
38 changed files with 1204 additions and 498 deletions

View File

@ -33,7 +33,6 @@
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
- 設定情報の移行は自動で行われますが、何らかの理由で失敗した場合、設定→その他→旧設定情報を移行 で再試行可能です
- Feat: 画面を重ねて表示するオプションを実装(実験的)
- 設定 → その他 → 実験的機能 → Enable stacking router view
- Enhance: プラグインの管理が強化されました
@ -48,12 +47,7 @@
- 文字数カウントを復活
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
- Enhance: 全体的なブラッシュアップ
- Enhance 全体的なパフォーマンス向上
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
- デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置することである程度クラシックUIを再現できます
- また、デッキでナビゲーションバーを上部に表示するオプションを実装予定です
### Server
- Enhance 全体的なパフォーマンス向上

View File

@ -1128,7 +1128,7 @@ pleaseAgreeAllToContinue: "Has d'acceptar tots els camps de dalt per poder conti
continue: "Continuar"
preservedUsernames: "Noms d'usuaris reservats"
preservedUsernamesDescription: "Llistat de noms d'usuaris que no es poden fer servir separats per salts de linia. Aquests noms d'usuaris no estaran disponibles quan es creï un compte d'usuari normal, però els administradors els poden fer servir per crear comptes manualment. Per altre banda els comptes ja creats amb aquests noms d'usuari no es veure'n afectats."
createNoteFromTheFile: "Escriu una nota incloent aquest fitxer"
createNoteFromTheFile: "Compon una nota des d'aquest fitxer"
archive: "Arxiu"
archived: "Arxivat"
unarchive: "Desarxivar"

View File

@ -1,6 +1,6 @@
{
"name": "misskey",
"version": "2025.3.2-beta.18",
"version": "2025.3.2-beta.16",
"codename": "nasubi",
"repository": {
"type": "git",

View File

@ -34,7 +34,7 @@
"typescript": "5.8.2",
"uuid": "11.1.0",
"json5": "2.2.3",
"vite": "6.2.3",
"vite": "6.2.2",
"vue": "3.5.13"
},
"devDependencies": {

View File

@ -74,7 +74,7 @@
"typescript": "5.8.2",
"uuid": "11.1.0",
"v-code-diff": "1.13.1",
"vite": "6.2.3",
"vite": "6.2.2",
"vue": "3.5.13",
"vuedraggable": "next",
"wanakana": "5.3.1"

View File

@ -54,6 +54,9 @@ export async function mainBoot() {
case 'visitor':
rootComponent = await import('@/ui/visitor.vue').then(x => x.default);
break;
case 'classic':
rootComponent = await import('@/ui/classic.vue').then(x => x.default);
break;
default:
rootComponent = await import('@/ui/universal.vue').then(x => x.default);
break;

View File

@ -658,6 +658,7 @@ function emitUpdReaction(emoji: string, delta: number) {
<style lang="scss" module>
.root {
position: relative;
transition: box-shadow 0.1s ease;
font-size: 1.05em;
overflow: clip;
contain: content;
@ -723,8 +724,6 @@ function emitUpdReaction(emoji: string, delta: number) {
}
.skipRender {
// TODO: TransitionGroupnote
// TransitionskipRender
content-visibility: auto;
contain-intrinsic-size: 0 150px;
}

View File

@ -13,22 +13,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #default="{ items: notes }">
<component
:is="prefer.s.animation ? TransitionGroup : 'div'" :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]"
:enterActiveClass="$style.transition_x_enterActive"
:leaveActiveClass="$style.transition_x_leaveActive"
:enterFromClass="$style.transition_x_enterFrom"
:leaveToClass="$style.transition_x_leaveTo"
:moveClass=" $style.transition_x_move"
tag="div"
>
<div :class="[$style.root, { [$style.noGap]: noGap, '_gaps': !noGap }]">
<template v-for="(note, i) in notes" :key="note.id">
<MkNote :class="$style.note" :note="note" :withHardMute="true"/>
<div v-if="note._shouldInsertAd_" :class="$style.ad">
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
</div>
</template>
</component>
</div>
</template>
</MkPagination>
</template>
@ -56,20 +48,6 @@ defineExpose({
</script>
<style lang="scss" module>
.transition_x_move,
.transition_x_enterActive,
.transition_x_leaveActive {
transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important;
}
.transition_x_enterFrom,
.transition_x_leaveTo {
opacity: 0;
transform: translateY(-50%);
}
.transition_x_leaveActive {
position: absolute;
}
.root {
container-type: inline-size;
@ -97,8 +75,4 @@ defineExpose({
}
}
}
.ad:empty {
display: none;
}
</style>

View File

@ -14,27 +14,19 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<template #default="{ items: notifications }">
<component
:is="prefer.s.animation ? TransitionGroup : 'div'" :class="[$style.notifications]"
:enterActiveClass="$style.transition_x_enterActive"
:leaveActiveClass="$style.transition_x_leaveActive"
:enterFromClass="$style.transition_x_enterFrom"
:leaveToClass="$style.transition_x_leaveTo"
:moveClass=" $style.transition_x_move"
tag="div"
>
<div :class="$style.notifications">
<template v-for="(notification, i) in notifications" :key="notification.id">
<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :class="$style.item" :note="notification.note" :withHardMute="true"/>
<XNotification v-else :class="$style.item" :notification="notification" :withTime="true" :full="true"/>
</template>
</component>
</div>
</template>
</MkPagination>
</MkPullToRefresh>
</template>
<script lang="ts" setup>
import { onUnmounted, onMounted, computed, useTemplateRef, TransitionGroup } from 'vue';
import { onUnmounted, onDeactivated, onMounted, computed, useTemplateRef, onActivated } from 'vue';
import * as Misskey from 'misskey-js';
import type { notificationTypes } from '@@/js/const.js';
import MkPagination from '@/components/MkPagination.vue';
@ -103,20 +95,6 @@ defineExpose({
</script>
<style lang="scss" module>
.transition_x_move,
.transition_x_enterActive,
.transition_x_leaveActive {
transition: opacity 0.3s cubic-bezier(0,.5,.5,1), transform 0.3s cubic-bezier(0,.5,.5,1) !important;
}
.transition_x_enterFrom,
.transition_x_leaveTo {
opacity: 0;
transform: translateY(-50%);
}
.transition_x_leaveActive {
position: absolute;
}
.notifications {
container-type: inline-size;
background: var(--MI_THEME-panel);

View File

@ -1314,7 +1314,7 @@ html[data-color-scheme=light] .preview {
padding: 0 24px;
margin: 0;
width: 100%;
font-size: 110%;
font-size: 16px;
border: none;
border-radius: 0;
background: transparent;

View File

@ -159,13 +159,12 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
const onDrag = (ev: MouseEvent | TouchEvent) => {
ev.preventDefault();
let beforeValue = finalValue.value;
const containerRect = containerEl.value!.getBoundingClientRect();
const pointerX = 'touches' in ev && ev.touches.length > 0 ? ev.touches[0].clientX : 'clientX' in ev ? ev.clientX : 0;
const pointerPositionOnContainer = pointerX - (containerRect.left + (thumbWidth / 2));
rawValue.value = Math.min(1, Math.max(0, pointerPositionOnContainer / (containerEl.value!.offsetWidth - thumbWidth)));
if (props.continuousUpdate && beforeValue !== finalValue.value) {
if (props.continuousUpdate) {
emit('update:modelValue', finalValue.value);
}
};

View File

@ -4,24 +4,22 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<component
:is="prefer.s.animation ? TransitionGroup : 'div'"
:enterActiveClass="$style.transition_x_enterActive"
:leaveActiveClass="$style.transition_x_leaveActive"
:enterFromClass="$style.transition_x_enterFrom"
:leaveToClass="$style.transition_x_leaveTo"
:moveClass="$style.transition_x_move"
<TransitionGroup
:enterActiveClass="prefer.s.animation ? $style.transition_x_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_x_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_x_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_x_leaveTo : ''"
:moveClass="prefer.s.animation ? $style.transition_x_move : ''"
tag="div" :class="$style.root"
>
<XReaction v-for="[reaction, count] in reactions" :key="reaction" :reaction="reaction" :count="count" :isInitial="initialReactions.has(reaction)" :note="note" @reactionToggled="onMockToggleReaction"/>
<slot v-if="hasMoreReactions" name="more"/>
</component>
</TransitionGroup>
</template>
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { inject, watch, ref } from 'vue';
import { TransitionGroup } from 'vue';
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';

View File

@ -9,8 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:leaveActiveClass="prefer.s.animation ? $style.transition_tooltip_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_tooltip_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_tooltip_leaveTo : ''"
appear :css="prefer.s.animation"
@afterLeave="emit('closed')"
appear @afterLeave="emit('closed')"
>
<div v-show="showing" ref="el" :class="$style.root" class="_acrylic _shadow" :style="{ zIndex, maxWidth: maxWidth + 'px' }">
<slot>

View File

@ -36,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
</div>
</div>
<div v-else></div>
</template>
<script lang="ts" setup>
@ -52,7 +53,7 @@ import { prefer } from '@/preferences.js';
type Ad = (typeof instance)['ads'][number];
const props = defineProps<{
preferForms: string[];
prefer: string[];
specify?: Ad;
}>();
@ -71,7 +72,7 @@ const choseAd = (): Ad | null => {
ratio: 0,
} : ad);
let ads = allAds.filter(ad => props.preferForms.includes(ad.place));
let ads = allAds.filter(ad => props.prefer.includes(ad.place));
if (ads.length === 0) {
ads = allAds.filter(ad => ad.place === 'square');

View File

@ -68,7 +68,7 @@ const emit = defineEmits<{
(ev: 'update:tab', key: string);
}>();
//const viewId = inject(DI.viewId);
const viewId = inject(DI.viewId);
const injectedPageMetadata = inject(DI.pageMetadata);
const pageMetadata = computed(() => props.overridePageMetadata ?? injectedPageMetadata.value);

View File

@ -145,6 +145,13 @@ export const navbarItemDef = reactive({
miLocalStorage.setItem('ui', 'deck');
unisonReload();
},
}, {
text: i18n.ts.classic,
active: ui === 'classic',
action: () => {
miLocalStorage.setItem('ui', 'classic');
unisonReload();
},
}], ev.currentTarget ?? ev.target);
},
},

View File

@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkInfo v-else>{{ i18n.ts._chat.chatNotAvailableForThisAccountOrServer }}</MkInfo>
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkInput
v-model="searchQuery"

View File

@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
<MkA v-if="$i && $i.id === flash.userId" :to="`/play/${flash.id}/edit`" style="color: var(--MI_THEME-accent);">{{ i18n.ts._play.editThisPage }}</MkA>
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
</div>
<MkError v-else-if="error" @retry="fetchFlash()"/>
<MkLoading v-else/>

View File

@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFollowButton v-if="!$i || $i.id != post.user.id" v-model:user="post.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/>
</div>
</div>
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkContainer :max-height="300" :foldable="true" class="other">
<template #icon><i class="ti ti-clock"></i></template>
<template #header>{{ i18n.ts.recentPosts }}</template>

View File

@ -80,7 +80,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="page.createdAt != page.updatedAt"><i class="ti ti-clock-edit"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
</div>
</div>
<MkAd :preferForms="['horizontal', 'horizontal-big']"/>
<MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkContainer :max-height="300" :foldable="true" class="other">
<template #icon><i class="ti ti-clock"></i></template>
<template #header>{{ i18n.ts.recentPosts }}</template>

View File

@ -0,0 +1,173 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<SearchMarker path="/settings/accessibility" :label="i18n.ts.accessibility" :keywords="['accessibility']" icon="ti ti-accessible">
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff">
<SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword>
</MkFeatureBanner>
<div class="_gaps_s">
<SearchMarker :keywords="['animation', 'motion', 'reduce']">
<MkPreferenceContainer k="animation">
<MkSwitch v-model="reduceAnimation">
<template #label><SearchLabel>{{ i18n.ts.reduceUiAnimation }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif']">
<MkPreferenceContainer k="disableShowingAnimatedImages">
<MkSwitch v-model="disableShowingAnimatedImages">
<template #label><SearchLabel>{{ i18n.ts.disableShowingAnimatedImages }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['mfm', 'enable', 'show', 'animated']">
<MkPreferenceContainer k="animatedMfm">
<MkSwitch v-model="animatedMfm">
<template #label><SearchLabel>{{ i18n.ts.enableAnimatedMfm }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['swipe', 'horizontal', 'tab']">
<MkPreferenceContainer k="enableHorizontalSwipe">
<MkSwitch v-model="enableHorizontalSwipe">
<template #label><SearchLabel>{{ i18n.ts.enableHorizontalSwipe }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['keep', 'screen', 'display', 'on']">
<MkPreferenceContainer k="keepScreenOn">
<MkSwitch v-model="keepScreenOn">
<template #label><SearchLabel>{{ i18n.ts.keepScreenOn }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['native', 'system', 'video', 'audio', 'player', 'media']">
<MkPreferenceContainer k="useNativeUiForVideoAudioPlayer">
<MkSwitch v-model="useNativeUiForVideoAudioPlayer">
<template #label><SearchLabel>{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['text', 'selectable']">
<MkPreferenceContainer k="makeEveryTextElementsSelectable">
<MkSwitch v-model="makeEveryTextElementsSelectable">
<template #label><SearchLabel>{{ i18n.ts._settings.makeEveryTextElementsSelectable }}</SearchLabel></template>
<template #caption>{{ i18n.ts._settings.makeEveryTextElementsSelectable_description }}</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="['contextmenu', 'system', 'native']">
<MkPreferenceContainer k="contextMenu">
<MkSelect v-model="contextMenu">
<template #label><SearchLabel>{{ i18n.ts._contextMenu.title }}</SearchLabel></template>
<option value="app">{{ i18n.ts._contextMenu.app }}</option>
<option value="appWithShift">{{ i18n.ts._contextMenu.appWithShift }}</option>
<option value="native">{{ i18n.ts._contextMenu.native }}</option>
</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>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue';
import { prefer } from '@/preferences.js';
import { reloadAsk } from '@/utility/reload-ask.js';
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');
const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages');
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 makeEveryTextElementsSelectable = prefer.model('makeEveryTextElementsSelectable');
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,
makeEveryTextElementsSelectable,
], async () => {
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
definePage(() => ({
title: i18n.ts.accessibility,
icon: 'ti ti-accessible',
}));
</script>

View File

@ -122,6 +122,11 @@ const menuDef = computed<SuperMenuDef[]>(() => [{
text: i18n.ts.sounds,
to: '/settings/sounds',
active: currentPage.value?.route.name === 'sounds',
}, {
icon: 'ti ti-accessible',
text: i18n.ts.accessibility,
to: '/settings/accessibility',
active: currentPage.value?.route.name === 'accessibility',
}, {
icon: 'ti ti-plug',
text: i18n.ts.plugins,

View File

@ -40,6 +40,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>{{ i18n.ts.display }}</template>
<option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option>
<option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option>
<option value="top">{{ i18n.ts._menuDisplay.top }}</option>
<!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
</MkRadios>
<SearchMarker :keywords="['navbar', 'sidebar', 'toggle', 'button', 'sub']">

View File

@ -104,6 +104,15 @@ SPDX-License-Identifier: AGPL-3.0-only
</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>
</SearchMarker>
</div>
</MkFolder>
</SearchMarker>
@ -155,15 +164,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</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>
</div>
<hr>
@ -418,116 +418,6 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkFolder>
</SearchMarker>
<SearchMarker :keywords="['accessibility']">
<MkFolder>
<template #label><SearchLabel>{{ i18n.ts.accessibility }}</SearchLabel></template>
<template #icon><i class="ti ti-accessible"></i></template>
<div class="_gaps_m">
<MkFeatureBanner icon="/client-assets/mens_room_3d.png" color="#0011ff">
<SearchKeyword>{{ i18n.ts._settings.accessibilityBanner }}</SearchKeyword>
</MkFeatureBanner>
<div class="_gaps_s">
<SearchMarker :keywords="['animation', 'motion', 'reduce']">
<MkPreferenceContainer k="animation">
<MkSwitch v-model="reduceAnimation">
<template #label><SearchLabel>{{ i18n.ts.reduceUiAnimation }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif']">
<MkPreferenceContainer k="disableShowingAnimatedImages">
<MkSwitch v-model="disableShowingAnimatedImages">
<template #label><SearchLabel>{{ i18n.ts.disableShowingAnimatedImages }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['mfm', 'enable', 'show', 'animated']">
<MkPreferenceContainer k="animatedMfm">
<MkSwitch v-model="animatedMfm">
<template #label><SearchLabel>{{ i18n.ts.enableAnimatedMfm }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['swipe', 'horizontal', 'tab']">
<MkPreferenceContainer k="enableHorizontalSwipe">
<MkSwitch v-model="enableHorizontalSwipe">
<template #label><SearchLabel>{{ i18n.ts.enableHorizontalSwipe }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['keep', 'screen', 'display', 'on']">
<MkPreferenceContainer k="keepScreenOn">
<MkSwitch v-model="keepScreenOn">
<template #label><SearchLabel>{{ i18n.ts.keepScreenOn }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['native', 'system', 'video', 'audio', 'player', 'media']">
<MkPreferenceContainer k="useNativeUiForVideoAudioPlayer">
<MkSwitch v-model="useNativeUiForVideoAudioPlayer">
<template #label><SearchLabel>{{ i18n.ts.useNativeUIForVideoAudioPlayer }}</SearchLabel></template>
</MkSwitch>
</MkPreferenceContainer>
</SearchMarker>
<SearchMarker :keywords="['text', 'selectable']">
<MkPreferenceContainer k="makeEveryTextElementsSelectable">
<MkSwitch v-model="makeEveryTextElementsSelectable">
<template #label><SearchLabel>{{ i18n.ts._settings.makeEveryTextElementsSelectable }}</SearchLabel></template>
<template #caption>{{ i18n.ts._settings.makeEveryTextElementsSelectable_description }}</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="['contextmenu', 'system', 'native']">
<MkPreferenceContainer k="contextMenu">
<MkSelect v-model="contextMenu">
<template #label><SearchLabel>{{ i18n.ts._contextMenu.title }}</SearchLabel></template>
<option value="app">{{ i18n.ts._contextMenu.app }}</option>
<option value="appWithShift">{{ i18n.ts._contextMenu.appWithShift }}</option>
<option value="native">{{ i18n.ts._contextMenu.native }}</option>
</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>
</MkFolder>
</SearchMarker>
<SearchMarker :keywords="['performance']">
<MkFolder>
<template #label><SearchLabel>{{ i18n.ts.performance }}</SearchLabel></template>
@ -781,18 +671,6 @@ const defaultFollowWithReplies = prefer.model('defaultFollowWithReplies');
const chatShowSenderName = prefer.model('chat.showSenderName');
const chatSendOnEnter = prefer.model('chat.sendOnEnter');
const useStickyIcons = prefer.model('useStickyIcons');
const reduceAnimation = prefer.model('animation', v => !v, v => !v);
const animatedMfm = prefer.model('animatedMfm');
const disableShowingAnimatedImages = prefer.model('disableShowingAnimatedImages');
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 makeEveryTextElementsSelectable = prefer.model('makeEveryTextElementsSelectable');
const fontSize = ref(miLocalStorage.getItem('fontSize'));
const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
@ -800,22 +678,6 @@ watch(lang, () => {
miLocalStorage.removeItem('localeVersion');
});
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([
hemisphere,
lang,
@ -838,11 +700,6 @@ watch([
enableSeasonalScreenEffect,
chatShowSenderName,
useStickyIcons,
keepScreenOn,
contextMenu,
fontSize,
useSystemFont,
makeEveryTextElementsSelectable,
], async () => {
await reloadAsk({ reason: i18n.ts.reloadToApplySetting, unison: true });
});

View File

@ -128,6 +128,10 @@ export const ROUTE_DEF = [{
path: '/sounds',
name: 'sounds',
component: page(() => import('@/pages/settings/sounds.vue')),
}, {
path: '/accessibility',
name: 'accessibility',
component: page(() => import('@/pages/settings/accessibility.vue')),
}, {
path: '/plugin/install',
name: 'plugin',

View File

@ -42,5 +42,5 @@ mainRouter.addListener('change', ctx => {
mainRouter.init();
export function useRouter(): Router {
return inject(DI.router, null) ?? mainRouter;
return inject(DI.router) ?? mainRouter;
}

View File

@ -28,6 +28,9 @@
}
html {
background-color: var(--MI_THEME-bg);
color: var(--MI_THEME-fg);
accent-color: var(--MI_THEME-accent);
overflow: auto;
overflow-wrap: break-word;
font-family: 'Hiragino Maru Gothic Pro', "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif;
@ -36,11 +39,6 @@ html {
text-size-adjust: 100%;
tab-size: 2;
-webkit-text-size-adjust: 100%;
touch-action: manipulation;
scroll-behavior: smooth;
background-color: var(--MI_THEME-bg);
color: var(--MI_THEME-fg);
accent-color: var(--MI_THEME-accent);
&, * {
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
@ -96,19 +94,11 @@ html._themeChanging_ {
}
}
html,
body,
#misskey_app {
html, body {
touch-action: manipulation;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overscroll-behavior: none;
}
body {
/* NOTE: htmlにも overflow: clip を設定したいところだが、設定すると何故か少なくともChromeで html が main thread scrolling になりパフォーマンスが(多分)落ちる */
overflow: clip;
scroll-behavior: smooth;
}
a {

View File

@ -14,8 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XUpload v-if="uploads.length > 0"/>
<component
:is="prefer.s.animation ? TransitionGroup : 'div'"
<TransitionGroup
tag="div"
:class="[$style.notifications, {
[$style.notificationsPosition_leftTop]: prefer.s.notificationPosition === 'leftTop',
@ -25,16 +24,16 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.notificationsStackAxis_vertical]: prefer.s.notificationStackAxis === 'vertical',
[$style.notificationsStackAxis_horizontal]: prefer.s.notificationStackAxis === 'horizontal',
}]"
:moveClass="$style.transition_notification_move"
:enterActiveClass="$style.transition_notification_enterActive"
:leaveActiveClass="$style.transition_notification_leaveActive"
:enterFromClass="$style.transition_notification_enterFrom"
:leaveToClass="$style.transition_notification_leaveTo"
:moveClass="prefer.s.animation ? $style.transition_notification_move : ''"
:enterActiveClass="prefer.s.animation ? $style.transition_notification_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_notification_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_notification_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_notification_leaveTo : ''"
>
<div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
<XNotification :notification="notification"/>
</div>
</component>
</TransitionGroup>
<XStreamIndicator/>
@ -46,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { defineAsyncComponent, ref, TransitionGroup } from 'vue';
import { defineAsyncComponent, ref } from 'vue';
import * as Misskey from 'misskey-js';
import { swInject } from './sw-inject.js';
import XNotification from './notification.vue';

View File

@ -0,0 +1,212 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="azykntjl">
<div class="body">
<div class="left">
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
</button>
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
<i class="ti ti-home ti-fw"></i>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" class="divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
<span v-if="navbarItemDef[item].indicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</component>
</template>
<div class="divider"></div>
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-dashboard ti-fw"></i>
</MkA>
<button v-click-anime class="item _button" @click="more">
<i class="ti ti-dots ti-fw"></i>
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
</div>
<div class="right">
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-settings ti-fw"></i>
</MkA>
<button v-click-anime class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/>
</button>
<div class="post" @click="os.post()">
<MkButton class="button" gradate full rounded>
<i class="ti ti-pencil ti-fw"></i>
</MkButton>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
import { openInstanceMenu } from './_common_/common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import MkButton from '@/components/MkButton.vue';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
import { $i } from '@/i.js';
const WINDOW_THRESHOLD = 1400;
const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
const menu = ref(prefer.s.menu);
// const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
const otherNavItemIndicated = computed<boolean>(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
if (navbarItemDef[def].indicated) return true;
}
return false;
});
function more(ev: MouseEvent) {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
src: ev.currentTarget ?? ev.target,
anchor: { x: 'center', y: 'bottom' },
}, {
closed: () => dispose(),
});
}
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({
withExtraOperation: true,
}, ev);
}
onMounted(() => {
window.addEventListener('resize', () => {
settingsWindowed.value = (window.innerWidth >= WINDOW_THRESHOLD);
}, { passive: true });
});
</script>
<style lang="scss" scoped>
.azykntjl {
$height: 60px;
$avatar-size: 32px;
$avatar-margin: 8px;
position: sticky;
top: 0;
z-index: 1000;
width: 100%;
height: $height;
background-color: var(--MI_THEME-bg);
> .body {
max-width: 1380px;
margin: 0 auto;
display: flex;
> .right,
> .left {
> .item {
position: relative;
font-size: 0.9em;
display: inline-block;
padding: 0 12px;
line-height: $height;
> i,
> .avatar {
margin-right: 0;
}
> i {
left: 10px;
}
> .avatar {
width: $avatar-size;
height: $avatar-size;
vertical-align: middle;
}
> .indicator {
position: absolute;
top: 0;
left: 0;
color: var(--MI_THEME-navIndicator);
font-size: 8px;
}
&:hover {
text-decoration: none;
color: var(--MI_THEME-navHoverFg);
}
&.active {
color: var(--MI_THEME-navActive);
}
}
> .divider {
display: inline-block;
height: 16px;
margin: 0 10px;
border-right: solid 0.5px var(--MI_THEME-divider);
}
> .instance {
display: inline-block;
position: relative;
width: 56px;
height: 100%;
vertical-align: bottom;
> img {
display: inline-block;
width: 24px;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
}
> .post {
display: inline-block;
> .button {
width: 40px;
height: 40px;
padding: 0;
min-width: 0;
}
}
> .account {
display: inline-flex;
align-items: center;
vertical-align: top;
margin-right: 8px;
> .acct {
margin-left: 8px;
}
}
}
> .right {
margin-left: auto;
}
}
}
</style>

View File

@ -0,0 +1,245 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="npcljfve" :class="{ iconOnly }">
<button v-click-anime class="item _button account" @click="openAccountMenu">
<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/>
</button>
<div class="post" data-cy-open-post-form @click="os.post">
<MkButton class="button" gradate full rounded>
<i class="ti ti-pencil ti-fw"></i><span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span>
</MkButton>
</div>
<div class="divider"></div>
<MkA v-click-anime class="item index" activeClass="active" to="/" exact>
<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
</MkA>
<template v-for="item in menu">
<div v-if="item === '-'" class="divider"></div>
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
<i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
<span v-if="navbarItemDef[item].indicated" class="indicator _blink">
<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
<i v-else class="_indicatorCircle"></i>
</span>
</component>
</template>
<div class="divider"></div>
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
</MkA>
<button v-click-anime class="item _button" @click="more">
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
<span v-if="otherNavItemIndicated" class="indicator _blink"><i class="_indicatorCircle"></i></span>
</button>
<MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null">
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
</MkA>
<div class="divider"></div>
<div class="about">
<button v-click-anime class="item _button" @click="openInstanceMenu">
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" draggable="false"/>
</button>
</div>
<!--<MisskeyLogo class="misskey"/>-->
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, computed, watch, ref, useTemplateRef } from 'vue';
import { openInstanceMenu } from './_common_/common.js';
// import { host } from '@@/js/config.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import MkButton from '@/components/MkButton.vue';
// import { StickySidebar } from '@/utility/sticky-sidebar.js';
// import { mainRouter } from '@/router.js';
//import MisskeyLogo from '@assets/client/misskey.svg';
import { store } from '@/store.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
import { $i } from '@/i.js';
const WINDOW_THRESHOLD = 1400;
const menu = ref(prefer.s.menu);
const menuDisplay = computed(store.makeGetterSetter('menuDisplay'));
const otherNavItemIndicated = computed<boolean>(() => {
for (const def in navbarItemDef) {
if (menu.value.includes(def)) continue;
if (navbarItemDef[def].indicated) return true;
}
return false;
});
const el = useTemplateRef('el');
// let accounts = $ref([]);
// let connection = $ref(null);
const iconOnly = ref(false);
const settingsWindowed = ref(false);
function calcViewState() {
iconOnly.value = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon');
settingsWindowed.value = (window.innerWidth > WINDOW_THRESHOLD);
}
function more(ev: MouseEvent) {
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {
src: ev.currentTarget ?? ev.target,
}, {
closed: () => dispose(),
});
}
function openAccountMenu(ev: MouseEvent) {
openAccountMenu_({
withExtraOperation: true,
}, ev);
}
watch(store.r.menuDisplay, () => {
calcViewState();
});
</script>
<style lang="scss" scoped>
.npcljfve {
$ui-font-size: 1em; // TODO:
$nav-icon-only-width: 78px; // TODO:
$avatar-size: 32px;
$avatar-margin: 8px;
padding: 0 16px;
box-sizing: border-box;
width: 260px;
&.iconOnly {
flex: 0 0 $nav-icon-only-width;
width: $nav-icon-only-width !important;
> .divider {
margin: 8px auto;
width: calc(100% - 32px);
}
> .post {
> .button {
width: 46px;
height: 46px;
padding: 0;
}
}
> .item {
padding-left: 0;
width: 100%;
text-align: center;
font-size: $ui-font-size * 1.1;
line-height: 3.7rem;
> i,
> .avatar {
margin-right: 0;
}
> i {
left: 10px;
}
> .text {
display: none;
}
}
}
> .divider {
margin: 10px 0;
border-top: solid 0.5px var(--MI_THEME-divider);
}
> .post {
position: sticky;
top: 0;
z-index: 1;
padding: 16px 0;
background: var(--MI_THEME-bg);
> .button {
min-width: 0;
}
}
> .about {
fill: currentColor;
padding: 8px 0 16px 0;
text-align: center;
> .item {
display: block;
width: 32px;
margin: 0 auto;
img {
display: block;
width: 100%;
}
}
}
> .item {
position: relative;
display: block;
font-size: $ui-font-size;
line-height: 2.6rem;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 100%;
text-align: left;
box-sizing: border-box;
> i {
width: 32px;
}
> i,
> .avatar {
margin-right: $avatar-margin;
}
> .avatar {
width: $avatar-size;
height: $avatar-size;
vertical-align: middle;
}
> .indicator {
position: absolute;
top: 0;
left: 0;
color: var(--MI_THEME-navIndicator);
font-size: 8px;
&:has(.itemIndicateValueIcon) {
animation: none;
left: auto;
right: 20px;
}
}
&:hover {
text-decoration: none;
color: var(--MI_THEME-navHoverFg);
}
&.active {
color: var(--MI_THEME-navActive);
}
}
}
</style>

View File

@ -0,0 +1,326 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div class="gbhvwtnk" :class="{ wallpaper }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`">
<XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/>
<div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }">
<div v-if="!showMenuOnTop" class="sidebar">
<XSidebar/>
</div>
<div v-else-if="!pageMetadata?.needWideArea" ref="widgetsLeft" class="widgets left">
<XWidgets place="left" :marginTop="'var(--MI-margin)'" @mounted="attachSticky(widgetsLeft)"/>
</div>
<main class="main" @contextmenu.stop="onContextmenu">
<div class="content">
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']"/>
<RouterView v-else/>
</div>
</main>
<div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" class="widgets right">
<XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--MI-margin)'" @mounted="attachSticky(widgetsRight)"/>
</div>
</div>
<Transition :name="prefer.s.animation ? 'tray-back' : ''">
<div
v-if="widgetsShowing"
class="tray-back _modalBg"
@click="widgetsShowing = false"
@touchstart.passive="widgetsShowing = false"
></div>
</Transition>
<Transition :name="prefer.s.animation ? 'tray' : ''">
<XWidgets v-if="widgetsShowing" class="tray"/>
</Transition>
<iframe v-if="prefer.s.aiChanMode" ref="live2d" class="ivnzpscs" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe>
<XCommon/>
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, onMounted, provide, ref, computed, useTemplateRef } from 'vue';
import { instanceName } from '@@/js/config.js';
import { isLink } from '@@/js/is-link.js';
import XSidebar from './classic.sidebar.vue';
import XCommon from './_common_/common.vue';
import type { PageMetadata } from '@/page.js';
import { StickySidebar } from '@/utility/sticky-sidebar.js';
import * as os from '@/os.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
import { miLocalStorage } from '@/local-storage.js';
import { mainRouter } from '@/router.js';
import { prefer } from '@/preferences.js';
import { DI } from '@/di.js';
const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
const DESKTOP_THRESHOLD = 1100;
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
const pageMetadata = ref<null | PageMetadata>(null);
const widgetsShowing = ref(false);
const fullView = ref(false);
const globalHeaderHeight = ref(0);
const wallpaper = miLocalStorage.getItem('wallpaper') != null;
const showMenuOnTop = computed(() => store.s.menuDisplay === 'top');
const live2d = useTemplateRef('live2d');
const widgetsLeft = ref<HTMLElement>();
const widgetsRight = ref<HTMLElement>();
provide(DI.router, mainRouter);
provideMetadataReceiver((metadataGetter) => {
const info = metadataGetter();
pageMetadata.value = info;
if (pageMetadata.value) {
if (isRoot.value && pageMetadata.value.title === instanceName) {
window.document.title = pageMetadata.value.title;
} else {
window.document.title = `${pageMetadata.value.title} | ${instanceName}`;
}
}
});
provideReactiveMetadata(pageMetadata);
provide('shouldHeaderThin', showMenuOnTop.value);
provide('forceSpacerMin', true);
function attachSticky(el: HTMLElement) {
const sticky = new StickySidebar(el, 0, store.s.menuDisplay === 'top' ? 60 : 0); // TODO: 60px
window.addEventListener('scroll', () => {
sticky.calc(window.scrollY);
}, { passive: true });
}
function top() {
window.scroll({ top: 0, behavior: 'smooth' });
}
function onContextmenu(ev: MouseEvent) {
if (isLink(ev.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
if (window.getSelection().toString() !== '') return;
const path = mainRouter.getCurrentFullPath();
os.contextMenu([{
type: 'label',
text: path,
}, {
icon: fullView.value ? 'ti ti-minimize' : 'ti ti-maximize',
text: fullView.value ? i18n.ts.quitFullView : i18n.ts.fullView,
action: () => {
fullView.value = !fullView.value;
},
}, {
icon: 'ti ti-window-maximize',
text: i18n.ts.openInWindow,
action: () => {
os.pageWindow(path);
},
}], ev);
}
function onAiClick(ev) {
//if (this.live2d) this.live2d.click(ev);
}
if (window.innerWidth < 1024) {
const currentUI = miLocalStorage.getItem('ui');
miLocalStorage.setItem('ui_temp', currentUI ?? 'default');
miLocalStorage.setItem('ui', 'default');
window.location.reload();
}
window.document.documentElement.style.overflowY = 'scroll';
onMounted(() => {
window.addEventListener('resize', () => {
isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD);
}, { passive: true });
if (prefer.s.aiChanMode) {
const iframeRect = live2d.value.getBoundingClientRect();
window.addEventListener('mousemove', ev => {
live2d.value.contentWindow.postMessage({
type: 'moveCursor',
body: {
x: ev.clientX - iframeRect.left,
y: ev.clientY - iframeRect.top,
},
}, '*');
}, { passive: true });
window.addEventListener('touchmove', ev => {
live2d.value.contentWindow.postMessage({
type: 'moveCursor',
body: {
x: ev.touches[0].clientX - iframeRect.left,
y: ev.touches[0].clientY - iframeRect.top,
},
}, '*');
}, { passive: true });
}
});
</script>
<style lang="scss" scoped>
.tray-enter-active,
.tray-leave-active {
opacity: 1;
transform: translateX(0);
transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.tray-enter-from,
.tray-leave-active {
opacity: 0;
transform: translateX(240px);
}
.tray-back-enter-active,
.tray-back-leave-active {
opacity: 1;
transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
}
.tray-back-enter-from,
.tray-back-leave-active {
opacity: 0;
}
.gbhvwtnk {
$ui-font-size: 1em;
$widgets-hide-threshold: 1200px;
min-height: 100dvh;
box-sizing: border-box;
&.wallpaper {
background: var(--MI_THEME-wallpaperOverlay);
//backdrop-filter: var(--MI-blur, blur(4px));
}
> .columns {
display: flex;
justify-content: center;
max-width: 100%;
//margin: 32px 0;
&.fullView {
margin: 0;
> .sidebar {
display: none;
}
> .widgets {
display: none;
}
> .main {
margin: 0;
border-radius: 0;
box-shadow: none;
width: 100%;
}
}
> .main {
min-width: 0;
width: 750px;
margin: 0 16px 0 0;
border-left: solid 1px var(--MI_THEME-divider);
border-right: solid 1px var(--MI_THEME-divider);
border-radius: 0;
overflow: clip;
--MI-margin: 12px;
}
> .widgets {
//--MI_THEME-panelBorder: none;
width: 300px;
padding-bottom: calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
@media (max-width: $widgets-hide-threshold) {
display: none;
}
&.left {
margin-right: 16px;
}
}
> .sidebar {
margin-top: 16px;
}
&.withGlobalHeader {
> .main {
margin-top: 0;
border: solid 1px var(--MI_THEME-divider);
border-radius: var(--MI-radius);
--MI-stickyTop: var(--globalHeaderHeight);
}
> .widgets {
--MI-stickyTop: var(--globalHeaderHeight);
margin-top: 0;
}
}
@media (max-width: 850px) {
margin: 0;
> .sidebar {
border-right: solid 0.5px var(--MI_THEME-divider);
}
> .main {
margin: 0;
border-radius: 0;
box-shadow: none;
width: 100%;
}
}
}
> .tray-back {
z-index: 1001;
}
> .tray {
position: fixed;
top: 0;
right: 0;
z-index: 1001;
height: 100dvh;
padding: var(--MI-margin) var(--MI-margin) calc(var(--MI-margin) + env(safe-area-inset-bottom, 0px));
box-sizing: border-box;
overflow: auto;
background: var(--MI_THEME-bg);
}
> .ivnzpscs {
position: fixed;
bottom: 0;
right: 0;
width: 300px;
height: 600px;
border: none;
pointer-events: none;
}
}
.content {
height: 100%;
}
</style>

View File

@ -221,6 +221,32 @@ async function deleteProfile() {
</script>
<style>
html,
body {
width: 100%;
height: 100%;
position: fixed;
top: 0;
left: 0;
overscroll-behavior: none;
}
body {
/* NOTE: htmlにも overflow: clip を設定したいところだが、設定すると何故か少なくともChromeで html が main thread scrolling になりパフォーマンスが(多分)落ちる */
overflow: clip;
}
#misskey_app {
width: 100%;
height: 100%;
overflow: clip;
position: absolute;
top: 0;
left: 0;
}
</style>
<style lang="scss" module>
.transition_menuDrawerBg_enterActive,
.transition_menuDrawerBg_leaveActive {

View File

@ -5,116 +5,30 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<div :class="$style.root">
<XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/>
<div :class="$style.contents" @contextmenu.stop="onContextmenu">
<div>
<XPreferenceRestore v-if="shouldSuggestRestoreBackup"/>
<XAnnouncements v-if="$i"/>
<XStatusBars :class="$style.statusbars"/>
</div>
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :class="$style.content"/>
<RouterView v-else :class="$style.content"/>
<div v-if="isMobile" ref="navFooter" :class="$style.nav">
<button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator" class="_blink"><i class="_indicatorCircle"></i></span></button>
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button>
<button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')">
<i :class="$style.navButtonIcon" class="ti ti-bell"></i>
<span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator" class="_blink">
<span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span>
</span>
</button>
<button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i></button>
<button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button>
</div>
</div>
<div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets">
<XWidgets/>
</div>
<Transition
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
>
<div
v-if="drawerMenuShowing"
:class="$style.menuDrawerBg"
class="_modalBg"
@click="drawerMenuShowing = false"
@touchstart.passive="drawerMenuShowing = false"
></div>
</Transition>
<Transition
:enterActiveClass="prefer.s.animation ? $style.transition_menuDrawer_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_menuDrawer_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_menuDrawer_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_menuDrawer_leaveTo : ''"
>
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
<XDrawerMenu/>
</div>
</Transition>
<Transition
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
>
<div
v-if="widgetsShowing"
:class="$style.widgetsDrawerBg"
class="_modalBg"
@click="widgetsShowing = false"
@touchstart.passive="widgetsShowing = false"
></div>
</Transition>
<Transition
:enterActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterActive : ''"
:leaveActiveClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
:enterFromClass="prefer.s.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
:leaveToClass="prefer.s.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
>
<div v-if="widgetsShowing" :class="$style.widgetsDrawer">
<button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button>
<XWidgets/>
</div>
</Transition>
<XCommon/>
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, provide, onMounted, computed, ref, watch, useTemplateRef } from 'vue';
import { provide, onMounted, computed, ref } from 'vue';
import { instanceName } from '@@/js/config.js';
import { isLink } from '@@/js/is-link.js';
import XCommon from './_common_/common.vue';
import type { PageMetadata } from '@/page.js';
import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/i.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { deviceKind } from '@/utility/device-kind.js';
import { miLocalStorage } from '@/local-storage.js';
import { mainRouter } from '@/router.js';
import { prefer } from '@/preferences.js';
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
import { DI } from '@/di.js';
const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
const XPreferenceRestore = defineAsyncComponent(() => import('@/ui/_common_/PreferenceRestore.vue'));
const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index');
const DESKTOP_THRESHOLD = 1100;
@ -128,8 +42,6 @@ window.addEventListener('resize', () => {
});
const pageMetadata = ref<null | PageMetadata>(null);
const widgetsShowing = ref(false);
const navFooter = useTemplateRef('navFooter');
provide(DI.router, mainRouter);
provideMetadataReceiver((metadataGetter) => {
@ -145,14 +57,6 @@ provideMetadataReceiver((metadataGetter) => {
});
provideReactiveMetadata(pageMetadata);
const menuIndicated = computed(() => {
for (const def in navbarItemDef) {
if (def === 'notifications') continue; //
if (navbarItemDef[def].indicated) return true;
}
return false;
});
const drawerMenuShowing = ref(false);
mainRouter.on('change', () => {
@ -192,22 +96,31 @@ const onContextmenu = (ev) => {
},
}], ev);
};
const navFooterHeight = ref(0);
watch(navFooter, () => {
if (navFooter.value) {
navFooterHeight.value = navFooter.value.offsetHeight;
window.document.body.style.setProperty('--MI-minBottomSpacing', 'var(--MI-minBottomSpacingMobile)');
} else {
navFooterHeight.value = 0;
window.document.body.style.setProperty('--MI-minBottomSpacing', '0px');
}
}, {
immediate: true,
});
</script>
<style>
html,
body {
width: 100%;
height: 100%;
overscroll-behavior: none;
}
body {
/* NOTE: htmlにも overflow: clip を設定したいところだが、設定すると何故か少なくともChromeで html が main thread scrolling になりパフォーマンスが(多分)落ちる */
overflow: clip;
}
#misskey_app {
width: 100%;
height: 100%;
overflow: clip;
position: absolute;
top: 0;
left: 0;
}
</style>
<style lang="scss" module>
$ui-font-size: 1em; // TODO:
$widgets-hide-threshold: 1090px;

View File

@ -323,43 +323,43 @@ export const searchIndexes: SearchIndexItem[] = [
label: i18n.ts.emojiStyle,
keywords: ['emoji', 'style', 'native', 'system', 'fluent', 'twemoji'],
},
{
id: 'wo0s0CaI1',
label: i18n.ts.pinnedList,
keywords: ['pinned', 'list'],
},
],
label: i18n.ts.general,
keywords: ['general'],
},
{
id: '5G6O6qdis',
id: 'l78F2W9Ok',
children: [
{
id: 'khT3n6byY',
id: 'iOJ3Crlky',
label: i18n.ts.showFixedPostForm,
keywords: ['post', 'form', 'timeline'],
},
{
id: 'q5ElfNSou',
id: 'CQldliCSi',
label: i18n.ts.showFixedPostFormInChannel,
keywords: ['post', 'form', 'timeline', 'channel'],
},
{
id: '3GcWIaZf8',
id: 'd2H4E5ys6',
label: i18n.ts.collapseRenotes,
keywords: ['renote', i18n.ts.collapseRenotesDescription],
},
{
id: 'd2H4E5ys6',
id: 'yb11lSY1G',
label: i18n.ts.showGapBetweenNotesInTimeline,
keywords: ['note', 'timeline', 'gap'],
},
{
id: '1LHOhDKGW',
id: 'fL49Zxe9i',
label: i18n.ts.disableStreamingTimeline,
keywords: ['disable', 'streaming', 'timeline'],
},
{
id: 'DSzwvTp7i',
label: i18n.ts.pinnedList,
keywords: ['pinned', 'list'],
},
{
id: 'ykifk3NHS',
label: i18n.ts.showNoteActionsOnlyHover,
@ -489,79 +489,17 @@ export const searchIndexes: SearchIndexItem[] = [
id: '96LnS1sxB',
children: [
{
id: 'vPQPvmntL',
label: i18n.ts.reduceUiAnimation,
keywords: ['animation', 'motion', 'reduce'],
},
{
id: 'wfJ91vwzq',
label: i18n.ts.disableShowingAnimatedImages,
keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'],
},
{
id: '42b1L4xdq',
label: i18n.ts.enableAnimatedMfm,
keywords: ['mfm', 'enable', 'show', 'animated'],
},
{
id: 'dLkRNHn3k',
label: i18n.ts.enableHorizontalSwipe,
keywords: ['swipe', 'horizontal', 'tab'],
},
{
id: 'BvooTWFW5',
label: i18n.ts.keepScreenOn,
keywords: ['keep', 'screen', 'display', 'on'],
},
{
id: 'yzbghkAq0',
label: i18n.ts.useNativeUIForVideoAudioPlayer,
keywords: ['native', 'system', 'video', 'audio', 'player', 'media'],
},
{
id: 'aSbKFHbOy',
label: i18n.ts._settings.makeEveryTextElementsSelectable,
keywords: ['text', 'selectable'],
},
{
id: 'bTcAsPvNz',
label: i18n.ts.menuStyle,
keywords: ['menu', 'style', 'popup', 'drawer'],
},
{
id: 'lSVBaLnyW',
label: i18n.ts._contextMenu.title,
keywords: ['contextmenu', 'system', 'native'],
},
{
id: 'pec0uMPq5',
label: i18n.ts.fontSize,
keywords: ['font', 'size'],
},
{
id: 'Eh7vTluDO',
label: i18n.ts.useSystemFont,
keywords: ['font', 'system', 'native'],
},
],
label: i18n.ts.accessibility,
keywords: ['accessibility', i18n.ts._settings.accessibilityBanner],
},
{
id: 'vTRSKf1JA',
children: [
{
id: '2VjlA02wB',
id: '5h8vhCX1S',
label: i18n.ts.turnOffToImprovePerformance,
keywords: ['blur'],
},
{
id: 'f6J0lmg1g',
id: 'Cbjosj3TG',
label: i18n.ts.turnOffToImprovePerformance,
keywords: ['blur', 'modal'],
},
{
id: 'hQqXhfNg8',
id: 'BKndoHcCj',
label: i18n.ts.turnOffToImprovePerformance,
keywords: ['sticky'],
},
@ -570,55 +508,55 @@ export const searchIndexes: SearchIndexItem[] = [
keywords: ['performance'],
},
{
id: 'utM8dEobb',
id: '4yCgcFElF',
label: i18n.ts.dataSaver,
keywords: ['datasaver'],
},
{
id: 'gOUvwkE9t',
id: 'DILm2LlCn',
children: [
{
id: 'iUMUvFURf',
id: 'Fd0rFTSry',
label: i18n.ts.squareAvatars,
keywords: ['avatar', 'icon', 'square'],
},
{
id: 'ceyPO9Ywi',
id: 'xNsLokqeA',
label: i18n.ts.seasonalScreenEffect,
keywords: ['effect', 'show'],
},
{
id: 'ztwIlsXhP',
id: 'sZcalFBE8',
label: i18n.ts.openImageInNewTab,
keywords: ['image', 'photo', 'picture', 'media', 'thumbnail', 'new', 'tab'],
},
{
id: 'vLSsQbZEo',
id: 'Eh7vTluDO',
label: i18n.ts.withRepliesByDefaultForNewlyFollowed,
keywords: ['follow', 'replies'],
},
{
id: 'hQt85bBIX',
id: 'vTRSKf1JA',
label: i18n.ts.whenServerDisconnected,
keywords: ['server', 'disconnect', 'reconnect', 'reload', 'streaming'],
},
{
id: 'C9SyK2m0',
id: 'zlO5cBZFH',
label: i18n.ts.numberOfPageCache,
keywords: ['cache', 'page'],
},
{
id: '2U0iVUtfW',
id: 'huQ8nc4iD',
label: i18n.ts.forceShowAds,
keywords: ['ad', 'show'],
},
{
id: '1rA7ADEXY',
id: 'nJWfqwQ4R',
label: i18n.ts.hemisphere,
keywords: [],
},
{
id: 'vRayx89Rt',
id: 'kwEEgTlwR',
label: i18n.ts.additionalEmojiDictionary,
keywords: ['emoji', 'dictionary', 'additional', 'extra'],
},
@ -968,6 +906,70 @@ export const searchIndexes: SearchIndexItem[] = [
path: '/settings/account-data',
icon: 'ti ti-package',
},
{
id: 'f08Mi1Uwn',
children: [
{
id: 'C5dRH2Ypy',
label: i18n.ts.reduceUiAnimation,
keywords: ['animation', 'motion', 'reduce'],
},
{
id: '5mZxz2cru',
label: i18n.ts.disableShowingAnimatedImages,
keywords: ['disable', 'animation', 'image', 'photo', 'picture', 'media', 'thumbnail', 'gif'],
},
{
id: 'c0Iy5hL5o',
label: i18n.ts.enableAnimatedMfm,
keywords: ['mfm', 'enable', 'show', 'animated'],
},
{
id: '4HYFjs2Nv',
label: i18n.ts.enableHorizontalSwipe,
keywords: ['swipe', 'horizontal', 'tab'],
},
{
id: 'kYVJ3SVNq',
label: i18n.ts.keepScreenOn,
keywords: ['keep', 'screen', 'display', 'on'],
},
{
id: 'w4Bv0meAt',
label: i18n.ts.useNativeUIForVideoAudioPlayer,
keywords: ['native', 'system', 'video', 'audio', 'player', 'media'],
},
{
id: 'b1GYEEJeh',
label: i18n.ts._settings.makeEveryTextElementsSelectable,
keywords: ['text', 'selectable'],
},
{
id: 'vVLxwINTJ',
label: i18n.ts.menuStyle,
keywords: ['menu', 'style', 'popup', 'drawer'],
},
{
id: '14cMhMLHL',
label: i18n.ts._contextMenu.title,
keywords: ['contextmenu', 'system', 'native'],
},
{
id: 'oSo4LXMX9',
label: i18n.ts.fontSize,
keywords: ['font', 'size'],
},
{
id: '7LQSAThST',
label: i18n.ts.useSystemFont,
keywords: ['font', 'system', 'native'],
},
],
label: i18n.ts.accessibility,
keywords: ['accessibility', i18n.ts._settings.accessibilityBanner],
path: '/settings/accessibility',
icon: 'ti ti-accessible',
},
] as const;
export type SearchIndex = typeof searchIndexes;

View File

@ -1,7 +1,7 @@
{
"type": "module",
"name": "misskey-js",
"version": "2025.3.2-beta.18",
"version": "2025.3.2-beta.16",
"description": "Misskey SDK for JavaScript",
"license": "MIT",
"main": "./built/index.js",

View File

@ -720,7 +720,7 @@ importers:
version: 15.1.1
'@vitejs/plugin-vue':
specifier: 5.2.3
version: 5.2.3(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
version: 5.2.3(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
'@vue/compiler-sfc':
specifier: 3.5.13
version: 3.5.13
@ -857,8 +857,8 @@ importers:
specifier: 1.13.1
version: 1.13.1(vue@3.5.13(typescript@5.8.2))
vite:
specifier: 6.2.3
version: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
specifier: 6.2.2
version: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vue:
specifier: 3.5.13
version: 3.5.13(typescript@5.8.2)
@ -910,7 +910,7 @@ importers:
version: 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)
'@storybook/react-vite':
specifier: 8.6.7
version: 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
version: 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@storybook/test':
specifier: 8.6.7
version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
@ -925,7 +925,7 @@ importers:
version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2))
'@storybook/vue3-vite':
specifier: 8.6.7
version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
version: 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
'@testing-library/vue':
specifier: 8.1.0
version: 8.1.0(@vue/compiler-sfc@3.5.13)(@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2)))(vue@3.5.13(typescript@5.8.2))
@ -1075,7 +1075,7 @@ importers:
version: 15.1.1
'@vitejs/plugin-vue':
specifier: 5.2.3
version: 5.2.3(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
version: 5.2.3(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))
'@vue/compiler-sfc':
specifier: 3.5.13
version: 3.5.13
@ -1128,8 +1128,8 @@ importers:
specifier: 11.1.0
version: 11.1.0
vite:
specifier: 6.2.3
version: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
specifier: 6.2.2
version: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vue:
specifier: 3.5.13
version: 3.5.13(typescript@5.8.2)
@ -10520,8 +10520,8 @@ packages:
vite-plugin-turbosnap@1.0.3:
resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
vite@6.2.3:
resolution: {integrity: sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==}
vite@6.2.2:
resolution: {integrity: sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@ -12520,12 +12520,12 @@ snapshots:
'@types/yargs': 17.0.19
chalk: 4.1.2
'@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
'@joshwooding/vite-plugin-react-docgen-typescript@0.5.0(typescript@5.8.2)(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
dependencies:
glob: 10.4.5
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.8.2)
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
optionalDependencies:
typescript: 5.8.2
@ -14007,13 +14007,13 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
'@storybook/builder-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
'@storybook/builder-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
dependencies:
'@storybook/csf-plugin': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
browser-assert: 1.2.1
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
ts-dedent: 2.2.0
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
'@storybook/components@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))':
dependencies:
@ -14076,11 +14076,11 @@ snapshots:
react-dom: 19.0.0(react@19.0.0)
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
'@storybook/react-vite@8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
'@storybook/react-vite@8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(rollup@4.36.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
dependencies:
'@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.2)(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@joshwooding/vite-plugin-react-docgen-typescript': 0.5.0(typescript@5.8.2)(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@rollup/pluginutils': 5.1.4(rollup@4.36.0)
'@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@storybook/react': 8.6.7(@storybook/test@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(typescript@5.8.2)
find-up: 5.0.0
magic-string: 0.30.17
@ -14090,7 +14090,7 @@ snapshots:
resolve: 1.22.8
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
tsconfig-paths: 4.2.0
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
optionalDependencies:
'@storybook/test': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))
transitivePeerDependencies:
@ -14139,15 +14139,15 @@ snapshots:
dependencies:
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
'@storybook/vue3-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))':
'@storybook/vue3-vite@8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))':
dependencies:
'@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@storybook/builder-vite': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@storybook/vue3': 8.6.7(storybook@8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5))(vue@3.5.13(typescript@5.8.2))
find-package-json: 1.2.0
magic-string: 0.30.17
storybook: 8.6.7(bufferutil@4.0.9)(prettier@3.5.3)(utf-8-validate@6.0.5)
typescript: 5.8.2
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vue-component-meta: 2.0.16(typescript@5.8.2)
vue-docgen-api: 4.75.1(vue@3.5.13(typescript@5.8.2))
transitivePeerDependencies:
@ -14911,9 +14911,9 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-vue@5.2.3(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))':
'@vitejs/plugin-vue@5.2.3(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))(vue@3.5.13(typescript@5.8.2))':
dependencies:
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vue: 3.5.13(typescript@5.8.2)
'@vitest/coverage-v8@3.0.9(vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
@ -14948,14 +14948,14 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
'@vitest/mocker@3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
'@vitest/mocker@3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))':
dependencies:
'@vitest/spy': 3.0.9
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
msw: 2.7.3(@types/node@22.13.11)(typescript@5.8.2)
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
'@vitest/pretty-format@2.0.5':
dependencies:
@ -22124,7 +22124,7 @@ snapshots:
debug: 4.4.0(supports-color@8.1.1)
es-module-lexer: 1.6.0
pathe: 2.0.3
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
transitivePeerDependencies:
- '@types/node'
- jiti
@ -22141,7 +22141,7 @@ snapshots:
vite-plugin-turbosnap@1.0.3: {}
vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3):
vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3):
dependencies:
esbuild: 0.25.1
postcss: 8.5.3
@ -22160,7 +22160,7 @@ snapshots:
vitest@3.0.9(@types/debug@4.1.12)(@types/node@22.13.11)(happy-dom@17.4.4)(jsdom@26.0.0(bufferutil@4.0.9)(canvas@3.1.0)(utf-8-validate@6.0.5))(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3):
dependencies:
'@vitest/expect': 3.0.9
'@vitest/mocker': 3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@vitest/mocker': 3.0.9(msw@2.7.3(@types/node@22.13.11)(typescript@5.8.2))(vite@6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3))
'@vitest/pretty-format': 3.0.9
'@vitest/runner': 3.0.9
'@vitest/snapshot': 3.0.9
@ -22176,7 +22176,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
vite: 6.2.3(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite: 6.2.2(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
vite-node: 3.0.9(@types/node@22.13.11)(sass@1.86.0)(terser@5.39.0)(tsx@4.19.3)
why-is-node-running: 2.3.0
optionalDependencies:

View File

@ -16,7 +16,7 @@
"remark-parse": "11.0.0",
"typescript": "5.8.2",
"unified": "11.0.5",
"vite": "6.2.3",
"vite": "6.2.1",
"vite-node": "3.0.8",
"vitest": "3.0.8"
}
@ -2724,9 +2724,9 @@
}
},
"node_modules/vite": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz",
"integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.1.tgz",
"integrity": "sha512-n2GnqDb6XPhlt9B8olZPrgMD/es/Nd1RdChF6CBD/fHW6pUyUTt2sQW2fPRX5GiD9XEa6+8A6A4f2vT6pSsE7Q==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@ -17,7 +17,7 @@
"remark-parse": "11.0.0",
"typescript": "5.8.2",
"unified": "11.0.5",
"vite": "6.2.3",
"vite": "6.2.1",
"vite-node": "3.0.8",
"vitest": "3.0.8"
}