Merge branch 'develop' into sensitive-channel-mute

This commit is contained in:
anatawa12 2025-12-20 16:51:13 +09:00 committed by GitHub
commit fbefba7f0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 122 additions and 50 deletions

View File

@ -9,8 +9,10 @@ v2025.12.0で行われた「configの`trustProxy`のデフォルト値を`false`
- 依存関係の更新 - 依存関係の更新
### Client ### Client
- Enhance: デッキのUI説明を追加
- Fix: バージョン表記のないPlayが正しく動作しない問題を修正 - Fix: バージョン表記のないPlayが正しく動作しない問題を修正
バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。 バージョン表記のないものは v0.x 系として実行されます。v1.x 系で動作させたい場合は必ずバージョン表記を含めてください。
- Fix: デッキUIでメニュー位置を下にしているとプロファイル削除ボタンが表示されないのを修正
- Enhance: センシティブチャンネルの投稿が多くの場所で畳まれるようになりました - Enhance: センシティブチャンネルの投稿が多くの場所で畳まれるようになりました

View File

@ -2893,6 +2893,15 @@ _deck:
usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります" usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります"
flexible: "幅を自動調整" flexible: "幅を自動調整"
enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする" enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする"
showHowToUse: "UIの説明を見る"
_howToUse:
addColumn_title: "カラム追加"
addColumn_description: "カラムの種類を選んで追加できます。"
settings_title: "UI設定"
settings_description: "デッキUIの詳細設定を行えます。"
switchProfile_title: "プロファイル切り替え"
switchProfile_description: "UIのレイアウトをプロファイルとして保存し、いつでも切り替えられるようにできます。"
_columns: _columns:
main: "メイン" main: "メイン"

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.12.2-beta.1", "version": "2025.12.2-beta.2",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -7,7 +7,6 @@ import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import si from 'systeminformation';
import { Mutex } from 'async-mutex'; import { Mutex } from 'async-mutex';
import fetch from 'node-fetch'; import fetch from 'node-fetch';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -84,6 +83,7 @@ export class AiService {
@bindThis @bindThis
private async getCpuFlags(): Promise<string[]> { private async getCpuFlags(): Promise<string[]> {
const si = await import('systeminformation');
const str = await si.cpuFlags(); const str = await si.cpuFlags();
return str.split(/\s+/); return str.split(/\s+/);
} }

View File

@ -4,13 +4,12 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import si from 'systeminformation';
import Xev from 'xev'; import Xev from 'xev';
import * as osUtils from 'os-utils'; import * as osUtils from 'os-utils';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { MiMeta } from '@/models/_.js'; import { MiMeta } from '@/models/_.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { OnApplicationShutdown } from '@nestjs/common';
const ev = new Xev(); const ev = new Xev();
@ -97,12 +96,14 @@ function cpuUsage(): Promise<number> {
// MEMORY STAT // MEMORY STAT
async function mem() { async function mem() {
const si = await import('systeminformation');
const data = await si.mem(); const data = await si.mem();
return data; return data;
} }
// NETWORK STAT // NETWORK STAT
async function net() { async function net() {
const si = await import('systeminformation');
const iface = await si.networkInterfaceDefault(); const iface = await si.networkInterfaceDefault();
const data = await si.networkStats(iface); const data = await si.networkStats(iface);
return data[0]; return data[0];
@ -110,5 +111,6 @@ async function net() {
// FS STAT // FS STAT
async function fs() { async function fs() {
const si = await import('systeminformation');
return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); return await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 }));
} }

View File

@ -4,15 +4,11 @@
*/ */
import * as os from 'node:os'; import * as os from 'node:os';
import sysUtils from 'systeminformation';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
export async function showMachineInfo(parentLogger: Logger) { export async function showMachineInfo(parentLogger: Logger) {
const logger = parentLogger.createSubLogger('machine'); const logger = parentLogger.createSubLogger('machine');
logger.debug(`Hostname: ${os.hostname()}`); logger.debug(`Hostname: ${os.hostname()}`);
logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`); logger.debug(`Platform: ${process.platform} Arch: ${process.arch}`);
const mem = await sysUtils.mem(); logger.debug(`CPU: ${os.cpus().length} core MEM: ${(os.totalmem() / 1024 / 1024 / 1024).toFixed(1)}GB (available: ${(os.freemem() / 1024 / 1024 / 1024).toFixed(1)}GB)`);
const totalmem = (mem.total / 1024 / 1024 / 1024).toFixed(1);
const availmem = (mem.available / 1024 / 1024 / 1024).toFixed(1);
logger.debug(`CPU: ${os.cpus().length} core MEM: ${totalmem}GB (available: ${availmem}GB)`);
} }

View File

@ -4,7 +4,6 @@
*/ */
import * as os from 'node:os'; import * as os from 'node:os';
import si from 'systeminformation';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
@ -112,6 +111,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) { ) {
super(meta, paramDef, async () => { super(meta, paramDef, async () => {
const si = await import('systeminformation');
const memStats = await si.mem(); const memStats = await si.mem();
const fsStats = await si.fsSize(); const fsStats = await si.fsSize();
const netInterface = await si.networkInterfaceDefault(); const netInterface = await si.networkInterfaceDefault();

View File

@ -4,7 +4,6 @@
*/ */
import * as os from 'node:os'; import * as os from 'node:os';
import si from 'systeminformation';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js'; import { Endpoint } from '@/server/api/endpoint-base.js';
import { MiMeta } from '@/models/_.js'; import { MiMeta } from '@/models/_.js';
@ -93,6 +92,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}, },
}; };
const si = await import('systeminformation');
const memStats = await si.mem(); const memStats = await si.mem();
const fsStats = await si.fsSize(); const fsStats = await si.fsSize();

View File

@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div <div
:class="[$style.root, { [$style.modal]: modal, _popup: modal }]" :class="[$style.root]"
@dragover.stop="onDragover" @dragover.stop="onDragover"
@dragenter="onDragenter" @dragenter="onDragenter"
@dragleave="onDragleave" @dragleave="onDragleave"
@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, onUnmounted } from 'vue'; import { watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed, useTemplateRef, onUnmounted } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import insertTextAtCursor from 'insert-text-at-cursor'; import insertTextAtCursor from 'insert-text-at-cursor';
@ -161,8 +161,6 @@ import { closeTip } from '@/tips.js';
const $i = ensureSignin(); const $i = ensureSignin();
const modal = inject(DI.inModal, false);
const props = withDefaults(defineProps<PostFormProps & { const props = withDefaults(defineProps<PostFormProps & {
fixed?: boolean; fixed?: boolean;
autofocus?: boolean; autofocus?: boolean;
@ -1447,13 +1445,6 @@ defineExpose({
.root { .root {
position: relative; position: relative;
container-type: inline-size; container-type: inline-size;
&.modal {
width: 100%;
max-width: 520px;
overflow-x: clip;
overflow-y: auto;
}
} }
//#region header //#region header
@ -1722,7 +1713,8 @@ html[data-color-scheme=light] .preview {
min-width: 100%; min-width: 100%;
width: 100%; width: 100%;
min-height: 90px; min-height: 90px;
height: 100%; max-height: 500px;
field-sizing: content;
} }
.textCount { .textCount {

View File

@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkPostForm <MkPostForm
ref="form" ref="form"
:class="$style.form" :class="$style.form"
class="_popup"
v-bind="props" v-bind="props"
autofocus autofocus
freezeAfterPosted freezeAfterPosted
@ -73,7 +74,8 @@ function onModalClosed() {
<style lang="scss" module> <style lang="scss" module>
.form { .form {
max-height: 100%; width: 100%;
max-width: 520px;
margin: 0 auto auto auto; margin: 0 auto auto auto;
} }
</style> </style>

View File

@ -122,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker> <SearchMarker>
<MkFolder :defaultOpen="true"> <MkFolder :defaultOpen="true">
<template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template> <template #icon><SearchIcon><i class="ti ti-bolt"></i></SearchIcon></template>
<template #label><SearchLabel>Misskey® Reactions Boost Technology (RBT)</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> <template #label><SearchLabel>Misskey® Reactions Boost Technology (RBT)</SearchLabel></template>
<template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template> <template v-if="rbtForm.savedState.enableReactionsBuffering" #suffix>Enabled</template>
<template v-else #suffix>Disabled</template> <template v-else #suffix>Disabled</template>
<template v-if="rbtForm.modified.value" #footer> <template v-if="rbtForm.modified.value" #footer>

View File

@ -90,7 +90,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['lockdown']"> <SearchMarker :keywords="['lockdown']">
<FormSection> <FormSection>
<template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> <template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel></template>
<div class="_gaps_m"> <div class="_gaps_m">
<SearchMarker :keywords="['login', 'signin']"> <SearchMarker :keywords="['login', 'signin']">
@ -213,9 +213,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch } from 'vue'; import { ref, computed, watch } from 'vue';
import type { MkSelectItem } from '@/components/MkSelect.vue';
import MkSwitch from '@/components/MkSwitch.vue'; import MkSwitch from '@/components/MkSwitch.vue';
import MkSelect from '@/components/MkSelect.vue'; import MkSelect from '@/components/MkSelect.vue';
import type { MkSelectItem } from '@/components/MkSelect.vue';
import FormSection from '@/components/form/section.vue'; import FormSection from '@/components/form/section.vue';
import { misskeyApi } from '@/utility/misskey-api.js'; import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';

View File

@ -107,7 +107,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<SearchMarker :keywords="['follow', 'message']"> <SearchMarker :keywords="['follow', 'message']">
<MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false"> <MkInput v-model="profile.followedMessage" :max="200" manualSave :mfmPreview="false">
<template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template> <template #label><SearchLabel>{{ i18n.ts._profile.followedMessage }}</SearchLabel></template>
<template #caption> <template #caption>
<div><SearchText>{{ i18n.ts._profile.followedMessageDescription }}</SearchText></div> <div><SearchText>{{ i18n.ts._profile.followedMessageDescription }}</SearchText></div>
<div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div> <div>{{ i18n.ts._profile.followedMessageDescriptionForLockedAccount }}</div>

View File

@ -12,6 +12,7 @@ export const TIPS = [
'clips', 'clips',
'userLists', 'userLists',
'postForm', 'postForm',
'deck',
'tl.home', 'tl.home',
'tl.local', 'tl.local',
'tl.social', 'tl.social',

View File

@ -14,6 +14,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-if="store.r.realtimeMode.value" class="ti ti-bolt ti-fw"></i> <i v-if="store.r.realtimeMode.value" class="ti ti-bolt ti-fw"></i>
<i v-else class="ti ti-bolt-off ti-fw"></i> <i v-else class="ti ti-bolt-off ti-fw"></i>
</button> </button>
<button v-if="!iconOnly && showWidgetButton" v-tooltip.noDelay.right="i18n.ts.widgets" class="_button" :class="[$style.widget]" @click="() => emit('widgetButtonClick')">
<i class="ti ti-apps ti-fw"></i>
</button>
</div> </div>
<div :class="$style.middle"> <div :class="$style.middle">
<MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact> <MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact>
@ -51,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkA> </MkA>
</div> </div>
<div :class="$style.bottom"> <div :class="$style.bottom">
<button v-if="showWidgetButton" v-tooltip.noDelay.right="i18n.ts.widgets" class="_button" :class="[$style.widget]" @click="() => emit('widgetButtonClick')"> <button v-if="iconOnly && showWidgetButton" v-tooltip.noDelay.right="i18n.ts.widgets" class="_button" :class="[$style.widget]" @click="() => emit('widgetButtonClick')">
<i class="ti ti-apps ti-fw"></i> <i class="ti ti-apps ti-fw"></i>
</button> </button>
<button v-if="iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode"> <button v-if="iconOnly" v-tooltip.noDelay.right="i18n.ts.realtimeMode" class="_button" :class="[$style.realtimeMode, store.r.realtimeMode.value ? $style.on : null]" @click="toggleRealtimeMode">
@ -436,6 +439,12 @@ function menuEdit() {
} }
} }
.widget {
display: inline-block;
width: var(--top-height);
margin-left: auto;
}
.bottom { .bottom {
position: sticky; position: sticky;
bottom: 0; bottom: 0;

View File

@ -38,36 +38,39 @@ SPDX-License-Identifier: AGPL-3.0-only
@headerWheel="onWheel" @headerWheel="onWheel"
/> />
</section> </section>
<div v-if="layout.length === 0" class="_panel" :class="$style.onboarding"> <div v-if="layout.length === 0" class="_panel _gaps" :class="$style.onboarding">
<div>{{ i18n.ts._deck.introduction }}</div> <div>{{ i18n.ts._deck.introduction }}</div>
<div>{{ i18n.ts._deck.introduction2 }}</div> <div>{{ i18n.ts._deck.introduction2 }}</div>
<MkInfo v-if="!store.r.tips.value.deck" closable @close="closeTip('deck')">
<button class="_textButton" @click="showTour">{{ i18n.ts._deck.showHowToUse }}</button>
</MkInfo>
</div> </div>
</div> </div>
<div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu"> <div v-if="prefer.r['deck.menuPosition'].value === 'right'" :class="$style.sideMenu">
<div :class="$style.sideMenuTop"> <div :class="$style.sideMenuTop">
<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> <button ref="swicthProfileButtonEl" v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.sideMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> <button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.sideMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
</div> </div>
<div :class="$style.sideMenuMiddle"> <div :class="$style.sideMenuMiddle">
<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> <button ref="addColumnButtonEl" v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.sideMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
</div> </div>
<div :class="$style.sideMenuBottom"> <div :class="$style.sideMenuBottom">
<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> <button ref="settingsButtonEl" v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.sideMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
</div> </div>
</div> </div>
</div> </div>
<div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu"> <div v-if="prefer.r['deck.menuPosition'].value === 'bottom'" :class="$style.bottomMenu">
<div :class="$style.bottomMenuLeft"> <div :class="$style.bottomMenuLeft">
<button v-tooltip.noDelay.left="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button> <button ref="swicthProfileButtonEl" v-tooltip.noDelay.top="`${i18n.ts._deck.profile}: ${prefer.s['deck.profile']}`" :class="$style.bottomMenuButton" class="_button" @click="switchProfileMenu"><i class="ti ti-caret-down"></i></button>
<button v-tooltip.noDelay.left="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button> <button v-tooltip.noDelay.top="i18n.ts._deck.deleteProfile" :class="$style.bottomMenuButton" class="_button" @click="deleteProfile"><i class="ti ti-trash"></i></button>
</div> </div>
<div :class="$style.bottomMenuMiddle"> <div :class="$style.bottomMenuMiddle">
<button v-tooltip.noDelay.left="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button> <button ref="addColumnButtonEl" v-tooltip.noDelay.top="i18n.ts._deck.addColumn" :class="$style.bottomMenuButton" class="_button" @click="addColumn"><i class="ti ti-plus"></i></button>
</div> </div>
<div :class="$style.bottomMenuRight"> <div :class="$style.bottomMenuRight">
<button v-tooltip.noDelay.left="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button> <button ref="settingsButtonEl" v-tooltip.noDelay.top="i18n.ts.settings" :class="$style.bottomMenuButton" class="_button" @click="showSettings"><i class="ti ti-settings-2"></i></button>
</div> </div>
</div> </div>
@ -96,6 +99,7 @@ import { $i } from '@/i.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { deviceKind } from '@/utility/device-kind.js'; import { deviceKind } from '@/utility/device-kind.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { store } from '@/store.js';
import XMainColumn from '@/ui/deck/main-column.vue'; import XMainColumn from '@/ui/deck/main-column.vue';
import XTlColumn from '@/ui/deck/tl-column.vue'; import XTlColumn from '@/ui/deck/tl-column.vue';
import XAntennaColumn from '@/ui/deck/antenna-column.vue'; import XAntennaColumn from '@/ui/deck/antenna-column.vue';
@ -107,10 +111,13 @@ import XMentionsColumn from '@/ui/deck/mentions-column.vue';
import XDirectColumn from '@/ui/deck/direct-column.vue'; import XDirectColumn from '@/ui/deck/direct-column.vue';
import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue'; import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue';
import XChatColumn from '@/ui/deck/chat-column.vue'; import XChatColumn from '@/ui/deck/chat-column.vue';
import MkInfo from '@/components/MkInfo.vue';
import { mainRouter } from '@/router.js'; import { mainRouter } from '@/router.js';
import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js'; import { columns, layout, columnTypes, switchProfileMenu, addColumn as addColumnToStore, deleteProfile as deleteProfile_ } from '@/deck.js';
import { shouldSuggestRestoreBackup } from '@/preferences/utility.js'; import { shouldSuggestRestoreBackup } from '@/preferences/utility.js';
import { shouldSuggestReload } from '@/utility/reload-suggest.js'; import { shouldSuggestReload } from '@/utility/reload-suggest.js';
import { startTour } from '@/utility/tour.js';
import { closeTip } from '@/tips.js';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue')); const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue'));
@ -163,6 +170,9 @@ function showSettings() {
} }
const columnsEl = useTemplateRef('columnsEl'); const columnsEl = useTemplateRef('columnsEl');
const addColumnButtonEl = useTemplateRef('addColumnButtonEl');
const settingsButtonEl = useTemplateRef('settingsButtonEl');
const swicthProfileButtonEl = useTemplateRef('swicthProfileButtonEl');
const addColumn = async (ev) => { const addColumn = async (ev) => {
const { canceled, result: column } = await os.select({ const { canceled, result: column } = await os.select({
@ -218,6 +228,30 @@ async function deleteProfile() {
os.success(); os.success();
} }
function showTour() {
if (addColumnButtonEl.value == null ||
settingsButtonEl.value == null ||
swicthProfileButtonEl.value == null) {
return;
}
startTour([{
element: addColumnButtonEl.value,
title: i18n.ts._deck._howToUse.addColumn_title,
description: i18n.ts._deck._howToUse.addColumn_description,
}, {
element: settingsButtonEl.value,
title: i18n.ts._deck._howToUse.settings_title,
description: i18n.ts._deck._howToUse.settings_description,
}, {
element: swicthProfileButtonEl.value,
title: i18n.ts._deck._howToUse.switchProfile_title,
description: i18n.ts._deck._howToUse.switchProfile_description,
}]).then(() => {
closeTip('deck');
});
}
window.document.documentElement.style.overflowY = 'hidden'; window.document.documentElement.style.overflowY = 'hidden';
window.document.documentElement.style.scrollBehavior = 'auto'; window.document.documentElement.style.scrollBehavior = 'auto';
</script> </script>
@ -345,7 +379,7 @@ window.document.documentElement.style.scrollBehavior = 'auto';
} }
.bottomMenuButton { .bottomMenuButton {
display: block; display: inline-block;
height: 100%; height: 100%;
aspect-ratio: 1; aspect-ratio: 1;
} }

View File

@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/> <XTitlebar v-if="prefer.r.showTitlebar.value" style="flex-shrink: 0;"/>
<div :class="$style.nonTitlebarArea"> <div :class="$style.nonTitlebarArea">
<XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!isDesktop" @widgetButtonClick="widgetsShowing = true"/> <XSidebar v-if="!isMobile" :class="$style.sidebar" :showWidgetButton="!showWidgetsSide" @widgetButtonClick="widgetsShowing = true"/>
<div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu"> <div :class="[$style.contents, !isMobile && prefer.r.showTitlebar.value ? $style.withSidebarAndTitlebar : null]" @contextmenu.stop="onContextmenu">
<div> <div>
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/> <XMobileFooterMenu v-if="isMobile" ref="navFooter" v-model:drawerMenuShowing="drawerMenuShowing" v-model:widgetsShowing="widgetsShowing"/>
</div> </div>
<div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets"> <div v-if="showWidgetsSide && !pageMetadata?.needWideArea" :class="$style.widgets">
<XWidgets/> <XWidgets/>
</div> </div>
</div> </div>
@ -64,7 +64,8 @@ const DESKTOP_THRESHOLD = 1100;
const MOBILE_THRESHOLD = 500; const MOBILE_THRESHOLD = 500;
// UI deviceKind === 'desktop' // UI deviceKind === 'desktop'
const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); const showWidgetsSide = window.innerWidth >= DESKTOP_THRESHOLD;
const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD;
@ -102,14 +103,6 @@ if (window.innerWidth > 1024) {
} }
} }
onMounted(() => {
if (!isDesktop.value) {
window.addEventListener('resize', () => {
if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true;
}, { passive: true });
}
});
const onContextmenu = (ev) => { const onContextmenu = (ev) => {
if (isLink(ev.target)) return; if (isLink(ev.target)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;

View File

@ -10948,6 +10948,36 @@ export interface Locale extends ILocale {
* *
*/ */
"enableSyncBetweenDevicesForProfiles": string; "enableSyncBetweenDevicesForProfiles": string;
/**
* UIの説明を見る
*/
"showHowToUse": string;
"_howToUse": {
/**
*
*/
"addColumn_title": string;
/**
*
*/
"addColumn_description": string;
/**
* UI設定
*/
"settings_title": string;
/**
* UIの詳細設定を行えます
*/
"settings_description": string;
/**
*
*/
"switchProfile_title": string;
/**
* UIのレイアウトをプロファイルとして保存し
*/
"switchProfile_description": string;
};
"_columns": { "_columns": {
/** /**
* *

View File

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