From a45ccc18b403682be3f1af901aec5a542401ea08 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Fri, 4 Jul 2025 18:33:41 +0900
Subject: [PATCH 01/40] refactor
---
packages/backend/test/unit/ReactionService.ts | 70 +++++++++----------
1 file changed, 35 insertions(+), 35 deletions(-)
diff --git a/packages/backend/test/unit/ReactionService.ts b/packages/backend/test/unit/ReactionService.ts
index 1957f4544c..3cfb4ff3f8 100644
--- a/packages/backend/test/unit/ReactionService.ts
+++ b/packages/backend/test/unit/ReactionService.ts
@@ -21,73 +21,73 @@ describe('ReactionService', () => {
});
describe('normalize', () => {
- test('絵文字リアクションはそのまま', async () => {
- assert.strictEqual(await reactionService.normalize('👍'), '👍');
- assert.strictEqual(await reactionService.normalize('🍅'), '🍅');
+ test('絵文字リアクションはそのまま', () => {
+ assert.strictEqual(reactionService.normalize('👍'), '👍');
+ assert.strictEqual(reactionService.normalize('🍅'), '🍅');
});
- test('既存のリアクションは絵文字化する pudding', async () => {
- assert.strictEqual(await reactionService.normalize('pudding'), '🍮');
+ test('既存のリアクションは絵文字化する pudding', () => {
+ assert.strictEqual(reactionService.normalize('pudding'), '🍮');
});
- test('既存のリアクションは絵文字化する like', async () => {
- assert.strictEqual(await reactionService.normalize('like'), '👍');
+ test('既存のリアクションは絵文字化する like', () => {
+ assert.strictEqual(reactionService.normalize('like'), '👍');
});
- test('既存のリアクションは絵文字化する love', async () => {
- assert.strictEqual(await reactionService.normalize('love'), '❤');
+ test('既存のリアクションは絵文字化する love', () => {
+ assert.strictEqual(reactionService.normalize('love'), '❤');
});
- test('既存のリアクションは絵文字化する laugh', async () => {
- assert.strictEqual(await reactionService.normalize('laugh'), '😆');
+ test('既存のリアクションは絵文字化する laugh', () => {
+ assert.strictEqual(reactionService.normalize('laugh'), '😆');
});
- test('既存のリアクションは絵文字化する hmm', async () => {
- assert.strictEqual(await reactionService.normalize('hmm'), '🤔');
+ test('既存のリアクションは絵文字化する hmm', () => {
+ assert.strictEqual(reactionService.normalize('hmm'), '🤔');
});
- test('既存のリアクションは絵文字化する surprise', async () => {
- assert.strictEqual(await reactionService.normalize('surprise'), '😮');
+ test('既存のリアクションは絵文字化する surprise', () => {
+ assert.strictEqual(reactionService.normalize('surprise'), '😮');
});
- test('既存のリアクションは絵文字化する congrats', async () => {
- assert.strictEqual(await reactionService.normalize('congrats'), '🎉');
+ test('既存のリアクションは絵文字化する congrats', () => {
+ assert.strictEqual(reactionService.normalize('congrats'), '🎉');
});
- test('既存のリアクションは絵文字化する angry', async () => {
- assert.strictEqual(await reactionService.normalize('angry'), '💢');
+ test('既存のリアクションは絵文字化する angry', () => {
+ assert.strictEqual(reactionService.normalize('angry'), '💢');
});
- test('既存のリアクションは絵文字化する confused', async () => {
- assert.strictEqual(await reactionService.normalize('confused'), '😥');
+ test('既存のリアクションは絵文字化する confused', () => {
+ assert.strictEqual(reactionService.normalize('confused'), '😥');
});
- test('既存のリアクションは絵文字化する rip', async () => {
- assert.strictEqual(await reactionService.normalize('rip'), '😇');
+ test('既存のリアクションは絵文字化する rip', () => {
+ assert.strictEqual(reactionService.normalize('rip'), '😇');
});
- test('既存のリアクションは絵文字化する star', async () => {
- assert.strictEqual(await reactionService.normalize('star'), '⭐');
+ test('既存のリアクションは絵文字化する star', () => {
+ assert.strictEqual(reactionService.normalize('star'), '⭐');
});
- test('異体字セレクタ除去', async () => {
- assert.strictEqual(await reactionService.normalize('㊗️'), '㊗');
+ test('異体字セレクタ除去', () => {
+ assert.strictEqual(reactionService.normalize('㊗️'), '㊗');
});
- test('異体字セレクタ除去 必要なし', async () => {
- assert.strictEqual(await reactionService.normalize('㊗'), '㊗');
+ test('異体字セレクタ除去 必要なし', () => {
+ assert.strictEqual(reactionService.normalize('㊗'), '㊗');
});
- test('fallback - null', async () => {
- assert.strictEqual(await reactionService.normalize(null), '❤');
+ test('fallback - null', () => {
+ assert.strictEqual(reactionService.normalize(null), '❤');
});
- test('fallback - empty', async () => {
- assert.strictEqual(await reactionService.normalize(''), '❤');
+ test('fallback - empty', () => {
+ assert.strictEqual(reactionService.normalize(''), '❤');
});
- test('fallback - unknown', async () => {
- assert.strictEqual(await reactionService.normalize('unknown'), '❤');
+ test('fallback - unknown', () => {
+ assert.strictEqual(reactionService.normalize('unknown'), '❤');
});
});
From 7cf1eccd0434229b1b5d261d44e5e0bccf7a81ee Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sat, 5 Jul 2025 08:31:20 +0900
Subject: [PATCH 02/40] clean up
---
packages/frontend/src/pages/user/home.vue | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index ea77444afd..ed3ae6a2aa 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -25,7 +25,6 @@ SPDX-License-Identifier: AGPL-3.0-only
-
@@ -23,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
import { markRaw } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import MkNote from '@/components/MkNote.vue';
-import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { Paginator } from '@/utility/paginator.js';
From e6ec15e397e150a12486d097d4b789a98b7ae639 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 6 Jul 2025 09:54:49 +0900
Subject: [PATCH 11/40] =?UTF-8?q?feat:=20=E7=89=B9=E5=AE=9A=E3=81=AE?=
=?UTF-8?q?=E3=83=89=E3=83=A9=E3=82=A4=E3=83=96=E3=83=95=E3=82=A1=E3=82=A4?=
=?UTF-8?q?=E3=83=AB=E3=82=92=E6=B7=BB=E4=BB=98=E3=81=97=E3=81=A6=E3=81=84?=
=?UTF-8?q?=E3=82=8B=E3=83=81=E3=83=A3=E3=83=83=E3=83=88=E3=83=A1=E3=83=83?=
=?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E4=B8=80=E8=A6=A7=E3=81=A7?=
=?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
CHANGELOG.md | 1 +
locales/index.d.ts | 4 +
locales/ja-JP.yml | 1 +
.../backend/src/server/api/endpoint-list.ts | 1 +
.../drive/files/attached-chat-messages.ts | 85 +++++++++++++++++++
.../frontend/src/components/MkUserList.vue | 2 +-
.../frontend/src/pages/admin-file.chat.vue | 38 +++++++++
packages/frontend/src/pages/admin-file.vue | 24 ++++--
packages/misskey-js/etc/misskey-js.api.md | 8 ++
.../misskey-js/src/autogen/apiClientJSDoc.ts | 11 +++
packages/misskey-js/src/autogen/endpoint.ts | 3 +
packages/misskey-js/src/autogen/entities.ts | 2 +
packages/misskey-js/src/autogen/types.ts | 83 ++++++++++++++++++
13 files changed, 257 insertions(+), 6 deletions(-)
create mode 100644 packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
create mode 100644 packages/frontend/src/pages/admin-file.chat.vue
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 516f5f5fcd..cf422f5808 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
- Feat: ノートの下書き機能
- Feat: クリップ内でノートを検索できるように
- Feat: Playを検索できるように
+- Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように
### Client
- Feat: モデログを検索できるように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index ce6ee44653..d5ec9f5d77 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -10890,6 +10890,10 @@ export interface Locale extends ILocale {
* 添付されているノート
*/
"attachedNotes": string;
+ /**
+ * 利用
+ */
+ "usage": string;
/**
* このページは、このファイルをアップロードしたユーザーしか閲覧できません。
*/
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0d2d8c4611..760f89ef64 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2885,6 +2885,7 @@ _fileViewer:
url: "URL"
uploadedAt: "追加日"
attachedNotes: "添付されているノート"
+ usage: "利用"
thisPageCanBeSeenFromTheAuthor: "このページは、このファイルをアップロードしたユーザーしか閲覧できません。"
_externalResourceInstaller:
diff --git a/packages/backend/src/server/api/endpoint-list.ts b/packages/backend/src/server/api/endpoint-list.ts
index eb83c11b39..5c4a58a6fc 100644
--- a/packages/backend/src/server/api/endpoint-list.ts
+++ b/packages/backend/src/server/api/endpoint-list.ts
@@ -168,6 +168,7 @@ export * as 'clips/update' from './endpoints/clips/update.js';
export * as 'drive' from './endpoints/drive.js';
export * as 'drive/files' from './endpoints/drive/files.js';
export * as 'drive/files/attached-notes' from './endpoints/drive/files/attached-notes.js';
+export * as 'drive/files/attached-chat-messages' from './endpoints/drive/files/attached-chat-messages.js';
export * as 'drive/files/check-existence' from './endpoints/drive/files/check-existence.js';
export * as 'drive/files/create' from './endpoints/drive/files/create.js';
export * as 'drive/files/delete' from './endpoints/drive/files/delete.js';
diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
new file mode 100644
index 0000000000..5be477f468
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/drive/files/attached-chat-messages.ts
@@ -0,0 +1,85 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { DriveFilesRepository, ChatMessagesRepository } from '@/models/_.js';
+import { QueryService } from '@/core/QueryService.js';
+import { DI } from '@/di-symbols.js';
+import { RoleService } from '@/core/RoleService.js';
+import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
+import { ApiError } from '../../../error.js';
+
+export const meta = {
+ tags: ['drive', 'chat'],
+
+ requireCredential: true,
+
+ kind: 'read:drive',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'ChatMessage',
+ },
+ },
+
+ errors: {
+ noSuchFile: {
+ message: 'No such file.',
+ code: 'NO_SUCH_FILE',
+ id: '485ce26d-f5d2-4313-9783-e689d131eafb',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ sinceId: { type: 'string', format: 'misskey:id' },
+ untilId: { type: 'string', format: 'misskey:id' },
+ sinceDate: { type: 'integer' },
+ untilDate: { type: 'integer' },
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ fileId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['fileId'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint { // eslint-disable-line import/no-default-export
+ constructor(
+ @Inject(DI.driveFilesRepository)
+ private driveFilesRepository: DriveFilesRepository,
+
+ @Inject(DI.chatMessagesRepository)
+ private chatMessagesRepository: ChatMessagesRepository,
+
+ private chatEntityService: ChatEntityService,
+ private queryService: QueryService,
+ private roleService: RoleService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const file = await this.driveFilesRepository.findOneBy({
+ id: ps.fileId,
+ userId: await this.roleService.isModerator(me) ? undefined : me.id,
+ });
+
+ if (file == null) {
+ throw new ApiError(meta.errors.noSuchFile);
+ }
+
+ const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate);
+ query.andWhere('message.fileId = :fileId', { fileId: file.id });
+
+ const messages = await query.limit(ps.limit).getMany();
+
+ return await this.chatEntityService.packMessagesDetailed(messages, me);
+ });
+ }
+}
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index c639e18b1d..e3469d0fd7 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
diff --git a/packages/frontend/src/pages/admin-file.chat.vue b/packages/frontend/src/pages/admin-file.chat.vue
new file mode 100644
index 0000000000..e451da51a3
--- /dev/null
+++ b/packages/frontend/src/pages/admin-file.chat.vue
@@ -0,0 +1,38 @@
+
+
+
+
+ {{ i18n.ts._fileViewer.thisPageCanBeSeenFromTheAuthor }}
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 8495642a8c..7a49ba542f 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -44,8 +44,19 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.delete }}
-
-
+
+
+
+
{{ i18n.ts.requireAdminForView }}
@@ -86,12 +97,15 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { iAmAdmin, iAmModerator } from '@/i.js';
+import MkTabs from '@/components/MkTabs.vue';
const tab = ref('overview');
const file = ref
(null);
const info = ref(null);
const isSensitive = ref(false);
+const usageTab = ref<'note' | 'chat'>('note');
const XNotes = defineAsyncComponent(() => import('./drive.file.notes.vue'));
+const XChat = defineAsyncComponent(() => import('./admin-file.chat.vue'));
const props = defineProps<{
fileId: string,
@@ -147,9 +161,9 @@ const headerTabs = computed(() => [{
title: i18n.ts.overview,
icon: 'ti ti-info-circle',
}, iAmModerator ? {
- key: 'notes',
- title: i18n.ts._fileViewer.attachedNotes,
- icon: 'ti ti-pencil',
+ key: 'usage',
+ title: i18n.ts._fileViewer.usage,
+ icon: 'ti ti-plus',
} : null, iAmModerator ? {
key: 'ip',
title: 'IP',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index f38e959fb2..d584efe731 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1226,6 +1226,12 @@ type DateString = string;
// @public (undocumented)
type DriveFile = components['schemas']['DriveFile'];
+// @public (undocumented)
+type DriveFilesAttachedChatMessagesRequest = operations['drive___files___attached-chat-messages']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesAttachedChatMessagesResponse = operations['drive___files___attached-chat-messages']['responses']['200']['content']['application/json'];
+
// @public (undocumented)
type DriveFilesAttachedNotesRequest = operations['drive___files___attached-notes']['requestBody']['content']['application/json'];
@@ -1740,6 +1746,8 @@ declare namespace entities {
DriveResponse,
DriveFilesRequest,
DriveFilesResponse,
+ DriveFilesAttachedChatMessagesRequest,
+ DriveFilesAttachedChatMessagesResponse,
DriveFilesAttachedNotesRequest,
DriveFilesAttachedNotesResponse,
DriveFilesCheckExistenceRequest,
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 60e238351c..4a13045592 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -2018,6 +2018,17 @@ declare module '../api.js' {
credential?: string | null,
): Promise>;
+ /**
+ * No description provided.
+ *
+ * **Credential required**: *Yes* / **Permission**: *read:drive*
+ */
+ request(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise>;
+
/**
* Find the notes to which the given file is attached.
*
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 929cca183f..5ef493946c 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -275,6 +275,8 @@ import type {
DriveResponse,
DriveFilesRequest,
DriveFilesResponse,
+ DriveFilesAttachedChatMessagesRequest,
+ DriveFilesAttachedChatMessagesResponse,
DriveFilesAttachedNotesRequest,
DriveFilesAttachedNotesResponse,
DriveFilesCheckExistenceRequest,
@@ -833,6 +835,7 @@ export type Endpoints = {
'clips/update': { req: ClipsUpdateRequest; res: ClipsUpdateResponse };
'drive': { req: EmptyRequest; res: DriveResponse };
'drive/files': { req: DriveFilesRequest; res: DriveFilesResponse };
+ 'drive/files/attached-chat-messages': { req: DriveFilesAttachedChatMessagesRequest; res: DriveFilesAttachedChatMessagesResponse };
'drive/files/attached-notes': { req: DriveFilesAttachedNotesRequest; res: DriveFilesAttachedNotesResponse };
'drive/files/check-existence': { req: DriveFilesCheckExistenceRequest; res: DriveFilesCheckExistenceResponse };
'drive/files/create': { req: DriveFilesCreateRequest; res: DriveFilesCreateResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 002dfaaf30..a11bbefde5 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -278,6 +278,8 @@ export type ClipsUpdateResponse = operations['clips___update']['responses']['200
export type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
export type DriveFilesRequest = operations['drive___files']['requestBody']['content']['application/json'];
export type DriveFilesResponse = operations['drive___files']['responses']['200']['content']['application/json'];
+export type DriveFilesAttachedChatMessagesRequest = operations['drive___files___attached-chat-messages']['requestBody']['content']['application/json'];
+export type DriveFilesAttachedChatMessagesResponse = operations['drive___files___attached-chat-messages']['responses']['200']['content']['application/json'];
export type DriveFilesAttachedNotesRequest = operations['drive___files___attached-notes']['requestBody']['content']['application/json'];
export type DriveFilesAttachedNotesResponse = operations['drive___files___attached-notes']['responses']['200']['content']['application/json'];
export type DriveFilesCheckExistenceRequest = operations['drive___files___check-existence']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index bea4090aba..df6a22ec41 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -1653,6 +1653,15 @@ export type paths = {
*/
post: operations['drive___files'];
};
+ '/drive/files/attached-chat-messages': {
+ /**
+ * drive/files/attached-chat-messages
+ * @description No description provided.
+ *
+ * **Credential required**: *Yes* / **Permission**: *read:drive*
+ */
+ post: operations['drive___files___attached-chat-messages'];
+ };
'/drive/files/attached-notes': {
/**
* drive/files/attached-notes
@@ -18748,6 +18757,80 @@ export interface operations {
};
};
};
+ 'drive___files___attached-chat-messages': {
+ requestBody: {
+ content: {
+ 'application/json': {
+ /** Format: misskey:id */
+ sinceId?: string;
+ /** Format: misskey:id */
+ untilId?: string;
+ sinceDate?: number;
+ untilDate?: number;
+ /** @default 10 */
+ limit?: number;
+ /** Format: misskey:id */
+ fileId: string;
+ };
+ };
+ };
+ responses: {
+ /** @description OK (with results) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['ChatMessage'][];
+ };
+ };
+ /** @description Client error */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
'drive___files___attached-notes': {
requestBody: {
content: {
From 40a35968f0a1e49a6ccae43028790b01c7e39ec6 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 6 Jul 2025 15:54:33 +0900
Subject: [PATCH 12/40] clean up
---
packages/frontend/src/style.scss | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 6a9aa08b30..7027b844d8 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -45,27 +45,6 @@ html {
&, * {
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
scrollbar-width: thin;
-
- &::-webkit-scrollbar {
- width: 6px;
- height: 6px;
- }
-
- &::-webkit-scrollbar-track {
- background: inherit;
- }
-
- &::-webkit-scrollbar-thumb {
- background: var(--MI_THEME-scrollbarHandle);
-
- &:hover {
- background: var(--MI_THEME-scrollbarHandleHover);
- }
-
- &:active {
- background: var(--MI_THEME-accent);
- }
- }
}
&.f-1 {
From 004cfd5e4b3847175653cafa225fe7a6480af728 Mon Sep 17 00:00:00 2001
From: syuilo <4439005+syuilo@users.noreply.github.com>
Date: Sun, 6 Jul 2025 15:57:21 +0900
Subject: [PATCH 13/40] clean up
---
packages/frontend/src/components/MkEmojiPicker.vue | 5 -----
packages/frontend/src/components/MkTabs.vue | 4 ----
.../src/components/global/MkPageHeader.tabs.vue | 4 ----
packages/frontend/src/ui/deck/column.vue | 12 ------------
4 files changed, 25 deletions(-)
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 86f019c95c..68da098439 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -684,13 +684,8 @@ defineExpose({
height: 100%;
overflow-y: auto;
overflow-x: hidden;
-
scrollbar-width: none;
- &::-webkit-scrollbar {
- display: none;
- }
-
> .group {
&:not(.index) {
padding: 4px 0 8px 0;
diff --git a/packages/frontend/src/components/MkTabs.vue b/packages/frontend/src/components/MkTabs.vue
index a1f30100d0..5c4a67b026 100644
--- a/packages/frontend/src/components/MkTabs.vue
+++ b/packages/frontend/src/components/MkTabs.vue
@@ -169,10 +169,6 @@ onUnmounted(() => {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-
- &::-webkit-scrollbar {
- display: none;
- }
}
.tabsInner {
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 255fca8f86..f2173b2e22 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -194,10 +194,6 @@ onUnmounted(() => {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-
- &::-webkit-scrollbar {
- display: none;
- }
}
.tabsInner {
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 4e79b301e3..11937fda24 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -368,10 +368,6 @@ function onDrop(ev) {
> .body {
background: transparent !important;
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
-
- &::-webkit-scrollbar-track {
- background: transparent;
- }
}
}
@@ -397,10 +393,6 @@ function onDrop(ev) {
> .body {
background: var(--MI_THEME-bg) !important;
scrollbar-color: var(--MI_THEME-scrollbarHandle) transparent;
-
- &::-webkit-scrollbar-track {
- background: inherit;
- }
}
}
}
@@ -487,9 +479,5 @@ function onDrop(ev) {
container-type: size;
background-color: var(--MI_THEME-bg);
scrollbar-color: var(--MI_THEME-scrollbarHandle) var(--MI_THEME-panel);
-
- &::-webkit-scrollbar-track {
- background: var(--MI_THEME-panel);
- }
}
From 9dddc847505501c9d269d6f3a8090203711cb0f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
<67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 6 Jul 2025 17:24:34 +0900
Subject: [PATCH 14/40] =?UTF-8?q?refactor(frontend):=20menu=E3=81=AE?=
=?UTF-8?q?=E5=9E=8B=E5=AE=9A=E7=BE=A9=E3=81=AE=E5=8F=AF=E8=AA=AD=E6=80=A7?=
=?UTF-8?q?=E5=90=91=E4=B8=8A=20(#16261)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/frontend/src/types/menu.ts | 117 ++++++++++++++++++++++++----
1 file changed, 101 insertions(+), 16 deletions(-)
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index fae7370341..6f2f08cb6d 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -4,10 +4,10 @@
*/
import * as Misskey from 'misskey-js';
-import type { Component, ComputedRef, Ref } from 'vue';
+import type { Component, ComputedRef, Ref, MaybeRef } from 'vue';
import type { ComponentProps as CP } from 'vue-component-type-helpers';
-type ComponentProps = { [K in keyof CP]: CP[K] | Ref[K]> };
+type ComponentProps = { [K in keyof CP]: MaybeRef[K]> };
type MenuRadioOptionsDef = Record;
@@ -15,22 +15,107 @@ type Text = string | ComputedRef;
export type MenuAction = (ev: MouseEvent) => void;
-export type MenuDivider = { type: 'divider' };
-export type MenuNull = undefined;
-export type MenuLabel = { type: 'label', text: Text, caption?: Text };
-export type MenuLink = { type: 'link', to: string, text: Text, caption?: Text, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
-export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: Text, caption?: Text, icon?: string, indicate?: boolean };
-export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
-export type MenuSwitch = { type: 'switch', ref: Ref, text: Text, caption?: Text, icon?: string, disabled?: boolean | Ref };
-export type MenuButton = { type?: 'button', text: Text, caption?: Text, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean | ComputedRef, avatar?: Misskey.entities.User; action: MenuAction };
-export type MenuRadio = { type: 'radio', text: Text, caption?: Text, icon?: string, ref: Ref, options: MenuRadioOptionsDef, disabled?: boolean | Ref };
-export type MenuRadioOption = { type: 'radioOption', text: Text, caption?: Text, action: MenuAction; active?: boolean | ComputedRef };
-export type MenuComponent = { type: 'component', component: T, props?: ComponentProps };
-export type MenuParent = { type: 'parent', text: Text, caption?: Text, icon?: string, children: MenuItem[] | (() => Promise