From eca8c7a52f9d190a89b380290b5b50d0cb96c750 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 10:01:34 +0900
Subject: [PATCH 01/62] tweak ui

---
 packages/frontend/src/store.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index b4713a323e..8a43ba892d 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -118,7 +118,7 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'deviceAccount',
 		default: [
 			'notifications',
-			'favorites',
+			'clips',
 			'drive',
 			'followRequests',
 			'-',

From c836157edb869e80b15f51bb8f48725e3b898b9a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 14:12:33 +0900
Subject: [PATCH 02/62] =?UTF-8?q?enhance:=20=E4=BA=8C=E8=A6=81=E7=B4=A0?=
 =?UTF-8?q?=E8=AA=8D=E8=A8=BC=E8=A8=AD=E5=AE=9A=E6=99=82=E3=81=AE=E3=82=BB?=
 =?UTF-8?q?=E3=82=AD=E3=83=A5=E3=83=AA=E3=83=86=E3=82=A3=E3=82=92=E5=BC=B7?=
 =?UTF-8?q?=E5=8C=96=20(#11863)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 二要素認証設定時のセキュリティを強化

パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要にする

* Update CoreModule.ts

* Update 2fa.ts

* wip

* wip

* Update 2fa.ts

* tweak
---
 CHANGELOG.md                                  |  9 +--
 locales/index.d.ts                            |  3 +-
 locales/ja-JP.yml                             |  3 +-
 packages/backend/src/core/CoreModule.ts       |  6 ++
 packages/backend/src/core/UserAuthService.ts  | 45 ++++++++++++
 .../src/server/api/SigninApiService.ts        | 28 +++-----
 .../src/server/api/endpoints/i/2fa/done.ts    |  2 +-
 .../server/api/endpoints/i/2fa/key-done.ts    | 20 +++++-
 .../api/endpoints/i/2fa/register-key.ts       | 20 +++++-
 .../server/api/endpoints/i/2fa/register.ts    | 21 +++++-
 .../server/api/endpoints/i/2fa/remove-key.ts  | 20 +++++-
 .../server/api/endpoints/i/2fa/unregister.ts  | 20 +++++-
 .../server/api/endpoints/i/change-password.ts | 22 +++++-
 .../server/api/endpoints/i/delete-account.ts  | 23 ++++--
 .../server/api/endpoints/i/update-email.ts    | 20 +++++-
 packages/backend/test/e2e/2fa.ts              | 49 +++++++++++++
 packages/frontend/src/components/MkInput.vue  |  4 ++
 .../src/components/MkPasswordDialog.vue       | 70 +++++++++++++++++++
 packages/frontend/src/os.ts                   | 13 ++++
 packages/frontend/src/pages/settings/2fa.vue  | 65 ++++++++---------
 .../frontend/src/pages/settings/email.vue     | 20 +++---
 .../frontend/src/pages/settings/other.vue     | 10 ++-
 .../frontend/src/pages/settings/security.vue  | 29 ++++----
 23 files changed, 400 insertions(+), 122 deletions(-)
 create mode 100644 packages/backend/src/core/UserAuthService.ts
 create mode 100644 packages/frontend/src/components/MkPasswordDialog.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 082b448c2b..92bb2c8161 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,8 @@
 - Feat: プロフィールでのリンク検証
 - Feat: 通知をテストできるようになりました
 - Feat: PWAのアイコンが設定できるようになりました
+- Enhance: 二要素認証設定時のセキュリティを強化
+	- パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました
 - Enhance: manifest.jsonをオーバーライド可能に
 - Enhance: 依存関係の更新
 - Enhance: ローカリゼーションの更新
@@ -40,10 +42,8 @@
 - Feat: Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`)
 - Feat: クライアントを起動している間、デバイスの画面が自動でオフになるのを防ぐオプションを追加
 - Feat: 新しい実績を追加
-- Enhance: ノート詳細ページを改修
-	- 読み込み時のパフォーマンスが向上しました
-	- リノート一覧、リアクション一覧がタブとして追加されました
-		- ノートのメニューからは当該項目は消えました
+- Enhance: ノート詳細ページでリノート一覧、リアクション一覧タブを追加
+	- ノートのメニューからは当該項目は消えました
 - Enhance: プロフィールにその人が作ったPlayの一覧出せるように
 - Enhance: メニューのスイッチの動作を改善
 - Enhance: 絵文字ピッカーの検索の表示件数を100件に増加
@@ -62,6 +62,7 @@
 - Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
 - Enhance: Mk:apiが失敗した時にエラー型の値(AiScript 0.16.0で追加)を返すように
 - Enhance: ScratchpadでAsync:系関数やボタンのコールバックなどのエラーにもダイアログを出すように(試験的なためPlayなどには未実装)
+- Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました
 - Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善
 - Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように
 - Enhance: 細かなデザインの調整
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 784f53355a..f7bc350e2b 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1119,6 +1119,8 @@ export interface Locale {
     "verifiedLink": string;
     "notifyNotes": string;
     "unnotifyNotes": string;
+    "authentication": string;
+    "authenticationRequiredToContinue": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
@@ -1833,7 +1835,6 @@ export interface Locale {
     "_2fa": {
         "alreadyRegistered": string;
         "registerTOTP": string;
-        "passwordToTOTP": string;
         "step1": string;
         "step2": string;
         "step2Click": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index a7a6200472..5436cf0494 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1116,6 +1116,8 @@ keepScreenOn: "デバイスの画面を常にオンにする"
 verifiedLink: "このリンク先の所有者であることが確認されました"
 notifyNotes: "投稿を通知"
 unnotifyNotes: "投稿の通知を解除"
+authentication: "認証"
+authenticationRequiredToContinue: "続けるには認証を行ってください"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
@@ -1750,7 +1752,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "既に設定は完了しています。"
   registerTOTP: "認証アプリの設定を開始"
-  passwordToTOTP: "パスワードを入力してください"
   step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。"
   step2: "次に、表示されているQRコードをアプリでスキャンします。"
   step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。"
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 18271ee346..78333e70a5 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -51,6 +51,7 @@ import { UserKeypairService } from './UserKeypairService.js';
 import { UserListService } from './UserListService.js';
 import { UserMutingService } from './UserMutingService.js';
 import { UserSuspendService } from './UserSuspendService.js';
+import { UserAuthService } from './UserAuthService.js';
 import { VideoProcessingService } from './VideoProcessingService.js';
 import { WebhookService } from './WebhookService.js';
 import { ProxyAccountService } from './ProxyAccountService.js';
@@ -177,6 +178,7 @@ const $UserKeypairService: Provider = { provide: 'UserKeypairService', useExisti
 const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
 const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
 const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
+const $UserAuthService: Provider = { provide: 'UserAuthService', useExisting: UserAuthService };
 const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService };
 const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
 const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
@@ -306,6 +308,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserListService,
 		UserMutingService,
 		UserSuspendService,
+		UserAuthService,
 		VideoProcessingService,
 		WebhookService,
 		UtilityService,
@@ -428,6 +431,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserListService,
 		$UserMutingService,
 		$UserSuspendService,
+		$UserAuthService,
 		$VideoProcessingService,
 		$WebhookService,
 		$UtilityService,
@@ -551,6 +555,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		UserListService,
 		UserMutingService,
 		UserSuspendService,
+		UserAuthService,
 		VideoProcessingService,
 		WebhookService,
 		UtilityService,
@@ -672,6 +677,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$UserListService,
 		$UserMutingService,
 		$UserSuspendService,
+		$UserAuthService,
 		$VideoProcessingService,
 		$WebhookService,
 		$UtilityService,
diff --git a/packages/backend/src/core/UserAuthService.ts b/packages/backend/src/core/UserAuthService.ts
new file mode 100644
index 0000000000..ccf4dfc6bd
--- /dev/null
+++ b/packages/backend/src/core/UserAuthService.ts
@@ -0,0 +1,45 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { QueryFailedError } from 'typeorm';
+import * as OTPAuth from 'otpauth';
+import { DI } from '@/di-symbols.js';
+import type { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
+import { bindThis } from '@/decorators.js';
+import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
+import type { MiLocalUser } from '@/models/User.js';
+
+@Injectable()
+export class UserAuthService {
+	constructor(
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+
+		@Inject(DI.userProfilesRepository)
+		private userProfilesRepository: UserProfilesRepository,
+	) {
+	}
+
+	@bindThis
+	public async twoFactorAuthenticate(profile: MiUserProfile, token: string): Promise<void> {
+		if (profile.twoFactorBackupSecret?.includes(token)) {
+			await this.userProfilesRepository.update({ userId: profile.userId }, {
+				twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token),
+			});
+		} else {
+			const delta = OTPAuth.TOTP.validate({
+				secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!),
+				digits: 6,
+				token,
+				window: 5,
+			});
+
+			if (delta === null) {
+				throw new Error('authentication failed');
+			}
+		}
+	}
+}
diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts
index 48d74e2b02..150f3f24d4 100644
--- a/packages/backend/src/server/api/SigninApiService.ts
+++ b/packages/backend/src/server/api/SigninApiService.ts
@@ -19,6 +19,7 @@ import type { MiLocalUser } from '@/models/User.js';
 import { IdService } from '@/core/IdService.js';
 import { bindThis } from '@/decorators.js';
 import { WebAuthnService } from '@/core/WebAuthnService.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 import { RateLimiterService } from './RateLimiterService.js';
 import { SigninService } from './SigninService.js';
 import type { AuthenticationResponseJSON } from '@simplewebauthn/typescript-types';
@@ -42,6 +43,7 @@ export class SigninApiService {
 		private idService: IdService,
 		private rateLimiterService: RateLimiterService,
 		private signinService: SigninService,
+		private userAuthService: UserAuthService,
 		private webAuthnService: WebAuthnService,
 	) {
 	}
@@ -124,7 +126,7 @@ export class SigninApiService {
 		const same = await bcrypt.compare(password, profile.password!);
 
 		const fail = async (status?: number, failure?: { id: string }) => {
-		// Append signin history
+			// Append signin history
 			await this.signinsRepository.insert({
 				id: this.idService.genId(),
 				createdAt: new Date(),
@@ -154,27 +156,15 @@ export class SigninApiService {
 				});
 			}
 
-			if (profile.twoFactorBackupSecret?.includes(token)) {
-				await this.userProfilesRepository.update({ userId: profile.userId }, {
-					twoFactorBackupSecret: profile.twoFactorBackupSecret.filter((secret) => secret !== token),
-				});
-				return this.signinService.signin(request, reply, user);
-			}
-
-			const delta = OTPAuth.TOTP.validate({
-				secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret!),
-				digits: 6,
-				token,
-				window: 1,
-			});
-
-			if (delta === null) {
+			try {
+				await this.userAuthService.twoFactorAuthenticate(profile, token);
+			} catch (e) {
 				return await fail(403, {
 					id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f',
 				});
-			} else {
-				return this.signinService.signin(request, reply, user);
 			}
+
+			return this.signinService.signin(request, reply, user);
 		} else if (body.credential) {
 			if (!same && !profile.usePasswordLessLogin) {
 				return await fail(403, {
@@ -203,6 +193,6 @@ export class SigninApiService {
 			reply.code(200);
 			return authRequest;
 		}
-	// never get here
+		// never get here
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
index c6a193fbb5..9f8e2894b8 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts
@@ -47,7 +47,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret),
 				digits: 6,
 				token,
-				window: 1,
+				window: 5,
 			});
 
 			if (delta === null) {
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
index 4b0e761bb2..6d530aba3b 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts
@@ -12,6 +12,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
 import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/_.js';
 import { WebAuthnService } from '@/core/WebAuthnService.js';
 import { ApiError } from '@/server/api/error.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -37,6 +38,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 		name: { type: 'string', minLength: 1, maxLength: 30 },
 		credential: { type: 'object' },
 	},
@@ -54,16 +56,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private userSecurityKeysRepository: UserSecurityKeysRepository,
 
 		private webAuthnService: WebAuthnService,
+		private userAuthService: UserAuthService,
 		private userEntityService: UserEntityService,
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
index b4d5237941..c39005f2dd 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts
@@ -10,6 +10,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { WebAuthnService } from '@/core/WebAuthnService.js';
 import { ApiError } from '@/server/api/error.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -41,6 +42,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['password'],
 } as const;
@@ -53,8 +55,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 		private userProfilesRepository: UserProfilesRepository,
 
 		private webAuthnService: WebAuthnService,
+		private userAuthService: UserAuthService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOne({
 				where: {
 					userId: me.id,
@@ -66,10 +70,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				throw new ApiError(meta.errors.userNotFound);
 			}
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
index 9d027b25bb..b358c812ee 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -12,6 +12,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { ApiError } from '@/server/api/error.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -31,6 +32,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['password'],
 } as const;
@@ -43,14 +45,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
+
+		private userAuthService: UserAuthService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
index ad2cb8c20b..da8ac98556 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts
@@ -11,6 +11,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '@/server/api/error.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -30,6 +31,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 		credentialId: { type: 'string' },
 	},
 	required: ['password', 'credentialId'],
@@ -45,15 +47,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private userProfilesRepository: UserProfilesRepository,
 
 		private userEntityService: UserEntityService,
+		private userAuthService: UserAuthService,
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
index b834dfff4c..338f12c5cd 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts
@@ -11,6 +11,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '@/server/api/error.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -30,6 +31,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['password'],
 } as const;
@@ -41,15 +43,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private userProfilesRepository: UserProfilesRepository,
 
 		private userEntityService: UserEntityService,
+		private userAuthService: UserAuthService,
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password ?? '');
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts
index 868cff8ad7..a3c37ffdb7 100644
--- a/packages/backend/src/server/api/endpoints/i/change-password.ts
+++ b/packages/backend/src/server/api/endpoints/i/change-password.ts
@@ -8,6 +8,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { UserProfilesRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -20,6 +21,7 @@ export const paramDef = {
 	properties: {
 		currentPassword: { type: 'string' },
 		newPassword: { type: 'string', minLength: 1 },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['currentPassword', 'newPassword'],
 } as const;
@@ -29,14 +31,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
+
+		private userAuthService: UserAuthService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.currentPassword, profile.password!);
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.currentPassword, profile.password!);
+
+			if (!passwordMatched) {
 				throw new Error('incorrect password');
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts
index f318d9cda9..fbac845fda 100644
--- a/packages/backend/src/server/api/endpoints/i/delete-account.ts
+++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts
@@ -9,6 +9,7 @@ import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { DeleteAccountService } from '@/core/DeleteAccountService.js';
 import { DI } from '@/di-symbols.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 
 export const meta = {
 	requireCredential: true,
@@ -20,6 +21,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		password: { type: 'string' },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['password'],
 } as const;
@@ -33,19 +35,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
 
+		private userAuthService: UserAuthService,
 		private deleteAccountService: DeleteAccountService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
+
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
+
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
 			const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id });
 			if (userDetailed.isDeleted) {
 				return;
 			}
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password!);
-
-			if (!same) {
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password!);
+			if (!passwordMatched) {
 				throw new Error('incorrect password');
 			}
 
diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts
index 77135bf855..a36b3a732b 100644
--- a/packages/backend/src/server/api/endpoints/i/update-email.ts
+++ b/packages/backend/src/server/api/endpoints/i/update-email.ts
@@ -14,6 +14,7 @@ import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { L_CHARS, secureRndstr } from '@/misc/secure-rndstr.js';
+import { UserAuthService } from '@/core/UserAuthService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -46,6 +47,7 @@ export const paramDef = {
 	properties: {
 		password: { type: 'string' },
 		email: { type: 'string', nullable: true },
+		token: { type: 'string', nullable: true },
 	},
 	required: ['password'],
 } as const;
@@ -61,15 +63,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		private userEntityService: UserEntityService,
 		private emailService: EmailService,
+		private userAuthService: UserAuthService,
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const token = ps.token;
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id });
 
-			// Compare password
-			const same = await bcrypt.compare(ps.password, profile.password!);
+			if (profile.twoFactorEnabled) {
+				if (token == null) {
+					throw new Error('authentication failed');
+				}
 
-			if (!same) {
+				try {
+					await this.userAuthService.twoFactorAuthenticate(profile, token);
+				} catch (e) {
+					throw new Error('authentication failed');
+				}
+			}
+
+			const passwordMatched = await bcrypt.compare(ps.password, profile.password!);
+			if (!passwordMatched) {
 				throw new ApiError(meta.errors.incorrectPassword);
 			}
 
diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index 80d2e9d353..ed967d2620 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -60,10 +60,12 @@ describe('2要素認証', () => {
 	};
 
 	const keyDoneParam = (param: {
+		token: string,
 		keyName: string,
 		credentialId: Buffer,
 		creationOptions: PublicKeyCredentialCreationOptionsJSON,
 	}): {
+		token: string,
 		password: string,
 		name: string,
 		credential: RegistrationResponseJSON,
@@ -94,6 +96,7 @@ describe('2要素認証', () => {
 
 		return {
 			password,
+			token: param.token,
 			name: param.keyName,
 			credential: <RegistrationResponseJSON>{
 				id: param.credentialId.toString('base64url'),
@@ -218,6 +221,12 @@ describe('2要素認証', () => {
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.notEqual(signinResponse.body.i, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 
 	test('が設定でき、セキュリティキーでログインできる。', async () => {
@@ -233,6 +242,7 @@ describe('2要素認証', () => {
 
 		const registerKeyResponse = await api('/i/2fa/register-key', {
 			password,
+			token: otpToken(registerResponse.body.secret),
 		}, alice);
 		assert.strictEqual(registerKeyResponse.status, 200);
 		assert.notEqual(registerKeyResponse.body.rp, undefined);
@@ -241,6 +251,7 @@ describe('2要素認証', () => {
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
 		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
@@ -271,6 +282,12 @@ describe('2要素認証', () => {
 		}));
 		assert.strictEqual(signinResponse2.status, 200);
 		assert.notEqual(signinResponse2.body.i, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 
 	test('が設定でき、セキュリティキーでパスワードレスログインできる。', async () => {
@@ -285,6 +302,7 @@ describe('2要素認証', () => {
 		assert.strictEqual(doneResponse.status, 200);
 
 		const registerKeyResponse = await api('/i/2fa/register-key', {
+			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
 		assert.strictEqual(registerKeyResponse.status, 200);
@@ -292,6 +310,7 @@ describe('2要素認証', () => {
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
 		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
@@ -326,6 +345,12 @@ describe('2要素認証', () => {
 		});
 		assert.strictEqual(signinResponse2.status, 200);
 		assert.notEqual(signinResponse2.body.i, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 
 	test('が設定でき、設定したセキュリティキーの名前を変更できる。', async () => {
@@ -340,6 +365,7 @@ describe('2要素認証', () => {
 		assert.strictEqual(doneResponse.status, 200);
 
 		const registerKeyResponse = await api('/i/2fa/register-key', {
+			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
 		assert.strictEqual(registerKeyResponse.status, 200);
@@ -347,6 +373,7 @@ describe('2要素認証', () => {
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
 		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
@@ -367,6 +394,12 @@ describe('2要素認証', () => {
 		assert.strictEqual(securityKeys.length, 1);
 		assert.strictEqual(securityKeys[0].name, renamedKey);
 		assert.notEqual(securityKeys[0].lastUsed, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 
 	test('が設定でき、設定したセキュリティキーを削除できる。', async () => {
@@ -381,6 +414,7 @@ describe('2要素認証', () => {
 		assert.strictEqual(doneResponse.status, 200);
 
 		const registerKeyResponse = await api('/i/2fa/register-key', {
+			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
 		assert.strictEqual(registerKeyResponse.status, 200);
@@ -388,6 +422,7 @@ describe('2要素認証', () => {
 		const keyName = 'example-key';
 		const credentialId = crypto.randomBytes(0x41);
 		const keyDoneResponse = await api('/i/2fa/key-done', keyDoneParam({
+			token: otpToken(registerResponse.body.secret),
 			keyName,
 			credentialId,
 			creationOptions: registerKeyResponse.body,
@@ -400,6 +435,7 @@ describe('2要素認証', () => {
 		assert.strictEqual(iResponse.status, 200);
 		for (const key of iResponse.body.securityKeysList) {
 			const removeKeyResponse = await api('/i/2fa/remove-key', {
+				token: otpToken(registerResponse.body.secret),
 				password,
 				credentialId: key.id,
 			}, alice);
@@ -418,6 +454,12 @@ describe('2要素認証', () => {
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.notEqual(signinResponse.body.i, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 
 	test('が設定でき、設定解除できる。(パスワードのみでログインできる。)', async () => {
@@ -438,6 +480,7 @@ describe('2要素認証', () => {
 		assert.strictEqual(usersShowResponse.body.twoFactorEnabled, true);
 
 		const unregisterResponse = await api('/i/2fa/unregister', {
+			token: otpToken(registerResponse.body.secret),
 			password,
 		}, alice);
 		assert.strictEqual(unregisterResponse.status, 204);
@@ -447,5 +490,11 @@ describe('2要素認証', () => {
 		});
 		assert.strictEqual(signinResponse.status, 200);
 		assert.notEqual(signinResponse.body.i, undefined);
+
+		// 後片付け
+		await api('/i/2fa/unregister', {
+			password,
+			token: otpToken(registerResponse.body.secret),
+		}, alice);
 	});
 });
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index e9397ce86f..315ce958c5 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -155,6 +155,10 @@ onMounted(() => {
 		}
 	});
 });
+
+defineExpose({
+	focus,
+});
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
new file mode 100644
index 0000000000..afb4929fcf
--- /dev/null
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -0,0 +1,70 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkModalWindow
+	ref="dialog"
+	:width="370"
+	:height="400"
+	@close="onClose"
+	@closed="emit('closed')"
+>
+	<template #header>{{ i18n.ts.authentication }}</template>
+
+	<MkSpacer :marginMin="20" :marginMax="28">
+		<div style="padding: 0 0 16px 0; text-align: center;">
+			<i class="ti ti-lock" style="font-size: 32px; color: var(--accent);"></i>
+			<div style="margin-top: 10px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
+		</div>
+
+		<div class="_gaps">
+			<MkInput ref="passwordInput" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true">
+				<template #prefix><i class="ti ti-password"></i></template>
+			</MkInput>
+
+			<MkInput v-if="$i.twoFactorEnabled" v-model="token" type="text" pattern="^([0-9]{6}|[A-Z0-9]{32})$" autocomplete="one-time-code" :spellcheck="false">
+				<template #label>{{ i18n.ts.token }} ({{ i18n.ts['2fa'] }})</template>
+				<template #prefix><i class="ti ti-123"></i></template>
+			</MkInput>
+
+			<MkButton :disabled="(password ?? '') == '' || ($i.twoFactorEnabled && (token ?? '') == '')" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-lock-open"></i> {{ i18n.ts.continue }}</MkButton>
+		</div>
+	</MkSpacer>
+</MkModalWindow>
+</template>
+
+<script lang="ts" setup>
+import { onMounted } from 'vue';
+import MkInput from '@/components/MkInput.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkModalWindow from '@/components/MkModalWindow.vue';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+
+const emit = defineEmits<{
+	(ev: 'done', v: { password: string; token: string | null; }): void;
+	(ev: 'closed'): void;
+	(ev: 'cancelled'): void;
+}>();
+
+const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const passwordInput = $shallowRef<InstanceType<typeof MkInput>>();
+const password = $ref('');
+const token = $ref(null);
+
+function onClose() {
+	emit('cancelled');
+	if (dialog) dialog.close();
+}
+
+function done(res) {
+	emit('done', { password, token });
+	if (dialog) dialog.close();
+}
+
+onMounted(() => {
+	if (passwordInput) passwordInput.focus();
+});
+</script>
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 4cd1c3ef31..8aed5797e1 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -17,6 +17,7 @@ import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
 import MkPageWindow from '@/components/MkPageWindow.vue';
 import MkToast from '@/components/MkToast.vue';
 import MkDialog from '@/components/MkDialog.vue';
+import MkPasswordDialog from '@/components/MkPasswordDialog.vue';
 import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
 import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue';
 import MkPopupMenu from '@/components/MkPopupMenu.vue';
@@ -333,6 +334,18 @@ export function inputDate(props: {
 	});
 }
 
+export function authenticateDialog(): Promise<{ canceled: true; result: undefined; } | {
+	canceled: false; result: { password: string; token: string | null; };
+}> {
+	return new Promise((resolve, reject) => {
+		popup(MkPasswordDialog, {}, {
+			done: result => {
+				resolve(result ? { canceled: false, result } : { canceled: true, result: undefined });
+			},
+		}, 'closed');
+	});
+}
+
 export function select<C = any>(props: {
 	title?: string | null;
 	text?: string | null;
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 37455ac2d0..8a89a3a86d 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -94,16 +94,12 @@ withDefaults(defineProps<{
 const usePasswordLessLogin = $computed(() => $i?.usePasswordLessLogin ?? false);
 
 async function registerTOTP(): Promise<void> {
-	const password = await os.inputText({
-		title: i18n.ts._2fa.registerTOTP,
-		text: i18n.ts._2fa.passwordToTOTP,
-		type: 'password',
-		autocomplete: 'current-password',
-	});
-	if (password.canceled) return;
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
 
 	const twoFactorData = await os.apiWithDialog('i/2fa/register', {
-		password: password.result,
+		password: auth.result.password,
+		token: auth.result.token,
 	});
 
 	os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
@@ -111,20 +107,17 @@ async function registerTOTP(): Promise<void> {
 	}, {}, 'closed');
 }
 
-function unregisterTOTP(): void {
-	os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-		autocomplete: 'current-password',
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		os.apiWithDialog('i/2fa/unregister', {
-			password: password,
-		}).catch(error => {
-			os.alert({
-				type: 'error',
-				text: error,
-			});
+async function unregisterTOTP(): Promise<void> {
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
+
+	os.apiWithDialog('i/2fa/unregister', {
+		password: auth.result.password,
+		token: auth.result.token,
+	}).catch(error => {
+		os.alert({
+			type: 'error',
+			text: error,
 		});
 	});
 }
@@ -150,15 +143,12 @@ async function unregisterKey(key) {
 	});
 	if (confirm.canceled) return;
 
-	const password = await os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-		autocomplete: 'current-password',
-	});
-	if (password.canceled) return;
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
 
 	await os.apiWithDialog('i/2fa/remove-key', {
-		password: password.result,
+		password: auth.result.password,
+		token: auth.result.token,
 		credentialId: key.id,
 	});
 	os.success();
@@ -181,16 +171,13 @@ async function renameKey(key) {
 }
 
 async function addSecurityKey() {
-	const password = await os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-		autocomplete: 'current-password',
-	});
-	if (password.canceled) return;
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
 
 	const registrationOptions = parseCreationOptionsFromJSON({
 		publicKey: await os.apiWithDialog('i/2fa/register-key', {
-			password: password.result,
+			password: auth.result.password,
+			token: auth.result.token,
 		}),
 	});
 
@@ -211,8 +198,12 @@ async function addSecurityKey() {
 	);
 	if (!credential) return;
 
+	const auth2 = await os.authenticateDialog();
+	if (auth2.canceled) return;
+
 	await os.apiWithDialog('i/2fa/key-done', {
-		password: password.result,
+		password: auth.result.password,
+		token: auth.result.token,
 		name: name.result,
 		credential: credential.toJSON(),
 	});
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 1a70c3dbfb..82b7f0ae4c 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -67,18 +67,16 @@ const onChangeReceiveAnnouncementEmail = (v) => {
 	});
 };
 
-const saveEmailAddress = () => {
-	os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		os.apiWithDialog('i/update-email', {
-			password: password,
-			email: emailAddress.value,
-		});
+async function saveEmailAddress() {
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
+
+	os.apiWithDialog('i/update-email', {
+		password: auth.result.password,
+		token: auth.result.token,
+		email: emailAddress.value,
 	});
-};
+}
 
 const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention'));
 const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply'));
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index c3278c22f3..e2fc021099 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -113,14 +113,12 @@ async function deleteAccount() {
 		if (canceled) return;
 	}
 
-	const { canceled, result: password } = await os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-	});
-	if (canceled) return;
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
 
 	await os.apiWithDialog('i/delete-account', {
-		password: password,
+		password: auth.result.password,
+		token: auth.result.token,
 	});
 
 	await os.alert({
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index 7b04ab974b..eacd34778d 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -55,13 +55,6 @@ const pagination = {
 };
 
 async function change() {
-	const { canceled: canceled1, result: currentPassword } = await os.inputText({
-		title: i18n.ts.currentPassword,
-		type: 'password',
-		autocomplete: 'current-password',
-	});
-	if (canceled1) return;
-
 	const { canceled: canceled2, result: newPassword } = await os.inputText({
 		title: i18n.ts.newPassword,
 		type: 'password',
@@ -84,21 +77,23 @@ async function change() {
 		return;
 	}
 
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
+
 	os.apiWithDialog('i/change-password', {
-		currentPassword,
+		currentPassword: auth.result.password,
+		token: auth.result.token,
 		newPassword,
 	});
 }
 
-function regenerateToken() {
-	os.inputText({
-		title: i18n.ts.password,
-		type: 'password',
-	}).then(({ canceled, result: password }) => {
-		if (canceled) return;
-		os.api('i/regenerate-token', {
-			password: password,
-		});
+async function regenerateToken() {
+	const auth = await os.authenticateDialog();
+	if (auth.canceled) return;
+
+	os.api('i/regenerate-token', {
+		password: auth.result.password,
+		token: auth.result.token,
 	});
 }
 

From 7dc9fe4e24a3974bd61b05002af4864aa1ad8564 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: Fri, 22 Sep 2023 16:03:10 +0900
Subject: [PATCH 03/62] =?UTF-8?q?feat(frontend):=20=E3=82=BB=E3=83=B3?=
 =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E3=83=A1=E3=83=87?=
 =?UTF-8?q?=E3=82=A3=E3=82=A2=E3=82=92=E7=9B=AE=E7=AB=8B=E3=81=9F=E3=81=9B?=
 =?UTF-8?q?=E3=82=8B=E8=A8=AD=E5=AE=9A=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1185?=
 =?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (add) highlight sensitive image

* Update Changelog

* (change) 設定の位置

* (add) apply mediaHighlight to video

* (change) image -> media

* Update CHANGELOG

* やっぱもうちょっと太い方がいい

* (fix) style

* Update ja-JP.yml

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 .../frontend/src/components/MkMediaImage.vue  | 20 +++++++++++++++++--
 .../frontend/src/components/MkMediaVideo.vue  | 20 +++++++++++++++++--
 .../frontend/src/pages/settings/general.vue   |  3 +++
 packages/frontend/src/store.ts                |  4 ++++
 7 files changed, 46 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92bb2c8161..68db7f41c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -44,6 +44,7 @@
 - Feat: 新しい実績を追加
 - Enhance: ノート詳細ページでリノート一覧、リアクション一覧タブを追加
 	- ノートのメニューからは当該項目は消えました
+- Enhance: センシティブなメディアを目立たせる設定を追加
 - Enhance: プロフィールにその人が作ったPlayの一覧出せるように
 - Enhance: メニューのスイッチの動作を改善
 - Enhance: 絵文字ピッカーの検索の表示件数を100件に増加
diff --git a/locales/index.d.ts b/locales/index.d.ts
index f7bc350e2b..3009c99185 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -713,6 +713,7 @@ export interface Locale {
     "alwaysMarkSensitive": string;
     "loadRawImages": string;
     "disableShowingAnimatedImages": string;
+    "highlightSensitiveMedia": string;
     "verificationEmailSent": string;
     "notSet": string;
     "emailVerified": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5436cf0494..b30fd5333d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -710,6 +710,7 @@ lockedAccountInfo: "フォローを承認制にしても、ノートの公開範
 alwaysMarkSensitive: "デフォルトでメディアをセンシティブ設定にする"
 loadRawImages: "添付画像のサムネイルをオリジナル画質にする"
 disableShowingAnimatedImages: "アニメーション画像を再生しない"
+highlightSensitiveMedia: "メディアがセンシティブであることを分かりやすく表示"
 verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。"
 notSet: "未設定"
 emailVerified: "メールアドレスが確認されました"
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index c2d442f59f..cc1c28a9e1 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
+<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitive]" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
 	<component
 		:is="disableImageLink ? 'div' : 'a'"
 		v-bind="disableImageLink ? {
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:title="image.comment || image.name"
 			:width="image.properties.width"
 			:height="image.properties.height"
-			:style="hide ? 'filter: brightness(0.5);' : null"
+			:style="hide ? 'filter: brightness(0.7);' : null"
 		/>
 	</component>
 	<template v-if="hide">
@@ -124,6 +124,22 @@ function showMenu(ev: MouseEvent) {
 	position: relative;
 }
 
+.sensitive {
+	position: relative;
+	
+	&::after {
+		content: "";
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		pointer-events: none;
+		border-radius: inherit;
+		box-shadow: inset 0 0 0 4px var(--warn);
+	}
+}
+
 .hiddenText {
 	position: absolute;
 	left: 0;
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index c5afe09745..751b5f7570 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="hide" :class="$style.hidden" @click="hide = false">
+<div v-if="hide" :class="[$style.hidden, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]" @click="hide = false">
 	<!-- 【注意】dataSaverMode が有効になっている際には、hide が false になるまでサムネイルや動画を読み込まないようにすること -->
 	<div :class="$style.sensitive">
 		<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<span>{{ i18n.ts.clickToShow }}</span>
 	</div>
 </div>
-<div v-else :class="$style.visible">
+<div v-else :class="[$style.visible, (video.isSensitive && defaultStore.state.highlightSensitiveMedia) && $style.sensitiveContainer]">
 	<video
 		:class="$style.video"
 		:poster="video.thumbnailUrl"
@@ -49,6 +49,22 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enab
 	position: relative;
 }
 
+.sensitiveContainer {
+	position: relative;
+
+	&::after {
+		content: "";
+		position: absolute;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		pointer-events: none;
+		border-radius: inherit;
+		box-shadow: inset 0 0 0 4px var(--warn);
+	}
+}
+
 .hide {
 	display: block;
 	position: absolute;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 6c59830614..a536bd1baa 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -115,6 +115,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="useBlurEffect">{{ i18n.ts.useBlurEffect }}</MkSwitch>
 				<MkSwitch v-model="useBlurEffectForModal">{{ i18n.ts.useBlurEffectForModal }}</MkSwitch>
 				<MkSwitch v-model="disableShowingAnimatedImages">{{ i18n.ts.disableShowingAnimatedImages }}</MkSwitch>
+				<MkSwitch v-model="highlightSensitiveMedia">{{ i18n.ts.highlightSensitiveMedia }}</MkSwitch>
 				<MkSwitch v-model="squareAvatars">{{ i18n.ts.squareAvatars }}</MkSwitch>
 				<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
 				<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
@@ -234,6 +235,7 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
 const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
 const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds'));
 const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages'));
+const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia'));
 const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode'));
 const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab'));
 const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
@@ -283,6 +285,7 @@ watch([
 	overridedDeviceKind,
 	mediaListWithOneImageAppearance,
 	reactionsDisplaySize,
+	highlightSensitiveMedia,
 	keepScreenOn,
 ], async () => {
 	await reloadAsk();
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 8a43ba892d..16483f0cf7 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -185,6 +185,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: 'respect' as 'respect' | 'force' | 'ignore',
 	},
+	highlightSensitiveMedia: {
+		where: 'device',
+		default: false,
+	},
 	animation: {
 		where: 'device',
 		default: !window.matchMedia('(prefers-reduced-motion)').matches,

From 3bbc2e55b1640e6a784a9e77750f6e39f6645fda Mon Sep 17 00:00:00 2001
From: typeling1578 <pub@typeling1578.dev>
Date: Fri, 22 Sep 2023 16:43:01 +0900
Subject: [PATCH 04/62] =?UTF-8?q?fix(backend):=20mostr.pub,=20Mitra?=
 =?UTF-8?q?=E3=81=AE=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=82=92=E3=83=95?=
 =?UTF-8?q?=E3=82=A9=E3=83=AD=E3=83=BC=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#11791)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): mostr.pub, Mitraのユーザーをフォローできない問題を修正

* Revert "fix(backend): mostr.pub, Mitraのユーザーをフォローできない問題を修正"

This reverts commit 9685715e6470caffc2b0c7b991d55c5edf7fa0f1.

* fix ApResolverService

* Update CHANGELOG.md

* fix test

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 .../src/core/activitypub/ApResolverService.ts | 33 ++++++++++++++-----
 packages/backend/test/misc/mock-resolver.ts   |  3 +-
 3 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 68db7f41c5..dc81997d41 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -91,6 +91,7 @@
 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
 - Fix: ジョブキュー管理画面の認証を回避できる問題を修正
 - Fix: 一部のサーバー内部エラーがスタックトレースを返さないように修正
+- Fix: 一部のリモートユーザーをフォローすることができない問題を修正
 
 ## 13.14.2
 
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index 058869ca77..9ca63c9ec5 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -4,9 +4,10 @@
  */
 
 import { Inject, Injectable } from '@nestjs/common';
+import { IsNull, Not } from 'typeorm';
 import type { MiLocalUser, MiRemoteUser } from '@/models/User.js';
 import { InstanceActorService } from '@/core/InstanceActorService.js';
-import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/_.js';
+import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
 import type { Config } from '@/config.js';
 import { MetaService } from '@/core/MetaService.js';
 import { HttpRequestService } from '@/core/HttpRequestService.js';
@@ -32,6 +33,7 @@ export class Resolver {
 		private notesRepository: NotesRepository,
 		private pollsRepository: PollsRepository,
 		private noteReactionsRepository: NoteReactionsRepository,
+		private followRequestsRepository: FollowRequestsRepository,
 		private utilityService: UtilityService,
 		private instanceActorService: InstanceActorService,
 		private metaService: MetaService,
@@ -146,13 +148,24 @@ export class Resolver {
 				return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(async reaction =>
 					this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, { uri: null })));
 			case 'follows':
-				// rest should be <followee id>
-				if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI');
-
-				return Promise.all(
-					[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
-				)
-					.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url)));
+				return this.followRequestsRepository.findOneBy({ id: parsed.id })
+					.then(async followRequest => {
+						if (followRequest == null) throw new Error('resolveLocal: invalid follow request ID');
+						const [follower, followee] = await Promise.all([
+							this.usersRepository.findOneBy({
+								id: followRequest.followerId,
+								host: IsNull(),
+							}),
+							this.usersRepository.findOneBy({
+								id: followRequest.followeeId,
+								host: Not(IsNull()),
+							}),
+						]);
+						if (follower == null || followee == null) {
+							throw new Error('resolveLocal: follower or followee does not exist');
+						}
+						return this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url));
+					});
 			default:
 				throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
 		}
@@ -177,6 +190,9 @@ export class ApResolverService {
 		@Inject(DI.noteReactionsRepository)
 		private noteReactionsRepository: NoteReactionsRepository,
 
+		@Inject(DI.followRequestsRepository)
+		private followRequestsRepository: FollowRequestsRepository,
+
 		private utilityService: UtilityService,
 		private instanceActorService: InstanceActorService,
 		private metaService: MetaService,
@@ -196,6 +212,7 @@ export class ApResolverService {
 			this.notesRepository,
 			this.pollsRepository,
 			this.noteReactionsRepository,
+			this.followRequestsRepository,
 			this.utilityService,
 			this.instanceActorService,
 			this.metaService,
diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts
index 07abe515c3..7cba7a2aa8 100644
--- a/packages/backend/test/misc/mock-resolver.ts
+++ b/packages/backend/test/misc/mock-resolver.ts
@@ -15,7 +15,7 @@ import type { LoggerService } from '@/core/LoggerService.js';
 import type { MetaService } from '@/core/MetaService.js';
 import type { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
-import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository } from '@/models/_.js';
+import type { NoteReactionsRepository, NotesRepository, PollsRepository, UsersRepository, FollowRequestsRepository } from '@/models/_.js';
 
 type MockResponse = {
 	type: string;
@@ -33,6 +33,7 @@ export class MockResolver extends Resolver {
 			{} as NotesRepository,
 			{} as PollsRepository,
 			{} as NoteReactionsRepository,
+			{} as FollowRequestsRepository,
 			{} as UtilityService,
 			{} as InstanceActorService,
 			{} as MetaService,

From ee83b9542e219ce2301f3adb3cb3b0fde3b96152 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 16:46:08 +0900
Subject: [PATCH 05/62] New Crowdin updates (#11856)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)
---
 locales/cs-CZ.yml |  1 -
 locales/de-DE.yml |  7 +++++-
 locales/en-US.yml |  7 +++++-
 locales/es-ES.yml |  1 -
 locales/fr-FR.yml |  1 +
 locales/id-ID.yml |  1 -
 locales/it-IT.yml | 29 ++++++++++++++----------
 locales/ja-KS.yml |  1 -
 locales/ko-KR.yml |  1 -
 locales/pt-PT.yml |  3 +++
 locales/ru-RU.yml |  1 -
 locales/sv-SE.yml |  1 -
 locales/th-TH.yml |  1 -
 locales/vi-VN.yml | 56 ++++++++++++++++++++++++++++++++++++++++++++---
 locales/zh-CN.yml |  6 ++++-
 locales/zh-TW.yml | 23 +++++++++++--------
 16 files changed, 105 insertions(+), 35 deletions(-)

diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 18e7f02e69..9a751abc78 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1679,7 +1679,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Již jste zaregistrovali dvoufaktorové ověřovací zařízení."
   registerTOTP: "Registrovat aplikaci autentizátoru"
-  passwordToTOTP: "Zadejte své heslo"
   step1: "Nejprve si do zařízení nainstalujte aplikaci pro ověřování (například {a} nebo {b})."
   step2: "Poté naskenujte QR kód zobrazený na této obrazovce."
   step2Click: "Kliknutím na tento QR kód můžete zaregistrovat 2FA do bezpečnostního klíče nebo aplikace autentizace telefonu."
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 16f210202b..596a6e5fd8 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -1112,6 +1112,10 @@ renotes: "Renotes"
 loadReplies: "Antworten anzeigen"
 loadConversation: "Unterhaltung anzeigen"
 pinnedList: "Angeheftete Liste"
+keepScreenOn: "Bildschirm angeschaltet lassen"
+verifiedLink: "Link-Besitz wurde verifiziert"
+notifyNotes: "Über neue Notizen benachrichtigen"
+unnotifyNotes: "Nicht über neue Notizen benachrichtigen"
 _announcement:
   forExistingUsers: "Nur für existierende Nutzer"
   forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
@@ -1712,7 +1716,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
   registerTOTP: "Authentifizierungs-App registrieren"
-  passwordToTOTP: "Bitte Passwort eingeben"
   step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
   step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
   step2Click: "Durch Klicken dieses QR-Codes kannst du Verifikation mit deinem Security-Token oder einer App registrieren."
@@ -1890,6 +1893,7 @@ _profile:
   metadataContent: "Inhalt"
   changeAvatar: "Profilbild ändern"
   changeBanner: "Banner ändern"
+  verifiedLinkDescription: "Gibst du hier eine URL ein, die einen Link zu deinem Profile enthält, wird neben diesem Feld ein Icon zur Besitzbestätigung angezeigt."
 _exportOrImport:
   allNotes: "Alle Notizen"
   favoritedNotes: "Als Favorit markierte Notizen"
@@ -2008,6 +2012,7 @@ _notification:
   youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten"
   yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
   pollEnded: "Umfrageergebnisse sind verfügbar"
+  newNote: "Neue Notiz"
   unreadAntennaNote: "Antenne {name}"
   emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
   achievementEarned: "Errungenschaft freigeschaltet"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 2d13e777d0..527c68d839 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1112,6 +1112,10 @@ renotes: "Renotes"
 loadReplies: "Show replies"
 loadConversation: "Show conversation"
 pinnedList: "Pinned list"
+keepScreenOn: "Keep screen on"
+verifiedLink: "Link ownership has been verified"
+notifyNotes: "Notify about new notes"
+unnotifyNotes: "Stop notifying about new notes"
 _announcement:
   forExistingUsers: "Existing users only"
   forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
@@ -1712,7 +1716,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "You have already registered a 2-factor authentication device."
   registerTOTP: "Register authenticator app"
-  passwordToTOTP: "Enter your password"
   step1: "First, install an authentication app (such as {a} or {b}) on your device."
   step2: "Then, scan the QR code displayed on this screen."
   step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app."
@@ -1890,6 +1893,7 @@ _profile:
   metadataContent: "Content"
   changeAvatar: "Change avatar"
   changeBanner: "Change banner"
+  verifiedLinkDescription: "By entering an URL that contains a link to your profile here, an ownership verification icon can be displayed next to the field."
 _exportOrImport:
   allNotes: "All notes"
   favoritedNotes: "Favorite notes"
@@ -2008,6 +2012,7 @@ _notification:
   youReceivedFollowRequest: "You've received a follow request"
   yourFollowRequestAccepted: "Your follow request was accepted"
   pollEnded: "Poll results have become available"
+  newNote: "New note"
   unreadAntennaNote: "Antenna {name}"
   emptyPushNotificationMessage: "Push notifications have been updated"
   achievementEarned: "Achievement unlocked"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index bd638af9c2..bfa779d78a 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1705,7 +1705,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Ya has completado la configuración."
   registerTOTP: "Registrar aplicación autenticadora"
-  passwordToTOTP: "Ingresa tu contraseña"
   step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
   step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
   step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app.\nTocar este código QR te permitirá registrar la autenticación 2FA a tu llave de seguridad o aplicación autenticadora."
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 3d9de3ef32..64a59522f2 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -480,6 +480,7 @@ createAccount: "Créer un compte"
 existingAccount: "Compte existant"
 regenerate: "Générer à nouveau"
 fontSize: "Taille de la police"
+limitTo: "Limiter à {x}"
 noFollowRequests: "Vous n’avez aucune demande d’abonnement en attente"
 openImageInNewTab: "Ouvrir les images dans un nouvel onglet"
 dashboard: "Tableau de bord"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index c817850fe2..e39b49774d 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -1683,7 +1683,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor."
   registerTOTP: "Daftarkan aplikasi autentikator"
-  passwordToTOTP: "Masukkan kata sandimu"
   step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu."
   step2: "Lalu, pindai kode QR yang ada di layar."
   step2Click: "Mengeklik kode QR ini akan membolehkanmu untuk mendaftarkan 2FA ke security-key atau aplikasi autentikator ponsel."
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index d8455c3d81..9810e6015a 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -134,7 +134,7 @@ renoteMute: "Silenzia i Rinota"
 renoteUnmute: "Non silenziare i Rinota"
 block: "Blocca"
 unblock: "Sblocca"
-suspend: "Sospendi"
+suspend: "Sospensione"
 unsuspend: "Revoca la sospensione"
 blockConfirm: "Vuoi davvero bloccare il profilo?"
 unblockConfirm: "Vuoi davvero sbloccare il profilo?"
@@ -180,7 +180,7 @@ youHaveNoLists: "Non hai ancora creato nessuna lista"
 followConfirm: "Vuoi seguire {name}?"
 proxyAccount: "Profilo proxy"
 proxyAccountDescription: "Un profilo proxy funziona come follower per i profili remoti, sotto certe condizioni. Ad esempio, quando un profilo locale ne inserisce uno remoto in una lista (senza seguirlo), se nessun altro segue quel profilo remoto, le attività non possono essere distribuite. Dunque, il profilo proxy le seguirà per tutti."
-host: "Server remoto"
+host: "Host"
 selectUser: "Seleziona profilo"
 recipient: "Destinatario"
 annotation: "Annotazione preventiva"
@@ -287,7 +287,7 @@ images: "Immagini"
 image: "Immagini"
 birthday: "Compleanno"
 yearsOld: "{age} anni"
-registeredDate: "Iscrizione a.."
+registeredDate: "Data iscrizione"
 location: "Posizione"
 theme: "Tema"
 themeForLightMode: "Tema da utilizzare per il modo chiaro"
@@ -496,7 +496,7 @@ noFollowRequests: "Non hai alcuna richiesta di follow"
 openImageInNewTab: "Apri le immagini in un nuovo tab"
 dashboard: "Pannello di controllo"
 local: "Locale"
-remote: "Remoto"
+remote: "Remota"
 total: "Totale"
 weekOverWeekChanges: "Settimanale"
 dayOverDayChanges: "Giornaliero"
@@ -551,8 +551,8 @@ installedDate: "Data installazione"
 lastUsedDate: "Data di ultimo uso"
 state: "Stato"
 sort: "Ordina per"
-ascendingOrder: "Ascendente"
-descendingOrder: "Discendente"
+ascendingOrder: "Aumenta"
+descendingOrder: "Diminuisce"
 scratchpad: "ScratchPad"
 scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey."
 output: "Uscita"
@@ -621,7 +621,7 @@ emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronic
 email: "Email"
 emailAddress: "Indirizzo di posta elettronica"
 smtpConfig: "Impostazioni del server SMTP"
-smtpHost: "Server remoto"
+smtpHost: "Host SMTP"
 smtpPort: "Porta"
 smtpUser: "Nome utente"
 smtpPass: "Password"
@@ -1112,6 +1112,10 @@ renotes: "Rinota"
 loadReplies: "Leggi le risposte"
 loadConversation: "Leggi la conversazione"
 pinnedList: "Elenco in primo piano"
+keepScreenOn: "Mantieni lo schermo acceso"
+verifiedLink: "Abbiamo confermato la validità di questo collegamento"
+notifyNotes: "Notifica nuove Note"
+unnotifyNotes: "Interrompi le notifiche di nuove Note"
 _announcement:
   forExistingUsers: "Solo ai profili attuali"
   forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@@ -1462,10 +1466,10 @@ _role:
   _condition:
     isLocal: "Profilo locale"
     isRemote: "Profilo remoto"
-    createdLessThan: "Creato meno di"
-    createdMoreThan: "Creato più di"
-    followersLessThanOrEq: "Ha meno di N follower"
-    followersMoreThanOrEq: "Ha più di N follower"
+    createdLessThan: "Profilo creato da meno di N"
+    createdMoreThan: "Profilo creato da più di N"
+    followersLessThanOrEq: "Profilo con N follower o meno"
+    followersMoreThanOrEq: "Profilo con N follower o più"
     followingLessThanOrEq: "Segue N profili o meno"
     followingMoreThanOrEq: "Segue N profili o più"
     notesLessThanOrEq: "Conteggio Note inferiore o uguale a"
@@ -1712,7 +1716,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "La configurazione è stata già completata."
   registerTOTP: "Registra un'app di autenticazione"
-  passwordToTOTP: "Inserire la password"
   step1: "Innanzitutto, installare sul dispositivo un'applicazione di autenticazione come {a} o {b}."
   step2: "Quindi, scansionare il codice QR visualizzato con l'app."
   step2Click: "Cliccando sul codice QR, puoi registrarlo con l'app di autenticazione o il portachiavi installato sul tuo dispositivo."
@@ -1890,6 +1893,7 @@ _profile:
   metadataContent: "Contenuto"
   changeAvatar: "Modifica immagine profilo"
   changeBanner: "Cambia intestazione"
+  verifiedLinkDescription: "Puoi verificare il tuo profilo mostrando una icona. Devi inserire la URL alla pagina che contiene un link al tuo profilo."
 _exportOrImport:
   allNotes: "Tutte le note"
   favoritedNotes: "Note preferite"
@@ -2008,6 +2012,7 @@ _notification:
   youReceivedFollowRequest: "Hai ricevuto una richiesta di follow"
   yourFollowRequestAccepted: "La tua richiesta di follow è stata accettata"
   pollEnded: "Risultati del sondaggio."
+  newNote: "Nuove Note"
   unreadAntennaNote: "Antenna {name}"
   emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
   achievementEarned: "Obiettivo raggiunto"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 936f6ebb7b..d5d414ea77 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -1697,7 +1697,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "もう設定終わっとるわ。"
   registerTOTP: "認証アプリの設定はじめる"
-  passwordToTOTP: "パスワードを入れてーや"
   step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。"
   step2: "次に、ここにあるQRコードをアプリでスキャンしてな~。"
   step2Click: "QRコードをクリックすると、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 23996cbe95..9e405396ba 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -1699,7 +1699,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "이미 설정이 완료되었습니다."
   registerTOTP: "인증 앱 설정 시작"
-  passwordToTOTP: "비밀번호를 입력하세요."
   step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다."
   step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다."
   step2Click: "QR 코드를 클릭하면 기기에 설치된 인증 앱에 등록할 수 있습니다."
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 826f0b45a1..737bab9adc 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -411,6 +411,7 @@ aboutMisskey: "Sobre Misskey"
 administrator: "Administrador"
 token: "Símbolo"
 2fa: "Autenticação de dois fatores"
+setupOf2fa: "Configuração de autenticação de dois fatores"
 totp: "Aplicativo Autenticador"
 totpDescription: "Digite a senha de uso único informado pelo aplicativo autenticador"
 moderator: "Moderador"
@@ -918,6 +919,7 @@ pleaseSelect: "Por favor, selecione."
 reverse: "Inversão"
 colored: "Colorido"
 refreshInterval: "Intervalo de atualização"
+label: "Etiqueta"
 type: "Tipo"
 speed: "Velocidade"
 slow: "Lento"
@@ -1008,6 +1010,7 @@ waitingForMailAuth: "Verificação de e-mail pendente "
 icon: "Avatar"
 replies: "Responder"
 renotes: "Repostar"
+keepScreenOn: "Manter a tela do dispositivo sempre ligada"
 _initialAccountSetting:
   followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
 _serverSettings:
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 597b5f5791..edf531dfcc 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1608,7 +1608,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Двухфакторная аутентификация уже настроена."
   registerTOTP: "Начните настраивать приложение-аутентификатор"
-  passwordToTOTP: "Пожалуйста, введите свой пароль"
   step1: "Прежде всего, установите на устройство приложение для аутентификации, например, {a} или {b}."
   step2: "Далее отсканируйте отображаемый QR-код при помощи приложения."
   step2Click: "Нажав на QR-код, вы можете зарегистрироваться с помощью приложения для аутентификации или брелка для ключей, установленного на вашем устройстве."
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 0c47ef6890..507492d52c 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -510,7 +510,6 @@ _sfx:
   chat: "Chatt"
   antenna: "Antenner"
 _2fa:
-  passwordToTOTP: "Skriv in ditt lösenord"
   renewTOTPCancel: "Nej tack"
 _antennaSources:
   all: "Alla noter"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index 852e341648..f9262fea7e 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -1696,7 +1696,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "คุณได้ลงทะเบียนอุปกรณ์ยืนยันตัวตนแบบ 2 ชั้นแล้ว"
   registerTOTP: "ลงทะเบียนแอพตัวตรวจสอบสิทธิ์"
-  passwordToTOTP: "กรอกรหัสผ่าน"
   step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ"
   step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้"
   step2Click: "การคลิกที่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA กับคีย์ความปลอดภัยหรือแอปตรวจสอบความถูกต้องของโทรศัพท์ได้"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 7b74e9714f..dec9e7f888 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1006,9 +1006,9 @@ enableChartsForRemoteUser: "Tạo biểu đồ người dùng từ xa"
 video: "Video"
 videos: "Các video"
 dataSaver: "Tiết kiệm dung lượng"
-accountMigration: "Gộp chung tài khoản"
+accountMigration: "Chuyển tài khoản"
 accountMoved: "Người dùng này đã chuyển sang một tài khoản mới:"
-accountMovedShort: "Tài khoản này đã được gộp"
+accountMovedShort: "Tài khoản này đã được chuyển"
 operationForbidden: "Thao tác này không thể thực hiện"
 forceShowAds: "Luôn hiện quảng cáo"
 notificationDisplay: "Thông báo"
@@ -1046,9 +1046,12 @@ renotes: "Đăng lại"
 loadReplies: "Hiển thị các trả lời"
 pinnedList: "Các mục đã được ghim"
 keepScreenOn: "Giữ màn hình luôn bật"
+verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này"
 _announcement:
   forExistingUsers: "Chỉ những người dùng đã tồn tại"
+  forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó."
   end: "Lưu trữ thông báo"
+  tooManyActiveAnnouncementDescription: "Có quá nhiều thông báo sẽ làm trải nghiệm của người dùng tệ đi. Vui lòng lưu trữ những thông báo đã hết hiệu lực."
   readConfirmTitle: "Đánh dấu là đã đọc?"
   readConfirmText: "Điều này sẽ đánh dấu nội dung của \"{title}\" là đã đọc."
 _initialAccountSetting:
@@ -1056,7 +1059,28 @@ _initialAccountSetting:
   letsStartAccountSetup: "Để bắt đầu, hãy cùng thiết lập tài khoản nhé."
   letsFillYourProfile: "Đầu tiên, hãy thiết lập hồ sơ của bạn."
   profileSetting: "Thiết lập hồ sơ"
+  privacySetting: "Cài đặt quyền riêng tư"
+  theseSettingsCanEditLater: "Bạn vẫn có thể thay đổi những cài đặt này."
+  youCanEditMoreSettingsInSettingsPageLater: "Còn rất nhiều những cài đặt khác bạn có thể thay đổi ở trang \"Cài đặt\". Hãy nhớ ghé thăm trong lần sau nhé."
+  followUsers: "Thử theo dõi một vài người mà bạn có thể thích để xây dựng dòng thời gian của mình."
+  pushNotificationDescription: "Bật thông báo đẩy sẽ cho phép bạn nhận thông báo từ {name} trực tiếp từ thiết bị của bạn."
+  initialAccountSettingCompleted: "Thiết lập tài khoản thành công!"
+  haveFun: "Hãy tận hưởng {name} nhé!"
+  ifYouNeedLearnMore: "Nếu bạn muốn tìm hiểu thêm về cách sử dụng {name} (Misskey), hãy vào {link}."
+  skipAreYouSure: "Bạn thực sự muốn bỏ qua mục thiết lập tài khoản?"
+  laterAreYouSure: "Bạn thực sự muốn thiết lập tài khoản vào lúc khác?"
+_serverSettings:
+  iconUrl: "Biểu tượng URL"
+  appIconResolutionMustBe: "Độ phân giải tối thiểu là {resolution}."
+  manifestJsonOverride: "Ghi đè manifest.json"
 _accountMigration:
+  moveFrom: "Chuyển một tài khoản khác vào tài khoản này"
+  moveFromLabel: "Tài khoản gốc #{n}"
+  moveTo: "Chuyển tài khoản này vào một tài khoản khác"
+  moveCannotBeUndone: "Việc chuyển tài khoản không thể huỷ."
+  moveAccountDescription: "Điều này sẽ chuyển tài khoản này sang một tài khoản khác.\n ・Những người theo dõi sẽ tự động được chuyển sang tài khoản mới\n ・Tài khoản này sẽ tự bỏ theo dõi những người mà bạn đã theo dõi trước đây\n ・Bạn sẽ không thể đăng tút mới, v.v trên tài khoản này\n\nDù việc chuyển người theo dõi được diễn ra tự động, bạn vẫn phải tự chuẩn bị một vài bước để chuyển danh sách những người dùng bạn đang theo dõi. Để làm vậy, vui lòng thực hiện việc xuất dữ liệu những người dùng đã theo dõi mà sau này bạn sẽ dùng để nhập vào tài khoản mới ở menu Cài đặt. Hành động tương tự áp dụng với danh sách những người dùng bị chặn hoặc tắt tiếng.\n\n(Điều này áp dụng cho phiên bản Misskey v13.12.0 và sau này. Các phần mềm ActivityPub khác , ví dụ như Mastodon, sẽ có thể hoạt động khác đi.)"
+  startMigration: "Chuyển"
+  movedAndCannotBeUndone: "\nTài khoản này đã được chuyển đi.\nViệc di chuyển tài khoản không thể bị huỷ bỏ."
   movedTo: "Tài khoản mới:"
 _achievements:
   earnedAt: "Ngày thu nhận"
@@ -1096,6 +1120,8 @@ _achievements:
       title: "Hàng tinh đăng bài"
       description: "Đã đăng bài 50,000 lần rồi"
     _notes100000:
+      title: "ALL YOUR NOTE ARE BELONG TO US"
+      description: "Đăng 100,000 tút"
       flavor: "Liệu viết bài gì tầm này vậy? "
     _login3:
       title: "Sơ cấp I"
@@ -1127,6 +1153,15 @@ _achievements:
     _login400:
       title: "Khách hàng thường xuyên cấp III"
       description: "Tổng số ngày đăng nhập đạt 400 ngày"
+    _login1000:
+      flavor: "Cảm ơn bạn đã sử dụng Misskey!"
+    _noteFavorited1:
+      title: "Nhà thiên văn học"
+    _myNoteFavorited1:
+      title: "Đi tìm những ngôi sao"
+    _profileFilled:
+      title: "Luôn sẵn sàng"
+      description: "Thiết lập tài khoản của bạn"
     _markedAsCat:
       title: "Tôi là một con mèo"
       description: "Bật chế độ mèo"
@@ -1152,8 +1187,18 @@ _achievements:
     _followers10:
       title: "FOLLOW ME!!"
       description: "Người theo dõi bạn vượt lên 10 người"
+    _followers50:
+      title: "Từng chút một"
+      description: "Đạt được 50 lượt theo dõi"
+    _followers100:
+      title: "Người nổi tiếng"
+      description: "Đạt được 100 lượt theo dõi"
+    _followers300:
+      title: "Vui lòng xếp thành hàng nào"
+      description: "Đạt được 300 lượt theo dõi"
     _followers500:
       title: "Trạm phát sóng"
+      description: "Đạt được 500 lượt theo dõi"
     _followers1000:
       title: "Người có tầm ảnh hưởng"
       description: "Người theo dõi bạn vượt lên 1000 người"
@@ -1172,11 +1217,15 @@ _achievements:
       description: "Tìm thấy được những kho báu cất giấu"
     _client30min:
       title: "Giải lao xỉu"
+      description: "Giữ Misskey mở trong ít nhất 30 phút"
+    _client60min:
+      description: "Giữ Misskey mở trong ít nhất 60 phút"
     _noteDeletedWithin1min:
       title: "Xem như không có gì đâu nha"
     _postedAtLateNight:
       title: "Loài ăn đêm"
       description: "Đăng bài trong đêm khuya "
+      flavor: "Đến giờ đi ngủ rồi."
     _postedAt0min0sec:
       title: "Tín hiệu báo giờ"
       description: "Đăng bài vào 0 phút 0 giây"
@@ -1207,6 +1256,8 @@ _achievements:
     _setNameToSyuilo:
       title: "Ngưỡng mộ với vị thần"
       description: "Đạt tên là syuilo"
+    _passedSinceAccountCreated1:
+      title: "Kỷ niệm một năm"
     _loggedInOnBirthday:
       title: "Sinh nhật vủi vẻ"
       description: "Đăng nhập vào ngày sinh"
@@ -1466,7 +1517,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
   registerTOTP: "Đăng ký ứng dụng xác thực"
-  passwordToTOTP: "Nhắn mật mã"
   step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
   step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
   step2Click: "Quét mã QR trên ứng dụng xác thực (Authy, Google authenticator, v.v.)"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index ef32d1aa23..3026682890 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -1113,6 +1113,9 @@ loadReplies: "查看回复"
 loadConversation: "查看对话"
 pinnedList: "已置顶的列表"
 keepScreenOn: "保持设备屏幕开启"
+verifiedLink: "已验证的链接"
+notifyNotes: "打开发帖通知"
+unnotifyNotes: "关闭发帖通知"
 _announcement:
   forExistingUsers: "仅限现有用户"
   forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。"
@@ -1713,7 +1716,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "此设备已被注册"
   registerTOTP: "开始设置认证应用"
-  passwordToTOTP: "请输入您的密码"
   step1: "首先,在您的设备上安装验证应用,例如 {a} 或 {b}。"
   step2: "然后,扫描屏幕上显示的二维码。"
   step2Click: "通过点击二维码,您可以使用设备上安装的身份验证器应用程序或密钥环进行注册"
@@ -1891,6 +1893,7 @@ _profile:
   metadataContent: "内容"
   changeAvatar: "修改头像"
   changeBanner: "修改横幅"
+  verifiedLinkDescription: "如果将内容设置为 URL,当链接所指向的网页内包含自己的个人资料链接时,可以显示一个已验证图标。"
 _exportOrImport:
   allNotes: "所有帖子"
   favoritedNotes: "收藏的帖子"
@@ -2009,6 +2012,7 @@ _notification:
   youReceivedFollowRequest: "您有新的关注请求"
   yourFollowRequestAccepted: "您的关注请求已通过"
   pollEnded: "问卷调查结果已生成。"
+  newNote: "新的帖子"
   unreadAntennaNote: "天线 {name}"
   emptyPushNotificationMessage: "推送通知已更新"
   achievementEarned: "获得成就"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 39f2f2ff29..a61e3b242b 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -15,7 +15,7 @@ gotIt: "知道了"
 cancel: "取消"
 noThankYou: "現在不要"
 enterUsername: "輸入使用者名稱"
-renotedBy: "{user} 轉傳了"
+renotedBy: "{user} 轉發"
 noNotes: "無貼文"
 noNotifications: "沒有通知"
 instance: "伺服器"
@@ -106,10 +106,10 @@ unfollow: "取消追隨"
 followRequestPending: "追隨許可待批准"
 enterEmoji: "輸入表情符號"
 renote: "轉發"
-unrenote: "取消轉傳"
-renoted: "轉傳成功"
-cantRenote: "無法轉傳此貼文。"
-cantReRenote: "無法轉傳之前已經轉傳過的內容。"
+unrenote: "取消轉發"
+renoted: "轉發成功"
+cantRenote: "無法轉發此貼文。"
+cantReRenote: "無法轉發之前已經轉發過的內容。"
 quote: "引用"
 inChannelRenote: "在頻道內轉發"
 inChannelQuote: "在頻道內引用"
@@ -546,7 +546,7 @@ recentUsed: "最近使用"
 install: "安裝"
 uninstall: "解除安裝"
 installedApps: "已授權的應用程式"
-nothing: "無"
+nothing: "查無項目"
 installedDate: "安裝時間"
 lastUsedDate: "最後上線日期"
 state: "狀態"
@@ -657,7 +657,7 @@ behavior: "行為"
 sample: "範例"
 abuseReports: "檢舉"
 reportAbuse: "檢舉"
-reportAbuseRenote: "檢舉轉貼"
+reportAbuseRenote: "檢舉轉發貼文"
 reportAbuseOf: "檢舉{name}"
 fillAbuseReportDescription: "請填寫檢舉的詳細理由。如有需要,請附上相關 URL。"
 abuseReported: "檢舉完成。感謝您的報告。"
@@ -1112,6 +1112,10 @@ renotes: "轉發"
 loadReplies: "閱覽回覆"
 loadConversation: "閱覽對話"
 pinnedList: "已置頂的清單"
+keepScreenOn: "保持設備螢幕開啟"
+verifiedLink: "已驗證連結"
+notifyNotes: "開啟貼文通知"
+unnotifyNotes: "關閉貼文通知"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@@ -1687,7 +1691,7 @@ _ago:
   future: "未來"
   justNow: "剛剛"
   secondsAgo: "{n} 秒前"
-  minutesAgo: "{n}分鐘前 "
+  minutesAgo: "{n} 分鐘前 "
   hoursAgo: "{n} 小時前"
   daysAgo: "{n} 天前"
   weeksAgo: "{n} 週前"
@@ -1712,7 +1716,6 @@ _timelineTutorial:
 _2fa:
   alreadyRegistered: "此裝置已被註冊過了"
   registerTOTP: "開始設定驗證應用程式"
-  passwordToTOTP: "請輸入密碼"
   step1: "首先,在您的裝置上安裝驗證程式,例如 {a} 或 {b}。"
   step2: "然後,掃描螢幕上的 QR 碼。"
   step2Click: "您可以點擊 QR 碼,以使用裝置上的驗證應用程式或金鑰環註冊。"
@@ -1890,6 +1893,7 @@ _profile:
   metadataContent: "内容"
   changeAvatar: "更換大頭貼"
   changeBanner: "變更橫幅圖像"
+  verifiedLinkDescription: "如果輸入包含您個人資料的網站 URL,欄位旁邊將出現驗證圖示。"
 _exportOrImport:
   allNotes: "所有貼文"
   favoritedNotes: "「我的最愛」貼文"
@@ -2008,6 +2012,7 @@ _notification:
   youReceivedFollowRequest: "您有新的追隨請求"
   yourFollowRequestAccepted: "您的追隨請求已通過"
   pollEnded: "問卷調查已產生結果"
+  newNote: "新的貼文"
   unreadAntennaNote: "天線 {name}"
   emptyPushNotificationMessage: "推送通知已更新"
   achievementEarned: "獲得成就"

From 032b6c6afb3b3fbdbbde482049f05dd4562ff669 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 16:47:42 +0900
Subject: [PATCH 06/62] update deps

---
 package.json                     |   2 +-
 packages/backend/package.json    |   4 +-
 packages/frontend/package.json   |  10 +-
 packages/misskey-js/package.json |   2 +-
 pnpm-lock.yaml                   | 454 +++++++++----------------------
 5 files changed, 133 insertions(+), 339 deletions(-)

diff --git a/package.json b/package.json
index 8b5f574947..be3e7762e6 100644
--- a/package.json
+++ b/package.json
@@ -56,7 +56,7 @@
 		"cross-env": "7.0.3",
 		"cypress": "13.2.0",
 		"eslint": "8.49.0",
-		"start-server-and-test": "2.0.0"
+		"start-server-and-test": "2.0.1"
 	},
 	"optionalDependencies": {
 		"@tensorflow/tfjs-core": "4.4.0"
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 466416393c..27747e3cd4 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -78,7 +78,7 @@
 		"@simplewebauthn/server": "8.1.1",
 		"@sinonjs/fake-timers": "11.1.0",
 		"@swc/cli": "0.1.62",
-		"@swc/core": "1.3.86",
+		"@swc/core": "1.3.87",
 		"accepts": "1.3.8",
 		"ajv": "8.12.0",
 		"archiver": "6.0.1",
@@ -86,7 +86,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.11.2",
+		"bullmq": "4.11.3",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 8c3b49f18b..6285c1bc33 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -110,7 +110,7 @@
 		"@types/ws": "8.5.5",
 		"@typescript-eslint/eslint-plugin": "6.7.2",
 		"@typescript-eslint/parser": "6.7.2",
-		"@vitest/coverage-v8": "0.34.4",
+		"@vitest/coverage-v8": "0.34.5",
 		"@vue/runtime-core": "3.3.4",
 		"acorn": "8.10.0",
 		"cross-env": "7.0.3",
@@ -127,14 +127,14 @@
 		"prettier": "3.0.3",
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
-		"start-server-and-test": "2.0.0",
-		"storybook": "7.4.2",
+		"start-server-and-test": "2.0.1",
+		"storybook": "7.4.3",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"summaly": "github:misskey-dev/summaly",
 		"vite-plugin-turbosnap": "1.0.3",
-		"vitest": "0.34.4",
+		"vitest": "0.34.5",
 		"vitest-fetch-mock": "0.2.2",
 		"vue-eslint-parser": "9.3.1",
-		"vue-tsc": "1.8.11"
+		"vue-tsc": "1.8.13"
 	}
 }
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 444d1c39e3..c1238f1a17 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -39,7 +39,7 @@
 	],
 	"dependencies": {
 		"@swc/cli": "0.1.62",
-		"@swc/core": "1.3.86",
+		"@swc/core": "1.3.87",
 		"eventemitter3": "5.0.1",
 		"reconnecting-websocket": "4.4.0"
 	}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8649cd581f..e857d7a78d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,8 +51,8 @@ importers:
         specifier: 8.49.0
         version: 8.49.0
       start-server-and-test:
-        specifier: 2.0.0
-        version: 2.0.0
+        specifier: 2.0.1
+        version: 2.0.1
 
   packages/backend:
     dependencies:
@@ -121,10 +121,10 @@ importers:
         version: 2.1.5
       '@swc/cli':
         specifier: 0.1.62
-        version: 0.1.62(@swc/core@1.3.86)(chokidar@3.5.3)
+        version: 0.1.62(@swc/core@1.3.87)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.86
-        version: 1.3.86
+        specifier: 1.3.87
+        version: 1.3.87
       accepts:
         specifier: 1.3.8
         version: 1.3.8
@@ -147,8 +147,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.11.2
-        version: 4.11.2
+        specifier: 4.11.3
+        version: 4.11.3
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -489,7 +489,7 @@ importers:
         version: 8.0.0
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.86)
+        version: 0.2.29(@swc/core@1.3.87)
       '@types/accepts':
         specifier: 1.3.5
         version: 1.3.5
@@ -844,7 +844,7 @@ importers:
         version: 7.4.3
       '@storybook/jest':
         specifier: 0.2.2
-        version: 0.2.2(vitest@0.34.4)
+        version: 0.2.2(vitest@0.34.5)
       '@storybook/manager-api':
         specifier: 7.4.3
         version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
@@ -918,8 +918,8 @@ importers:
         specifier: 6.7.2
         version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
       '@vitest/coverage-v8':
-        specifier: 0.34.4
-        version: 0.34.4(vitest@0.34.4)
+        specifier: 0.34.5
+        version: 0.34.5(vitest@0.34.5)
       '@vue/runtime-core':
         specifier: 3.3.4
         version: 3.3.4
@@ -969,11 +969,11 @@ importers:
         specifier: 18.2.0
         version: 18.2.0(react@18.2.0)
       start-server-and-test:
-        specifier: 2.0.0
-        version: 2.0.0
+        specifier: 2.0.1
+        version: 2.0.1
       storybook:
-        specifier: 7.4.2
-        version: 7.4.2
+        specifier: 7.4.3
+        version: 7.4.3
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
         version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0)
@@ -984,26 +984,26 @@ importers:
         specifier: 1.0.3
         version: 1.0.3
       vitest:
-        specifier: 0.34.4
-        version: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
+        specifier: 0.34.5
+        version: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
       vitest-fetch-mock:
         specifier: 0.2.2
-        version: 0.2.2(vitest@0.34.4)
+        version: 0.2.2(vitest@0.34.5)
       vue-eslint-parser:
         specifier: 9.3.1
         version: 9.3.1(eslint@8.49.0)
       vue-tsc:
-        specifier: 1.8.11
-        version: 1.8.11(typescript@5.2.2)
+        specifier: 1.8.13
+        version: 1.8.13(typescript@5.2.2)
 
   packages/misskey-js:
     dependencies:
       '@swc/cli':
         specifier: 0.1.62
-        version: 0.1.62(@swc/core@1.3.86)(chokidar@3.5.3)
+        version: 0.1.62(@swc/core@1.3.87)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.86
-        version: 1.3.86
+        specifier: 1.3.87
+        version: 1.3.87
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
@@ -1016,7 +1016,7 @@ importers:
         version: 7.37.0(@types/node@20.6.3)
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.86)
+        version: 0.2.29(@swc/core@1.3.87)
       '@types/jest':
         specifier: 29.5.5
         version: 29.5.5
@@ -6217,30 +6217,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@7.4.2:
-    resolution: {integrity: sha512-MgdXr9QJ2sNk0fUshQ7hk4Ec9IkbPWR6alrmDByIOEU9bThx0j4OxU9uTLBy8r5uZsSL6nNtRyCvSP8YSKaQHQ==}
-    dependencies:
-      '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 7.4.2
-      '@storybook/manager': 7.4.2
-      '@storybook/node-logger': 7.4.2
-      '@types/ejs': 3.1.2
-      '@types/find-cache-dir': 3.2.1
-      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.17)
-      browser-assert: 1.2.1
-      ejs: 3.1.8
-      esbuild: 0.18.17
-      esbuild-plugin-alias: 0.2.1
-      express: 4.18.2
-      find-cache-dir: 3.3.2
-      fs-extra: 11.1.1
-      process: 0.11.10
-      util: 0.12.5
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@storybook/builder-manager@7.4.3:
     resolution: {integrity: sha512-6jzxZ2J1jFaZXn7ZucEgV6XyUe+FJ9uuoMRZcZefoCKeXK/BOPCefijYWP3DPgqqVh3/JLUglIpz0MH9k8cBaw==}
     dependencies:
@@ -6306,17 +6282,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/channels@7.4.2:
-    resolution: {integrity: sha512-Q95KnV+fTGaAV3S875+d5LlGg+bdC3bUnki3engODDS4ViSRHJ1bnXnqxKmAaS3O/52geIyWWR766YvwHw3avw==}
-    dependencies:
-      '@storybook/client-logger': 7.4.2
-      '@storybook/core-events': 7.4.2
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.4.3:
     resolution: {integrity: sha512-lIoRX3EV0wKPX8ojIrJUtsOv4+Gv8r9pfJpam/NdyYd+rs0AjDK13ieINRfBMnJkfjsWa3vmZtGMBEVvDKwTMw==}
     dependencies:
@@ -6328,22 +6293,22 @@ packages:
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@7.4.2:
-    resolution: {integrity: sha512-WleObtC7OU2lT+pI2vTdXZPFMKDGbg3bkUJ+PG8+yqGg53ea5ZkwKWg9qHpXuiMkYDztqhbA8kYrny1GqFuVdg==}
+  /@storybook/cli@7.4.3:
+    resolution: {integrity: sha512-/lGtXbzNropsCF4srEGxiHzCU7b2wlV13LrSj3H3zOnHEAJlFcNpyNzO+4jKHfNTjjqEtcRGJ1OxrSYuGZTVjg==}
     hasBin: true
     dependencies:
       '@babel/core': 7.22.11
       '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
       '@babel/types': 7.22.17
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 7.4.2
-      '@storybook/core-common': 7.4.2
-      '@storybook/core-events': 7.4.2
-      '@storybook/core-server': 7.4.2
-      '@storybook/csf-tools': 7.4.2
-      '@storybook/node-logger': 7.4.2
-      '@storybook/telemetry': 7.4.2
-      '@storybook/types': 7.4.2
+      '@storybook/codemod': 7.4.3
+      '@storybook/core-common': 7.4.3
+      '@storybook/core-events': 7.4.3
+      '@storybook/core-server': 7.4.3
+      '@storybook/csf-tools': 7.4.3
+      '@storybook/node-logger': 7.4.3
+      '@storybook/telemetry': 7.4.3
+      '@storybook/types': 7.4.3
       '@types/semver': 7.5.2
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6380,28 +6345,22 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.4.2:
-    resolution: {integrity: sha512-LC8tYrYSJwF4DHRdNYh6y8hSvccwUIv5/WOZKJDmKx7mcEm6HsVuUu16C9jsl7iy6IqJYxgVz1va3WS6852E+A==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.4.3:
     resolution: {integrity: sha512-Nhngo9X4HjN00aRhgIVGWbwkWPe0Fz8PySuxnd8nAxSsz7KpdLFyYo2TbZZ3TX51FG5Fxcb0G5OHuunItP7EWQ==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@7.4.2:
-    resolution: {integrity: sha512-wU+SLHG/PpLptI0aWEhPxwFPcX7uYe+Id21DKNPg/HvYaLG3N+/DPDef+lm3Vaov9w4OD74iuQ3knT67SSkvmw==}
+  /@storybook/codemod@7.4.3:
+    resolution: {integrity: sha512-UwnsyVeUa+wLIeE/zO0slV3mwsPgS3DstZAWbjWUfFlJKZjgg1++Zkv0GmxkEyirsnf/g4r6Aq+KhIdIHmdzag==}
     dependencies:
       '@babel/core': 7.22.11
       '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
       '@babel/types': 7.22.17
       '@storybook/csf': 0.1.0
-      '@storybook/csf-tools': 7.4.2
-      '@storybook/node-logger': 7.4.2
-      '@storybook/types': 7.4.2
+      '@storybook/csf-tools': 7.4.3
+      '@storybook/node-logger': 7.4.3
+      '@storybook/types': 7.4.3
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
@@ -6443,37 +6402,6 @@ packages:
       '@storybook/preview-api': 7.4.3
     dev: true
 
-  /@storybook/core-common@7.4.2:
-    resolution: {integrity: sha512-Qj9S97TYO+jSNdC2+LrMFtZRvTnELeFnRtn/MDWhkM6mpZgRglxlZuXi5enJjqTh0dISAUxPpTtXNAJDfX99JA==}
-    dependencies:
-      '@storybook/core-events': 7.4.2
-      '@storybook/node-logger': 7.4.2
-      '@storybook/types': 7.4.2
-      '@types/find-cache-dir': 3.2.1
-      '@types/node': 16.18.46
-      '@types/node-fetch': 2.6.4
-      '@types/pretty-hrtime': 1.0.1
-      chalk: 4.1.2
-      esbuild: 0.18.17
-      esbuild-register: 3.4.2(esbuild@0.18.17)
-      file-system-cache: 2.3.0
-      find-cache-dir: 3.3.2
-      find-up: 5.0.0
-      fs-extra: 11.1.1
-      glob: 10.3.0
-      handlebars: 4.7.7
-      lazy-universal-dotenv: 4.0.0
-      node-fetch: 2.7.0
-      picomatch: 2.3.1
-      pkg-dir: 5.0.0
-      pretty-hrtime: 1.0.3
-      resolve-from: 5.0.0
-      ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@storybook/core-common@7.4.3:
     resolution: {integrity: sha512-jwIBUnWitZzw0VfKC77yN8DvTyePLVnAjbA2lPMbMIdO9ZY2lfD4AQ4QpuWsxJyAllFC4slOFDNgCDHx2AlYWw==}
     dependencies:
@@ -6505,70 +6433,12 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.4.2:
-    resolution: {integrity: sha512-WCEBw+Ew8DrccnB0hpP9TXadreoOlMnWCyuXU2XrvmK/vde009leWQIsLs1rY+L17zDVuogBms62AxrDDJmMpw==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.4.3:
     resolution: {integrity: sha512-FRfipCijMnVbGxL1ZjOLM836lyd/TGQcUFeVjTQWW/+pIGHELqDHiYeq68hqoGTKl0G0np59CJPWYTUZA4Dl9Q==}
     dependencies:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@7.4.2:
-    resolution: {integrity: sha512-4aaFQTjb8jsbzJeCs+VTo3gdyK3r3VhQN2sxn6k/lcKjQFeO84+iqGgGmb+oWUVz2TJL+JrNh7SUXkVsMZBXVQ==}
-    dependencies:
-      '@aw-web-design/x-default-browser': 1.4.126
-      '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 7.4.2
-      '@storybook/channels': 7.4.2
-      '@storybook/core-common': 7.4.2
-      '@storybook/core-events': 7.4.2
-      '@storybook/csf': 0.1.0
-      '@storybook/csf-tools': 7.4.2
-      '@storybook/docs-mdx': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/manager': 7.4.2
-      '@storybook/node-logger': 7.4.2
-      '@storybook/preview-api': 7.4.2
-      '@storybook/telemetry': 7.4.2
-      '@storybook/types': 7.4.2
-      '@types/detect-port': 1.3.2
-      '@types/node': 16.18.46
-      '@types/pretty-hrtime': 1.0.1
-      '@types/semver': 7.5.2
-      better-opn: 3.0.2
-      chalk: 4.1.2
-      cli-table3: 0.6.3
-      compression: 1.7.4
-      detect-port: 1.5.1
-      express: 4.18.2
-      fs-extra: 11.1.1
-      globby: 11.1.0
-      ip: 2.0.0
-      lodash: 4.17.21
-      open: 8.4.2
-      pretty-hrtime: 1.0.3
-      prompts: 2.4.2
-      read-pkg-up: 7.0.1
-      semver: 7.5.4
-      serve-favicon: 2.5.0
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-      ts-dedent: 2.2.0
-      util: 0.12.5
-      util-deprecate: 1.0.2
-      watchpack: 2.4.0
-      ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
-    transitivePeerDependencies:
-      - bufferutil
-      - encoding
-      - supports-color
-      - utf-8-validate
-    dev: true
-
   /@storybook/core-server@7.4.3:
     resolution: {integrity: sha512-yl9HaVwk/xJV9zq76n/oR1cE39wAFmNmKVPOJAtr3+c7wS0tnBkw7T+GqZ2Seyv+xkcZUWS8KRH74HqwPwG0Bw==}
     dependencies:
@@ -6630,22 +6500,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@7.4.2:
-    resolution: {integrity: sha512-5AvF2YRcYHIqQqskb3R8JvsmSWnNwkP0CGmP8Zq7zIfK/C+npKb/onv5YQlbSgh+2UrVxVdIDLc9AepBeXC3uQ==}
-    dependencies:
-      '@babel/generator': 7.22.10
-      '@babel/parser': 7.22.16
-      '@babel/traverse': 7.22.11
-      '@babel/types': 7.22.17
-      '@storybook/csf': 0.1.0
-      '@storybook/types': 7.4.2
-      fs-extra: 11.1.1
-      recast: 0.23.1
-      ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
   /@storybook/csf-tools@7.4.3:
     resolution: {integrity: sha512-nkVakGx2kzou91lGcxnyFNiSEdnpx1a53lQTl/DLm0QpDbqQuu3ZbZWXZCpXV97t/6YPeCCnGLXodnI7PZyZBA==}
     dependencies:
@@ -6706,11 +6560,11 @@ packages:
       '@storybook/preview-api': 7.4.3
     dev: true
 
-  /@storybook/jest@0.2.2(vitest@0.34.4):
+  /@storybook/jest@0.2.2(vitest@0.34.5):
     resolution: {integrity: sha512-PUfp9WoqUA8NdAmiz3UahUsyAMr+g1Dv3BB0fqJZsE2IuE5o1Mgsv4iLGzFm+ohcQLIDQvwvvbQIpxe8eY7TNw==}
     dependencies:
       '@storybook/expect': 28.1.3-5
-      '@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.4)
+      '@testing-library/jest-dom': 6.1.2(@types/jest@28.1.3)(vitest@0.34.5)
       '@types/jest': 28.1.3
       jest-mock: 27.5.1
     transitivePeerDependencies:
@@ -6744,10 +6598,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/manager@7.4.2:
-    resolution: {integrity: sha512-MtjmbAaf4hUObAa2ETQkm0+SzESoPeNm+TyzwZU5qq3Ouj4IOj2Ugd8EJPO3isdHxYt26A255tW/G9mb9v20fQ==}
-    dev: true
-
   /@storybook/manager@7.4.3:
     resolution: {integrity: sha512-7U92tYwjt0DIKX7vCKNSZefuEavdnJYa5/zSjdlo0LtfBmGRBak1eq/sVLGfzrZ+wKIlCXgNh3f8OLy8RMnOOw==}
     dev: true
@@ -6756,10 +6606,6 @@ packages:
     resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
     dev: true
 
-  /@storybook/node-logger@7.4.2:
-    resolution: {integrity: sha512-iSBjhnMpWY9Hs5KGnf/xfHjGtGl740LUg4Gce874DuL773Mdc4hdppSKr4X/Pp1/AD67mNuifSXYx3V7d6XzTQ==}
-    dev: true
-
   /@storybook/node-logger@7.4.3:
     resolution: {integrity: sha512-pL13PPMUttflTWKVeDIKxPIJtBRl50Fzck12/7uiNROtBIrSV9DZSgOjInAazjo4tl+7fDj9lgkGeMEz00E8aQ==}
     dev: true
@@ -6768,25 +6614,6 @@ packages:
     resolution: {integrity: sha512-6NMaAvL4a26jR50UPz+Q6VATY3lHZWw1ru/weFgiV0rat632RFdiFyrMMrjbUWu9HDJE4fbCzrIZU0jGVs1wlQ==}
     dev: true
 
-  /@storybook/preview-api@7.4.2:
-    resolution: {integrity: sha512-ihTHRYzI/sI6bD215aYppiWF+1u38TrlsNjFYJ/Grftbti5d40g5wCwvAXK41SxJNYpk6CRtfvNKOwbEAC33gg==}
-    dependencies:
-      '@storybook/channels': 7.4.2
-      '@storybook/client-logger': 7.4.2
-      '@storybook/core-events': 7.4.2
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/types': 7.4.2
-      '@types/qs': 6.9.7
-      dequal: 2.0.3
-      lodash: 4.17.21
-      memoizerific: 1.11.3
-      qs: 6.11.1
-      synchronous-promise: 2.0.17
-      ts-dedent: 2.2.0
-      util-deprecate: 1.0.2
-    dev: true
-
   /@storybook/preview-api@7.4.3:
     resolution: {integrity: sha512-qKwfH2+qN1Zpz2UX6dQLiTU5x2JH3o/+jOY4GYF6c3atTm5WAu1OvCYAJVb6MdXfAhZNuPwDKnJR8VmzWplWBg==}
     dependencies:
@@ -6916,22 +6743,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/telemetry@7.4.2:
-    resolution: {integrity: sha512-ZAh1Bjk9JVpL5j0Aj3PHr3XEDZcOrFeugVyRuuul2gAyu6SbpPhl8Dd2Wr9YJS0ZDWs3u0CgKRCeFPPAi3QosA==}
-    dependencies:
-      '@storybook/client-logger': 7.4.2
-      '@storybook/core-common': 7.4.2
-      '@storybook/csf-tools': 7.4.2
-      chalk: 4.1.2
-      detect-package-manager: 2.0.1
-      fetch-retry: 5.0.4
-      fs-extra: 11.1.1
-      read-pkg-up: 7.0.1
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
   /@storybook/telemetry@7.4.3:
     resolution: {integrity: sha512-gA7QfQSdDocNKP0KfrmIhD8ZgW5G4zZD/NL0OsATlkL3H/DehH3Ugjfffh7Ao2JZRXogHp8p9EQCVfPW7iKgBQ==}
     dependencies:
@@ -6970,15 +6781,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.4.2:
-    resolution: {integrity: sha512-OOJ2TeS3Zzc6spHbdH+JXml0q4IHuYt9axmXAv1/pkhqHjA5072pyUacmlYNQeihpQOOsKLiCQUQlvtMy9fTnQ==}
-    dependencies:
-      '@storybook/channels': 7.4.2
-      '@types/babel__core': 7.20.0
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.4.3:
     resolution: {integrity: sha512-DrHC1hIiw9TqDILLokDnvbUPNxGz5iJaYFEv30uvYE0s9MvgEUPblCChEUjaHOps7zQTznMPf8ULfoXlgqxk2A==}
     dependencies:
@@ -7040,7 +6842,7 @@ packages:
       - supports-color
     dev: true
 
-  /@swc/cli@0.1.62(@swc/core@1.3.86)(chokidar@3.5.3):
+  /@swc/cli@0.1.62(@swc/core@1.3.87)(chokidar@3.5.3):
     resolution: {integrity: sha512-kOFLjKY3XH1DWLfXL1/B5MizeNorHR8wHKEi92S/Zi9Md/AK17KSqR8MgyRJ6C1fhKHvbBCl8wboyKAFXStkYw==}
     engines: {node: '>= 12.13'}
     hasBin: true
@@ -7052,7 +6854,7 @@ packages:
         optional: true
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.3.86
+      '@swc/core': 1.3.87
       chokidar: 3.5.3
       commander: 7.2.0
       fast-glob: 3.3.1
@@ -7081,8 +6883,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.86:
-    resolution: {integrity: sha512-hMvSDms0sJJHNtRa3Vhmr9StWN1vmikbf5VE0IZUYGnF1/JZTkXU1h6CdNUY4Hr6i7uCZjH6BEhxFHX1JtKV4w==}
+  /@swc/core-darwin-arm64@1.3.87:
+    resolution: {integrity: sha512-/LxLjPat1LA9CXS7Cn2M4MIqwNOoDF4KjcikPkO08H54rd6WubhaJnr0sLDjms3adRr+pmcCL0yfsUBTX//85A==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [darwin]
@@ -7098,8 +6900,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-x64@1.3.86:
-    resolution: {integrity: sha512-Jro6HVH4uSOBM7tTDaQNKLNc8BJV7n+SO+Ft2HAZINyeKJS/8MfEYneG7Vmqg18gv00c6dz9AOCcyz+BR7BFkQ==}
+  /@swc/core-darwin-x64@1.3.87:
+    resolution: {integrity: sha512-hjSQNcW9BN8gEz3UQZ7Ye80ymbkFHLkUDeEek4lorRyq6S+uxvbL1f1mJAZnFPBpove7AXusykIalWMPvyOR2A==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [darwin]
@@ -7126,8 +6928,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.3.86:
-    resolution: {integrity: sha512-wYB9m0pzXJVSzedXSl4JwS3gKtvcPinpe9MbkddezpqL7OjyDP6pHHW9qIucsfgCrtMtbPC2nqulXLPtAAyIjw==}
+  /@swc/core-linux-arm-gnueabihf@1.3.87:
+    resolution: {integrity: sha512-JVyNIO3tGLPSQ59rJXeKaykTpPhRNozB+7PtYMvMcxpUbYGpEzWxTPkFAX2KKPvl0ejBdA0GW5OXeuPMvTwE0w==}
     engines: {node: '>=10'}
     cpu: [arm]
     os: [linux]
@@ -7143,8 +6945,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.86:
-    resolution: {integrity: sha512-fR44IyK5cdCaO8cC++IEH0Jn03tWnunJnjzA99LxlE5TRInSIOvFm+g5OSUQZDAvEXmQ38sd31LO2HOoDS1Edw==}
+  /@swc/core-linux-arm64-gnu@1.3.87:
+    resolution: {integrity: sha512-gLdZKIoql5vjrNjrwwsiS7d3vOAIzYUWqN97iGCSscQOg0MgYbfUnSTO4UEvH4BYlwRNlHepfTZ7ALoG8areUQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -7160,8 +6962,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.86:
-    resolution: {integrity: sha512-EUPfdbK4dUk/nkX3Vmv/47XH+DqHOa9JI0CTthvJ8/ZXei1MKDUsUc+tI1zMQX2uCuSkSWsEIEpCmA0tMwFhtw==}
+  /@swc/core-linux-arm64-musl@1.3.87:
+    resolution: {integrity: sha512-WQ5tirVBiU8lUODQ25dt8JRCZHyRDInBe4fkGuxzImMa017zYPWa2WxrKK8LdDF7DzrAITlGl9VeoeE/l0WJbw==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -7177,8 +6979,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.86:
-    resolution: {integrity: sha512-snVZZWv8XgNVaKrTxtO3rUN+BbbB6I8Fqwe8zM/DWGJ096J13r89doQ48x5ZyO+bW4D48eZIWP5pdfSW7oBE3w==}
+  /@swc/core-linux-x64-gnu@1.3.87:
+    resolution: {integrity: sha512-/vQSH7ZKOuT1It9GzpJ9UFnsOP/dQr1VLUrKQFBlHp9owIWNb2oUrZdNla+KhljCIIahh0JfQ08sycKeycCNzQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -7194,8 +6996,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.86:
-    resolution: {integrity: sha512-PnnksUJymEJkdnbV2orOSOSB441UqsxYbJge9zbr5UTRXUfWO3eFRV0iTBegjTlOQGbW6yN+YRSDkenTbmCI6g==}
+  /@swc/core-linux-x64-musl@1.3.87:
+    resolution: {integrity: sha512-C1NUeISJDyMlIk4919bjcpHvjyjzbkjW7v53gUdN41Y4BPlEk7UKcLez7UHMjdMGA/o9721SLqYVp4/NrQErUw==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -7211,8 +7013,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.86:
-    resolution: {integrity: sha512-XlGEGyHwLndm08VvgeAPGj40L+Hx575MQC+2fsyB1uSNUN+uf7fvke+wc7k50a92CaQe/8foLyIR5faayozEJA==}
+  /@swc/core-win32-arm64-msvc@1.3.87:
+    resolution: {integrity: sha512-AE7JKDJ0OsV9LsYGFfYKMTkGNfsy1au4RT5jT1rxr5MTOsmMD7P2mgiRF8drgc1WX3uOJbTHQfgdVTYroAGfdA==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [win32]
@@ -7228,8 +7030,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.3.86:
-    resolution: {integrity: sha512-U1BhZa1x9yn+wZGTQmt1cYR79a0FzW/wL6Jas1Pn0bykKLxdRU4mCeZt2P+T3buLm8jr8LpPWiCrbvr658PzwA==}
+  /@swc/core-win32-ia32-msvc@1.3.87:
+    resolution: {integrity: sha512-2V+5uvisaTPXd5lvTujNLNlEC2LPo07gEUQVGdKGsbhtLAYAggVXBnHjxU1TkuyA6NlciMS59tPKW+L2u2KpTw==}
     engines: {node: '>=10'}
     cpu: [ia32]
     os: [win32]
@@ -7245,16 +7047,16 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.86:
-    resolution: {integrity: sha512-wRoQUajqpE3wITHhZVj/6BPu/QwHriFHLHuJA+9y6PeGtUtTmntL42aBKXIFhfL767dYFtohyNg1uZ9eqbGyGQ==}
+  /@swc/core-win32-x64-msvc@1.3.87:
+    resolution: {integrity: sha512-2Xak7TidlRuNQamLZC3fEOdUCmMiBzD2BW8+Dnn29f4odzamgAFfeYJ/PnqN7jdTWOINLn95tex4JBm3Pm11HQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@swc/core@1.3.86:
-    resolution: {integrity: sha512-bEXUtm37bcmJ3q+geG7Zy4rJNUzpxalXQUrrqX1ZoGj3HRtzdeVZ0L/um3fG2j16qe61t8TX/OIZ2G6j6dkG/w==}
+  /@swc/core@1.3.87:
+    resolution: {integrity: sha512-u33Mi/EBvb+g/xpYKyxODB5XvKYqISmy81J+lhFS/Oahja0PbJWZdKEGwSQEFvBecp6E+PfaTOLPOoF1EWcRrw==}
     engines: {node: '>=10'}
     requiresBuild: true
     peerDependencies:
@@ -7265,25 +7067,25 @@ packages:
     dependencies:
       '@swc/types': 0.1.4
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.3.86
-      '@swc/core-darwin-x64': 1.3.86
-      '@swc/core-linux-arm-gnueabihf': 1.3.86
-      '@swc/core-linux-arm64-gnu': 1.3.86
-      '@swc/core-linux-arm64-musl': 1.3.86
-      '@swc/core-linux-x64-gnu': 1.3.86
-      '@swc/core-linux-x64-musl': 1.3.86
-      '@swc/core-win32-arm64-msvc': 1.3.86
-      '@swc/core-win32-ia32-msvc': 1.3.86
-      '@swc/core-win32-x64-msvc': 1.3.86
+      '@swc/core-darwin-arm64': 1.3.87
+      '@swc/core-darwin-x64': 1.3.87
+      '@swc/core-linux-arm-gnueabihf': 1.3.87
+      '@swc/core-linux-arm64-gnu': 1.3.87
+      '@swc/core-linux-arm64-musl': 1.3.87
+      '@swc/core-linux-x64-gnu': 1.3.87
+      '@swc/core-linux-x64-musl': 1.3.87
+      '@swc/core-win32-arm64-msvc': 1.3.87
+      '@swc/core-win32-ia32-msvc': 1.3.87
+      '@swc/core-win32-x64-msvc': 1.3.87
 
-  /@swc/jest@0.2.29(@swc/core@1.3.86):
+  /@swc/jest@0.2.29(@swc/core@1.3.87):
     resolution: {integrity: sha512-8reh5RvHBsSikDC3WGCd5ZTd2BXKkyOdK7QwynrCH58jk2cQFhhHhFBg/jvnWZehUQe/EoOImLENc9/DwbBFow==}
     engines: {npm: '>= 7.0.0'}
     peerDependencies:
       '@swc/core': '*'
     dependencies:
       '@jest/create-cache-key-function': 27.5.1
-      '@swc/core': 1.3.86
+      '@swc/core': 1.3.87
       jsonc-parser: 3.2.0
     dev: true
 
@@ -7460,7 +7262,7 @@ packages:
       pretty-format: 27.5.1
     dev: true
 
-  /@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.4):
+  /@testing-library/jest-dom@6.1.2(@types/jest@28.1.3)(vitest@0.34.5):
     resolution: {integrity: sha512-NP9jl1Q2qDDtx+cqogowtQtmgD2OVs37iMSIsTv5eN5ETRkf26Kj6ugVwA93/gZzzFWQAsgkKkcftDe91BJCkQ==}
     engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
     peerDependencies:
@@ -7487,7 +7289,7 @@ packages:
       dom-accessibility-api: 0.5.16
       lodash: 4.17.21
       redent: 3.0.0
-      vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
+      vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
     dev: true
 
   /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0):
@@ -8300,8 +8102,8 @@ packages:
       vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
       vue: 3.3.4
 
-  /@vitest/coverage-v8@0.34.4(vitest@0.34.4):
-    resolution: {integrity: sha512-TZ5ghzhmg3COQqfBShL+zRQEInHmV9TSwghTdfkHpCTyTOr+rxo6x41vCNcVfWysWULtqtBVpY6YFNovxnESfA==}
+  /@vitest/coverage-v8@0.34.5(vitest@0.34.5):
+    resolution: {integrity: sha512-97xjhRTSdmeeHCm2nNHhT3hLsMYkAhHXm/rwj6SZ3voka8xiCJrwgtfIjoZIFEL4OO0KezGmVuHWQXcMunULIA==}
     peerDependencies:
       vitest: '>=0.32.0 <1'
     dependencies:
@@ -8316,43 +8118,43 @@ packages:
       std-env: 3.3.3
       test-exclude: 6.0.0
       v8-to-istanbul: 9.1.0
-      vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
+      vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitest/expect@0.34.4:
-    resolution: {integrity: sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==}
+  /@vitest/expect@0.34.5:
+    resolution: {integrity: sha512-/3RBIV9XEH+nRpRMqDJBufKIOQaYUH2X6bt0rKSCW0MfKhXFLYsR5ivHifeajRSTsln0FwJbitxLKHSQz/Xwkw==}
     dependencies:
-      '@vitest/spy': 0.34.4
-      '@vitest/utils': 0.34.4
+      '@vitest/spy': 0.34.5
+      '@vitest/utils': 0.34.5
       chai: 4.3.7
     dev: true
 
-  /@vitest/runner@0.34.4:
-    resolution: {integrity: sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==}
+  /@vitest/runner@0.34.5:
+    resolution: {integrity: sha512-RDEE3ViVvl7jFSCbnBRyYuu23XxmvRTSZWW6W4M7eC5dOsK75d5LIf6uhE5Fqf809DQ1+9ICZZNxhIolWHU4og==}
     dependencies:
-      '@vitest/utils': 0.34.4
+      '@vitest/utils': 0.34.5
       p-limit: 4.0.0
       pathe: 1.1.1
     dev: true
 
-  /@vitest/snapshot@0.34.4:
-    resolution: {integrity: sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==}
+  /@vitest/snapshot@0.34.5:
+    resolution: {integrity: sha512-+ikwSbhu6z2yOdtKmk/aeoDZ9QPm2g/ZO5rXT58RR9Vmu/kB2MamyDSx77dctqdZfP3Diqv4mbc/yw2kPT8rmA==}
     dependencies:
       magic-string: 0.30.3
       pathe: 1.1.1
       pretty-format: 29.7.0
     dev: true
 
-  /@vitest/spy@0.34.4:
-    resolution: {integrity: sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==}
+  /@vitest/spy@0.34.5:
+    resolution: {integrity: sha512-epsicsfhvBjRjCMOC/3k00mP/TBGQy8/P0DxOFiWyLt55gnZ99dqCfCiAsKO17BWVjn4eZRIjKvcqNmSz8gvmg==}
     dependencies:
       tinyspy: 2.1.1
     dev: true
 
-  /@vitest/utils@0.34.4:
-    resolution: {integrity: sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==}
+  /@vitest/utils@0.34.5:
+    resolution: {integrity: sha512-ur6CmmYQoeHMwmGb0v+qwkwN3yopZuZyf4xt1DBBSGBed8Hf9Gmbm/5dEWqgpLPdRx6Av6jcWXrjcKfkTzg/pw==}
     dependencies:
       diff-sequences: 29.6.3
       loupe: 2.3.6
@@ -8448,8 +8250,8 @@ packages:
       '@vue/compiler-dom': 3.3.4
       '@vue/shared': 3.3.4
 
-  /@vue/language-core@1.8.11(typescript@5.2.2):
-    resolution: {integrity: sha512-+MZOBGqGwfld6hpo0DB47x8eNM0dNqk15ZdfOhj19CpvuYuOWCeVdOEGZunKDyo3QLkTn3kLOSysJzg7FDOQBA==}
+  /@vue/language-core@1.8.13(typescript@5.2.2):
+    resolution: {integrity: sha512-nata2fYBZAkl4QJrU+IcArJCMTHt1VP8ePL/Z7eUPC2AF+Cm7Qgo9ksNCPBzZRh1LYjCaSaqV7njqNogwpsMVg==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
@@ -8518,11 +8320,11 @@ packages:
       '@vue/server-renderer': 3.3.4(vue@3.3.4)
     dev: true
 
-  /@vue/typescript@1.8.11(typescript@5.2.2):
-    resolution: {integrity: sha512-skUmMDiPUUtu1flPmf2YybF+PX8IzBtMioQOaNn6Ck/RhdrPJGj1AX/7s3Buf9G6ln+/KHR1XQuti/FFxw5XVA==}
+  /@vue/typescript@1.8.13(typescript@5.2.2):
+    resolution: {integrity: sha512-ALJjHFqQ3dgZVCI/ogAS/dZ7JEhIi1N0Em5I7uwabY1p9RDRK3odLsycMHyxZRjm5dLI15c07eeBloHiD2Otlg==}
     dependencies:
       '@volar/typescript': 1.10.0
-      '@vue/language-core': 1.8.11(typescript@5.2.2)
+      '@vue/language-core': 1.8.13(typescript@5.2.2)
     transitivePeerDependencies:
       - typescript
     dev: true
@@ -9448,8 +9250,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.11.2:
-    resolution: {integrity: sha512-ffDw8B2+N8x+HVRTb9/nEngb3ubEYb8vdKKTJfUiUoPWPUcHzCkylSVWWf7dcr6KjzKElaCWEjVsKzHjmyhmnw==}
+  /bullmq@4.11.3:
+    resolution: {integrity: sha512-Z5zmgK2gJ+FIFhPBHpB5zRGGD2/89MMm/qIM3SMrt1fgP7RCy7y4qGrQVj7OmuNr43y8UG0UtQlYVADpSZAVTA==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -10851,14 +10653,6 @@ packages:
   /ee-first@1.1.1:
     resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
 
-  /ejs@3.1.8:
-    resolution: {integrity: sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==}
-    engines: {node: '>=0.10.0'}
-    hasBin: true
-    dependencies:
-      jake: 10.8.5
-    dev: true
-
   /ejs@3.1.9:
     resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
     engines: {node: '>=0.10.0'}
@@ -17941,9 +17735,9 @@ packages:
     resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==}
     dev: false
 
-  /start-server-and-test@2.0.0:
-    resolution: {integrity: sha512-UqKLw0mJbfrsG1jcRLTUlvuRi9sjNuUiDOLI42r7R5fA9dsFoywAy9DoLXNYys9B886E4RCKb+qM1Gzu96h7DQ==}
-    engines: {node: '>=6'}
+  /start-server-and-test@2.0.1:
+    resolution: {integrity: sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==}
+    engines: {node: '>=16'}
     hasBin: true
     dependencies:
       arg: 5.0.2
@@ -17977,11 +17771,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@7.4.2:
-    resolution: {integrity: sha512-UuYmdxEWEQAepfjgQFbbHTq47Xxpw16naAvJ9n/nsjMnOhYupm1ZIdWYaeNjz4LOfz+1WzgU7us0IvaBrxzl4g==}
+  /storybook@7.4.3:
+    resolution: {integrity: sha512-afp7trR23jKt8ruGMPjkNAk3A/4CaLo20iPWAODznlF7u+XWnqGm1S+ZUiJFf13Jzj8jmJf/d7/xDHxY3qVMUA==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 7.4.2
+      '@storybook/cli': 7.4.3
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -19134,8 +18928,8 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
-    resolution: {integrity: sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==}
+  /vite-node@0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
+    resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
     dependencies:
@@ -19197,20 +18991,20 @@ packages:
     optionalDependencies:
       fsevents: 2.3.2
 
-  /vitest-fetch-mock@0.2.2(vitest@0.34.4):
+  /vitest-fetch-mock@0.2.2(vitest@0.34.5):
     resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
     engines: {node: '>=14.14.0'}
     peerDependencies:
       vitest: '>=0.16.0'
     dependencies:
       cross-fetch: 3.1.5
-      vitest: 0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
+      vitest: 0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - encoding
     dev: true
 
-  /vitest@0.34.4(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0):
-    resolution: {integrity: sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==}
+  /vitest@0.34.5(happy-dom@10.0.3)(sass@1.68.0)(terser@5.20.0):
+    resolution: {integrity: sha512-CPI68mmnr2DThSB3frSuE5RLm9wo5wU4fbDrDwWQQB1CWgq9jQVoQwnQSzYAjdoBOPoH2UtXpOgHVge/uScfZg==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
     peerDependencies:
@@ -19243,11 +19037,11 @@ packages:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
       '@types/node': 20.6.3
-      '@vitest/expect': 0.34.4
-      '@vitest/runner': 0.34.4
-      '@vitest/snapshot': 0.34.4
-      '@vitest/spy': 0.34.4
-      '@vitest/utils': 0.34.4
+      '@vitest/expect': 0.34.5
+      '@vitest/runner': 0.34.5
+      '@vitest/snapshot': 0.34.5
+      '@vitest/spy': 0.34.5
+      '@vitest/utils': 0.34.5
       acorn: 8.10.0
       acorn-walk: 8.2.0
       cac: 6.7.14
@@ -19263,7 +19057,7 @@ packages:
       tinybench: 2.5.0
       tinypool: 0.7.0
       vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
-      vite-node: 0.34.4(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite-node: 0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19343,14 +19137,14 @@ packages:
       he: 1.2.0
     dev: true
 
-  /vue-tsc@1.8.11(typescript@5.2.2):
-    resolution: {integrity: sha512-BzfiMdPqDHBlysx4g26NkfVHSQwGD/lTRausmxN9sFyjXz34OWfsbkh0YsVkX84Hu65In1fFlxHiG39Tr4Vojg==}
+  /vue-tsc@1.8.13(typescript@5.2.2):
+    resolution: {integrity: sha512-Hl8zUXPVK2KzPtbXeMCN0CSFkwvD96YOtYt9KvJPG9W8QGcNpGk9KHwPuGMxA8blWXSIli7gtsoC+clICEVdVg==}
     hasBin: true
     peerDependencies:
       typescript: '*'
     dependencies:
-      '@vue/language-core': 1.8.11(typescript@5.2.2)
-      '@vue/typescript': 1.8.11(typescript@5.2.2)
+      '@vue/language-core': 1.8.13(typescript@5.2.2)
+      '@vue/typescript': 1.8.13(typescript@5.2.2)
       semver: 7.5.4
       typescript: 5.2.2
     dev: true

From 526b3ae0e49caeee05b299272e80c30f69c91d92 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Fri, 22 Sep 2023 16:52:43 +0900
Subject: [PATCH 07/62] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=82=92=E9=99=A4=E5=A4=96=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=A2?=
 =?UTF-8?q?=E3=83=B3=E3=83=86=E3=83=8A=20(#11277)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: ユーザを除外できるアンテナ

* feat(backend/api): ユーザを除外できるアンテナの作成・更新

* feat(frontend): ユーザを除外できるアンテナの作成・更新

* docs(changelog): add アンテナで一部のユーザを除外したすべてのノートから受信できるようになりました

* fix: d.ts生成時にexport defaultを生成するように

* fix CHANGELOG.md

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                           |  1 +
 locales/index.d.ts                                     |  1 +
 locales/ja-JP.yml                                      |  1 +
 .../migration/1689325027964-UserBlacklistAnntena.js    | 10 ++++++++++
 packages/backend/src/core/AntennaService.ts            |  6 ++++++
 packages/backend/src/models/Antenna.ts                 |  4 ++--
 packages/backend/src/models/json-schema/antenna.ts     |  2 +-
 .../src/server/api/endpoints/antennas/create.ts        |  2 +-
 .../src/server/api/endpoints/antennas/update.ts        |  2 +-
 packages/frontend/src/pages/my-antennas/editor.vue     |  3 ++-
 10 files changed, 26 insertions(+), 6 deletions(-)
 create mode 100644 packages/backend/migration/1689325027964-UserBlacklistAnntena.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc81997d41..e6c66f0859 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 - Feat: プロフィールでのリンク検証
 - Feat: 通知をテストできるようになりました
 - Feat: PWAのアイコンが設定できるようになりました
+- Enhance: アンテナの受信ソースに指定したユーザを除外するものを追加
 - Enhance: 二要素認証設定時のセキュリティを強化
 	- パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました
 - Enhance: manifest.jsonをオーバーライド可能に
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3009c99185..f6b6daae89 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1917,6 +1917,7 @@ export interface Locale {
         "homeTimeline": string;
         "users": string;
         "userList": string;
+        "userBlacklist": string;
     };
     "_weekday": {
         "sunday": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b30fd5333d..82ced0aa3b 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1834,6 +1834,7 @@ _antennaSources:
   homeTimeline: "フォローしているユーザーのノート"
   users: "指定した一人または複数のユーザーのノート"
   userList: "指定したリストのユーザーのノート"
+  userBlacklist: "指定した一人または複数のユーザーを除いた全てのノート"
 
 _weekday:
   sunday: "日曜日"
diff --git a/packages/backend/migration/1689325027964-UserBlacklistAnntena.js b/packages/backend/migration/1689325027964-UserBlacklistAnntena.js
new file mode 100644
index 0000000000..ce246b20f8
--- /dev/null
+++ b/packages/backend/migration/1689325027964-UserBlacklistAnntena.js
@@ -0,0 +1,10 @@
+export class UserBlacklistAnntena1689325027964 {
+    name = 'UserBlacklistAnntena1689325027964'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TYPE "antenna_src_enum" ADD VALUE 'users_blacklist' AFTER 'list'`);
+    }
+
+    async down(queryRunner) {
+    }
+}
diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index a7e74eeef0..841ce4b84a 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -119,6 +119,12 @@ export class AntennaService implements OnApplicationShutdown {
 				return this.utilityService.getFullApAccount(username, host).toLowerCase();
 			});
 			if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false;
+		} else if (antenna.src === 'users_blacklist') {
+			const accts = antenna.users.map(x => {
+				const { username, host } = Acct.parse(x);
+				return this.utilityService.getFullApAccount(username, host).toLowerCase();
+			});
+			if (accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false;
 		}
 
 		const keywords = antenna.keywords
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index 7c1d80cc46..dc398b6dd2 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -41,8 +41,8 @@ export class MiAntenna {
 	})
 	public name: string;
 
-	@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
-	public src: 'home' | 'all' | 'users' | 'list';
+	@Column('enum', { enum: ['home', 'all', 'users', 'list', 'users_blacklist'] })
+	public src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
 
 	@Column({
 		...id(),
diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts
index 3f58cbee6c..7b6475919c 100644
--- a/packages/backend/src/models/json-schema/antenna.ts
+++ b/packages/backend/src/models/json-schema/antenna.ts
@@ -47,7 +47,7 @@ export const packedAntennaSchema = {
 		src: {
 			type: 'string',
 			optional: false, nullable: false,
-			enum: ['home', 'all', 'users', 'list'],
+			enum: ['home', 'all', 'users', 'list', 'users_blacklist'],
 		},
 		userListId: {
 			type: 'string',
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index f1170efe42..15fca4904d 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -47,7 +47,7 @@ export const paramDef = {
 	type: 'object',
 	properties: {
 		name: { type: 'string', minLength: 1, maxLength: 100 },
-		src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
+		src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] },
 		userListId: { type: 'string', format: 'misskey:id', nullable: true },
 		keywords: { type: 'array', items: {
 			type: 'array', items: {
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index b020a7595f..0e98746881 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -46,7 +46,7 @@ export const paramDef = {
 	properties: {
 		antennaId: { type: 'string', format: 'misskey:id' },
 		name: { type: 'string', minLength: 1, maxLength: 100 },
-		src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
+		src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'users_blacklist'] },
 		userListId: { type: 'string', format: 'misskey:id', nullable: true },
 		keywords: { type: 'array', items: {
 			type: 'array', items: {
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index cd920b75e6..4add66c396 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -16,12 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
 				<option value="users">{{ i18n.ts._antennaSources.users }}</option>
 				<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
+				<option value="users_blacklist">{{ i18n.ts._antennaSources.userBlacklist }}</option>
 			</MkSelect>
 			<MkSelect v-if="src === 'list'" v-model="userListId">
 				<template #label>{{ i18n.ts.userList }}</template>
 				<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
 			</MkSelect>
-			<MkTextarea v-else-if="src === 'users'" v-model="users">
+			<MkTextarea v-else-if="src === 'users' || src === 'users_blacklist'" v-model="users">
 				<template #label>{{ i18n.ts.users }}</template>
 				<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
 			</MkTextarea>

From f748c9144c93e93d84230d44daa324f638a1d0f1 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 16:59:37 +0900
Subject: [PATCH 08/62] 2023.9.0-beta.11

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index be3e7762e6..99c9550be4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-beta.10",
+	"version": "2023.9.0-beta.11",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From d2ae80dd210866f3ebff49351ae1aaa7eb30df2b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 21:09:14 +0900
Subject: [PATCH 09/62] =?UTF-8?q?fix(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E4=B8=80=E8=A6=A7=E3=81=8C?=
 =?UTF-8?q?=E7=A8=AE=E5=88=A5=E3=81=94=E3=81=A8=E3=81=AB=E3=83=95=E3=82=A3?=
 =?UTF-8?q?=E3=83=AB=E3=82=BF=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #11865
---
 packages/frontend/src/components/MkNoteDetailed.vue | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 0bcf98cca2..94c6833b14 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -158,11 +158,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<span style="margin-left: 4px;">{{ appearNote.reactions[reaction] }}</span>
 				</button>
 			</div>
-			<MkPagination :pagination="reactionsPagination">
+			<MkPagination v-if="reactionTabType" :key="reactionTabType" :pagination="reactionsPagination">
 				<template #default="{ items }">
-					<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
-						<MkUserCardMini :user="item.user" :withChart="false"/>
-					</MkA>
+					<div class="_gaps_s">
+						<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
+							<MkUserCardMini :user="item.user" :withChart="false"/>
+						</MkA>
+					</div>
 				</template>
 			</MkPagination>
 		</div>

From 3085739e0ef17b0b1c38940adfd760089bd96ff3 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Sep 2023 21:10:54 +0900
Subject: [PATCH 10/62] :art:

Resolve #11867
---
 packages/frontend/src/components/MkNote.vue | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 0bc94f5bd2..b397f3eee9 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -938,9 +938,6 @@ function readPromo() {
 	height: 32px;
 	margin: 2px;
 	padding: 0 6px;
-	border: dashed 1px var(--divider);
-	border-radius: 4px;
-	background: transparent;
 	opacity: .8;
 }
 </style>

From 1924bd20bb294bf2fdec6df922b3ec663102ae7c 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: Sat, 23 Sep 2023 09:08:14 +0900
Subject: [PATCH 11/62] =?UTF-8?q?enhance(frontend):=20=E3=83=97=E3=83=A9?=
 =?UTF-8?q?=E3=82=B0=E3=82=A4=E3=83=B3=E3=81=AE=E3=82=BD=E3=83=BC=E3=82=B9?=
 =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E7=A2=BA=E8=AA=8D=E3=83=BB?=
 =?UTF-8?q?=E3=82=B3=E3=83=94=E3=83=BC=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#11873)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (add) plugin: view and copy source code

* (fix) plugin permission ui

* Update Changelog
---
 CHANGELOG.md                                  |  2 +
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 .../frontend/src/pages/settings/plugin.vue    | 61 ++++++++++++++-----
 packages/frontend/src/store.ts                |  3 +
 5 files changed, 52 insertions(+), 16 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e6c66f0859..7c4134bdef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,7 @@
 - Enhance: ノート詳細ページ読み込み時のパフォーマンスが向上しました
 - Enhance: タイムラインでリスト/アンテナ選択時のパフォーマンスを改善
 - Enhance: 「Moderation note」、「Add moderation note」をローカライズできるように
+- Enhance: プラグインのソースコードを確認・コピーできるように
 - Enhance: 細かなデザインの調整
 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
@@ -76,6 +77,7 @@
 - Fix: Misskeyプラグインをインストールする際のAiScriptバージョンのチェックが0.14.0以降に対応していない問題を修正
 - Fix: 他のサーバーのユーザーへ「メッセージを送信」した時の初期テキストのメンションが間違っている問題を修正
 - Fix: 環境によってはMisskey Webが開けない問題を修正
+- Fix: プラグインの権限リストが見れない問題を修正
 
 ### Server
 - Change: cacheRemoteFilesの初期値はfalseになりました
diff --git a/locales/index.d.ts b/locales/index.d.ts
index f6b6daae89..256f178124 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1632,6 +1632,7 @@ export interface Locale {
         "install": string;
         "installWarn": string;
         "manage": string;
+        "viewSource": string;
     };
     "_preferencesBackups": {
         "list": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 82ced0aa3b..90d025d80f 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1549,6 +1549,7 @@ _plugin:
   install: "プラグインのインストール"
   installWarn: "信頼できないプラグインはインストールしないでください。"
   manage: "プラグインの管理"
+  viewSource: "ソースを表示"
 
 _preferencesBackups:
   list: "作成したバックアップ"
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index e9bc8573b3..4a2d8d600e 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -10,28 +10,49 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<FormSection>
 		<template #label>{{ i18n.ts.manage }}</template>
 		<div class="_gaps_s">
-			<div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;">
-				<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
+			<div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_m" style="padding: 20px;">
+				<div class="_gaps_s">
+					<span style="display: flex; align-items: center;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
+					<MkSwitch :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch>
+				</div>
 
-				<MkSwitch :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch>
-
-				<MkKeyValue>
-					<template #key>{{ i18n.ts.author }}</template>
-					<template #value>{{ plugin.author }}</template>
-				</MkKeyValue>
-				<MkKeyValue>
-					<template #key>{{ i18n.ts.description }}</template>
-					<template #value>{{ plugin.description }}</template>
-				</MkKeyValue>
-				<MkKeyValue>
-					<template #key>{{ i18n.ts.permission }}</template>
-					<template #value>{{ plugin.permission }}</template>
-				</MkKeyValue>
+				<div class="_gaps_s">
+					<MkKeyValue>
+						<template #key>{{ i18n.ts.author }}</template>
+						<template #value>{{ plugin.author }}</template>
+					</MkKeyValue>
+					<MkKeyValue>
+						<template #key>{{ i18n.ts.description }}</template>
+						<template #value>{{ plugin.description }}</template>
+					</MkKeyValue>
+					<MkKeyValue>
+						<template #key>{{ i18n.ts.permission }}</template>
+						<template #value>
+							<ul style="margin-top: 0; margin-bottom: 0;">
+								<li v-for="permission in plugin.permissions" :key="permission">{{ i18n.ts._permissions[permission] }}</li>
+								<li v-if="!plugin.permissions || plugin.permissions.length === 0">{{ i18n.ts.none }}</li>
+							</ul>
+						</template>
+					</MkKeyValue>
+				</div>
 
 				<div class="_buttons">
 					<MkButton v-if="plugin.config" inline @click="config(plugin)"><i class="ti ti-settings"></i> {{ i18n.ts.settings }}</MkButton>
 					<MkButton inline danger @click="uninstall(plugin)"><i class="ti ti-trash"></i> {{ i18n.ts.uninstall }}</MkButton>
 				</div>
+
+				<MkFolder>
+					<template #icon><i class="ti ti-code"></i></template>
+					<template #label>{{ i18n.ts._plugin.viewSource }}</template>
+
+					<div class="_gaps_s">
+						<div class="_buttons">
+							<MkButton inline @click="copy(plugin)"><i class="ti ti-copy"></i> {{ i18n.ts.copy }}</MkButton>
+						</div>
+
+						<MkCode :code="plugin.src ?? ''"/>
+					</div>
+				</MkFolder>
 			</div>
 		</div>
 	</FormSection>
@@ -44,8 +65,11 @@ import FormLink from '@/components/form/link.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
+import MkCode from '@/components/MkCode.vue';
+import MkFolder from '@/components/MkFolder.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import * as os from '@/os.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { ColdDeviceStorage } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
@@ -61,6 +85,11 @@ function uninstall(plugin) {
 	});
 }
 
+function copy(plugin) {
+	copyToClipboard(plugin.src ?? '');
+	os.success();
+}
+
 // TODO: この処理をstore側にactionとして移動し、設定画面を開くAiScriptAPIを実装できるようにする
 async function config(plugin) {
 	const config = plugin.config;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 16483f0cf7..8a7ee62eff 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -381,6 +381,9 @@ export type Plugin = {
 	src: string | null;
 	version: string;
 	ast: any[];
+	author?: string;
+	description?: string;
+	permissions?: string[];
 };
 
 interface Watcher {

From 98209be01ae4ec408e84df41c8c6f9d270792b68 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 09:37:26 +0900
Subject: [PATCH 12/62] Update ja-JP.yml

Fix #11870
---
 locales/ja-JP.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 82ced0aa3b..287895959c 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2080,6 +2080,7 @@ _notification:
 
   _types:
     all: "すべて"
+    note: "ユーザーの新規投稿"
     follow: "フォロー"
     mention: "メンション"
     reply: "リプライ"

From e8a098af62c7bd2f9953a814f7c43a64e6602343 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 09:59:45 +0900
Subject: [PATCH 13/62] Update index.d.ts

---
 locales/index.d.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 256f178124..dbd485379f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2166,6 +2166,7 @@ export interface Locale {
         "notificationWillBeDisplayedLikeThis": string;
         "_types": {
             "all": string;
+            "note": string;
             "follow": string;
             "mention": string;
             "reply": string;

From ad8ddbf12f9750446bb46cdb9a671f069f5bfd9f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 09:59:50 +0900
Subject: [PATCH 14/62] :art:

---
 packages/frontend/src/components/MkNoteDetailed.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 94c6833b14..2b61240b96 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -143,7 +143,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true"/>
 		</div>
 		<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
-			<MkPagination :pagination="renotesPagination">
+			<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
 				<template #default="{ items }">
 					<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
 						<MkUserCardMini :user="item.user" :withChart="false"/>
@@ -158,9 +158,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<span style="margin-left: 4px;">{{ appearNote.reactions[reaction] }}</span>
 				</button>
 			</div>
-			<MkPagination v-if="reactionTabType" :key="reactionTabType" :pagination="reactionsPagination">
+			<MkPagination v-if="reactionTabType" :key="reactionTabType" :pagination="reactionsPagination" :disableAutoLoad="true">
 				<template #default="{ items }">
-					<div class="_gaps_s">
+					<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
 						<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
 							<MkUserCardMini :user="item.user" :withChart="false"/>
 						</MkA>

From cac1c2f1e9b1f6121935ac74f6f0ea1410c97e64 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 10:00:38 +0900
Subject: [PATCH 15/62] =?UTF-8?q?fix(backend):=20notes/reactions=E3=81=AE?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=81=8C=E6=A9=9F=E8=83=BD=E3=81=97=E3=81=AA=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                  |  1 +
 .../server/api/endpoints/notes/reactions.ts   | 24 +++++++------------
 2 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c4134bdef..778e6cee1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -89,6 +89,7 @@
 - Enhance: 自分へのメンション一覧を取得する際のパフォーマンスを向上
 - Enhance: Docker環境でjemallocを使用することでメモリ使用量を削減
 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正
+- Fix: notes/reactionsのページネーションが機能しない問題を修正
 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように
 - Fix: 一部のfeatured noteを照会できない問題を修正
 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts
index dae31364ee..a2c1778199 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts
@@ -4,12 +4,13 @@
  */
 
 import { Inject, Injectable } from '@nestjs/common';
+import { Brackets, type FindOptionsWhere } from 'typeorm';
 import type { NoteReactionsRepository } from '@/models/_.js';
 import type { MiNoteReaction } from '@/models/NoteReaction.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js';
 import { DI } from '@/di-symbols.js';
-import type { FindOptionsWhere } from 'typeorm';
+import { QueryService } from '@/core/QueryService.js';
 
 export const meta = {
 	tags: ['notes', 'reactions'],
@@ -44,7 +45,6 @@ export const paramDef = {
 		noteId: { type: 'string', format: 'misskey:id' },
 		type: { type: 'string', nullable: true },
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
-		offset: { type: 'integer', default: 0 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
 	},
@@ -58,29 +58,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private noteReactionsRepository: NoteReactionsRepository,
 
 		private noteReactionEntityService: NoteReactionEntityService,
+		private queryService: QueryService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const query = {
-				noteId: ps.noteId,
-			} as FindOptionsWhere<MiNoteReaction>;
+			const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId)
+				.andWhere('reaction.noteId = :noteId', { noteId: ps.noteId })
+				.leftJoinAndSelect('reaction.user', 'user')
+				.leftJoinAndSelect('reaction.note', 'note');
 
 			if (ps.type) {
 				// ローカルリアクションはホスト名が . とされているが
 				// DB 上ではそうではないので、必要に応じて変換
 				const suffix = '@.:';
 				const type = ps.type.endsWith(suffix) ? ps.type.slice(0, ps.type.length - suffix.length) + ':' : ps.type;
-				query.reaction = type;
+				query.andWhere('reaction.reaction = :type', { type });
 			}
 
-			const reactions = await this.noteReactionsRepository.find({
-				where: query,
-				take: ps.limit,
-				skip: ps.offset,
-				order: {
-					id: -1,
-				},
-				relations: ['user', 'note'],
-			});
+			const reactions = await query.limit(ps.limit).getMany();
 
 			return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
 		});

From 063d24ad4f491e308a2c7a1ff71bcb3833a18edc Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 12:24:33 +0900
Subject: [PATCH 16/62] :art:

---
 packages/frontend/src/components/MkPasswordDialog.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index afb4929fcf..3abca7826f 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<MkSpacer :marginMin="20" :marginMax="28">
 		<div style="padding: 0 0 16px 0; text-align: center;">
-			<i class="ti ti-lock" style="font-size: 32px; color: var(--accent);"></i>
-			<div style="margin-top: 10px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
+			<img src="/fluent-emoji/1f510.png" alt="🔐" style="display: block; margin: 0 auto; width: 48px;">
+			<div style="margin-top: 16px;">{{ i18n.ts.authenticationRequiredToContinue }}</div>
 		</div>
 
 		<div class="_gaps">

From 8f77350089eebcf4fe07eb9fadd7cff9ea9fef11 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 12:38:20 +0900
Subject: [PATCH 17/62] =?UTF-8?q?enhance:=20PWA=E3=81=AEshort=5Fname?=
 =?UTF-8?q?=E3=82=92=E8=A8=AD=E5=AE=9A=E5=8F=AF=E8=83=BD=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                                    |  2 ++
 locales/ja-JP.yml                                     |  2 ++
 .../backend/migration/1695440131671-short-name.js     | 11 +++++++++++
 packages/backend/src/models/Meta.ts                   |  5 +++++
 .../backend/src/server/api/endpoints/admin/meta.ts    |  1 +
 .../src/server/api/endpoints/admin/update-meta.ts     |  5 +++++
 .../backend/src/server/web/ClientServerService.ts     |  4 ++--
 packages/frontend/src/pages/admin/settings.vue        |  8 ++++++++
 packages/misskey-js/etc/misskey-js.api.md             |  1 +
 packages/misskey-js/src/entities.ts                   |  1 +
 11 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 packages/backend/migration/1695440131671-short-name.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 778e6cee1c..dc99ee33fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 - Feat: プロフィールでのリンク検証
 - Feat: 通知をテストできるようになりました
 - Feat: PWAのアイコンが設定できるようになりました
+- Enhance: サーバー名の略称が設定できるようになりました
 - Enhance: アンテナの受信ソースに指定したユーザを除外するものを追加
 - Enhance: 二要素認証設定時のセキュリティを強化
 	- パスワード入力が必要な操作を行う際、二要素認証が有効であれば確認コードの入力も必要になりました
diff --git a/locales/index.d.ts b/locales/index.d.ts
index dbd485379f..da60550193 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1158,6 +1158,8 @@ export interface Locale {
         "appIconStyleRecommendation": string;
         "appIconResolutionMustBe": string;
         "manifestJsonOverride": string;
+        "shortName": string;
+        "shortNameDescription": string;
     };
     "_accountMigration": {
         "moveFrom": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index ae32891018..0869c0c455 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1156,6 +1156,8 @@ _serverSettings:
   appIconStyleRecommendation: "円形もしくは角丸にクロップされる場合があるため、塗り潰された余白のある背景を持つことが推奨されます。"
   appIconResolutionMustBe: "解像度は必ず{resolution}である必要があります。"
   manifestJsonOverride: "manifest.jsonのオーバーライド"
+  shortName: "略称"
+  shortNameDescription: "サーバーの正式名称が長い場合に、代わりに表示することのできる略称や通称。"
 
 _accountMigration:
   moveFrom: "別のアカウントからこのアカウントに移行"
diff --git a/packages/backend/migration/1695440131671-short-name.js b/packages/backend/migration/1695440131671-short-name.js
new file mode 100644
index 0000000000..2c37297fc1
--- /dev/null
+++ b/packages/backend/migration/1695440131671-short-name.js
@@ -0,0 +1,11 @@
+export class ShortName1695440131671 {
+    name = 'ShortName1695440131671'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "shortName" character varying(64)`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "shortName"`);
+    }
+}
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index a9e116341f..e69bef8e98 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -20,6 +20,11 @@ export class MiMeta {
 	})
 	public name: string | null;
 
+	@Column('varchar', {
+		length: 64, nullable: true,
+	})
+	public shortName: string | null;
+
 	@Column('varchar', {
 		length: 1024, nullable: true,
 	})
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index fe9c134d84..c3ba07cdd0 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -321,6 +321,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				maintainerEmail: instance.maintainerEmail,
 				version: this.config.version,
 				name: instance.name,
+				shortName: instance.shortName,
 				uri: this.config.url,
 				description: instance.description,
 				langs: instance.langs,
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 614e0a95d7..eabf1f306c 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -44,6 +44,7 @@ export const paramDef = {
 		backgroundImageUrl: { type: 'string', nullable: true },
 		logoImageUrl: { type: 'string', nullable: true },
 		name: { type: 'string', nullable: true },
+		shortName: { type: 'string', nullable: true },
 		description: { type: 'string', nullable: true },
 		defaultLightTheme: { type: 'string', nullable: true },
 		defaultDarkTheme: { type: 'string', nullable: true },
@@ -188,6 +189,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.name = ps.name;
 			}
 
+			if (ps.shortName !== undefined) {
+				set.shortName = ps.shortName;
+			}
+
 			if (ps.description !== undefined) {
 				set.description = ps.description;
 			}
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 7b1dd92d73..1faff24201 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -114,10 +114,10 @@ export class ClientServerService {
 		let manifest = {
 			// 空文字列の場合右辺を使いたいため
 			// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-			'short_name': instance.name || 'Misskey',
+			'short_name': instance.shortName || instance.name || this.config.host,
 			// 空文字列の場合右辺を使いたいため
 			// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-			'name': instance.name || 'Misskey',
+			'name': instance.name || this.config.host,
 			'start_url': '/',
 			'display': 'standalone',
 			'background_color': '#313a42',
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index adcf061149..f93678d728 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -14,6 +14,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<template #label>{{ i18n.ts.instanceName }}</template>
 					</MkInput>
 
+					<MkInput v-model="shortName">
+						<template #label>{{ i18n.ts._serverSettings.shortName }} ({{ i18n.ts.optional }})</template>
+						<template #caption>{{ i18n.ts._serverSettings.shortNameDescription }}</template>
+					</MkInput>
+
 					<MkTextarea v-model="description">
 						<template #label>{{ i18n.ts.instanceDescription }}</template>
 					</MkTextarea>
@@ -118,6 +123,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 let name: string | null = $ref(null);
+let shortName: string | null = $ref(null);
 let description: string | null = $ref(null);
 let maintainerName: string | null = $ref(null);
 let maintainerEmail: string | null = $ref(null);
@@ -133,6 +139,7 @@ let deeplIsPro: boolean = $ref(false);
 async function init(): Promise<void> {
 	const meta = await os.api('admin/meta');
 	name = meta.name;
+	shortName = meta.shortName;
 	description = meta.description;
 	maintainerName = meta.maintainerName;
 	maintainerEmail = meta.maintainerEmail;
@@ -149,6 +156,7 @@ async function init(): Promise<void> {
 function save(): void {
 	os.apiWithDialog('admin/update-meta', {
 		name,
+		shortName: shortName === '' ? null : shortName,
 		description,
 		maintainerName,
 		maintainerEmail,
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index d72652bd92..5cd679bce5 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2404,6 +2404,7 @@ type LiteInstanceMetadata = {
     maintainerEmail: string | null;
     version: string;
     name: string | null;
+    shortName: string | null;
     uri: string;
     description: string | null;
     langs: string[];
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 9a0114d71c..034201f9b9 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -298,6 +298,7 @@ export type LiteInstanceMetadata = {
 	maintainerEmail: string | null;
 	version: string;
 	name: string | null;
+	shortName: string | null;
 	uri: string;
 	description: string | null;
 	langs: string[];

From d8a023063d64dd55c9a827e3306efbc1ceb0c48f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 12:43:32 +0900
Subject: [PATCH 18/62] fix behavior of MkMenu.vue

---
 packages/frontend/src/components/MkMenu.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index ec8383fe84..b48f0e7651 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -36,14 +36,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
 			</button>
 			<button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
-				<MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)" />
+				<MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/>
 				<span :class="$style.switchText">{{ item.text }}</span>
 			</button>
-			<div v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
+			<button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<span>{{ item.text }}</span>
 				<span :class="$style.caret"><i class="ti ti-chevron-right ti-fw"></i></span>
-			</div>
+			</button>
 			<button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
 				<MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/>

From 5a4cf059eeeac882ba6ffda58095b7f2b451f787 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 12:46:19 +0900
Subject: [PATCH 19/62] update deps

---
 package.json                                  |   2 +-
 packages/backend/package.json                 |  12 +-
 .../backend/src/server/web/views/base.pug     |   2 +-
 packages/frontend/package.json                |  38 +-
 packages/misskey-js/package.json              |   2 +-
 packages/sw/package.json                      |   2 +-
 pnpm-lock.yaml                                | 861 ++++++++++--------
 7 files changed, 494 insertions(+), 425 deletions(-)

diff --git a/package.json b/package.json
index 99c9550be4..cdc6d7679a 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,7 @@
 		"@typescript-eslint/parser": "6.7.2",
 		"cross-env": "7.0.3",
 		"cypress": "13.2.0",
-		"eslint": "8.49.0",
+		"eslint": "8.50.0",
 		"start-server-and-test": "2.0.1"
 	},
 	"optionalDependencies": {
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 27747e3cd4..97fbaab308 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -71,9 +71,9 @@
 		"@fastify/multipart": "7.7.3",
 		"@fastify/static": "6.11.2",
 		"@fastify/view": "8.2.0",
-		"@nestjs/common": "10.2.5",
-		"@nestjs/core": "10.2.5",
-		"@nestjs/testing": "10.2.5",
+		"@nestjs/common": "10.2.6",
+		"@nestjs/core": "10.2.6",
+		"@nestjs/testing": "10.2.6",
 		"@peertube/http-signature": "1.7.0",
 		"@simplewebauthn/server": "8.1.1",
 		"@sinonjs/fake-timers": "11.1.0",
@@ -86,7 +86,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.11.3",
+		"bullmq": "4.11.4",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -174,7 +174,7 @@
 		"@simplewebauthn/typescript-types": "8.0.0",
 		"@swc/jest": "0.2.29",
 		"@types/accepts": "1.3.5",
-		"@types/archiver": "5.3.2",
+		"@types/archiver": "5.3.3",
 		"@types/bcryptjs": "2.4.4",
 		"@types/body-parser": "1.19.3",
 		"@types/cbor": "6.0.0",
@@ -216,7 +216,7 @@
 		"@typescript-eslint/parser": "6.7.2",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
-		"eslint": "8.49.0",
+		"eslint": "8.50.0",
 		"eslint-plugin-import": "2.28.1",
 		"execa": "8.0.1",
 		"jest": "29.7.0",
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index ab74a1f7f4..71bcf9462f 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -35,7 +35,7 @@ html
 		link(rel='prefetch' href=infoImageUrl)
 		link(rel='prefetch' href=notFoundImageUrl)
 		//- https://github.com/misskey-dev/misskey/issues/9842
-		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.32.0')
+		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.35.0')
 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
 
 		if !config.clientManifestExists
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 6285c1bc33..bce300ff6e 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -23,7 +23,7 @@
 		"@rollup/plugin-replace": "5.0.2",
 		"@rollup/pluginutils": "5.0.4",
 		"@syuilo/aiscript": "0.16.0",
-		"@tabler/icons-webfont": "2.32.0",
+		"@tabler/icons-webfont": "2.35.0",
 		"@vitejs/plugin-vue": "4.3.4",
 		"@vue-macros/reactivity-transform": "0.3.23",
 		"@vue/compiler-sfc": "3.3.4",
@@ -77,24 +77,24 @@
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
-		"@storybook/addon-actions": "7.4.3",
-		"@storybook/addon-essentials": "7.4.3",
-		"@storybook/addon-interactions": "7.4.3",
-		"@storybook/addon-links": "7.4.3",
-		"@storybook/addon-storysource": "7.4.3",
-		"@storybook/addons": "7.4.3",
-		"@storybook/blocks": "7.4.3",
-		"@storybook/core-events": "7.4.3",
+		"@storybook/addon-actions": "7.4.4",
+		"@storybook/addon-essentials": "7.4.4",
+		"@storybook/addon-interactions": "7.4.4",
+		"@storybook/addon-links": "7.4.4",
+		"@storybook/addon-storysource": "7.4.4",
+		"@storybook/addons": "7.4.4",
+		"@storybook/blocks": "7.4.4",
+		"@storybook/core-events": "7.4.4",
 		"@storybook/jest": "0.2.2",
-		"@storybook/manager-api": "7.4.3",
-		"@storybook/preview-api": "7.4.3",
-		"@storybook/react": "7.4.3",
-		"@storybook/react-vite": "7.4.3",
+		"@storybook/manager-api": "7.4.4",
+		"@storybook/preview-api": "7.4.4",
+		"@storybook/react": "7.4.4",
+		"@storybook/react-vite": "7.4.4",
 		"@storybook/testing-library": "0.2.1",
-		"@storybook/theming": "7.4.3",
-		"@storybook/types": "7.4.3",
-		"@storybook/vue3": "7.4.3",
-		"@storybook/vue3-vite": "7.4.3",
+		"@storybook/theming": "7.4.4",
+		"@storybook/types": "7.4.4",
+		"@storybook/vue3": "7.4.4",
+		"@storybook/vue3-vite": "7.4.4",
 		"@testing-library/vue": "7.0.0",
 		"@types/escape-regexp": "0.0.1",
 		"@types/estree": "1.0.1",
@@ -115,7 +115,7 @@
 		"acorn": "8.10.0",
 		"cross-env": "7.0.3",
 		"cypress": "13.2.0",
-		"eslint": "8.49.0",
+		"eslint": "8.50.0",
 		"eslint-plugin-import": "2.28.1",
 		"eslint-plugin-vue": "9.17.0",
 		"fast-glob": "3.3.1",
@@ -128,7 +128,7 @@
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.1",
-		"storybook": "7.4.3",
+		"storybook": "7.4.4",
 		"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
 		"summaly": "github:misskey-dev/summaly",
 		"vite-plugin-turbosnap": "1.0.3",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index c1238f1a17..140968f042 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -26,7 +26,7 @@
 		"@types/node": "20.6.3",
 		"@typescript-eslint/eslint-plugin": "6.7.2",
 		"@typescript-eslint/parser": "6.7.2",
-		"eslint": "8.49.0",
+		"eslint": "8.50.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 6793a070f7..6f9f2f62e9 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -16,7 +16,7 @@
 	"devDependencies": {
 		"@typescript-eslint/parser": "6.7.2",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.49.0",
+		"eslint": "8.50.0",
 		"eslint-plugin-import": "2.28.1",
 		"typescript": "5.2.2"
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e857d7a78d..40364d3605 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -37,10 +37,10 @@ importers:
     devDependencies:
       '@typescript-eslint/eslint-plugin':
         specifier: 6.7.2
-        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/parser':
         specifier: 6.7.2
-        version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -48,8 +48,8 @@ importers:
         specifier: 13.2.0
         version: 13.2.0
       eslint:
-        specifier: 8.49.0
-        version: 8.49.0
+        specifier: 8.50.0
+        version: 8.50.0
       start-server-and-test:
         specifier: 2.0.1
         version: 2.0.1
@@ -99,14 +99,14 @@ importers:
         specifier: 8.2.0
         version: 8.2.0
       '@nestjs/common':
-        specifier: 10.2.5
-        version: 10.2.5(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        specifier: 10.2.6
+        version: 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nestjs/core':
-        specifier: 10.2.5
-        version: 10.2.5(@nestjs/common@10.2.5)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        specifier: 10.2.6
+        version: 10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nestjs/testing':
-        specifier: 10.2.5
-        version: 10.2.5(@nestjs/common@10.2.5)(@nestjs/core@10.2.5)
+        specifier: 10.2.6
+        version: 10.2.6(@nestjs/common@10.2.6)(@nestjs/core@10.2.6)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
@@ -147,8 +147,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.11.3
-        version: 4.11.3
+        specifier: 4.11.4
+        version: 4.11.4
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -494,8 +494,8 @@ importers:
         specifier: 1.3.5
         version: 1.3.5
       '@types/archiver':
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
       '@types/bcryptjs':
         specifier: 2.4.4
         version: 2.4.4
@@ -609,10 +609,10 @@ importers:
         version: 8.5.5
       '@typescript-eslint/eslint-plugin':
         specifier: 6.7.2
-        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/parser':
         specifier: 6.7.2
-        version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -620,11 +620,11 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       eslint:
-        specifier: 8.49.0
-        version: 8.49.0
+        specifier: 8.50.0
+        version: 8.50.0
       eslint-plugin-import:
         specifier: 2.28.1
-        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)
+        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -662,8 +662,8 @@ importers:
         specifier: 0.16.0
         version: 0.16.0
       '@tabler/icons-webfont':
-        specifier: 2.32.0
-        version: 2.32.0
+        specifier: 2.35.0
+        version: 2.35.0
       '@vitejs/plugin-vue':
         specifier: 4.3.4
         version: 4.3.4(vite@4.4.9)(vue@3.3.4)
@@ -819,59 +819,59 @@ importers:
         version: 4.1.0(vue@3.3.4)
     devDependencies:
       '@storybook/addon-actions':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-essentials':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-links':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-storysource':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addons':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/blocks':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 7.4.3
-        version: 7.4.3
+        specifier: 7.4.4
+        version: 7.4.4
       '@storybook/jest':
         specifier: 0.2.2
         version: 0.2.2(vitest@0.34.5)
       '@storybook/manager-api':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 7.4.3
-        version: 7.4.3
+        specifier: 7.4.4
+        version: 7.4.4
       '@storybook/react':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
       '@storybook/react-vite':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.2)(typescript@5.2.2)(vite@4.4.9)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.2)(typescript@5.2.2)(vite@4.4.9)
       '@storybook/testing-library':
         specifier: 0.2.1
         version: 0.2.1
       '@storybook/theming':
-        specifier: 7.4.3
-        version: 7.4.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.4.4
+        version: 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 7.4.3
-        version: 7.4.3
+        specifier: 7.4.4
+        version: 7.4.4
       '@storybook/vue3':
-        specifier: 7.4.3
-        version: 7.4.3(@vue/compiler-core@3.3.4)(vue@3.3.4)
+        specifier: 7.4.4
+        version: 7.4.4(@vue/compiler-core@3.3.4)(vue@3.3.4)
       '@storybook/vue3-vite':
-        specifier: 7.4.3
-        version: 7.4.3(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4)
+        specifier: 7.4.4
+        version: 7.4.4(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4)
       '@testing-library/vue':
         specifier: 7.0.0
         version: 7.0.0(@vue/compiler-sfc@3.3.4)(vue@3.3.4)
@@ -913,10 +913,10 @@ importers:
         version: 8.5.5
       '@typescript-eslint/eslint-plugin':
         specifier: 6.7.2
-        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/parser':
         specifier: 6.7.2
-        version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       '@vitest/coverage-v8':
         specifier: 0.34.5
         version: 0.34.5(vitest@0.34.5)
@@ -933,14 +933,14 @@ importers:
         specifier: 13.2.0
         version: 13.2.0
       eslint:
-        specifier: 8.49.0
-        version: 8.49.0
+        specifier: 8.50.0
+        version: 8.50.0
       eslint-plugin-import:
         specifier: 2.28.1
-        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)
+        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)
       eslint-plugin-vue:
         specifier: 9.17.0
-        version: 9.17.0(eslint@8.49.0)
+        version: 9.17.0(eslint@8.50.0)
       fast-glob:
         specifier: 3.3.1
         version: 3.3.1
@@ -972,11 +972,11 @@ importers:
         specifier: 2.0.1
         version: 2.0.1
       storybook:
-        specifier: 7.4.3
-        version: 7.4.3
+        specifier: 7.4.4
+        version: 7.4.4
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.3)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
@@ -991,7 +991,7 @@ importers:
         version: 0.2.2(vitest@0.34.5)
       vue-eslint-parser:
         specifier: 9.3.1
-        version: 9.3.1(eslint@8.49.0)
+        version: 9.3.1(eslint@8.50.0)
       vue-tsc:
         specifier: 1.8.13
         version: 1.8.13(typescript@5.2.2)
@@ -1025,13 +1025,13 @@ importers:
         version: 20.6.3
       '@typescript-eslint/eslint-plugin':
         specifier: 6.7.2
-        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/parser':
         specifier: 6.7.2
-        version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       eslint:
-        specifier: 8.49.0
-        version: 8.49.0
+        specifier: 8.50.0
+        version: 8.50.0
       jest:
         specifier: 29.7.0
         version: 29.7.0(@types/node@20.6.3)
@@ -1065,16 +1065,16 @@ importers:
     devDependencies:
       '@typescript-eslint/parser':
         specifier: 6.7.2
-        version: 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+        version: 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
       eslint:
-        specifier: 8.49.0
-        version: 8.49.0
+        specifier: 8.50.0
+        version: 8.50.0
       eslint-plugin-import:
         specifier: 2.28.1
-        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)
+        version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)
       typescript:
         specifier: 5.2.2
         version: 5.2.2
@@ -3691,13 +3691,13 @@ packages:
     dev: false
     optional: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.49.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.50.0):
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
     dependencies:
-      eslint: 8.49.0
+      eslint: 8.50.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -3723,8 +3723,8 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint/js@8.49.0:
-    resolution: {integrity: sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==}
+  /@eslint/js@8.50.0:
+    resolution: {integrity: sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -4484,8 +4484,8 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.5(reflect-metadata@0.1.13)(rxjs@7.8.1):
-    resolution: {integrity: sha512-2BfkPZKmTVxflm8bhmClKKcHwhlyweEfbM25g7ldXIK9+utCPVXqBfZGORj2L8QagiT6bei48FJmGc2S1tiFEQ==}
+  /@nestjs/common@10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1):
+    resolution: {integrity: sha512-ma8R7n+FXsWM4XF9QXjjrsRceyRzid/xKmNKVOa/sTJntkVG8lL71BHBEfjtFvO6EJUqjs/15LbDc0iaN5nCwA==}
     peerDependencies:
       class-transformer: '*'
       class-validator: '*'
@@ -4504,8 +4504,8 @@ packages:
       uid: 2.0.2
     dev: false
 
-  /@nestjs/core@10.2.5(@nestjs/common@10.2.5)(reflect-metadata@0.1.13)(rxjs@7.8.1):
-    resolution: {integrity: sha512-O9AycZc4MjzIFrvCxcQVqfSNuN9eHZrfyVcYkp9CMPj6lGd9TQCZX2MmaP1CWs4UJBmTKflPdtPJ0sj9iIuvLQ==}
+  /@nestjs/core@10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1):
+    resolution: {integrity: sha512-oGQ2CoBeFRT7egG47MFqS89xlXBTIRZBkRpKRTPMftEfL1RMXhXIcIIaGfzp11wx6qxrBVxBXpVLM09oaqHpaQ==}
     requiresBuild: true
     peerDependencies:
       '@nestjs/common': ^10.0.0
@@ -4522,7 +4522,7 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.5(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
@@ -4535,8 +4535,8 @@ packages:
       - encoding
     dev: false
 
-  /@nestjs/testing@10.2.5(@nestjs/common@10.2.5)(@nestjs/core@10.2.5):
-    resolution: {integrity: sha512-CaM8STNVyDdMhYnDD6aYnVWPz2ienrSDgMl7nkCAC0wcvKhldWuXQ2syTvQE243IIFOX/bMNuW3MsiqbaXfXxQ==}
+  /@nestjs/testing@10.2.6(@nestjs/common@10.2.6)(@nestjs/core@10.2.6):
+    resolution: {integrity: sha512-uxlxHhpSvG4yDTPmuPneoQL1/UnBkOkzE+Zaz6bwURg7lc3uS4ZsXl75OL3pYaJH37rHYXYT9bGcYSpxVbwIrg==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
@@ -4548,8 +4548,8 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.5(reflect-metadata@0.1.13)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.5(@nestjs/common@10.2.5)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.6(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.6(@nestjs/common@10.2.6)(reflect-metadata@0.1.13)(rxjs@7.8.1)
       tslib: 2.6.2
     dev: false
 
@@ -5812,8 +5812,8 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-ROlhxTQxBtMvfUU8ZTZZ6M0ALbUuChm2Fkau9inZyLgaE/HJbjAUCU7TbHFQ7GgdqA3/Lnw0Soox8cmjI4QQWA==}
+  /@storybook/addon-actions@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-DT9KfP5UTXwptsfP46KOzMjEf73743VprIu1o/5nx+165qmAQhjfs3A+KlSOfEvNbKCYtxHuYTbEO81G0SHr5g==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5823,14 +5823,14 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       dequal: 2.0.3
       lodash: 4.17.21
       polished: 4.2.2
@@ -5846,8 +5846,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-backgrounds@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-NCcJKbz/kVSOXmoV1c+YoM28/oG9oO/kv1xwtX//cVv02SGerRCRqwB7zt0NzcLMSkrwaphRuXd55n0J7nGrBg==}
+  /@storybook/addon-backgrounds@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-cgfo0hoYcUuqm+wiDWetR8qJrMykms9lAZYV1Iv0GEeYJGCW+PH/2WemMBMLJA5/D+Zne1ZoJ9La69UhU4m7Cw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5857,14 +5857,14 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       memoizerific: 1.11.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -5874,8 +5874,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-controls@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-wlfr0Yx27GzQqb5iINQTwL8wCW1NK8+4bJ/HQe4SQOY1FpybOK59B421V6YyQ3tafDWU5MMKh2sElMY9z5Deqw==}
+  /@storybook/addon-controls@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-RGJDfaCu+I0fQyw8rOfUZZm8PTSAa9NmHfKLH/WXnxCrF1kUffVP7G40kRLYS1Ymm5Z3fp3/PVkz9tTUG6PNOQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5885,16 +5885,16 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.4.3
-      '@storybook/core-events': 7.4.3
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.4.3
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/blocks': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-common': 7.4.4
+      '@storybook/core-events': 7.4.4
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 7.4.4
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       lodash: 4.17.21
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -5906,27 +5906,27 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-c6r1nJY4fj/Uj9p7jHdicAS7quiK9RY0LJw+aB++FvcO1KavX33BlD2mxPIVU8a9oLJ3X4RUfNQz+OSABGy0xw==}
+  /@storybook/addon-docs@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-ZXFtBXyfn6uCiRzMsoDZEwAe6/KIYlyfZmm+zjpiXzGJqidEBVJBdPUXNRJZgpg/p0KyehPNtTGqZooJm3gamA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
       '@jest/transform': 29.7.0
       '@mdx-js/react': 2.3.0(react@18.2.0)
-      '@storybook/blocks': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 7.4.3
-      '@storybook/csf-tools': 7.4.3
+      '@storybook/blocks': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 7.4.4
+      '@storybook/csf-tools': 7.4.4
       '@storybook/global': 5.0.0
       '@storybook/mdx2-csf': 1.0.0
-      '@storybook/node-logger': 7.4.3
-      '@storybook/postinstall': 7.4.3
-      '@storybook/preview-api': 7.4.3
-      '@storybook/react-dom-shim': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/node-logger': 7.4.4
+      '@storybook/postinstall': 7.4.4
+      '@storybook/preview-api': 7.4.4
+      '@storybook/react-dom-shim': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       fs-extra: 11.1.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -5940,25 +5940,25 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-LYAauAz4YGWmdZw6umJisl3X0gk1UV9Ovm6b7hicNfKKYGlsWz9KNyi3kvV+harScBzcqENFl5kwezFu2Ltq9g==}
+  /@storybook/addon-essentials@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-LE/3jwJfAWYIUHs5uOqJRfP9b4APRXGhaBarUjZxmeIstJaN6Nd4n0AFgl8z+wKjlss/b5obC9uqeLHzbdJ3iw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/addon-actions': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-backgrounds': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-controls': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-highlight': 7.4.3
-      '@storybook/addon-measure': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-outline': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-toolbars': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-viewport': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.4.3
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.4.3
-      '@storybook/preview-api': 7.4.3
+      '@storybook/addon-actions': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-backgrounds': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-controls': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-highlight': 7.4.4
+      '@storybook/addon-measure': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-outline': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-toolbars': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-viewport': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-common': 7.4.4
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 7.4.4
+      '@storybook/preview-api': 7.4.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
@@ -5969,16 +5969,16 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@7.4.3:
-    resolution: {integrity: sha512-4FDvg+ZH5/H6b7qI6tVSygCaF5h7TStfyLXwxx07edot0vaaw4ir/0sbCAH9AUQ9/+08RiXsMFO5tgMUp/BjcA==}
+  /@storybook/addon-highlight@7.4.4:
+    resolution: {integrity: sha512-09vlOGp1vx+CWVhb+QVTmhodEMqosNLhu69D+FOB7Q+qGK0XdpPLa33DMcAYt/eFEAb55OYQY8NYuoO8kkFT+A==}
     dependencies:
-      '@storybook/core-events': 7.4.3
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.4.3
+      '@storybook/preview-api': 7.4.4
     dev: true
 
-  /@storybook/addon-interactions@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-72Uy7FGr3UbEq44D44ML/o/kC8jUuBETDgnNTC/J7n35OzHcBcas9cHzam87IG/M8uxTwKtuUlEzwyoNUjI3MA==}
+  /@storybook/addon-interactions@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-WwZ4v2oQBm+IqDUlwzGTaS6LaejHEblM4xjmrCIl9F2ChzYOVV0FFdBHoWiVTGQxXhfUVbYKAMYVAg212SOYIg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -5988,16 +5988,16 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-common': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/instrumenter': 7.4.3
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/instrumenter': 7.4.4
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       jest-mock: 27.5.1
       polished: 4.2.2
       react: 18.2.0
@@ -6010,8 +6010,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-links@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-flnwlKdePQtwgryFhJlju94DVvZBq477xaD1mG9zcqEe+QeN+1GGggIo6R9e2hEsWcAfpc2yKA4dJP9KS9xIHg==}
+  /@storybook/addon-links@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-ZDs7+FaWuLVl4Y5+qSr9Pu668wTdSQmMS1pY/QzwA7Lj3ADDEXMFTBt0MqGtyH7U5mmVQlmYm9d4WmHjvKU0qQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6021,22 +6021,22 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/csf': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/router': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/router': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       prop-types: 15.8.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-measure@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-a07/GV9WWvqy1MuJtDevHzPo/weY86s7JT+qjGk0bhQdThVcd94Z7whlQL/LgrdAi1XLdHY5R5LpUIk9UDluNw==}
+  /@storybook/addon-measure@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-gm8x9+PA7v48wJZltWNB41vvhqzWASDtuir8ozHYsYxdUsCpCnB79MPOQ0BsqbFYmscTaYghDAugLLS5+Tg8LQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6046,13 +6046,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       tiny-invariant: 1.3.1
@@ -6061,8 +6061,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-outline@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-QPcTjmNgj0+7NEzomfqNOnm2DgcRjqvYGCdlxfDbnNB0J+ZGlaUozL3ZbofJKx9qCoHf+j+Z1pwONHafJV6t4w==}
+  /@storybook/addon-outline@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-HJgxPOwq1TDWXqiBlqnFWxE9nlXlxH3XfKqhWTXORE1CKZ/BXdrPkdq2Hog8ON15K3PVdAcawgkO4YJ/n0JPaA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6072,13 +6072,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
@@ -6087,8 +6087,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-storysource@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Egqd9YcmXvDn2GHxANlhHh7x0HZYfqokrqHnNoEbeOTsOtPNkFyLyE8ZhxencgN5qR5FYok79iUxKr/tQ6BazA==}
+  /@storybook/addon-storysource@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-e/frZMYbriIwPl9xNapUaSEQrngbBPz4Nz5gNcPHF/uDM04a3hyVAMycZmFLHmuu1TH9FzZ9mhzTQjpJuACq0w==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6098,13 +6098,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/router': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/source-loader': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/router': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/source-loader': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       estraverse: 5.3.0
       prop-types: 15.8.1
       react: 18.2.0
@@ -6116,8 +6116,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-toolbars@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-sHILofAarfzku+8qhueELoZYCLTHuDtmnlfILjBrH/w7Et3Vnyn1wJcdal7VnQPbX9EiEkdFaiZybQdniBb+hQ==}
+  /@storybook/addon-toolbars@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-lbXO2weuYcly4nRTpK6tS4mhx/YbPAy2qpoePEL847I+hQJiKWsgSYbA6bnC5tXe0l6mq1VVcTFtEW2wyu9WQQ==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6127,11 +6127,11 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     transitivePeerDependencies:
@@ -6139,8 +6139,8 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-viewport@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-jDRG6ZMZ4ATOXiJQcXTpolTtIi8oAhbk6mbJyj65nClXgWqfZxMK9PMfJw5R7zHhAmrKoWNTDc72eayFOIHaNg==}
+  /@storybook/addon-viewport@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-8uRpOpNYHlCL3uAYhj6uoLHKmW2UtQXDff9KhO6mO+vgat25YKBeu1wwGUHnCL3vDSis6hfbjRSVbsfrekUYWg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6150,13 +6150,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       memoizerific: 1.11.3
       prop-types: 15.8.1
       react: 18.2.0
@@ -6166,36 +6166,36 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/addons@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-6XvXE3sRl78MceRDAnfPd6N6j9ltMCuTITjjqU2GU8iyAexJ4bYodfKcmUmAQmixuc+6UPbWmlrQKNmBDlp3rw==}
+  /@storybook/addons@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-uEOxUcJqDwr0FMSu/SHc76V9bWBxk3/9FuiTkkOA4rClLMXOrm/AhUurVsOEbsYgXNg7lqddguTPAEj3RFO/3A==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/blocks@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-uyZVx3er1qOPFpKJtsbozBwt1Os3zqiq+2se7xDBK6ERr07zaRHLgRci7+kI8T5mdlCxYiGV+kzx5Vx5/7XaXg==}
+  /@storybook/blocks@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-Xm/yeZkaBhqvg5dciBj2UFXQTGFpoy0HhO4ShvaWfhdEgBluufNAQ19hMvvoXPHgjiBzzxETq1mlY7LIdUzJEg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/channels': 7.4.3
-      '@storybook/client-logger': 7.4.3
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
+      '@storybook/channels': 7.4.4
+      '@storybook/client-logger': 7.4.4
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.4.4
       '@storybook/csf': 0.1.0
-      '@storybook/docs-tools': 7.4.3
+      '@storybook/docs-tools': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -6217,13 +6217,13 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@7.4.3:
-    resolution: {integrity: sha512-6jzxZ2J1jFaZXn7ZucEgV6XyUe+FJ9uuoMRZcZefoCKeXK/BOPCefijYWP3DPgqqVh3/JLUglIpz0MH9k8cBaw==}
+  /@storybook/builder-manager@7.4.4:
+    resolution: {integrity: sha512-8PY9YfcUf1gPGOprjWze6bfXpX0+r6YZTgktViI/m+SmgFy5nmEJ7FWqo7u1y6dqxDJdwDXGBqvUGVU0XNXQMQ==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 7.4.3
-      '@storybook/manager': 7.4.3
-      '@storybook/node-logger': 7.4.3
+      '@storybook/core-common': 7.4.4
+      '@storybook/manager': 7.4.4
+      '@storybook/node-logger': 7.4.4
       '@types/ejs': 3.1.2
       '@types/find-cache-dir': 3.2.1
       '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.17)
@@ -6241,8 +6241,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.4.3(typescript@5.2.2)(vite@4.4.9):
-    resolution: {integrity: sha512-zCxIsJ0KZ+tiz8KzlAT54jGTGEbscqFQfiHK/Du+EYWG2ulX1+goMxw5k9+ndiK/GgjJGSdVoFvcNQH3MPOM6A==}
+  /@storybook/builder-vite@7.4.4(typescript@5.2.2)(vite@4.4.9):
+    resolution: {integrity: sha512-FpHlwTmrT9gYxfke77HcHSVoTvJCgunLGnrmNUgLwC0vVAWibWWTGtunfcV2fjBjzqVuH398qpaM+kIS9rjR8A==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6256,15 +6256,15 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 7.4.3
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-common': 7.4.3
-      '@storybook/csf-plugin': 7.4.3
+      '@storybook/channels': 7.4.4
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-common': 7.4.4
+      '@storybook/csf-plugin': 7.4.4
       '@storybook/mdx2-csf': 1.0.0
-      '@storybook/node-logger': 7.4.3
-      '@storybook/preview': 7.4.3
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/node-logger': 7.4.4
+      '@storybook/preview': 7.4.4
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6293,22 +6293,33 @@ packages:
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@7.4.3:
-    resolution: {integrity: sha512-/lGtXbzNropsCF4srEGxiHzCU7b2wlV13LrSj3H3zOnHEAJlFcNpyNzO+4jKHfNTjjqEtcRGJ1OxrSYuGZTVjg==}
+  /@storybook/channels@7.4.4:
+    resolution: {integrity: sha512-YA2T3hClL95nFBBelm8wMOyWFDzfxKvyHAPQi+8YeYpZcPivwg/P9YnRhTTMbiZNkfoWKq4ZRuc79UP1iNLi3g==}
+    dependencies:
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-events': 7.4.4
+      '@storybook/global': 5.0.0
+      qs: 6.11.1
+      telejson: 7.2.0
+      tiny-invariant: 1.3.1
+    dev: true
+
+  /@storybook/cli@7.4.4:
+    resolution: {integrity: sha512-2t7T7VpN52eGTYUlwx+VtQf/PMsULXoWkD5eO7kD6NmJbqvdgbLn/pbYePg5eOmgar0VvNhm94UOyW+roMxALA==}
     hasBin: true
     dependencies:
       '@babel/core': 7.22.11
       '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
       '@babel/types': 7.22.17
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 7.4.3
-      '@storybook/core-common': 7.4.3
-      '@storybook/core-events': 7.4.3
-      '@storybook/core-server': 7.4.3
-      '@storybook/csf-tools': 7.4.3
-      '@storybook/node-logger': 7.4.3
-      '@storybook/telemetry': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/codemod': 7.4.4
+      '@storybook/core-common': 7.4.4
+      '@storybook/core-events': 7.4.4
+      '@storybook/core-server': 7.4.4
+      '@storybook/csf-tools': 7.4.4
+      '@storybook/node-logger': 7.4.4
+      '@storybook/telemetry': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/semver': 7.5.2
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6351,16 +6362,22 @@ packages:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@7.4.3:
-    resolution: {integrity: sha512-UwnsyVeUa+wLIeE/zO0slV3mwsPgS3DstZAWbjWUfFlJKZjgg1++Zkv0GmxkEyirsnf/g4r6Aq+KhIdIHmdzag==}
+  /@storybook/client-logger@7.4.4:
+    resolution: {integrity: sha512-rC/GcCy3DLtTI+oOHLBc6rq/c3oGF/mvdeWrhMM+berQplHJrOCI2pcldjVw8Fc25gLPK0LUlaOp1dfgt2Ri3Q==}
+    dependencies:
+      '@storybook/global': 5.0.0
+    dev: true
+
+  /@storybook/codemod@7.4.4:
+    resolution: {integrity: sha512-q9BeDXuDjUFb+In0DX+37vqofT0/j25CGGW629RfIoxd6VRr6oVoX6bBnyLO1zoSN+CmOQ9oKeLJjSiNFJntOw==}
     dependencies:
       '@babel/core': 7.22.11
       '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
       '@babel/types': 7.22.17
       '@storybook/csf': 0.1.0
-      '@storybook/csf-tools': 7.4.3
-      '@storybook/node-logger': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/csf-tools': 7.4.4
+      '@storybook/node-logger': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
@@ -6395,19 +6412,42 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/core-client@7.4.3:
-    resolution: {integrity: sha512-YRt07TxC+HUtnyvbpJbY8d2+2QfFExBL7zRbms9tIRorddWfPBq+lA2RS9zcjUJJJtNSz1+ST70FuGr1N3AXGg==}
+  /@storybook/components@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-tFOSu3IoAab/0aY2TY66Go0Nba7AB/+ZB9GFet+dxWypIKGLcPjyX2POIumJU4swzK+4IA8GxgDQ2itS6EOISQ==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/preview-api': 7.4.3
+      '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0)
+      '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/csf': 0.1.0
+      '@storybook/global': 5.0.0
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
+      memoizerific: 1.11.3
+      react: 18.2.0
+      react-dom: 18.2.0(react@18.2.0)
+      use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0)
+      util-deprecate: 1.0.2
+    transitivePeerDependencies:
+      - '@types/react'
+      - '@types/react-dom'
     dev: true
 
-  /@storybook/core-common@7.4.3:
-    resolution: {integrity: sha512-jwIBUnWitZzw0VfKC77yN8DvTyePLVnAjbA2lPMbMIdO9ZY2lfD4AQ4QpuWsxJyAllFC4slOFDNgCDHx2AlYWw==}
+  /@storybook/core-client@7.4.4:
+    resolution: {integrity: sha512-CENQOPML7ifh0fJFz1YvLzRv7QyU2SVAzw70bN5fDravCHwI1uQmnLF7QxnWrE8DtXlXosQZcAhCqAOQ7sfMxw==}
     dependencies:
-      '@storybook/core-events': 7.4.3
-      '@storybook/node-logger': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/preview-api': 7.4.4
+    dev: true
+
+  /@storybook/core-common@7.4.4:
+    resolution: {integrity: sha512-0VKVNucIzaKVr5dU6BYwSzpLRtBoDa3dRBP7VTauz8gjhNak+mOQY7ZqqQWxCr21C0vsYz2noER2/jnijfMn5g==}
+    dependencies:
+      '@storybook/core-events': 7.4.4
+      '@storybook/node-logger': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/find-cache-dir': 3.2.1
       '@types/node': 16.18.46
       '@types/node-fetch': 2.6.4
@@ -6439,24 +6479,30 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@7.4.3:
-    resolution: {integrity: sha512-yl9HaVwk/xJV9zq76n/oR1cE39wAFmNmKVPOJAtr3+c7wS0tnBkw7T+GqZ2Seyv+xkcZUWS8KRH74HqwPwG0Bw==}
+  /@storybook/core-events@7.4.4:
+    resolution: {integrity: sha512-kOf4I/a1XC9CaGFwJG5WR2KnkwrOkWX68TLh7OlelKxdl/WjxA4zfzaFPC/8zyCSLdGFLPKNqr1w+ezkb+9Irw==}
+    dependencies:
+      ts-dedent: 2.2.0
+    dev: true
+
+  /@storybook/core-server@7.4.4:
+    resolution: {integrity: sha512-ObWoTBgTf3D+4GsbHA0jfVz+rDEMS82U+dlla1LHqpazNKJVg6aTLBnr7V7n3TAOXrXZyKbrVu0Vk5ZePLFuOw==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 7.4.3
-      '@storybook/channels': 7.4.3
-      '@storybook/core-common': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/builder-manager': 7.4.4
+      '@storybook/channels': 7.4.4
+      '@storybook/core-common': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/csf': 0.1.0
-      '@storybook/csf-tools': 7.4.3
+      '@storybook/csf-tools': 7.4.4
       '@storybook/docs-mdx': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 7.4.3
-      '@storybook/node-logger': 7.4.3
-      '@storybook/preview-api': 7.4.3
-      '@storybook/telemetry': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/manager': 7.4.4
+      '@storybook/node-logger': 7.4.4
+      '@storybook/preview-api': 7.4.4
+      '@storybook/telemetry': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/detect-port': 1.3.2
       '@types/node': 16.18.46
       '@types/pretty-hrtime': 1.0.1
@@ -6491,24 +6537,24 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@7.4.3:
-    resolution: {integrity: sha512-xQCimGsrGD1JxvyFc0LrH10WZWb181r0beF19aGIAadczs/JWhT+nxF8OhfP1LK4wHj9jH+F4nIXEMpm9yI9Qg==}
+  /@storybook/csf-plugin@7.4.4:
+    resolution: {integrity: sha512-j5Ow2SBZjWX7c/StwMGZnB5ydiSFIZvR2ENTsbQ4UL1SEdF/GvzxtDjMPjhH3wCfyU3pmK6YV54ceD94IS22BQ==}
     dependencies:
-      '@storybook/csf-tools': 7.4.3
+      '@storybook/csf-tools': 7.4.4
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@7.4.3:
-    resolution: {integrity: sha512-nkVakGx2kzou91lGcxnyFNiSEdnpx1a53lQTl/DLm0QpDbqQuu3ZbZWXZCpXV97t/6YPeCCnGLXodnI7PZyZBA==}
+  /@storybook/csf-tools@7.4.4:
+    resolution: {integrity: sha512-/hhGg05WzMmk9jmkdGyCpy6mPn/PaNGSOggE95X0bZ81arzkqCCXej4vClxFdaCJyzWzqr/yQSBftpK1OWoHjA==}
     dependencies:
       '@babel/generator': 7.22.10
       '@babel/parser': 7.22.16
       '@babel/traverse': 7.22.11
       '@babel/types': 7.22.17
       '@storybook/csf': 0.1.0
-      '@storybook/types': 7.4.3
+      '@storybook/types': 7.4.4
       fs-extra: 11.1.1
       recast: 0.23.1
       ts-dedent: 2.2.0
@@ -6526,12 +6572,12 @@ packages:
     resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
     dev: true
 
-  /@storybook/docs-tools@7.4.3:
-    resolution: {integrity: sha512-T9oU10vIY3mC6Up+9rjN5LfBydhhIFhKzHPtUT9PfN1iEa0lO2TkT4m+vf2kcokPppUZNVbqiGjy9t/WYnpeZg==}
+  /@storybook/docs-tools@7.4.4:
+    resolution: {integrity: sha512-kCQgRsyAHxnHicu3G9px7mp0Lp2fckH23wfTz/qk6MdwKoO3V9lJs7tOjckq2DPu/m7BGs4lieBcMbOOUTHBmw==}
     dependencies:
-      '@storybook/core-common': 7.4.3
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/core-common': 7.4.4
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       '@types/doctrine': 0.0.3
       doctrine: 3.0.0
       lodash: 4.17.21
@@ -6550,14 +6596,14 @@ packages:
     resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
     dev: true
 
-  /@storybook/instrumenter@7.4.3:
-    resolution: {integrity: sha512-XVctoUOFthTCea2+BKFKeUbhWrRY+1I8THgsZx67X3MQDt9bafwQdFR9jTGBeC31oNi1b7nmTuaox0lneNlghA==}
+  /@storybook/instrumenter@7.4.4:
+    resolution: {integrity: sha512-WUaun2pb2AuFmsM+kBq7Q++Fjg9ibltUsfu75SELehKrE1lQ47H0f5AQaexy8Vqo8QXQuNtGo6RruztQfNIDQQ==}
     dependencies:
-      '@storybook/channels': 7.4.3
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/channels': 7.4.4
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.4.3
+      '@storybook/preview-api': 7.4.4
     dev: true
 
   /@storybook/jest@0.2.2(vitest@0.34.5):
@@ -6573,20 +6619,20 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/manager-api@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-o5oiL2cJKlY+HNBCdUo5QKT8yXTyYYvBKibSS3YfDKcjeR9RXP+RhdF5lLLh6TzPwfdtLrXQoVI4A/61v2kurQ==}
+  /@storybook/manager-api@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-NRYJQ/w1czvdkK7BblroTQ0kJoR1H+h3uthVCcWzFcLEGBbVXsurg0wasugi5UKHLqV7g8/8ZQToZvlPVAglAA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/channels': 7.4.3
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/channels': 7.4.4
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/csf': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/router': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/router': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -6598,31 +6644,31 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/manager@7.4.3:
-    resolution: {integrity: sha512-7U92tYwjt0DIKX7vCKNSZefuEavdnJYa5/zSjdlo0LtfBmGRBak1eq/sVLGfzrZ+wKIlCXgNh3f8OLy8RMnOOw==}
+  /@storybook/manager@7.4.4:
+    resolution: {integrity: sha512-JiYClrQ7emvgiPzM20lAx/xtsG8do6Rb2MDoomSH7IPhx1oIm5dft1IzMhC0NTsz41q3Or/VUsOQiFSo2sv/+Q==}
     dev: true
 
   /@storybook/mdx2-csf@1.0.0:
     resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
     dev: true
 
-  /@storybook/node-logger@7.4.3:
-    resolution: {integrity: sha512-pL13PPMUttflTWKVeDIKxPIJtBRl50Fzck12/7uiNROtBIrSV9DZSgOjInAazjo4tl+7fDj9lgkGeMEz00E8aQ==}
+  /@storybook/node-logger@7.4.4:
+    resolution: {integrity: sha512-KrxGAg1DbIZrVh2gOwoGGC+mSSmrEZTHKnJjAHrcNUODxT8MQxsVner6Z0fKisDlucLV9rjLcUH7/3AhCwWEiQ==}
     dev: true
 
-  /@storybook/postinstall@7.4.3:
-    resolution: {integrity: sha512-6NMaAvL4a26jR50UPz+Q6VATY3lHZWw1ru/weFgiV0rat632RFdiFyrMMrjbUWu9HDJE4fbCzrIZU0jGVs1wlQ==}
+  /@storybook/postinstall@7.4.4:
+    resolution: {integrity: sha512-e4vjNjEzDhXia2fZAJOCI1sQbfKLsWZtJFkY4Z6D1KnALTaJMv16p3fhghEoPZfv6TTTK6F0lSUvPxv4V5ie1A==}
     dev: true
 
-  /@storybook/preview-api@7.4.3:
-    resolution: {integrity: sha512-qKwfH2+qN1Zpz2UX6dQLiTU5x2JH3o/+jOY4GYF6c3atTm5WAu1OvCYAJVb6MdXfAhZNuPwDKnJR8VmzWplWBg==}
+  /@storybook/preview-api@7.4.4:
+    resolution: {integrity: sha512-ra0dAZ7yBrHP8xCxvA8sBcKvm2kHH2S4Yj8cECnU87uYCbuyFyfrSiWeFcu2+kJj35wLck+lfnPS/FYUqNr2BQ==}
     dependencies:
-      '@storybook/channels': 7.4.3
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-events': 7.4.3
+      '@storybook/channels': 7.4.4
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-events': 7.4.4
       '@storybook/csf': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.4.3
+      '@storybook/types': 7.4.4
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -6633,12 +6679,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@7.4.3:
-    resolution: {integrity: sha512-dItyGcql/rD6CWTKGUm58MguWC7L4KjlfNJmxxaHXnHRbaEjXPaRi9ztfmimIpAaBdBmreAZrZJYhLvOGG3CfA==}
+  /@storybook/preview@7.4.4:
+    resolution: {integrity: sha512-PFvmVc8+uIKniU3xJaxKfXYHNsXE3kqZt9wJMnSv8eL6UmXPpHQTST6UA7kd/THWyuRsLjrwefdRN5lnwTJYqQ==}
     dev: true
 
-  /@storybook/react-dom-shim@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-d8kkZU4kqmNluuOx65l5H0L9lRn8Ji5rVxu+4MUCWrn82dxRLvVcFG0sfGUzOTNfX1/yajL2MxVJ2hx9fzLutQ==}
+  /@storybook/react-dom-shim@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-0FL/A+dMLJ6zp4nm6mAqfNJT01wxRHnZCENeye8+oxaEMgsZ/Q8Z9LRPaf3B01upwP9x48F9uZkC0htQ6a4kIA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6647,8 +6693,8 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.4.3(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.2)(typescript@5.2.2)(vite@4.4.9):
-    resolution: {integrity: sha512-olDbrdRVmpgb+i8FKP/Fp8SImgpVzDP/nf7u0j7H+pOUCjecziV0cW1COE35i5x2yQleNNps7bN8EugqBlD7Dw==}
+  /@storybook/react-vite@7.4.4(react-dom@18.2.0)(react@18.2.0)(rollup@3.29.2)(typescript@5.2.2)(vite@4.4.9):
+    resolution: {integrity: sha512-cL0BIWJZFsmKmyp4VdKDYCiwe7L9ihTeyzRfr6oSs3hMKJKDo9Y5zw7NlbpZRXbKhEBYmh+irYrBLNdCRDN6Xw==}
     engines: {node: '>=16'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6657,8 +6703,8 @@ packages:
     dependencies:
       '@joshwooding/vite-plugin-react-docgen-typescript': 0.2.1(typescript@5.2.2)(vite@4.4.9)
       '@rollup/pluginutils': 5.0.4(rollup@3.29.2)
-      '@storybook/builder-vite': 7.4.3(typescript@5.2.2)(vite@4.4.9)
-      '@storybook/react': 7.4.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
+      '@storybook/builder-vite': 7.4.4(typescript@5.2.2)(vite@4.4.9)
+      '@storybook/react': 7.4.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
       '@vitejs/plugin-react': 3.1.0(vite@4.4.9)
       ast-types: 0.14.2
       magic-string: 0.30.3
@@ -6675,8 +6721,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@7.4.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
-    resolution: {integrity: sha512-koF1GLPBY5rm8t+6i70Iw6Ep/6T2C+XAlnP1dO/ZJAv8mmeQmOw+Kwh6nNPG8bthm0l1nImgqRw2ZwCD2AFoSA==}
+  /@storybook/react@7.4.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
+    resolution: {integrity: sha512-yfTFywMAAjsA1lUBW2j5UkIkH95sDhmplzkFfeSTJkxQpW2blKTARb0VQp6k5MYBpq0LsWrz8uBZZRLXXCuobw==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6686,13 +6732,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-client': 7.4.3
-      '@storybook/docs-tools': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-client': 7.4.4
+      '@storybook/docs-tools': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.4.3
-      '@storybook/react-dom-shim': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/preview-api': 7.4.4
+      '@storybook/react-dom-shim': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 16.18.46
@@ -6715,27 +6761,27 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/router@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-1ab1VTYzzOsBGKeT8xm1kLriIsIsiB/l3t7DdARJxLmPbddKyyXE018w17gfrARCWQ8SM99Ko6+pLmlZ2sm8ug==}
+  /@storybook/router@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-whaDTdbqluWWU+sMOK73QMh2xZS74/EcHwzmGN2zQFR+5m658cWjM9kZJez7Q4WWhBA0+VNqVhSQBJvXGjz48g==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@storybook/client-logger': 7.4.3
+      '@storybook/client-logger': 7.4.4
       memoizerific: 1.11.3
       qs: 6.11.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/source-loader@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Z0WUKpYsRSh8Wdc2C0URjIakvhnL7qpf9GM5rtXnRQ7Qaq0x9MmdUOuTj6z+HthbNPWza4KDaC8mViBxhhdiOg==}
+  /@storybook/source-loader@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-sT+QncqpIehyyQhurnvN3/Aal6Em+gXqrpUHXumD1P8LVE/kpMs55c/fb/FtrYrMKnTZ8KddXP8o4QiPvvqSCg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     dependencies:
       '@storybook/csf': 0.1.0
-      '@storybook/types': 7.4.3
+      '@storybook/types': 7.4.4
       estraverse: 5.3.0
       lodash: 4.17.21
       prettier: 2.8.8
@@ -6743,12 +6789,12 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/telemetry@7.4.3:
-    resolution: {integrity: sha512-gA7QfQSdDocNKP0KfrmIhD8ZgW5G4zZD/NL0OsATlkL3H/DehH3Ugjfffh7Ao2JZRXogHp8p9EQCVfPW7iKgBQ==}
+  /@storybook/telemetry@7.4.4:
+    resolution: {integrity: sha512-emW4oaZca8Bt+gbCuImEdNwWtnhhoQcjZ0BLuwT55nWgVarzNRg/Qw0NOrlfEuwfeXCI6QUJZyXohaEA5NjNuA==}
     dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-common': 7.4.3
-      '@storybook/csf-tools': 7.4.3
+      '@storybook/client-logger': 7.4.4
+      '@storybook/core-common': 7.4.4
+      '@storybook/csf-tools': 7.4.4
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -6781,6 +6827,20 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
+  /@storybook/theming@7.4.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-ABIwLRUj2IZKMGxKq+fCCFcY7w52P1a+q8j7qrlELaTe4M74K6rwTgRF0/AFgWeiGRkNuA7z8DjQ73xQLoLqUg==}
+    peerDependencies:
+      react: ^16.8.0 || ^17.0.0 || ^18.0.0
+      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+    dependencies:
+      '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
+      '@storybook/client-logger': 7.4.4
+      '@storybook/global': 5.0.0
+      memoizerific: 1.11.3
+      react: 18.2.0
+      react-dom: 18.2.0(react@18.2.0)
+    dev: true
+
   /@storybook/types@7.4.3:
     resolution: {integrity: sha512-DrHC1hIiw9TqDILLokDnvbUPNxGz5iJaYFEv30uvYE0s9MvgEUPblCChEUjaHOps7zQTznMPf8ULfoXlgqxk2A==}
     dependencies:
@@ -6790,17 +6850,26 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.4.3(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4):
-    resolution: {integrity: sha512-uOLAQa/Tyqdne8+2S7Opm5ui9Ii9x3fuGJb3SvnuMUMHB2Tz5BczyFwkjOiME3zE9pQ9Cjg18InBVagbLK7R0A==}
+  /@storybook/types@7.4.4:
+    resolution: {integrity: sha512-B0VdgGb1XGEb9g3UuEd9xANCIhR3anvA3w0uYSG+7uMOflnEawwZksTSxvvoGM2hx9vC4pNT4Fci9sEC903UkA==}
+    dependencies:
+      '@storybook/channels': 7.4.4
+      '@types/babel__core': 7.20.0
+      '@types/express': 4.17.17
+      file-system-cache: 2.3.0
+    dev: true
+
+  /@storybook/vue3-vite@7.4.4(@vue/compiler-core@3.3.4)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-RWMLyi+t2FFO+51SiebXPSQWWkA2PlPmmbRDYJSawrW1vMVx0QK1d4Jk9vA1foDte52EPc96J31k3pdsbBsVkA==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0
     dependencies:
-      '@storybook/builder-vite': 7.4.3(typescript@5.2.2)(vite@4.4.9)
-      '@storybook/core-server': 7.4.3
-      '@storybook/vue3': 7.4.3(@vue/compiler-core@3.3.4)(vue@3.3.4)
+      '@storybook/builder-vite': 7.4.4(typescript@5.2.2)(vite@4.4.9)
+      '@storybook/core-server': 7.4.4
+      '@storybook/vue3': 7.4.4(@vue/compiler-core@3.3.4)(vue@3.3.4)
       '@vitejs/plugin-vue': 4.3.4(vite@4.4.9)(vue@3.3.4)
       magic-string: 0.30.3
       react: 18.2.0
@@ -6819,18 +6888,18 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.4.3(@vue/compiler-core@3.3.4)(vue@3.3.4):
-    resolution: {integrity: sha512-c/dmqupgs6be+Vp5bpYQ4VEsSsCKrQfPVSRYto8D8rDqrQGoJvqhRCQbC+wVIia1am4HeYZctX0kUiCPlB++2g==}
+  /@storybook/vue3@7.4.4(@vue/compiler-core@3.3.4)(vue@3.3.4):
+    resolution: {integrity: sha512-M7ZOd3SxT+SkLz/XRTr1JpdtdU8aW49EypsTtNyF/oW6TzMaJmTTPF5mlzZipJ3GqagQg/nImDpa01w5pajz9Q==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       '@vue/compiler-core': ^3.0.0
       vue: ^3.0.0
     dependencies:
-      '@storybook/core-client': 7.4.3
-      '@storybook/docs-tools': 7.4.3
+      '@storybook/core-client': 7.4.4
+      '@storybook/docs-tools': 7.4.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.4.3
-      '@storybook/types': 7.4.3
+      '@storybook/preview-api': 7.4.4
+      '@storybook/types': 7.4.4
       '@vue/compiler-core': 3.3.4
       lodash: 4.17.21
       ts-dedent: 2.2.0
@@ -7119,14 +7188,14 @@ packages:
     dependencies:
       defer-to-connect: 2.0.1
 
-  /@tabler/icons-webfont@2.32.0:
-    resolution: {integrity: sha512-Dl3Zn58d2l6xPmfL9t6EVa3SVgVVJMKhaihzEAm9LCrTii5vQ4h0ZImqwgPUlv1up97lKyq21ZAiK5J5gXlcYw==}
+  /@tabler/icons-webfont@2.35.0:
+    resolution: {integrity: sha512-U9Xg6ntMLplwKvUGCvqfj/lw2+dKlhunqzw/0XDFVX/TOJwPcnKs9JO+/sGeAZdWYyJpvhAKxOZtXvvy9SoYBQ==}
     dependencies:
-      '@tabler/icons': 2.32.0
+      '@tabler/icons': 2.35.0
     dev: false
 
-  /@tabler/icons@2.32.0:
-    resolution: {integrity: sha512-w1oNvrnqFipoBEy2/0X4/IHo2aLsijuz4QRi/HizxqiaoMfmWG5X2DpEYTw9WnGvFmixpu/rtQsQAr7Wr0Mc2w==}
+  /@tabler/icons@2.35.0:
+    resolution: {integrity: sha512-qW/itKdmFvfGw6mAQ+cZy+2MYTXb0XdGAVhO3obYLJEfsSPMwQRO0S9ckFk1xMQX/Tj7REC3TEmWUBWNi3/o3g==}
     dev: false
 
   /@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0):
@@ -7340,8 +7409,8 @@ packages:
       '@types/node': 20.6.3
     dev: true
 
-  /@types/archiver@5.3.2:
-    resolution: {integrity: sha512-IctHreBuWE5dvBDz/0WeKtyVKVRs4h75IblxOACL92wU66v+HGAfEYAOyXkOFphvRJMhuXdI9huDXpX0FC6lCw==}
+  /@types/archiver@5.3.3:
+    resolution: {integrity: sha512-0ABdVcXL6jOwNGY+hjWPqrxUvKelBEwNLcuv/SV2vZ4YCH8w9NttFCt+/QqI5zgMX+iX/XqVy89/r7EmLJmMpQ==}
     dependencies:
       '@types/readdir-glob': 1.1.1
     dev: true
@@ -7945,7 +8014,7 @@ packages:
     dev: true
     optional: true
 
-  /@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.49.0)(typescript@5.2.2):
+  /@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2):
     resolution: {integrity: sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -7957,13 +8026,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/scope-manager': 6.7.2
-      '@typescript-eslint/type-utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
-      '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+      '@typescript-eslint/type-utils': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
+      '@typescript-eslint/utils': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       '@typescript-eslint/visitor-keys': 6.7.2
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.50.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -7974,7 +8043,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.7.2(eslint@8.49.0)(typescript@5.2.2):
+  /@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2):
     resolution: {integrity: sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -7989,7 +8058,7 @@ packages:
       '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2)
       '@typescript-eslint/visitor-keys': 6.7.2
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.50.0
       typescript: 5.2.2
     transitivePeerDependencies:
       - supports-color
@@ -8003,7 +8072,7 @@ packages:
       '@typescript-eslint/visitor-keys': 6.7.2
     dev: true
 
-  /@typescript-eslint/type-utils@6.7.2(eslint@8.49.0)(typescript@5.2.2):
+  /@typescript-eslint/type-utils@6.7.2(eslint@8.50.0)(typescript@5.2.2):
     resolution: {integrity: sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8014,9 +8083,9 @@ packages:
         optional: true
     dependencies:
       '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2)
-      '@typescript-eslint/utils': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+      '@typescript-eslint/utils': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.50.0
       ts-api-utils: 1.0.1(typescript@5.2.2)
       typescript: 5.2.2
     transitivePeerDependencies:
@@ -8049,19 +8118,19 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@6.7.2(eslint@8.49.0)(typescript@5.2.2):
+  /@typescript-eslint/utils@6.7.2(eslint@8.50.0)(typescript@5.2.2):
     resolution: {integrity: sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.2
       '@typescript-eslint/scope-manager': 6.7.2
       '@typescript-eslint/types': 6.7.2
       '@typescript-eslint/typescript-estree': 6.7.2(typescript@5.2.2)
-      eslint: 8.49.0
+      eslint: 8.50.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
@@ -9250,8 +9319,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.11.3:
-    resolution: {integrity: sha512-Z5zmgK2gJ+FIFhPBHpB5zRGGD2/89MMm/qIM3SMrt1fgP7RCy7y4qGrQVj7OmuNr43y8UG0UtQlYVADpSZAVTA==}
+  /bullmq@4.11.4:
+    resolution: {integrity: sha512-LuCR3ILngYa3CLC5jyf8DU4Yokj9T12MWwBogP3S4IiJUtbJsQ9GTGFxho3imRxXfcd9DUfrABT/pSoqVigXiQ==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -10975,7 +11044,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -10996,15 +11065,15 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       debug: 3.2.7(supports-color@5.5.0)
-      eslint: 8.49.0
+      eslint: 8.50.0
       eslint-import-resolver-node: 0.3.7
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.49.0):
+  /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2)(eslint@8.50.0):
     resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11014,16 +11083,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.7.2(eslint@8.49.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2)
       array-includes: 3.1.6
       array.prototype.findlastindex: 1.2.2
       array.prototype.flat: 1.3.1
       array.prototype.flatmap: 1.3.1
       debug: 3.2.7(supports-color@5.5.0)
       doctrine: 2.1.0
-      eslint: 8.49.0
+      eslint: 8.50.0
       eslint-import-resolver-node: 0.3.7
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.49.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.7)(eslint@8.50.0)
       has: 1.0.3
       is-core-module: 2.13.0
       is-glob: 4.0.3
@@ -11039,19 +11108,19 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.17.0(eslint@8.49.0):
+  /eslint-plugin-vue@9.17.0(eslint@8.50.0):
     resolution: {integrity: sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
-      eslint: 8.49.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
+      eslint: 8.50.0
       natural-compare: 1.4.0
       nth-check: 2.1.1
       postcss-selector-parser: 6.0.13
       semver: 7.5.4
-      vue-eslint-parser: 9.3.1(eslint@8.49.0)
+      vue-eslint-parser: 9.3.1(eslint@8.50.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -11087,15 +11156,15 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /eslint@8.49.0:
-    resolution: {integrity: sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==}
+  /eslint@8.50.0:
+    resolution: {integrity: sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.49.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.50.0)
       '@eslint-community/regexpp': 4.6.2
       '@eslint/eslintrc': 2.1.2
-      '@eslint/js': 8.49.0
+      '@eslint/js': 8.50.0
       '@humanwhocodes/config-array': 0.11.11
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
@@ -17771,11 +17840,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@7.4.3:
-    resolution: {integrity: sha512-afp7trR23jKt8ruGMPjkNAk3A/4CaLo20iPWAODznlF7u+XWnqGm1S+ZUiJFf13Jzj8jmJf/d7/xDHxY3qVMUA==}
+  /storybook@7.4.4:
+    resolution: {integrity: sha512-ixyov5hGz/nNad5N93xVYEqmiKXRPmPgKPjgkO8vmeaDBKy0yMGKAhCwZToqRwKflq6Xpas9BJ9Jwl1R2fsZRA==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 7.4.3
+      '@storybook/cli': 7.4.4
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -19095,14 +19164,14 @@ packages:
       - vue
     dev: true
 
-  /vue-eslint-parser@9.3.1(eslint@8.49.0):
+  /vue-eslint-parser@9.3.1(eslint@8.50.0):
     resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.49.0
+      eslint: 8.50.0
       eslint-scope: 7.2.0
       eslint-visitor-keys: 3.4.1
       espree: 9.5.2
@@ -19600,7 +19669,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.3)(@storybook/components@7.4.3)(@storybook/core-events@7.4.3)(@storybook/manager-api@7.4.3)(@storybook/preview-api@7.4.3)(@storybook/theming@7.4.3)(@storybook/types@7.4.3)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.3)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0):
     resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
     id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
     name: storybook-addon-misskey-theme
@@ -19621,13 +19690,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 7.4.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.4.3
-      '@storybook/manager-api': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.4.3
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
+      '@storybook/core-events': 7.4.4
+      '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.4.4
+      '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.4.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From 504541a158dc1268e6d6f3f1cf715a29b766ed2f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 12:48:17 +0900
Subject: [PATCH 20/62] 2023.9.0-rc.1

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index cdc6d7679a..2022abc711 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-beta.11",
+	"version": "2023.9.0-rc.1",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From ba6e85482ef29064afb112d128ff46c4e5aeaed8 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 14:26:06 +0900
Subject: [PATCH 21/62] =?UTF-8?q?=E3=82=B3=E3=83=9F=E3=83=83=E3=83=88?=
 =?UTF-8?q?=E5=BF=98=E3=82=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/server/api/endpoints/meta.ts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index c0cbfa3f48..fa6486ed18 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -40,6 +40,10 @@ export const meta = {
 				type: 'string',
 				optional: false, nullable: false,
 			},
+			shortName: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 			uri: {
 				type: 'string',
 				optional: false, nullable: false,
@@ -288,6 +292,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				version: this.config.version,
 
 				name: instance.name,
+				shortName: instance.shortName,
 				uri: this.config.url,
 				description: instance.description,
 				langs: instance.langs,

From 9e4d3ebe5f67244c18e1fb03d6d0264bff226b76 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 18:28:16 +0900
Subject: [PATCH 22/62] enhance(backend): refine moderation log (#10939)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update DriveService.ts
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            | 15 ++++
 locales/ja-JP.yml                             | 15 ++++
 .../backend/src/core/AnnouncementService.ts   | 15 +++-
 packages/backend/src/core/DriveService.ts     | 23 ++++--
 .../backend/src/core/ModerationLogService.ts  |  5 +-
 .../backend/src/core/NoteDeleteService.ts     | 12 ++-
 packages/backend/src/core/RoleService.ts      | 46 ++++++++++-
 .../endpoints/admin/announcements/create.ts   |  2 +-
 .../server/api/endpoints/admin/emoji/add.ts   |  2 +-
 .../server/api/endpoints/admin/queue/clear.ts |  2 +-
 .../api/endpoints/admin/queue/promote.ts      |  2 +-
 .../api/endpoints/admin/roles/assign.ts       |  2 +-
 .../api/endpoints/admin/roles/unassign.ts     |  2 +-
 .../api/endpoints/admin/roles/update.ts       | 15 ++--
 .../endpoints/admin/show-moderation-logs.ts   | 10 +++
 .../api/endpoints/admin/suspend-user.ts       |  2 +-
 .../api/endpoints/admin/unsuspend-user.ts     |  2 +-
 .../server/api/endpoints/admin/update-meta.ts | 10 ++-
 .../api/endpoints/admin/update-user-note.ts   | 11 +++
 .../api/endpoints/drive/files/delete.ts       |  6 +-
 .../src/server/api/endpoints/notes/delete.ts  |  2 +-
 packages/backend/src/types.ts                 | 79 +++++++++++++++++++
 .../backend/test/unit/AnnouncementService.ts  | 18 ++++-
 packages/frontend/src/pages/admin/index.vue   |  5 ++
 .../src/pages/admin/modlog.ModLog.vue         | 57 +++++++++++++
 packages/frontend/src/pages/admin/modlog.vue  | 67 ++++++++++++++++
 packages/frontend/src/router.ts               |  4 +
 packages/misskey-js/etc/misskey-js.api.md     | 48 ++++++++++-
 packages/misskey-js/src/consts.ts             | 79 +++++++++++++++++++
 packages/misskey-js/src/entities.ts           | 42 ++++++++++
 packages/misskey-js/src/index.ts              |  1 +
 32 files changed, 563 insertions(+), 39 deletions(-)
 create mode 100644 packages/frontend/src/pages/admin/modlog.ModLog.vue
 create mode 100644 packages/frontend/src/pages/admin/modlog.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dc99ee33fe..62810ebf44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@
 - Feat: 二要素認証でパスキーをサポートするようになりました
 - Feat: 指定したユーザーが投稿したときに通知できるようになりました
 - Feat: プロフィールでのリンク検証
+- Feat: モデレーションログ機能
 - Feat: 通知をテストできるようになりました
 - Feat: PWAのアイコンが設定できるようになりました
 - Enhance: サーバー名の略称が設定できるようになりました
diff --git a/locales/index.d.ts b/locales/index.d.ts
index da60550193..fd99f10b69 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -421,6 +421,7 @@ export interface Locale {
     "moderation": string;
     "moderationNote": string;
     "addModerationNote": string;
+    "moderationLogs": string;
     "nUsersMentioned": string;
     "securityKeyAndPasskey": string;
     "securityKey": string;
@@ -2248,6 +2249,20 @@ export interface Locale {
             "mention": string;
         };
     };
+    "_moderationLogTypes": {
+        "assignRole": string;
+        "unassignRole": string;
+        "updateRole": string;
+        "suspend": string;
+        "unsuspend": string;
+        "addCustomEmoji": string;
+        "updateServerSettings": string;
+        "updateUserNote": string;
+        "deleteDriveFile": string;
+        "deleteNote": string;
+        "createGlobalAnnouncement": string;
+        "createUserAnnouncement": string;
+    };
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0869c0c455..b396014ee2 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -418,6 +418,7 @@ moderator: "モデレーター"
 moderation: "モデレーション"
 moderationNote: "モデレーションノート"
 addModerationNote: "モデレーションノートを追加する"
+moderationLogs: "モデログ"
 nUsersMentioned: "{n}人が投稿"
 securityKeyAndPasskey: "セキュリティキー・パスキー"
 securityKey: "セキュリティキー"
@@ -2160,3 +2161,17 @@ _webhookSettings:
     renote: "Renoteされたとき"
     reaction: "リアクションがあったとき"
     mention: "メンションされたとき"
+
+_moderationLogTypes:
+  assignRole: "ロールへアサイン"
+  unassignRole: "ロールのアサイン解除"
+  updateRole: "ロール設定更新"
+  suspend: "凍結"
+  unsuspend: "凍結解除"
+  addCustomEmoji: "カスタム絵文字追加"
+  updateServerSettings: "サーバー設定更新"
+  updateUserNote: "モデレーションノート更新"
+  deleteDriveFile: "ファイルを削除"
+  deleteNote: "ノートを削除"
+  createGlobalAnnouncement: "全体のお知らせを作成"
+  createUserAnnouncement: "ユーザーへお知らせを作成"
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index 70f37516a4..31fcb139ea 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -12,6 +12,7 @@ import { bindThis } from '@/decorators.js';
 import { Packed } from '@/misc/json-schema.js';
 import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 @Injectable()
 export class AnnouncementService {
@@ -24,6 +25,7 @@ export class AnnouncementService {
 
 		private idService: IdService,
 		private globalEventService: GlobalEventService,
+		private moderationLogService: ModerationLogService,
 	) {
 	}
 
@@ -58,7 +60,7 @@ export class AnnouncementService {
 	}
 
 	@bindThis
-	public async create(values: Partial<MiAnnouncement>): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
+	public async create(values: Partial<MiAnnouncement>, moderator: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
 		const announcement = await this.announcementsRepository.insert({
 			id: this.idService.genId(),
 			createdAt: new Date(),
@@ -79,10 +81,21 @@ export class AnnouncementService {
 			this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
 				announcement: packed,
 			});
+
+			this.moderationLogService.log(moderator, 'createUserAnnouncement', {
+				announcementId: announcement.id,
+				announcement: announcement,
+				userId: values.userId,
+			});
 		} else {
 			this.globalEventService.publishBroadcastStream('announcementCreated', {
 				announcement: packed,
 			});
+
+			this.moderationLogService.log(moderator, 'createGlobalAnnouncement', {
+				announcementId: announcement.id,
+				announcement: announcement,
+			});
 		}
 
 		return {
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index e015d3dc41..2ff062142c 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -42,6 +42,7 @@ import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import { correctFilename } from '@/misc/correct-filename.js';
 import { isMimeImage } from '@/misc/is-mime-image.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 type AddFileArgs = {
 	/** User who wish to add file */
@@ -119,6 +120,7 @@ export class DriveService {
 		private globalEventService: GlobalEventService,
 		private queueService: QueueService,
 		private roleService: RoleService,
+		private moderationLogService: ModerationLogService,
 		private driveChart: DriveChart,
 		private perUserDriveChart: PerUserDriveChart,
 		private instanceChart: InstanceChart,
@@ -648,7 +650,7 @@ export class DriveService {
 	}
 
 	@bindThis
-	public async deleteFile(file: MiDriveFile, isExpired = false) {
+	public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
 		if (file.storedInternal) {
 			this.internalStorageService.del(file.accessKey!);
 
@@ -671,11 +673,11 @@ export class DriveService {
 			}
 		}
 
-		this.deletePostProcess(file, isExpired);
+		this.deletePostProcess(file, isExpired, deleter);
 	}
 
 	@bindThis
-	public async deleteFileSync(file: MiDriveFile, isExpired = false) {
+	public async deleteFileSync(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
 		if (file.storedInternal) {
 			this.internalStorageService.del(file.accessKey!);
 
@@ -702,11 +704,11 @@ export class DriveService {
 			await Promise.all(promises);
 		}
 
-		this.deletePostProcess(file, isExpired);
+		this.deletePostProcess(file, isExpired, deleter);
 	}
 
 	@bindThis
-	private async deletePostProcess(file: MiDriveFile, isExpired = false) {
+	private async deletePostProcess(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
 		// リモートファイル期限切れ削除後は直リンクにする
 		if (isExpired && file.userHost !== null && file.uri != null) {
 			this.driveFilesRepository.update(file.id, {
@@ -733,6 +735,17 @@ export class DriveService {
 				this.instanceChart.updateDrive(file, false);
 			}
 		}
+
+		if (file.userId) {
+			this.globalEventService.publishDriveStream(file.userId, 'fileDeleted', file.id);
+		}
+
+		if (deleter && await this.roleService.isModerator(deleter) && (file.userId !== deleter.id)) {
+			this.moderationLogService.log(deleter, 'deleteDriveFile', {
+				fileId: file.id,
+				fileUserId: file.userId,
+			});
+		}
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts
index b0e5b794d0..f7f9063d92 100644
--- a/packages/backend/src/core/ModerationLogService.ts
+++ b/packages/backend/src/core/ModerationLogService.ts
@@ -9,6 +9,7 @@ import type { ModerationLogsRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
 import { IdService } from '@/core/IdService.js';
 import { bindThis } from '@/decorators.js';
+import { ModerationLogPayloads, moderationLogTypes } from '@/types.js';
 
 @Injectable()
 export class ModerationLogService {
@@ -21,13 +22,13 @@ export class ModerationLogService {
 	}
 
 	@bindThis
-	public async insertModerationLog(moderator: { id: MiUser['id'] }, type: string, info?: Record<string, any>) {
+	public async log<T extends typeof moderationLogTypes[number]>(moderator: { id: MiUser['id'] }, type: T, info?: ModerationLogPayloads[T]) {
 		await this.moderationLogsRepository.insert({
 			id: this.idService.genId(),
 			createdAt: new Date(),
 			userId: moderator.id,
 			type: type,
-			info: info ?? {},
+			info: (info as any) ?? {},
 		});
 	}
 }
diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts
index 69fff36a02..c99f92b9cb 100644
--- a/packages/backend/src/core/NoteDeleteService.ts
+++ b/packages/backend/src/core/NoteDeleteService.ts
@@ -23,6 +23,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { MetaService } from '@/core/MetaService.js';
 import { SearchService } from '@/core/SearchService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 @Injectable()
 export class NoteDeleteService {
@@ -48,6 +49,7 @@ export class NoteDeleteService {
 		private apDeliverManagerService: ApDeliverManagerService,
 		private metaService: MetaService,
 		private searchService: SearchService,
+		private moderationLogService: ModerationLogService,
 		private notesChart: NotesChart,
 		private perUserNotesChart: PerUserNotesChart,
 		private instanceChart: InstanceChart,
@@ -58,7 +60,7 @@ export class NoteDeleteService {
 	 * @param user 投稿者
 	 * @param note 投稿
 	 */
-	async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false) {
+	async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) {
 		const deletedAt = new Date();
 		const cascadingNotes = await this.findCascadingNotes(note);
 
@@ -131,6 +133,14 @@ export class NoteDeleteService {
 			id: note.id,
 			userId: user.id,
 		});
+
+		if (deleter && (note.userId !== deleter.id)) {
+			this.moderationLogService.log(deleter, 'deleteNote', {
+				noteId: note.id,
+				noteUserId: note.userId,
+				note: note,
+			});
+		}
 	}
 
 	@bindThis
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 518f283695..39f21ecec4 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -18,6 +18,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { StreamMessages } from '@/server/api/stream/types.js';
 import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 import type { Packed } from '@/misc/json-schema.js';
 import type { OnApplicationShutdown } from '@nestjs/common';
 
@@ -98,6 +99,7 @@ export class RoleService implements OnApplicationShutdown {
 		private userEntityService: UserEntityService,
 		private globalEventService: GlobalEventService,
 		private idService: IdService,
+		private moderationLogService: ModerationLogService,
 	) {
 		//this.onMessage = this.onMessage.bind(this);
 
@@ -374,9 +376,11 @@ export class RoleService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null): Promise<void> {
+	public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null, moderator?: MiUser): Promise<void> {
 		const now = new Date();
 
+		const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
+
 		const existing = await this.roleAssignmentsRepository.findOneBy({
 			roleId: roleId,
 			userId: userId,
@@ -406,10 +410,19 @@ export class RoleService implements OnApplicationShutdown {
 		});
 
 		this.globalEventService.publishInternalEvent('userRoleAssigned', created);
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'assignRole', {
+				roleId: roleId,
+				roleName: role.name,
+				userId: userId,
+				expiresAt: expiresAt ? expiresAt.toISOString() : null,
+			});
+		}
 	}
 
 	@bindThis
-	public async unassign(userId: MiUser['id'], roleId: MiRole['id']): Promise<void> {
+	public async unassign(userId: MiUser['id'], roleId: MiRole['id'], moderator?: MiUser): Promise<void> {
 		const now = new Date();
 
 		const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId });
@@ -430,6 +443,15 @@ export class RoleService implements OnApplicationShutdown {
 		});
 
 		this.globalEventService.publishInternalEvent('userRoleUnassigned', existing);
+
+		if (moderator) {
+			const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
+			this.moderationLogService.log(moderator, 'unassignRole', {
+				roleId: roleId,
+				roleName: role.name,
+				userId: userId,
+			});
+		}
 	}
 
 	@bindThis
@@ -451,6 +473,26 @@ export class RoleService implements OnApplicationShutdown {
 		redisPipeline.exec();
 	}
 
+	@bindThis
+	public async update(role: MiRole, params: Partial<MiRole>, moderator?: MiUser): Promise<void> {
+		const date = new Date();
+		await this.rolesRepository.update(role.id, {
+			updatedAt: date,
+			...params,
+		});
+
+		const updated = await this.rolesRepository.findOneByOrFail({ id: role.id });
+		this.globalEventService.publishInternalEvent('roleUpdated', updated);
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'updateRole', {
+				roleId: role.id,
+				before: role,
+				after: updated,
+			});
+		}
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.redisForSub.off('message', this.onMessage);
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
index c2f69bb159..262b36b9a4 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				forExistingUsers: ps.forExistingUsers,
 				needConfirmationToRead: ps.needConfirmationToRead,
 				userId: ps.userId,
-			});
+			}, me);
 
 			return packed;
 		});
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
index 7bd920c312..fc297c4702 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
 			});
 
-			this.moderationLogService.insertModerationLog(me, 'addEmoji', {
+			this.moderationLogService.log(me, 'addCustomEmoji', {
 				emojiId: emoji.id,
 			});
 
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
index b61c580034..c9142e9885 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
@@ -30,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		super(meta, paramDef, async (ps, me) => {
 			this.queueService.destroy();
 
-			this.moderationLogService.insertModerationLog(me, 'clearQueue');
+			this.moderationLogService.log(me, 'clearQueue');
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
index 8d16cddd00..0cba5b4e25 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					break;
 			}
 
-			this.moderationLogService.insertModerationLog(me, 'promoteQueue');
+			this.moderationLogService.log(me, 'promoteQueue');
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
index 9a005982d4..a0f3edd867 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
@@ -83,7 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return;
 			}
 
-			await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null);
+			await this.roleService.assign(user.id, role.id, ps.expiresAt ? new Date(ps.expiresAt) : null, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
index 0a79296c05..4c27583111 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.noSuchUser);
 			}
 
-			await this.roleService.unassign(user.id, role.id);
+			await this.roleService.unassign(user.id, role.id, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index 65fdb4b4b4..e4e59e487c 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -9,6 +9,7 @@ import type { RolesRepository } from '@/models/_.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '@/server/api/error.js';
+import { RoleService } from '@/core/RoleService.js';
 
 export const meta = {
 	tags: ['admin', 'role'],
@@ -70,16 +71,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.rolesRepository)
 		private rolesRepository: RolesRepository,
 
-		private globalEventService: GlobalEventService,
+		private roleService: RoleService,
 	) {
-		super(meta, paramDef, async (ps) => {
-			const roleExist = await this.rolesRepository.exist({ where: { id: ps.roleId } });
-			if (!roleExist) {
+		super(meta, paramDef, async (ps, me) => {
+			const role = await this.rolesRepository.findOneBy({ id: ps.roleId });
+			if (role == null) {
 				throw new ApiError(meta.errors.noSuchRole);
 			}
 
 			const date = new Date();
-			await this.rolesRepository.update(ps.roleId, {
+			await this.roleService.update(role, {
 				updatedAt: date,
 				name: ps.name,
 				description: ps.description,
@@ -95,9 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				canEditMembersByModerator: ps.canEditMembersByModerator,
 				displayOrder: ps.displayOrder,
 				policies: ps.policies,
-			});
-			const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId });
-			this.globalEventService.publishInternalEvent('roleUpdated', updated);
+			}, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
index d5f97ab149..f87a5a3574 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts
@@ -62,6 +62,8 @@ export const paramDef = {
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
+		type: { type: 'string', nullable: true },
+		userId: { type: 'string', format: 'misskey:id', nullable: true },
 	},
 	required: [],
 } as const;
@@ -78,6 +80,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId);
 
+			if (ps.type != null) {
+				query.andWhere('report.type = :type', { type: ps.type });
+			}
+
+			if (ps.userId != null) {
+				query.andWhere('report.userId = :userId', { userId: ps.userId });
+			}
+
 			const reports = await query.limit(ps.limit).getMany();
 
 			return await this.moderationLogEntityService.packMany(reports);
diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
index bcf12fa4e8..89199f8bff 100644
--- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
@@ -60,7 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				isSuspended: true,
 			});
 
-			this.moderationLogService.insertModerationLog(me, 'suspend', {
+			this.moderationLogService.log(me, 'suspend', {
 				targetId: user.id,
 			});
 
diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
index 59e89e15bd..a2779148ed 100644
--- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -45,7 +45,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				isSuspended: false,
 			});
 
-			this.moderationLogService.insertModerationLog(me, 'unsuspend', {
+			this.moderationLogService.log(me, 'unsuspend', {
 				targetId: user.id,
 			});
 
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index eabf1f306c..ea6ebdd1fe 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -441,8 +441,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.manifestJsonOverride = ps.manifestJsonOverride;
 			}
 
+			const before = await this.metaService.fetch(true);
+
 			await this.metaService.update(set);
-			this.moderationLogService.insertModerationLog(me, 'updateMeta');
+
+			const after = await this.metaService.fetch(true);
+
+			this.moderationLogService.log(me, 'updateServerSettings', {
+				before,
+				after,
+			});
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
index c86a43977e..2e9fd5ad29 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { UserProfilesRepository, UsersRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { DI } from '@/di-symbols.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -32,6 +33,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
+
+		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const user = await this.usersRepository.findOneBy({ id: ps.userId });
@@ -40,9 +43,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new Error('user not found');
 			}
 
+			const currentProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
+
 			await this.userProfilesRepository.update({ userId: user.id }, {
 				moderationNote: ps.text,
 			});
+
+			this.moderationLogService.log(me, 'updateUserNote', {
+				userId: user.id,
+				before: currentProfile.moderationNote,
+				after: ps.text,
+			});
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
index d7fdc81cdb..7b67a31e08 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
@@ -65,11 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			// Delete
-			await this.driveService.deleteFile(file);
-
-			// Publish fileDeleted event
-			this.globalEventService.publishDriveStream(me.id, 'fileDeleted', file.id);
+			await this.driveService.deleteFile(file, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts
index 74062a58f5..55aaaf4f78 100644
--- a/packages/backend/src/server/api/endpoints/notes/delete.ts
+++ b/packages/backend/src/server/api/endpoints/notes/delete.ts
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			// この操作を行うのが投稿者とは限らない(例えばモデレーター)ため
-			await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note);
+			await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, false, me);
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 0a28d88d08..7946e66b82 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -26,3 +26,82 @@ export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as
 export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const;
 
 export const ffVisibility = ['public', 'followers', 'private'] as const;
+
+export const moderationLogTypes = [
+	'updateServerSettings',
+	'suspend',
+	'unsuspend',
+	'updateUserNote',
+	'addCustomEmoji',
+	'assignRole',
+	'unassignRole',
+	'updateRole',
+	'deleteRole',
+	'clearQueue',
+	'promoteQueue',
+	'deleteDriveFile',
+	'deleteNote',
+	'createGlobalAnnouncement',
+	'createUserAnnouncement',
+] as const;
+
+export type ModerationLogPayloads = {
+	updateServerSettings: {
+		before: any | null;
+		after: any | null;
+	};
+	suspend: {
+		targetId: string;
+	};
+	unsuspend: {
+		targetId: string;
+	};
+	updateUserNote: {
+		userId: string;
+		before: string | null;
+		after: string | null;
+	};
+	addCustomEmoji: {
+		emojiId: string;
+	};
+	assignRole: {
+		userId: string;
+		roleId: string;
+		roleName: string;
+		expiresAt: string | null;
+	};
+	unassignRole: {
+		userId: string;
+		roleId: string;
+		roleName: string;
+	};
+	updateRole: {
+		roleId: string;
+		before: any;
+		after: any;
+	};
+	deleteRole: {
+		roleId: string;
+		roleName: string;
+	};
+	clearQueue: Record<string, never>;
+	promoteQueue: Record<string, never>;
+	deleteDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
+	deleteNote: {
+		noteId: string;
+		noteUserId: string;
+		note: any;
+	};
+	createGlobalAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
+	createUserAnnouncement: {
+		announcementId: string;
+		announcement: any;
+		userId: string;
+	};
+};
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index 721fbb7345..8f61d91ba9 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -16,6 +16,7 @@ import { genAidx } from '@/misc/id/aidx.js';
 import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
@@ -29,6 +30,7 @@ describe('AnnouncementService', () => {
 	let announcementsRepository: AnnouncementsRepository;
 	let announcementReadsRepository: AnnouncementReadsRepository;
 	let globalEventService: jest.Mocked<GlobalEventService>;
+	let moderationLogService: jest.Mocked<ModerationLogService>;
 
 	function createUser(data: Partial<MiUser> = {}) {
 		const un = secureRndstr(16);
@@ -71,8 +73,11 @@ describe('AnnouncementService', () => {
 						publishMainStream: jest.fn(),
 						publishBroadcastStream: jest.fn(),
 					};
-				}
-				if (typeof token === 'function') {
+				} else if (token === ModerationLogService) {
+					return {
+						log: jest.fn(),
+					};
+				} else if (typeof token === 'function') {
 					const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
 					const Mock = moduleMocker.generateFromMetadata(mockMetadata);
 					return new Mock();
@@ -87,6 +92,7 @@ describe('AnnouncementService', () => {
 		announcementsRepository = app.get<AnnouncementsRepository>(DI.announcementsRepository);
 		announcementReadsRepository = app.get<AnnouncementReadsRepository>(DI.announcementReadsRepository);
 		globalEventService = app.get<GlobalEventService>(GlobalEventService) as jest.Mocked<GlobalEventService>;
+		moderationLogService = app.get<ModerationLogService>(ModerationLogService) as jest.Mocked<ModerationLogService>;
 	});
 
 	afterEach(async () => {
@@ -155,10 +161,11 @@ describe('AnnouncementService', () => {
 
 	describe('create', () => {
 		test('通常', async () => {
+			const me = await createUser();
 			const result = await announcementService.create({
 				title: 'Title',
 				text: 'Text',
-			});
+			}, me);
 
 			expect(result.raw.title).toBe('Title');
 			expect(result.packed.title).toBe('Title');
@@ -166,15 +173,17 @@ describe('AnnouncementService', () => {
 			expect(globalEventService.publishBroadcastStream).toHaveBeenCalled();
 			expect(globalEventService.publishBroadcastStream.mock.lastCall![0]).toBe('announcementCreated');
 			expect((globalEventService.publishBroadcastStream.mock.lastCall![1] as any).announcement).toBe(result.packed);
+			expect(moderationLogService.log).toHaveBeenCalled();
 		});
 
 		test('ユーザー指定', async () => {
+			const me = await createUser();
 			const user = await createUser();
 			const result = await announcementService.create({
 				title: 'Title',
 				text: 'Text',
 				userId: user.id,
-			});
+			}, me);
 
 			expect(result.raw.title).toBe('Title');
 			expect(result.packed.title).toBe('Title');
@@ -184,6 +193,7 @@ describe('AnnouncementService', () => {
 			expect(globalEventService.publishMainStream.mock.lastCall![0]).toBe(user.id);
 			expect(globalEventService.publishMainStream.mock.lastCall![1]).toBe('announcementCreated');
 			expect((globalEventService.publishMainStream.mock.lastCall![2] as any).announcement).toBe(result.packed);
+			expect(moderationLogService.log).toHaveBeenCalled();
 		});
 	});
 
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index ab4e7620dd..944ba7b950 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -145,6 +145,11 @@ const menuDef = $computed(() => [{
 		text: i18n.ts.abuseReports,
 		to: '/admin/abuses',
 		active: currentPage?.route.name === 'abuses',
+	}, {
+		icon: 'ti ti-list-search',
+		text: i18n.ts.moderationLogs,
+		to: '/admin/modlog',
+		active: currentPage?.route.name === 'modlog',
 	}],
 }, {
 	title: i18n.ts.settings,
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
new file mode 100644
index 0000000000..3a474f73a8
--- /dev/null
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -0,0 +1,57 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkFolder>
+	<template #label>{{ i18n.ts._moderationLogTypes[log.type] }}</template>
+	<template #icon>
+		<MkAvatar :user="log.user" :class="$style.avatar"/>
+	</template>
+	<template #suffix>
+		<MkTime :time="log.createdAt" mode="detail"/>
+	</template>
+
+	<div :class="$style.root">
+		<div>{{ i18n.ts.moderator }}: {{ log.userId }}</div>
+
+		<template v-if="log.type === 'suspend'">
+			<div>{{ i18n.ts.user }}: {{ log.info.targetId }}</div>
+		</template>
+		<template v-else-if="log.type === 'unsuspend'">
+			<div>{{ i18n.ts.user }}: {{ log.info.targetId }}</div>
+		</template>
+		<template v-else-if="log.type === 'assignRole'">
+			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
+			<div>{{ i18n.ts.role }}: {{ log.info.roleName }} [{{ log.info.roleId }}]</div>
+		</template>
+		<template v-else-if="log.type === 'unassignRole'">
+			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
+			<div>{{ i18n.ts.role }}: {{ log.info.roleName }} [{{ log.info.roleId }}]</div>
+		</template>
+	</div>
+</MkFolder>
+</template>
+
+<script lang="ts" setup>
+import * as Misskey from 'misskey-js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { dateString } from '@/filters/date.js';
+import MkFolder from '@/components/MkFolder.vue';
+
+const props = defineProps<{
+	log: Misskey.entities.ModerationLog;
+}>();
+</script>
+
+<style lang="scss" module>
+.root {
+}
+
+.avatar {
+	width: 18px;
+	height: 18px;
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
new file mode 100644
index 0000000000..da043f1b8f
--- /dev/null
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -0,0 +1,67 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+	<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
+	<MkSpacer :contentMax="900">
+		<div>
+			<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+				<MkSelect v-model="type" style="margin: 0; flex: 1;">
+					<template #label>{{ i18n.ts.type }}</template>
+					<option :value="null">{{ i18n.ts.all }}</option>
+					<option v-for="t in Misskey.moderationLogTypes" :key="t" :value="t">{{ t }}</option>
+				</MkSelect>
+				<MkInput v-model="moderatorId" style="margin: 0; flex: 1;">
+					<template #label>{{ i18n.ts.moderator }}(ID)</template>
+				</MkInput>
+			</div>
+
+			<MkPagination v-slot="{items}" ref="logs" :pagination="pagination" style="margin-top: var(--margin);">
+				<div class="_gaps_s">
+					<XModLog v-for="item in items" :key="item.id" :log="item"/>
+				</div>
+			</MkPagination>
+		</div>
+	</MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue';
+import * as Misskey from 'misskey-js';
+import XHeader from './_header_.vue';
+import XModLog from './modlog.ModLog.vue';
+import MkSelect from '@/components/MkSelect.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkPagination from '@/components/MkPagination.vue';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+
+let logs = $shallowRef<InstanceType<typeof MkPagination>>();
+
+let type = $ref(null);
+let moderatorId = $ref('');
+
+const pagination = {
+	endpoint: 'admin/show-moderation-logs' as const,
+	limit: 30,
+	params: computed(() => ({
+		type,
+		userId: moderatorId === '' ? null : moderatorId,
+	})),
+};
+
+console.log(Misskey);
+
+const headerActions = $computed(() => []);
+
+const headerTabs = $computed(() => []);
+
+definePageMetadata({
+	title: i18n.ts.moderationLogs,
+	icon: 'ti ti-list-search',
+});
+</script>
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index e658477bbc..415d2f1974 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -395,6 +395,10 @@ export const routes = [{
 		path: '/abuses',
 		name: 'abuses',
 		component: page(() => import('./pages/admin/abuses.vue')),
+	}, {
+		path: '/modlog',
+		name: 'modlog',
+		component: page(() => import('./pages/admin/modlog.vue')),
 	}, {
 		path: '/settings',
 		name: 'settings',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 5cd679bce5..804733c066 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2278,7 +2278,8 @@ declare namespace entities {
         Invite,
         InviteLimit,
         UserSorting,
-        OriginType
+        OriginType,
+        ModerationLog
     }
 }
 export { entities }
@@ -2516,6 +2517,50 @@ type MessagingMessage = {
     groupId: UserGroup['id'] | null;
 };
 
+// @public (undocumented)
+type ModerationLog = {
+    id: ID;
+    createdAt: DateString;
+    userId: User['id'];
+    user: UserDetailed | null;
+} & ({
+    type: 'updateServerSettings';
+    info: ModerationLogPayloads['updateServerSettings'];
+} | {
+    type: 'suspend';
+    info: ModerationLogPayloads['suspend'];
+} | {
+    type: 'unsuspend';
+    info: ModerationLogPayloads['unsuspend'];
+} | {
+    type: 'updateUserNote';
+    info: ModerationLogPayloads['updateUserNote'];
+} | {
+    type: 'addCustomEmoji';
+    info: ModerationLogPayloads['addCustomEmoji'];
+} | {
+    type: 'assignRole';
+    info: ModerationLogPayloads['assignRole'];
+} | {
+    type: 'unassignRole';
+    info: ModerationLogPayloads['unassignRole'];
+} | {
+    type: 'updateRole';
+    info: ModerationLogPayloads['updateRole'];
+} | {
+    type: 'deleteRole';
+    info: ModerationLogPayloads['deleteRole'];
+} | {
+    type: 'clearQueue';
+    info: ModerationLogPayloads['clearQueue'];
+} | {
+    type: 'promoteQueue';
+    info: ModerationLogPayloads['promoteQueue'];
+});
+
+// @public (undocumented)
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement"];
+
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
 
@@ -2861,6 +2906,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
 // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
 // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
 // src/api.types.ts:631:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
+// src/entities.ts:579:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
 // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 6cf6dc07e7..346affc6a5 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -44,3 +44,82 @@ export const permissions = [
 	'read:flash-likes',
 	'write:flash-likes',
 ];
+
+export const moderationLogTypes = [
+	'updateServerSettings',
+	'suspend',
+	'unsuspend',
+	'updateUserNote',
+	'addCustomEmoji',
+	'assignRole',
+	'unassignRole',
+	'updateRole',
+	'deleteRole',
+	'clearQueue',
+	'promoteQueue',
+	'deleteDriveFile',
+	'deleteNote',
+	'createGlobalAnnouncement',
+	'createUserAnnouncement',
+] as const;
+
+export type ModerationLogPayloads = {
+	updateServerSettings: {
+		before: any | null;
+		after: any | null;
+	};
+	suspend: {
+		targetId: string;
+	};
+	unsuspend: {
+		targetId: string;
+	};
+	updateUserNote: {
+		userId: string;
+		before: string | null;
+		after: string | null;
+	};
+	addCustomEmoji: {
+		emojiId: string;
+	};
+	assignRole: {
+		userId: string;
+		roleId: string;
+		roleName: string;
+		expiresAt: string | null;
+	};
+	unassignRole: {
+		userId: string;
+		roleId: string;
+		roleName: string;
+	};
+	updateRole: {
+		roleId: string;
+		before: any;
+		after: any;
+	};
+	deleteRole: {
+		roleId: string;
+		roleName: string;
+	};
+	clearQueue: Record<string, never>;
+	promoteQueue: Record<string, never>;
+	deleteDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
+	deleteNote: {
+		noteId: string;
+		noteUserId: string;
+		note: any;
+	};
+	createGlobalAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
+	createUserAnnouncement: {
+		announcementId: string;
+		announcement: any;
+		userId: string;
+	};
+};
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 034201f9b9..0e6604cbaa 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -1,3 +1,5 @@
+import { ModerationLogPayloads } from './consts.js';
+
 export type ID = string;
 export type DateString = string;
 
@@ -566,3 +568,43 @@ export type UserSorting =
 	| '+updatedAt'
 	| '-updatedAt';
 export type OriginType = 'combined' | 'local' | 'remote';
+
+export type ModerationLog = {
+	id: ID;
+	createdAt: DateString;
+	userId: User['id'];
+	user: UserDetailed | null;
+} & ({
+	type: 'updateServerSettings';
+	info: ModerationLogPayloads['updateServerSettings'];
+} | {
+	type: 'suspend';
+	info: ModerationLogPayloads['suspend'];
+} | {
+	type: 'unsuspend';
+	info: ModerationLogPayloads['unsuspend'];
+} | {
+	type: 'updateUserNote';
+	info: ModerationLogPayloads['updateUserNote'];
+} | {
+	type: 'addCustomEmoji';
+	info: ModerationLogPayloads['addCustomEmoji'];
+} | {
+	type: 'assignRole';
+	info: ModerationLogPayloads['assignRole'];
+} | {
+	type: 'unassignRole';
+	info: ModerationLogPayloads['unassignRole'];
+} | {
+	type: 'updateRole';
+	info: ModerationLogPayloads['updateRole'];
+} | {
+	type: 'deleteRole';
+	info: ModerationLogPayloads['deleteRole'];
+} | {
+	type: 'clearQueue';
+	info: ModerationLogPayloads['clearQueue'];
+} | {
+	type: 'promoteQueue';
+	info: ModerationLogPayloads['promoteQueue'];
+});
diff --git a/packages/misskey-js/src/index.ts b/packages/misskey-js/src/index.ts
index ae4dd31fe0..e78501fdfd 100644
--- a/packages/misskey-js/src/index.ts
+++ b/packages/misskey-js/src/index.ts
@@ -17,6 +17,7 @@ export const notificationTypes = consts.notificationTypes;
 export const noteVisibilities = consts.noteVisibilities;
 export const mutedNoteReasons = consts.mutedNoteReasons;
 export const ffVisibility = consts.ffVisibility;
+export const moderationLogTypes = consts.moderationLogTypes;
 
 // api extractor not supported yet
 //export * as api from './api.js';

From 10924fd229e245b1bd13f7dec4c89925db956225 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 18:47:11 +0900
Subject: [PATCH 23/62] fix

---
 packages/backend/src/server/api/endpoints/drive/files/delete.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
index 7b67a31e08..f46bf49965 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts
@@ -65,7 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			await this.driveService.deleteFile(file, me);
+			await this.driveService.deleteFile(file, false, me);
 		});
 	}
 }

From b60b214c0cdaaafb7f6ea13c47bfc0763d9f22a6 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 18:47:21 +0900
Subject: [PATCH 24/62] New Crowdin updates (#11866)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)
---
 locales/ar-SA.yml |  1 +
 locales/bn-BD.yml |  1 +
 locales/cs-CZ.yml |  1 +
 locales/de-DE.yml | 10 +++++++-
 locales/en-US.yml | 10 +++++++-
 locales/es-ES.yml |  1 +
 locales/fr-FR.yml | 60 +++++++++++++++++++++++++++++++++++++++++++++++
 locales/id-ID.yml |  1 +
 locales/it-IT.yml |  1 +
 locales/ja-KS.yml |  1 +
 locales/ko-KR.yml |  1 +
 locales/pl-PL.yml |  1 +
 locales/ru-RU.yml |  1 +
 locales/sk-SK.yml |  1 +
 locales/th-TH.yml |  1 +
 locales/uk-UA.yml |  1 +
 locales/vi-VN.yml |  1 +
 locales/zh-CN.yml |  5 ++++
 locales/zh-TW.yml | 26 +++++++++++++-------
 19 files changed, 114 insertions(+), 11 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 4d5872c64d..f36641128c 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1140,6 +1140,7 @@ _plugin:
   install: "ثبّت إضافات"
   installWarn: "رجاءً لا تثبت إضافات غير موثوقة."
   manage: "إدارة الإضافات"
+  viewSource: "اظهر المصدر"
 _preferencesBackups:
   createdAt: "تم إنشاؤه: {date} {time}"
   updatedAt: "آخر تحديث: {date} {time}"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index f3475de225..e0e1e29433 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -889,6 +889,7 @@ _plugin:
   install: "প্লাগইন ইন্সটল করুন"
   installWarn: "অবিশ্বস্ত প্লাগইন ইনস্টল করবেন না।"
   manage: "প্লাগইন ম্যানেজ করুন"
+  viewSource: "উৎস দেখুন"
 _registry:
   scope: "স্কোপ"
   key: "কী"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 9a751abc78..36a104bdc3 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1492,6 +1492,7 @@ _plugin:
   install: "Instalovat plugin"
   installWarn: "Neinstalujte nedůvěryhodné pluginy."
   manage: "Správce pluginů"
+  viewSource: "Zobrazit zdroj"
 _preferencesBackups:
   list: "Vytvořit backup"
   saveNew: "Uložit novou zálohu"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 596a6e5fd8..b11cb7e5b9 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -710,6 +710,7 @@ lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt
 alwaysMarkSensitive: "Medien standardmäßig als sensibel markieren"
 loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen"
 disableShowingAnimatedImages: "Animierte Bilder nicht abspielen"
+highlightSensitiveMedia: "Sensitive Medien markieren"
 verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen."
 notSet: "Nicht konfiguriert"
 emailVerified: "Email-Adresse bestätigt"
@@ -913,7 +914,7 @@ typeToConfirm: "Bitte gib zur Bestätigung {x} ein"
 deleteAccount: "Benutzerkonto löschen"
 document: "Dokumentation"
 numberOfPageCache: "Seitencachegröße"
-numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung."
+numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, aber erhöht Last und Arbeitsspeicherauslastung auf dem Nutzergerät."
 logoutConfirm: "Wirklich abmelden?"
 lastActiveDate: "Zuletzt verwendet am"
 statusbar: "Statusleiste"
@@ -1116,6 +1117,8 @@ keepScreenOn: "Bildschirm angeschaltet lassen"
 verifiedLink: "Link-Besitz wurde verifiziert"
 notifyNotes: "Über neue Notizen benachrichtigen"
 unnotifyNotes: "Nicht über neue Notizen benachrichtigen"
+authentication: "Authentifikation"
+authenticationRequiredToContinue: "Bitte authentifiziere dich, um fortzufahren"
 _announcement:
   forExistingUsers: "Nur für existierende Nutzer"
   forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
@@ -1149,6 +1152,8 @@ _serverSettings:
   appIconStyleRecommendation: "Da das Icon zu einem Kreis oder Quadrat zugeschnitten wird, wird ein Icon mit gefülltem Margin um den Inhalt herum empfohlen."
   appIconResolutionMustBe: "Die Mindestauflösung ist {resolution}."
   manifestJsonOverride: "Überschreiben von manifest.json"
+  shortName: "Abkürzung"
+  shortNameDescription: "Ein Kürzel für den Namen der Instanz, der angezeigt werden kann, falls der volle Instanzname lang ist."
 _accountMigration:
   moveFrom: "Von einem anderen Konto zu diesem migrieren"
   moveFromSub: "Alias für ein anderes Konto erstellen"
@@ -1529,6 +1534,7 @@ _plugin:
   install: "Plugins installieren"
   installWarn: "Installiere bitte nur vertrauenswürdige Plugins."
   manage: "Plugins verwalten"
+  viewSource: "Quelltext anzeigen"
 _preferencesBackups:
   list: "Erstellte Backups"
   saveNew: "Neu erstellen"
@@ -1794,6 +1800,7 @@ _antennaSources:
   homeTimeline: "Notizen von Benutzern, denen gefolgt wird"
   users: "Notizen von einem oder mehreren angegebenen Benutzern"
   userList: "Notizen von allen Benutzern einer Liste"
+  userBlacklist: "Alle Notizen abgesehen derer angegebener Benutzer"
 _weekday:
   sunday: "Sonntag"
   monday: "Montag"
@@ -2022,6 +2029,7 @@ _notification:
   notificationWillBeDisplayedLikeThis: "Benachrichtigungen sehen so aus"
   _types:
     all: "Alle"
+    note: "Neue Notizen"
     follow: "Neue Follower"
     mention: "Erwähnungen"
     reply: "Antworten"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 527c68d839..5d9ec0370e 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -710,6 +710,7 @@ lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", yo
 alwaysMarkSensitive: "Mark as sensitive by default"
 loadRawImages: "Load original images instead of showing thumbnails"
 disableShowingAnimatedImages: "Don't play animated images"
+highlightSensitiveMedia: "Highlight sensitive media"
 verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification."
 notSet: "Not set"
 emailVerified: "Email has been verified"
@@ -913,7 +914,7 @@ typeToConfirm: "Please enter {x} to confirm"
 deleteAccount: "Delete account"
 document: "Documentation"
 numberOfPageCache: "Number of cached pages"
-numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
+numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device."
 logoutConfirm: "Really log out?"
 lastActiveDate: "Last used at"
 statusbar: "Status bar"
@@ -1116,6 +1117,8 @@ keepScreenOn: "Keep screen on"
 verifiedLink: "Link ownership has been verified"
 notifyNotes: "Notify about new notes"
 unnotifyNotes: "Stop notifying about new notes"
+authentication: "Authentication"
+authenticationRequiredToContinue: "Please authenticate to continue"
 _announcement:
   forExistingUsers: "Existing users only"
   forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
@@ -1149,6 +1152,8 @@ _serverSettings:
   appIconStyleRecommendation: "As the icon may be cropped to a square or circle, an icon with colored margin around the content is recommended."
   appIconResolutionMustBe: "The minimum resolution is {resolution}."
   manifestJsonOverride: "manifest.json Override"
+  shortName: "Short name"
+  shortNameDescription: "A shorthand for the instance's name that can be displayed if the full official name is long."
 _accountMigration:
   moveFrom: "Migrate another account to this one"
   moveFromSub: "Create alias to another account"
@@ -1529,6 +1534,7 @@ _plugin:
   install: "Install plugins"
   installWarn: "Please do not install untrustworthy plugins."
   manage: "Manage plugins"
+  viewSource: "View source"
 _preferencesBackups:
   list: "Created backups"
   saveNew: "Save new backup"
@@ -1794,6 +1800,7 @@ _antennaSources:
   homeTimeline: "Notes from followed users"
   users: "Notes from specific users"
   userList: "Notes from a specified list of users"
+  userBlacklist: "All notes except for those of one or more specified users"
 _weekday:
   sunday: "Sunday"
   monday: "Monday"
@@ -2022,6 +2029,7 @@ _notification:
   notificationWillBeDisplayedLikeThis: "Notifications look like this"
   _types:
     all: "All"
+    note: "New notes"
     follow: "New followers"
     mention: "Mentions"
     reply: "Replies"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index bfa779d78a..c6bb2c10de 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1518,6 +1518,7 @@ _plugin:
   install: "Instalar plugins"
   installWarn: "Por favor no instale plugins que no son de confianza"
   manage: "Gestionar plugins"
+  viewSource: "Ver la fuente"
 _preferencesBackups:
   list: "Respaldos creados"
   saveNew: "Guardar nuevo respaldo"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 64a59522f2..71446667d6 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -272,6 +272,7 @@ startMessaging: "Commencer à discuter"
 nUsersRead: "Lu par {n} personnes"
 agreeTo: "J’accepte {0}"
 agree: "Accepter"
+agreeBelow: "J’accepte ce qui suit"
 basicNotesBeforeCreateAccount: "Notes importantes"
 termsOfService: "Conditions d'utilisation"
 start: "Commencer"
@@ -406,6 +407,7 @@ aboutMisskey: "À propos de Misskey"
 administrator: "Administrateur"
 token: "Jeton"
 2fa: "Authentification à deux facteurs"
+setupOf2fa: "Configuration de l’authentification à deux facteurs"
 totp: "Application d'authentification"
 totpDescription: "Entrez un mot de passe à usage unique à l'aide d'une application d'authentification"
 moderator: "Modérateur·rice·s"
@@ -413,6 +415,7 @@ moderation: "Modérations"
 moderationNote: "Note de modération"
 addModerationNote: "Ajouter une note de modération"
 nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
+securityKeyAndPasskey: "Sécurité et clés de sécurité"
 securityKey: "Clé de sécurité"
 lastUsed: "Dernier utilisé"
 lastUsedAt: "Dernière utilisation : {t}"
@@ -797,6 +800,7 @@ popularPosts: "Les plus consultées"
 shareWithNote: "Partager dans une note"
 ads: "Publicité"
 expiration: "Échéance"
+startingperiod: "Commencer"
 memo: "Pense-bête"
 priority: "Priorité"
 high: "Haute"
@@ -958,6 +962,7 @@ internalServerError: "Erreur interne du serveur"
 copyErrorInfo: "Copier les détails de l’erreur"
 exploreOtherServers: "Trouver une autre instance"
 disableFederationOk: "Désactiver"
+likeOnly: "Les favoris uniquement"
 license: "Licence"
 video: "Vidéo"
 videos: "Vidéos"
@@ -978,6 +983,7 @@ horizontal: "Latéral"
 serverRules: "Règles du serveur"
 archive: "Archive"
 youFollowing: "Abonné·e"
+options: "Options"
 later: "Plus tard"
 goToMisskey: "Retour vers Misskey"
 expirationDate: "Date d’expiration"
@@ -990,12 +996,24 @@ icon: "Avatar"
 forYou: "Pour vous"
 replies: "Répondre"
 renotes: "Renoter"
+loadReplies: "Inclure les réponses"
+pinnedList: "Liste épinglée"
+notifyNotes: "Notifier à propos des nouvelles notes"
+authentication: "Authentification"
+authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer"
 _announcement:
   readConfirmTitle: "Marquer comme lu ?"
 _initialAccountSetting:
   profileSetting: "Paramètres du profil"
   privacySetting: "Paramètres de confidentialité"
+  initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
+  ifYouNeedLearnMore: "Si vous voulez en savoir plus comment utiliser {name}(Misskey), veuillez visiter {link}."
+  skipAreYouSure: "Désirez-vous ignorer la configuration du profile ?"
+_serverSettings:
+  iconUrl: "URL de l’icône"
 _accountMigration:
+  moveFrom: "Migrer un autre compte vers le présent compte"
+  moveFromSub: "Créer un alias vers un autre compte"
   moveToLabel: "Compte vers lequel vous migrez :"
   startMigration: "Migrer"
   movedTo: "Compte vers lequel vous migrez :"
@@ -1052,20 +1070,33 @@ _achievements:
     _login1000:
       flavor: "Merci d'utiliser Misskey !"
     _profileFilled:
+      title: "Bien préparé"
       description: "Configuration de votre profil"
     _markedAsCat:
       title: "Je suis un chat"
+      description: "Rendre votre compte comme un chat"
       flavor: "Je n'ai pas encore de nom"
+    _following1:
+      title: "Vous suivez votre premier utilisateur·rice"
     _following50:
       title: "Beaucoup d'amis"
     _followers10:
       title: "Abonnez-moi !"
+    _followers100:
+      title: "Populaire"
+    _followers500:
+      title: "Tour radio"
+    _followers1000:
+      title: "Influenceur·euse"
     _iLoveMisskey:
       title: "J’adore Misskey"
       description: "Publication « J’❤ #Misskey »"
+      flavor: "L'équipe de développement de Misskey apprécie vraiment votre aide !"
     _foundTreasure:
       title: "Chasse au trésor"
       description: "Vous avez trouvé le trésor caché"
+    _client30min:
+      title: "Pause bien méritée"
     _postedAtLateNight:
       flavor: "C’est l’heure d’aller au lit."
     _postedAt0min0sec:
@@ -1074,18 +1105,45 @@ _achievements:
       flavor: "Tic tac, tic tac, tic tac, ding !"
     _viewInstanceChart:
       title: "Analyste"
+    _outputHelloWorldOnScratchpad:
+      title: "Bonjour tout le monde !"
+    _open3windows:
+      title: "Multi-fenêtres"
+    _driveFolderCircularReference:
+      title: "Référence circulaire"
+    _setNameToSyuilo:
+      description: "Vous avez spécifié « syuilo » comme nom"
+    _passedSinceAccountCreated1:
+      title: "Premier anniversaire"
+    _passedSinceAccountCreated2:
+      title: "Second anniversaire"
+    _passedSinceAccountCreated3:
+      title: "3ème anniversaire"
     _loggedInOnBirthday:
       title: "Joyeux Anniversaire !"
+      description: "Vous vous êtes connecté à la date de votre anniversaire"
     _loggedInOnNewYearsDay:
       title: "Bonne année !"
     _cookieClicked:
       flavor: "Attendez une minute, vous êtes sur le mauvais site web ?"
+    _brainDiver:
+      flavor: "Misskey-Misskey La-Tu-Ma"
 _role:
+  new: "Nouveau rôle"
+  edit: "Modifier le rôle"
   name: "Nom du rôle"
   description: "Description du rôle"
   permission: "Rôle et autorisations"
   assignTarget: "Attribuer"
   condition: "Condition"
+  isPublic: "Rôle public"
+  options: "Options"
+  policies: "Stratégies"
+  baseRole: "Modèle de rôle"
+  useBaseValue: "Utiliser la valeur du modèle de rôle"
+  chooseRoleToAssign: "Sélectionner le rôle à assigner"
+  iconUrl: "URL de l’icône"
+  displayOrder: "Classement"
   priority: "Priorité"
   _priority:
     low: "Basse"
@@ -1144,6 +1202,7 @@ _plugin:
   install: "Installation de plugin"
   installWarn: "N’installez que des extensions provenant de sources de confiance."
   manage: "Gestion des plugins"
+  viewSource: "Afficher la source"
 _preferencesBackups:
   list: "Sauvegardes créées"
   saveNew: "Nouvelle sauvegarde"
@@ -1330,6 +1389,7 @@ _2fa:
   securityKeyNotSupported: "Votre navigateur ne prend pas en charge les clés de sécurité."
   securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion grâce à une clé de sécurité matérielle qui prend en charge FIDO2, ou bien en configurant l'authentification par empreinte digitale ou par code PIN sur votre appareil."
   securityKeyName: "Nom de la clé"
+  removeKey: "Supprimer la clé de sécurité"
   removeKeyConfirm: "Voulez-vous supprimer {name} ?"
   renewTOTPOk: "Reconfigurer"
   renewTOTPCancel: "Pas maintenant"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index e39b49774d..75baebaae4 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -1496,6 +1496,7 @@ _plugin:
   install: "Memasang plugin"
   installWarn: "Mohon jangan memasang plugin yang tidak dapat dipercayai."
   manage: "Manajemen plugin"
+  viewSource: "Lihat sumber"
 _preferencesBackups:
   list: "Cadangan yang dibuat"
   saveNew: "Simpan cadangan baru"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 9810e6015a..a69e75a374 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -1529,6 +1529,7 @@ _plugin:
   install: "Installa estensioni"
   installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili."
   manage: "Gestisci estensioni"
+  viewSource: "Visualizza sorgente"
 _preferencesBackups:
   list: "Elenco di impostazioni salvate in precedenza"
   saveNew: "Nuovo salvataggio"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index d5d414ea77..2c1f718192 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -1510,6 +1510,7 @@ _plugin:
   install: "プラグインのインストール"
   installWarn: "信頼できへんプラグインはインストールせんとってな"
   manage: "プラグインの管理"
+  viewSource: "ソースを表示"
 _preferencesBackups:
   list: "作ったバックアップ"
   saveNew: "新しく保存"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 9e405396ba..48aaa2016b 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -1512,6 +1512,7 @@ _plugin:
   install: "플러그인 설치"
   installWarn: "신뢰할 수 없는 플러그인은 설치하지 않는 것이 좋습니다."
   manage: "플러그인 관리"
+  viewSource: "소스 보기"
 _preferencesBackups:
   list: "생성한 백업"
   saveNew: "새 백업 만들기"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 065e228c0c..1e9d61706b 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -925,6 +925,7 @@ _plugin:
   install: "Zainstaluj wtyczki"
   installWarn: "Nie instaluj niezaufanych wtyczek."
   manage: "Zarządzanie wtyczkami"
+  viewSource: "Zobacz źródło"
 _preferencesBackups:
   list: "Utworzone kopie zapasowe"
   saveNew: "Zapisz nową kopię zapasową"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index edf531dfcc..0bd5db9268 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1427,6 +1427,7 @@ _plugin:
   install: "Установка расширений"
   installWarn: "Пожалуйста, не устанавливайте расширения, которым не доверяете."
   manage: "Управление расширениями"
+  viewSource: "Просмотр исходника"
 _preferencesBackups:
   list: "Существующие резервные копии"
   saveNew: "Создать резервную копию"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index a5639ce59b..8d2af55db9 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -978,6 +978,7 @@ _plugin:
   install: "Inštalova pluginy"
   installWarn: "Prosím neinštalujte nedôveryhodné pluginy."
   manage: "Spravovanie pluginov"
+  viewSource: "Ukázať zdroj"
 _preferencesBackups:
   list: "Vytvorené zálohy"
   saveNew: "Uložiť novú"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index f9262fea7e..cf2f3b9a23 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -1509,6 +1509,7 @@ _plugin:
   install: "ติดตั้งปลั๊กอิน"
   installWarn: "กรุณาอย่าติดตั้งปลั๊กอินที่ไม่น่าเชื่อถือนะคะ"
   manage: "จัดการปลั๊กอิน"
+  viewSource: "ดูต้นฉบับ"
 _preferencesBackups:
   list: "สร้างการสำรองข้อมูล"
   saveNew: "บันทึกใหม่"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 777933bf53..1516272c60 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -1180,6 +1180,7 @@ _plugin:
   install: "Встановити плагін"
   installWarn: "Будь ласка, не встановлюйте плагінів, яким ви не довіряєте."
   manage: "Керування плагінами"
+  viewSource: "Переглянути вихідний код"
 _preferencesBackups:
   list: "Створені бекапи"
   saveNew: "Зберегти як новий"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index dec9e7f888..40688e98f3 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1343,6 +1343,7 @@ _plugin:
   install: "Cài đặt tiện ích"
   installWarn: "Vui lòng không cài đặt những tiện ích đáng ngờ."
   manage: "Quản lý plugin"
+  viewSource: "Xem mã nguồn"
 _preferencesBackups:
   list: "Tạo sao lưu"
   saveNew: "Lưu bản sao lưu"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 3026682890..f1512ac348 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -710,6 +710,7 @@ lockedAccountInfo: "即使启用该功能,只要您不将帖子可见范围设
 alwaysMarkSensitive: "默认将媒体文件标记为敏感内容"
 loadRawImages: "添加附件图像的缩略图时使用原始图像质量"
 disableShowingAnimatedImages: "不播放动画"
+highlightSensitiveMedia: "高亮显示敏感媒体"
 verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。"
 notSet: "未设置"
 emailVerified: "电子邮件地址已验证"
@@ -1116,6 +1117,8 @@ keepScreenOn: "保持设备屏幕开启"
 verifiedLink: "已验证的链接"
 notifyNotes: "打开发帖通知"
 unnotifyNotes: "关闭发帖通知"
+authentication: "验证"
+authenticationRequiredToContinue: "要继续,请先进行验证"
 _announcement:
   forExistingUsers: "仅限现有用户"
   forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。"
@@ -1529,6 +1532,7 @@ _plugin:
   install: "安装插件"
   installWarn: "请不要安装不可信的插件。"
   manage: "管理插件..."
+  viewSource: "查看源代码"
 _preferencesBackups:
   list: "已创建的备份"
   saveNew: "另存为"
@@ -1794,6 +1798,7 @@ _antennaSources:
   homeTimeline: "已关注用户的帖子"
   users: "来自指定用户的帖子"
   userList: "来自指定列表中的帖子"
+  userBlacklist: "除掉已选择用户后所有的帖子"
 _weekday:
   sunday: "星期日"
   monday: "星期一"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index a61e3b242b..a031730a37 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -321,7 +321,7 @@ copyUrl: "複製URL"
 rename: "重新命名"
 avatar: "大頭貼"
 banner: "橫幅"
-displayOfSensitiveMedia: "顯示敏感媒體"
+displayOfSensitiveMedia: "敏感檔案的顯示"
 whenServerDisconnected: "與伺服器的連接中斷時"
 disconnectedFromServer: "與伺服器中斷連線"
 reload: "重新整理"
@@ -490,7 +490,7 @@ createAccount: "建立帳戶"
 existingAccount: "現有帳戶"
 regenerate: "再次生成"
 fontSize: "字體大小"
-mediaListWithOneImageAppearance: "只有一張圖片時的媒體列表高度"
+mediaListWithOneImageAppearance: "只有一張圖片時的檔案列表高度"
 limitTo: "上限為 {x}"
 noFollowRequests: "沒有追隨您的請求"
 openImageInNewTab: "於新分頁中開啟圖片"
@@ -707,9 +707,10 @@ driveUsage: "雲端硬碟使用量"
 noCrawle: "拒絕搜尋引擎索引"
 noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。"
 lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
-alwaysMarkSensitive: "預設將多媒體標記為敏感內容"
+alwaysMarkSensitive: "預設標記檔案為敏感內容"
 loadRawImages: "以原始圖檔顯示附件圖檔的縮圖"
 disableShowingAnimatedImages: "不播放動態圖檔"
+highlightSensitiveMedia: "強調敏感標記"
 verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的鏈接完成驗證。"
 notSet: "未設定"
 emailVerified: "已成功驗證您的電郵"
@@ -926,7 +927,7 @@ type: "類型"
 speed: "速度"
 slow: "慢"
 fast: "快"
-sensitiveMediaDetection: "敏感性媒體的檢測"
+sensitiveMediaDetection: "敏感檔案的檢測"
 localOnly: "僅限本地"
 remoteOnly: "僅限遠端"
 failedToUpload: "上傳失敗"
@@ -935,7 +936,7 @@ cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此
 cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制,無法上傳。"
 beta: "測試版"
 enableAutoSensitive: "自動 NSFW 判定"
-enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷多媒體內容是否需要標記 NSFW。即使關閉此功能,也可能會依實例規則而自動啟用。"
+enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。"
 activeEmailValidationDescription: "積極驗證使用者的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。"
 navbar: "導覽列"
 shuffle: "隨機"
@@ -1116,6 +1117,8 @@ keepScreenOn: "保持設備螢幕開啟"
 verifiedLink: "已驗證連結"
 notifyNotes: "開啟貼文通知"
 unnotifyNotes: "關閉貼文通知"
+authentication: "驗證"
+authenticationRequiredToContinue: "請於繼續前完成驗證"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@@ -1149,6 +1152,8 @@ _serverSettings:
   appIconStyleRecommendation: "因為可能會裁剪成圓形或圓角,所以建議用單色填滿邊框及背景。"
   appIconResolutionMustBe: "解析度必須為 {resolution}。"
   manifestJsonOverride: "覆寫 manifest.json"
+  shortName: "簡稱"
+  shortNameDescription: "如果伺服器的正式名稱很長,可用簡稱或通稱代替。"
 _accountMigration:
   moveFrom: "從其他帳戶遷移到這個帳戶"
   moveFromSub: "為另一個帳戶建立別名"
@@ -1478,7 +1483,7 @@ _role:
     or: "~或~"
     not: "~否"
 _sensitiveMediaDetection:
-  description: "您可以使用機器學習自動檢測敏感媒體並將其用於審查。 伺服器的負荷會稍微增加。"
+  description: "您可以使用機器學習自動檢測敏感檔案以便審查。這會稍微增加伺服器負荷。"
   sensitivity: "檢測敏感度"
   sensitivityDescription: "敏感度低時,誤檢測(偽陽性)會減少。敏感度高時,漏檢(偽陰性)會減少。"
   setSensitiveFlagAutomatically: "設定 NSFW 標籤"
@@ -1529,6 +1534,7 @@ _plugin:
   install: "安裝外掛組件"
   installWarn: "請不要安裝來源不明的外掛。"
   manage: "管理外掛"
+  viewSource: "檢視原始碼"
 _preferencesBackups:
   list: "已備份的設定檔"
   saveNew: "另存新檔"
@@ -1563,9 +1569,9 @@ _aboutMisskey:
   morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰"
   patrons: "贊助者"
 _displayOfSensitiveMedia:
-  respect: "隱藏被標記為敏感的多媒體內容"
-  ignore: "不隱藏被標記為敏感的多媒體內容"
-  force: "隱藏所有多媒體內容"
+  respect: "隱藏敏感檔案"
+  ignore: "顯示敏感檔案"
+  force: "隱藏所有檔案"
 _instanceTicker:
   none: "隱藏"
   remote: "向遠端使用者顯示"
@@ -1794,6 +1800,7 @@ _antennaSources:
   homeTimeline: "來自已追隨使用者的貼文"
   users: "來自特定使用者的貼文"
   userList: "來自特定清單中的貼文"
+  userBlacklist: "除指定使用者外的所有貼文"
 _weekday:
   sunday: "週日"
   monday: "週一"
@@ -2022,6 +2029,7 @@ _notification:
   notificationWillBeDisplayedLikeThis: "通知會以這樣的方式顯示"
   _types:
     all: "全部 "
+    note: "使用者的最新貼文"
     follow: "追隨中"
     mention: "提及"
     reply: "回覆"

From 7893da4d99c782bab33ac90e71b5fda4dbe0f243 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 18:47:40 +0900
Subject: [PATCH 25/62] 2023.9.0-rc.2

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 2022abc711..8e5540f68d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-rc.1",
+	"version": "2023.9.0-rc.2",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From c3ccec723fc562354a5b1bb0df21ac697011de45 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 20:34:45 +0900
Subject: [PATCH 26/62] .js

---
 packages/frontend/src/components/MkMenu.child.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkMenu.child.vue b/packages/frontend/src/components/MkMenu.child.vue
index cf955752f3..962dcd91eb 100644
--- a/packages/frontend/src/components/MkMenu.child.vue
+++ b/packages/frontend/src/components/MkMenu.child.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
 import MkMenu from './MkMenu.vue';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
 
 const props = defineProps<{
 	items: MenuItem[];

From 76c4fedb7f6076e9e379ef3ac801c5b7d8fa464e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 20:35:05 +0900
Subject: [PATCH 27/62] =?UTF-8?q?fix(frontend):=20=E5=AD=90=E3=83=A1?=
 =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AE=E8=A1=A8=E7=A4=BA=E4=BD=8D?=
 =?UTF-8?q?=E7=BD=AE=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkMenu.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index b48f0e7651..079f07ee47 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -40,9 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<span :class="$style.switchText">{{ item.text }}</span>
 			</button>
 			<button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)">
-				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
-				<span>{{ item.text }}</span>
-				<span :class="$style.caret"><i class="ti ti-chevron-right ti-fw"></i></span>
+				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i>
+				<span style="pointer-events: none;">{{ item.text }}</span>
+				<span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span>
 			</button>
 			<button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>

From fdf149cf52aa05ca2d716c39b2e0c9bf6315a318 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 20:38:49 +0900
Subject: [PATCH 28/62] Update CHANGELOG.md

---
 CHANGELOG.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 62810ebf44..0c85e850a9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -80,6 +80,9 @@
 - Fix: 他のサーバーのユーザーへ「メッセージを送信」した時の初期テキストのメンションが間違っている問題を修正
 - Fix: 環境によってはMisskey Webが開けない問題を修正
 - Fix: プラグインの権限リストが見れない問題を修正
+- Fix: 複数の階層があるメニューで、短くタップすると正常に動かない場合がある問題を修正
+- Fix: アニメーションがオフのとき、スマホで子メニューの選択ができない問題を修正
+- Fix: ドロワーメニューで、親メニュー項目をマウスでホバーすると子メニューが表示されてしまう問題を修正
 
 ### Server
 - Change: cacheRemoteFilesの初期値はfalseになりました

From 19bc9c20a651be62286573377348f4c61ae96587 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Sep 2023 20:50:02 +0900
Subject: [PATCH 29/62] improve moderation log

---
 locales/ja-JP.yml                              |  3 +++
 .../admin/federation/update-instance.ts        | 18 +++++++++++++++++-
 .../api/endpoints/admin/reset-password.ts      |  9 ++++++++-
 packages/backend/src/types.ts                  | 14 ++++++++++++++
 packages/misskey-js/etc/misskey-js.api.md      | 11 ++++++++++-
 packages/misskey-js/src/consts.ts              | 14 ++++++++++++++
 packages/misskey-js/src/entities.ts            |  9 +++++++++
 7 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b396014ee2..47bbb0aa55 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2175,3 +2175,6 @@ _moderationLogTypes:
   deleteNote: "ノートを削除"
   createGlobalAnnouncement: "全体のお知らせを作成"
   createUserAnnouncement: "ユーザーへお知らせを作成"
+  resetPassword: "パスワードをリセット"
+  suspendRemoteInstance: "リモートサーバーを停止"
+  unsuspendRemoteInstance: "リモートサーバーを再開"
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index fbb91837f2..357bf83e87 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -9,6 +9,7 @@ import type { InstancesRepository } from '@/models/_.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { DI } from '@/di-symbols.js';
 import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -34,6 +35,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		private utilityService: UtilityService,
 		private federatedInstanceService: FederatedInstanceService,
+		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) });
@@ -42,9 +44,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new Error('instance not found');
 			}
 
-			this.federatedInstanceService.update(instance.id, {
+			await this.federatedInstanceService.update(instance.id, {
 				isSuspended: ps.isSuspended,
 			});
+
+			if (instance.isSuspended !== ps.isSuspended) {
+				if (ps.isSuspended) {
+					this.moderationLogService.log(me, 'suspendRemoteInstance', {
+						id: instance.id,
+						host: instance.host,
+					});
+				} else {
+					this.moderationLogService.log(me, 'unsuspendRemoteInstance', {
+						id: instance.id,
+						host: instance.host,
+					});
+				}
+			}
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
index 0dd4fb4126..6ce7583276 100644
--- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
@@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { UsersRepository, UserProfilesRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -46,8 +47,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		@Inject(DI.userProfilesRepository)
 		private userProfilesRepository: UserProfilesRepository,
+
+		private moderationLogService: ModerationLogService,
 	) {
-		super(meta, paramDef, async (ps) => {
+		super(meta, paramDef, async (ps, me) => {
 			const user = await this.usersRepository.findOneBy({ id: ps.userId });
 
 			if (user == null) {
@@ -69,6 +72,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				password: hash,
 			});
 
+			this.moderationLogService.log(me, 'resetPassword', {
+				targetId: user.id,
+			});
+
 			return {
 				password: passwd,
 			};
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 7946e66b82..0a53e77b79 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -43,6 +43,9 @@ export const moderationLogTypes = [
 	'deleteNote',
 	'createGlobalAnnouncement',
 	'createUserAnnouncement',
+	'resetPassword',
+	'suspendRemoteInstance',
+	'unsuspendRemoteInstance',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -104,4 +107,15 @@ export type ModerationLogPayloads = {
 		announcement: any;
 		userId: string;
 	};
+	resetPassword: {
+		targetId: string;
+	};
+	suspendRemoteInstance: {
+		id: string;
+		host: string;
+	};
+	unsuspendRemoteInstance: {
+		id: string;
+		host: string;
+	};
 };
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 804733c066..adedea8755 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2556,10 +2556,19 @@ type ModerationLog = {
 } | {
     type: 'promoteQueue';
     info: ModerationLogPayloads['promoteQueue'];
+} | {
+    type: 'resetPassword';
+    info: ModerationLogPayloads['resetPassword'];
+} | {
+    type: 'suspendRemoteInstance';
+    info: ModerationLogPayloads['suspendRemoteInstance'];
+} | {
+    type: 'unsuspendRemoteInstance';
+    info: ModerationLogPayloads['unsuspendRemoteInstance'];
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 346affc6a5..a8962ab3d3 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -61,6 +61,9 @@ export const moderationLogTypes = [
 	'deleteNote',
 	'createGlobalAnnouncement',
 	'createUserAnnouncement',
+	'resetPassword',
+	'suspendRemoteInstance',
+	'unsuspendRemoteInstance',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -122,4 +125,15 @@ export type ModerationLogPayloads = {
 		announcement: any;
 		userId: string;
 	};
+	resetPassword: {
+		targetId: string;
+	};
+	suspendRemoteInstance: {
+		id: string;
+		host: string;
+	};
+	unsuspendRemoteInstance: {
+		id: string;
+		host: string;
+	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 0e6604cbaa..a1fc8befb4 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -607,4 +607,13 @@ export type ModerationLog = {
 } | {
 	type: 'promoteQueue';
 	info: ModerationLogPayloads['promoteQueue'];
+} | {
+	type: 'resetPassword';
+	info: ModerationLogPayloads['resetPassword'];
+} | {
+	type: 'suspendRemoteInstance';
+	info: ModerationLogPayloads['suspendRemoteInstance'];
+} | {
+	type: 'unsuspendRemoteInstance';
+	info: ModerationLogPayloads['unsuspendRemoteInstance'];
 });

From ed53b5f9bc1612caa165f50db1058d002364dd0f Mon Sep 17 00:00:00 2001
From: taichan <40626578+taichanNE30@users.noreply.github.com>
Date: Sun, 24 Sep 2023 07:54:58 +0900
Subject: [PATCH 30/62] =?UTF-8?q?fix:=20=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=81=A7=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C?=
 =?UTF-8?q?=E4=BD=BF=E3=82=8F=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E5=90=8D?=
 =?UTF-8?q?=E5=89=8D=E3=81=8C=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=A7=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=20#11877=20(#1187?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkNotification.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 99443a6409..7ba102fd97 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div :class="$style.tail">
 		<header :class="$style.header">
 			<span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span>
-			<span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: {{ notification.note.user.name ?? notification.note.user.username }}</span>
+			<span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: <MkUserName :user="notification.note.user"/></span>
 			<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
 			<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
 			<MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>

From 2ad3b1fd74f20698be13a4beb5ee4b818ea0f090 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 10:33:30 +0900
Subject: [PATCH 31/62] improve moderation log

---
 locales/index.d.ts                                  |  3 +++
 packages/backend/src/core/RoleService.ts            | 13 +++++++++++++
 .../src/server/api/endpoints/admin/roles/delete.ts  | 11 ++++-------
 packages/backend/src/types.ts                       |  2 +-
 packages/misskey-js/src/consts.ts                   |  2 +-
 5 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index fd99f10b69..1e5396ad63 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2262,6 +2262,9 @@ export interface Locale {
         "deleteNote": string;
         "createGlobalAnnouncement": string;
         "createUserAnnouncement": string;
+        "resetPassword": string;
+        "suspendRemoteInstance": string;
+        "unsuspendRemoteInstance": string;
     };
 }
 declare const locales: {
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 39f21ecec4..dea6dc68cd 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -493,6 +493,19 @@ export class RoleService implements OnApplicationShutdown {
 		}
 	}
 
+	@bindThis
+	public async delete(role: MiRole, moderator?: MiUser): Promise<void> {
+		await this.rolesRepository.delete({ id: role.id });
+		this.globalEventService.publishInternalEvent('roleDeleted', role);
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'deleteRole', {
+				roleId: role.id,
+				role: role,
+			});
+		}
+	}
+
 	@bindThis
 	public dispose(): void {
 		this.redisForSub.off('message', this.onMessage);
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
index 6e012f6428..7b989050eb 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
@@ -6,9 +6,9 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { RolesRepository } from '@/models/_.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '@/server/api/error.js';
+import { RoleService } from '@/core/RoleService.js';
 
 export const meta = {
 	tags: ['admin', 'role'],
@@ -41,17 +41,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.rolesRepository)
 		private rolesRepository: RolesRepository,
 
-		private globalEventService: GlobalEventService,
+		private roleService: RoleService,
 	) {
-		super(meta, paramDef, async (ps) => {
+		super(meta, paramDef, async (ps, me) => {
 			const role = await this.rolesRepository.findOneBy({ id: ps.roleId });
 			if (role == null) {
 				throw new ApiError(meta.errors.noSuchRole);
 			}
-			await this.rolesRepository.delete({
-				id: ps.roleId,
-			});
-			this.globalEventService.publishInternalEvent('roleDeleted', role);
+			await this.roleService.delete(role, me);
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 0a53e77b79..a58bb9585a 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -85,7 +85,7 @@ export type ModerationLogPayloads = {
 	};
 	deleteRole: {
 		roleId: string;
-		roleName: string;
+		role: any;
 	};
 	clearQueue: Record<string, never>;
 	promoteQueue: Record<string, never>;
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index a8962ab3d3..a5c8c2ba00 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -103,7 +103,7 @@ export type ModerationLogPayloads = {
 	};
 	deleteRole: {
 		roleId: string;
-		roleName: string;
+		role: any;
 	};
 	clearQueue: Record<string, never>;
 	promoteQueue: Record<string, never>;

From ed983a5baf581b28b063edfa25e0d80349e9318e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 10:46:09 +0900
Subject: [PATCH 32/62] improve moderation log

---
 locales/index.d.ts                            |  4 +
 locales/ja-JP.yml                             |  4 +
 .../backend/src/core/AnnouncementService.ts   | 77 ++++++++++++++++---
 .../endpoints/admin/announcements/delete.ts   |  5 +-
 .../endpoints/admin/announcements/update.ts   |  7 +-
 packages/backend/src/types.ts                 | 22 ++++++
 packages/misskey-js/etc/misskey-js.api.md     | 26 ++++++-
 packages/misskey-js/src/consts.ts             | 22 ++++++
 packages/misskey-js/src/entities.ts           | 24 ++++++
 9 files changed, 177 insertions(+), 14 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 1e5396ad63..c2f50dd54a 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2262,6 +2262,10 @@ export interface Locale {
         "deleteNote": string;
         "createGlobalAnnouncement": string;
         "createUserAnnouncement": string;
+        "updateGlobalAnnouncement": string;
+        "updateUserAnnouncement": string;
+        "deleteGlobalAnnouncement": string;
+        "deleteUserAnnouncement": string;
         "resetPassword": string;
         "suspendRemoteInstance": string;
         "unsuspendRemoteInstance": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 47bbb0aa55..43a3394264 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2175,6 +2175,10 @@ _moderationLogTypes:
   deleteNote: "ノートを削除"
   createGlobalAnnouncement: "全体のお知らせを作成"
   createUserAnnouncement: "ユーザーへお知らせを作成"
+  updateGlobalAnnouncement: "全体のお知らせを更新"
+  updateUserAnnouncement: "ユーザーのお知らせを更新"
+  deleteGlobalAnnouncement: "全体のお知らせを削除"
+  deleteUserAnnouncement: "ユーザーのお知らせを削除"
   resetPassword: "パスワードをリセット"
   suspendRemoteInstance: "リモートサーバーを停止"
   unsuspendRemoteInstance: "リモートサーバーを再開"
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index 31fcb139ea..2b4877788a 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -60,7 +60,7 @@ export class AnnouncementService {
 	}
 
 	@bindThis
-	public async create(values: Partial<MiAnnouncement>, moderator: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
+	public async create(values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
 		const announcement = await this.announcementsRepository.insert({
 			id: this.idService.genId(),
 			createdAt: new Date(),
@@ -82,20 +82,24 @@ export class AnnouncementService {
 				announcement: packed,
 			});
 
-			this.moderationLogService.log(moderator, 'createUserAnnouncement', {
-				announcementId: announcement.id,
-				announcement: announcement,
-				userId: values.userId,
-			});
+			if (moderator) {
+				this.moderationLogService.log(moderator, 'createUserAnnouncement', {
+					announcementId: announcement.id,
+					announcement: announcement,
+					userId: values.userId,
+				});
+			}
 		} else {
 			this.globalEventService.publishBroadcastStream('announcementCreated', {
 				announcement: packed,
 			});
 
-			this.moderationLogService.log(moderator, 'createGlobalAnnouncement', {
-				announcementId: announcement.id,
-				announcement: announcement,
-			});
+			if (moderator) {
+				this.moderationLogService.log(moderator, 'createGlobalAnnouncement', {
+					announcementId: announcement.id,
+					announcement: announcement,
+				});
+			}
 		}
 
 		return {
@@ -104,6 +108,59 @@ export class AnnouncementService {
 		};
 	}
 
+	@bindThis
+	public async update(announcement: MiAnnouncement, values: Partial<MiAnnouncement>, moderator?: MiUser): Promise<void> {
+		await this.announcementsRepository.update(announcement.id, {
+			updatedAt: new Date(),
+			title: values.title,
+			text: values.text,
+			/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */
+			imageUrl: values.imageUrl || null,
+			display: values.display,
+			icon: values.icon,
+			forExistingUsers: values.forExistingUsers,
+			needConfirmationToRead: values.needConfirmationToRead,
+			isActive: values.isActive,
+		});
+
+		const after = await this.announcementsRepository.findOneByOrFail({ id: announcement.id });
+
+		if (moderator) {
+			if (announcement.userId) {
+				this.moderationLogService.log(moderator, 'updateUserAnnouncement', {
+					announcementId: announcement.id,
+					before: announcement,
+					after: after,
+				});
+			} else {
+				this.moderationLogService.log(moderator, 'updateGlobalAnnouncement', {
+					announcementId: announcement.id,
+					before: announcement,
+					after: after,
+				});
+			}
+		}
+	}
+
+	@bindThis
+	public async delete(announcement: MiAnnouncement, moderator?: MiUser): Promise<void> {
+		await this.announcementsRepository.delete(announcement.id);
+
+		if (moderator) {
+			if (announcement.userId) {
+				this.moderationLogService.log(moderator, 'deleteUserAnnouncement', {
+					announcementId: announcement.id,
+					announcement: announcement,
+				});
+			} else {
+				this.moderationLogService.log(moderator, 'deleteGlobalAnnouncement', {
+					announcementId: announcement.id,
+					announcement: announcement,
+				});
+			}
+		}
+	}
+
 	@bindThis
 	public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
 		try {
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
index 80eb6d7a80..80ec281253 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { AnnouncementsRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
+import { AnnouncementService } from '@/core/AnnouncementService.js';
 import { ApiError } from '../../../error.js';
 
 export const meta = {
@@ -37,13 +38,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.announcementsRepository)
 		private announcementsRepository: AnnouncementsRepository,
+
+		private announcementService: AnnouncementService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const announcement = await this.announcementsRepository.findOneBy({ id: ps.id });
 
 			if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
 
-			await this.announcementsRepository.delete(announcement.id);
+			await this.announcementService.delete(announcement, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
index 782928048b..d36590c264 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { AnnouncementsRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
+import { AnnouncementService } from '@/core/AnnouncementService.js';
 import { ApiError } from '../../../error.js';
 
 export const meta = {
@@ -45,13 +46,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.announcementsRepository)
 		private announcementsRepository: AnnouncementsRepository,
+
+		private announcementService: AnnouncementService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const announcement = await this.announcementsRepository.findOneBy({ id: ps.id });
 
 			if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement);
 
-			await this.announcementsRepository.update(announcement.id, {
+			await this.announcementService.update(announcement, {
 				updatedAt: new Date(),
 				title: ps.title,
 				text: ps.text,
@@ -62,7 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				forExistingUsers: ps.forExistingUsers,
 				needConfirmationToRead: ps.needConfirmationToRead,
 				isActive: ps.isActive,
-			});
+			}, me);
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index a58bb9585a..b85388d6e4 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -43,6 +43,10 @@ export const moderationLogTypes = [
 	'deleteNote',
 	'createGlobalAnnouncement',
 	'createUserAnnouncement',
+	'updateGlobalAnnouncement',
+	'updateUserAnnouncement',
+	'deleteGlobalAnnouncement',
+	'deleteUserAnnouncement',
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
@@ -107,6 +111,24 @@ export type ModerationLogPayloads = {
 		announcement: any;
 		userId: string;
 	};
+	updateGlobalAnnouncement: {
+		announcementId: string;
+		before: any;
+		after: any;
+	};
+	updateUserAnnouncement: {
+		announcementId: string;
+		before: any;
+		after: any;
+	};
+	deleteGlobalAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
+	deleteUserAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
 	resetPassword: {
 		targetId: string;
 	};
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index adedea8755..b5d07a394c 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2556,6 +2556,30 @@ type ModerationLog = {
 } | {
     type: 'promoteQueue';
     info: ModerationLogPayloads['promoteQueue'];
+} | {
+    type: 'deleteDriveFile';
+    info: ModerationLogPayloads['deleteDriveFile'];
+} | {
+    type: 'deleteNote';
+    info: ModerationLogPayloads['deleteNote'];
+} | {
+    type: 'createGlobalAnnouncement';
+    info: ModerationLogPayloads['createGlobalAnnouncement'];
+} | {
+    type: 'createUserAnnouncement';
+    info: ModerationLogPayloads['createUserAnnouncement'];
+} | {
+    type: 'updateGlobalAnnouncement';
+    info: ModerationLogPayloads['updateGlobalAnnouncement'];
+} | {
+    type: 'updateUserAnnouncement';
+    info: ModerationLogPayloads['updateUserAnnouncement'];
+} | {
+    type: 'deleteGlobalAnnouncement';
+    info: ModerationLogPayloads['deleteGlobalAnnouncement'];
+} | {
+    type: 'deleteUserAnnouncement';
+    info: ModerationLogPayloads['deleteUserAnnouncement'];
 } | {
     type: 'resetPassword';
     info: ModerationLogPayloads['resetPassword'];
@@ -2568,7 +2592,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index a5c8c2ba00..dd4fd2609f 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -61,6 +61,10 @@ export const moderationLogTypes = [
 	'deleteNote',
 	'createGlobalAnnouncement',
 	'createUserAnnouncement',
+	'updateGlobalAnnouncement',
+	'updateUserAnnouncement',
+	'deleteGlobalAnnouncement',
+	'deleteUserAnnouncement',
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
@@ -125,6 +129,24 @@ export type ModerationLogPayloads = {
 		announcement: any;
 		userId: string;
 	};
+	updateGlobalAnnouncement: {
+		announcementId: string;
+		before: any;
+		after: any;
+	};
+	updateUserAnnouncement: {
+		announcementId: string;
+		before: any;
+		after: any;
+	};
+	deleteGlobalAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
+	deleteUserAnnouncement: {
+		announcementId: string;
+		announcement: any;
+	};
 	resetPassword: {
 		targetId: string;
 	};
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index a1fc8befb4..b157eb59f6 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -607,6 +607,30 @@ export type ModerationLog = {
 } | {
 	type: 'promoteQueue';
 	info: ModerationLogPayloads['promoteQueue'];
+} | {
+	type: 'deleteDriveFile';
+	info: ModerationLogPayloads['deleteDriveFile'];
+} | {
+	type: 'deleteNote';
+	info: ModerationLogPayloads['deleteNote'];
+} | {
+	type: 'createGlobalAnnouncement';
+	info: ModerationLogPayloads['createGlobalAnnouncement'];
+} | {
+	type: 'createUserAnnouncement';
+	info: ModerationLogPayloads['createUserAnnouncement'];
+} | {
+	type: 'updateGlobalAnnouncement';
+	info: ModerationLogPayloads['updateGlobalAnnouncement'];
+} | {
+	type: 'updateUserAnnouncement';
+	info: ModerationLogPayloads['updateUserAnnouncement'];
+} | {
+	type: 'deleteGlobalAnnouncement';
+	info: ModerationLogPayloads['deleteGlobalAnnouncement'];
+} | {
+	type: 'deleteUserAnnouncement';
+	info: ModerationLogPayloads['deleteUserAnnouncement'];
 } | {
 	type: 'resetPassword';
 	info: ModerationLogPayloads['resetPassword'];

From 8e5a90589d9203a9f5abb2c4d67151c126915b11 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 10:57:24 +0900
Subject: [PATCH 33/62] improve moderation log

---
 locales/index.d.ts                            |  2 +
 locales/ja-JP.yml                             |  2 +
 .../backend/src/core/CustomEmojiService.ts    | 41 ++++++++++++++++---
 .../server/api/endpoints/admin/emoji/add.ts   |  8 +---
 .../api/endpoints/admin/emoji/delete-bulk.ts  |  2 +-
 .../api/endpoints/admin/emoji/delete.ts       |  2 +-
 .../api/endpoints/admin/emoji/update.ts       |  2 +-
 packages/backend/src/types.ts                 | 12 ++++++
 packages/misskey-js/etc/misskey-js.api.md     |  8 +++-
 packages/misskey-js/src/consts.ts             | 12 ++++++
 packages/misskey-js/src/entities.ts           |  6 +++
 11 files changed, 81 insertions(+), 16 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index c2f50dd54a..0ca5919dd4 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2256,6 +2256,8 @@ export interface Locale {
         "suspend": string;
         "unsuspend": string;
         "addCustomEmoji": string;
+        "updateCustomEmoji": string;
+        "deleteCustomEmoji": string;
         "updateServerSettings": string;
         "updateUserNote": string;
         "deleteDriveFile": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 43a3394264..13ab6755e6 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2169,6 +2169,8 @@ _moderationLogTypes:
   suspend: "凍結"
   unsuspend: "凍結解除"
   addCustomEmoji: "カスタム絵文字追加"
+  updateCustomEmoji: "カスタム絵文字更新"
+  deleteCustomEmoji: "カスタム絵文字削除"
   updateServerSettings: "サーバー設定更新"
   updateUserNote: "モデレーションノート更新"
   deleteDriveFile: "ファイルを削除"
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index aa5490eba7..b14a8666e6 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -12,12 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import type { MiEmoji } from '@/models/Emoji.js';
-import type { EmojisRepository, MiRole } from '@/models/_.js';
+import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { query } from '@/misc/prelude/url.js';
 import type { Serialized } from '@/server/api/stream/types.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
 
@@ -36,6 +37,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		private utilityService: UtilityService,
 		private idService: IdService,
 		private emojiEntityService: EmojiEntityService,
+		private moderationLogService: ModerationLogService,
 		private globalEventService: GlobalEventService,
 	) {
 		this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
@@ -66,7 +68,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		isSensitive: boolean;
 		localOnly: boolean;
 		roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
-	}): Promise<MiEmoji> {
+	}, moderator?: MiUser): Promise<MiEmoji> {
 		const emoji = await this.emojisRepository.insert({
 			id: this.idService.genId(),
 			updatedAt: new Date(),
@@ -89,6 +91,13 @@ export class CustomEmojiService implements OnApplicationShutdown {
 			this.globalEventService.publishBroadcastStream('emojiAdded', {
 				emoji: await this.emojiEntityService.packDetailed(emoji.id),
 			});
+
+			if (moderator) {
+				this.moderationLogService.log(moderator, 'addCustomEmoji', {
+					emojiId: emoji.id,
+					emoji: emoji,
+				});
+			}
 		}
 
 		return emoji;
@@ -104,7 +113,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		isSensitive?: boolean;
 		localOnly?: boolean;
 		roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
-	}): Promise<void> {
+	}, moderator?: MiUser): Promise<void> {
 		const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
 		const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
 		if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists');
@@ -140,6 +149,14 @@ export class CustomEmojiService implements OnApplicationShutdown {
 				emoji: updated,
 			});
 		}
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'updateCustomEmoji', {
+				emojiId: emoji.id,
+				before: emoji,
+				after: updated,
+			});
+		}
 	}
 
 	@bindThis
@@ -231,7 +248,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	public async delete(id: MiEmoji['id']) {
+	public async delete(id: MiEmoji['id'], moderator?: MiUser) {
 		const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
 
 		await this.emojisRepository.delete(emoji.id);
@@ -241,16 +258,30 @@ export class CustomEmojiService implements OnApplicationShutdown {
 		this.globalEventService.publishBroadcastStream('emojiDeleted', {
 			emojis: [await this.emojiEntityService.packDetailed(emoji)],
 		});
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'deleteCustomEmoji', {
+				emojiId: emoji.id,
+				emoji: emoji,
+			});
+		}
 	}
 
 	@bindThis
-	public async deleteBulk(ids: MiEmoji['id'][]) {
+	public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) {
 		const emojis = await this.emojisRepository.findBy({
 			id: In(ids),
 		});
 
 		for (const emoji of emojis) {
 			await this.emojisRepository.delete(emoji.id);
+
+			if (moderator) {
+				this.moderationLogService.log(moderator, 'deleteCustomEmoji', {
+					emojiId: emoji.id,
+					emoji: emoji,
+				});
+			}
 		}
 
 		this.localEmojisCache.refresh();
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
index fc297c4702..24d3a8a943 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -8,7 +8,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { DriveFilesRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { CustomEmojiService } from '@/core/CustomEmojiService.js';
-import { ModerationLogService } from '@/core/ModerationLogService.js';
 import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 import { ApiError } from '../../../error.js';
 
@@ -61,7 +60,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private customEmojiService: CustomEmojiService,
 
 		private emojiEntityService: EmojiEntityService,
-		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
@@ -77,11 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				isSensitive: ps.isSensitive ?? false,
 				localOnly: ps.localOnly ?? false,
 				roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [],
-			});
-
-			this.moderationLogService.log(me, 'addCustomEmoji', {
-				emojiId: emoji.id,
-			});
+			}, me);
 
 			return this.emojiEntityService.packDetailed(emoji);
 		});
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
index 4221913049..e6c1bf317f 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts
@@ -30,7 +30,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private customEmojiService: CustomEmojiService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			await this.customEmojiService.deleteBulk(ps.ids);
+			await this.customEmojiService.deleteBulk(ps.ids, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
index f020e22182..58aa0b9950 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private customEmojiService: CustomEmojiService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			await this.customEmojiService.delete(ps.id);
+			await this.customEmojiService.delete(ps.id, me);
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
index f01be9e27a..2d69857408 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -84,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				isSensitive: ps.isSensitive,
 				localOnly: ps.localOnly,
 				roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction,
-			});
+			}, me);
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index b85388d6e4..ea78bb919a 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -33,6 +33,8 @@ export const moderationLogTypes = [
 	'unsuspend',
 	'updateUserNote',
 	'addCustomEmoji',
+	'updateCustomEmoji',
+	'deleteCustomEmoji',
 	'assignRole',
 	'unassignRole',
 	'updateRole',
@@ -70,6 +72,16 @@ export type ModerationLogPayloads = {
 	};
 	addCustomEmoji: {
 		emojiId: string;
+		emoji: any;
+	};
+	updateCustomEmoji: {
+		emojiId: string;
+		before: any;
+		after: any;
+	};
+	deleteCustomEmoji: {
+		emojiId: string;
+		emoji: any;
 	};
 	assignRole: {
 		userId: string;
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index b5d07a394c..d8b6aa44d8 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2538,6 +2538,12 @@ type ModerationLog = {
 } | {
     type: 'addCustomEmoji';
     info: ModerationLogPayloads['addCustomEmoji'];
+} | {
+    type: 'updateCustomEmoji';
+    info: ModerationLogPayloads['updateCustomEmoji'];
+} | {
+    type: 'deleteCustomEmoji';
+    info: ModerationLogPayloads['deleteCustomEmoji'];
 } | {
     type: 'assignRole';
     info: ModerationLogPayloads['assignRole'];
@@ -2592,7 +2598,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index dd4fd2609f..462ad16cc8 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -51,6 +51,8 @@ export const moderationLogTypes = [
 	'unsuspend',
 	'updateUserNote',
 	'addCustomEmoji',
+	'updateCustomEmoji',
+	'deleteCustomEmoji',
 	'assignRole',
 	'unassignRole',
 	'updateRole',
@@ -88,6 +90,16 @@ export type ModerationLogPayloads = {
 	};
 	addCustomEmoji: {
 		emojiId: string;
+		emoji: any;
+	};
+	updateCustomEmoji: {
+		emojiId: string;
+		before: any;
+		after: any;
+	};
+	deleteCustomEmoji: {
+		emojiId: string;
+		emoji: any;
 	};
 	assignRole: {
 		userId: string;
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index b157eb59f6..e6a97f0209 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -589,6 +589,12 @@ export type ModerationLog = {
 } | {
 	type: 'addCustomEmoji';
 	info: ModerationLogPayloads['addCustomEmoji'];
+} | {
+	type: 'updateCustomEmoji';
+	info: ModerationLogPayloads['updateCustomEmoji'];
+} | {
+	type: 'deleteCustomEmoji';
+	info: ModerationLogPayloads['deleteCustomEmoji'];
 } | {
 	type: 'assignRole';
 	info: ModerationLogPayloads['assignRole'];

From eb7c65ccb39face9caebda816924de91d57cb50b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:04:08 +0900
Subject: [PATCH 34/62] =?UTF-8?q?fix(frontend):=20=E3=82=A2=E3=82=AF?=
 =?UTF-8?q?=E3=82=BB=E3=82=B9=E3=83=88=E3=83=BC=E3=82=AF=E3=83=B3=E7=99=BA?=
 =?UTF-8?q?=E8=A1=8C=E3=81=AE=E7=94=BB=E9=9D=A2=E3=81=AE=E6=A8=A9=E9=99=90?=
 =?UTF-8?q?=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #11880
---
 packages/frontend/src/components/MkTokenGenerateWindow.vue | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 5f3c2f3a71..8958accc4a 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -32,7 +32,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
 				<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
 			</div>
-			<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
+			<div class="_gaps_s">
+				<MkSwitch v-for="kind in (initialPermissions || Misskey.permissions)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
+			</div>
 		</div>
 	</MkSpacer>
 </MkModalWindow>

From 7a3ddc869e3eaf560d4fff941e3568b5e702b0ba Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:07:46 +0900
Subject: [PATCH 35/62] Update CHANGELOG.md

---
 CHANGELOG.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0c85e850a9..46433bca35 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,9 @@
 
 ## 2023.9.0 (unreleased)
 
+### Note
+- meilisearchを使用する場合、v1.2以上が必要です
+
 ### General
 - Feat: OAuth 2.0のサポート
 - Feat: お知らせ機能の強化

From 72075314a8a1317b81d588da10b534cafaf1d650 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:08:51 +0900
Subject: [PATCH 36/62] :art:

---
 packages/frontend/src/components/MkNoteDetailed.vue | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 2b61240b96..06663d0477 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -145,9 +145,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
 			<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
 				<template #default="{ items }">
-					<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
-						<MkUserCardMini :user="item.user" :withChart="false"/>
-					</MkA>
+					<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); grid-gap: 12px;">
+						<MkA v-for="item in items" :key="item.id" :to="userPage(item.user)">
+							<MkUserCardMini :user="item.user" :withChart="false"/>
+						</MkA>
+					</div>
 				</template>
 			</MkPagination>
 		</div>

From 509cea511c46e28ba82272d1dd8fe606f0c8fe7a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:09:49 +0900
Subject: [PATCH 37/62] update deps

---
 packages/backend/package.json    |   4 +-
 packages/frontend/package.json   |   4 +-
 packages/misskey-js/package.json |   2 +-
 pnpm-lock.yaml                   | 269 ++++++++++++-------------------
 4 files changed, 105 insertions(+), 174 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 97fbaab308..c26b1238db 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -189,9 +189,9 @@
 		"@types/jsrsasign": "10.5.9",
 		"@types/mime-types": "2.1.1",
 		"@types/ms": "0.7.31",
-		"@types/node": "20.6.3",
+		"@types/node": "20.6.4",
 		"@types/node-fetch": "3.0.3",
-		"@types/nodemailer": "6.4.10",
+		"@types/nodemailer": "6.4.11",
 		"@types/oauth": "0.9.2",
 		"@types/oauth2orize": "1.11.1",
 		"@types/oauth2orize-pkce": "0.1.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index bce300ff6e..2e27f9e99c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -97,10 +97,10 @@
 		"@storybook/vue3-vite": "7.4.4",
 		"@testing-library/vue": "7.0.0",
 		"@types/escape-regexp": "0.0.1",
-		"@types/estree": "1.0.1",
+		"@types/estree": "1.0.2",
 		"@types/matter-js": "0.19.0",
 		"@types/micromatch": "4.0.2",
-		"@types/node": "20.6.3",
+		"@types/node": "20.6.4",
 		"@types/punycode": "2.1.0",
 		"@types/sanitize-html": "2.9.0",
 		"@types/throttle-debounce": "5.0.0",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 140968f042..d70472b137 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -23,7 +23,7 @@
 		"@microsoft/api-extractor": "7.37.0",
 		"@swc/jest": "0.2.29",
 		"@types/jest": "29.5.5",
-		"@types/node": "20.6.3",
+		"@types/node": "20.6.4",
 		"@typescript-eslint/eslint-plugin": "6.7.2",
 		"@typescript-eslint/parser": "6.7.2",
 		"eslint": "8.50.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 40364d3605..0297fdab6d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -539,14 +539,14 @@ importers:
         specifier: 0.7.31
         version: 0.7.31
       '@types/node':
-        specifier: 20.6.3
-        version: 20.6.3
+        specifier: 20.6.4
+        version: 20.6.4
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
       '@types/nodemailer':
-        specifier: 6.4.10
-        version: 6.4.10
+        specifier: 6.4.11
+        version: 6.4.11
       '@types/oauth':
         specifier: 0.9.2
         version: 0.9.2
@@ -630,7 +630,7 @@ importers:
         version: 8.0.1
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.6.3)
+        version: 29.7.0(@types/node@20.6.4)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -807,7 +807,7 @@ importers:
         version: 1.8.1
       vite:
         specifier: 4.4.9
-        version: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+        version: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
       vue:
         specifier: 3.3.4
         version: 3.3.4
@@ -879,8 +879,8 @@ importers:
         specifier: 0.0.1
         version: 0.0.1
       '@types/estree':
-        specifier: 1.0.1
-        version: 1.0.1
+        specifier: 1.0.2
+        version: 1.0.2
       '@types/matter-js':
         specifier: 0.19.0
         version: 0.19.0
@@ -888,8 +888,8 @@ importers:
         specifier: 4.0.2
         version: 4.0.2
       '@types/node':
-        specifier: 20.6.3
-        version: 20.6.3
+        specifier: 20.6.4
+        version: 20.6.4
       '@types/punycode':
         specifier: 2.1.0
         version: 2.1.0
@@ -976,7 +976,7 @@ importers:
         version: 7.4.4
       storybook-addon-misskey-theme:
         specifier: github:misskey-dev/storybook-addon-misskey-theme
-        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.3)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.4)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
@@ -1013,7 +1013,7 @@ importers:
     devDependencies:
       '@microsoft/api-extractor':
         specifier: 7.37.0
-        version: 7.37.0(@types/node@20.6.3)
+        version: 7.37.0(@types/node@20.6.4)
       '@swc/jest':
         specifier: 0.2.29
         version: 0.2.29(@swc/core@1.3.87)
@@ -1021,8 +1021,8 @@ importers:
         specifier: 29.5.5
         version: 29.5.5
       '@types/node':
-        specifier: 20.6.3
-        version: 20.6.3
+        specifier: 20.6.4
+        version: 20.6.4
       '@typescript-eslint/eslint-plugin':
         specifier: 6.7.2
         version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)
@@ -1034,7 +1034,7 @@ importers:
         version: 8.50.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.6.3)
+        version: 29.7.0(@types/node@20.6.4)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -4014,7 +4014,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4035,14 +4035,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.7.1
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.6.3)
+      jest-config: 29.7.0(@types/node@20.6.4)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4077,7 +4077,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       jest-mock: 29.7.0
     dev: true
 
@@ -4104,7 +4104,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4137,7 +4137,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4231,7 +4231,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4243,7 +4243,7 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
@@ -4262,7 +4262,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.2.2)
       typescript: 5.2.2
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4347,24 +4347,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.0(@types/node@20.6.3):
+  /@microsoft/api-extractor-model@7.28.0(@types/node@20.6.4):
     resolution: {integrity: sha512-QIMtUVm1tqiKG+M6ciFgRShcDoovyltaeg+CbyOnyr7SMrp6gg0ojK5/nToMqR9kAvsTS4QVgW4Twl50EoAjcw==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.60.0(@types/node@20.6.3)
+      '@rushstack/node-core-library': 3.60.0(@types/node@20.6.4)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.37.0(@types/node@20.6.3):
+  /@microsoft/api-extractor@7.37.0(@types/node@20.6.4):
     resolution: {integrity: sha512-df/wffWpDhYRw7kzdxeHGsCpim+dC8aFiZlsJb4uFvVPWhBZpDzOhQxSUTFx3Df1ORY+/JjuPR3fDE9Hq+PHzQ==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.0(@types/node@20.6.3)
+      '@microsoft/api-extractor-model': 7.28.0(@types/node@20.6.4)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.60.0(@types/node@20.6.3)
+      '@rushstack/node-core-library': 3.60.0(@types/node@20.6.4)
       '@rushstack/rig-package': 0.5.0
       '@rushstack/ts-command-line': 4.16.0
       colors: 1.2.5
@@ -5244,12 +5244,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@types/estree': 1.0.1
+      '@types/estree': 1.0.2
       estree-walker: 2.0.2
       picomatch: 2.3.1
       rollup: 3.29.2
 
-  /@rushstack/node-core-library@3.60.0(@types/node@20.6.3):
+  /@rushstack/node-core-library@3.60.0(@types/node@20.6.4):
     resolution: {integrity: sha512-PcyrqhILvzU+65wMFybQ2VeGNnU5JzhDq2OvUi3j6jPUxyllM7b2hrRUwCuVaYboewYzIbpzXFzgxe2K7ii1nw==}
     peerDependencies:
       '@types/node': '*'
@@ -5257,7 +5257,7 @@ packages:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -6276,23 +6276,12 @@ packages:
       remark-slug: 6.1.0
       rollup: 3.29.2
       typescript: 5.2.2
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/channels@7.4.3:
-    resolution: {integrity: sha512-lIoRX3EV0wKPX8ojIrJUtsOv4+Gv8r9pfJpam/NdyYd+rs0AjDK13ieINRfBMnJkfjsWa3vmZtGMBEVvDKwTMw==}
-    dependencies:
-      '@storybook/client-logger': 7.4.3
-      '@storybook/core-events': 7.4.3
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.4.4:
     resolution: {integrity: sha512-YA2T3hClL95nFBBelm8wMOyWFDzfxKvyHAPQi+8YeYpZcPivwg/P9YnRhTTMbiZNkfoWKq4ZRuc79UP1iNLi3g==}
     dependencies:
@@ -6356,12 +6345,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.4.3:
-    resolution: {integrity: sha512-Nhngo9X4HjN00aRhgIVGWbwkWPe0Fz8PySuxnd8nAxSsz7KpdLFyYo2TbZZ3TX51FG5Fxcb0G5OHuunItP7EWQ==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.4.4:
     resolution: {integrity: sha512-rC/GcCy3DLtTI+oOHLBc6rq/c3oGF/mvdeWrhMM+berQplHJrOCI2pcldjVw8Fc25gLPK0LUlaOp1dfgt2Ri3Q==}
     dependencies:
@@ -6389,29 +6372,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-qwRW8wGUuM+H6oKUXXoIDrZECXh/lzowrWXFAzZiocovYEhPtZfl/yvJLWHjOwtka3n7lA7J7EtcjWe8/tueJQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    dependencies:
-      '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0)
-      '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.4.3
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.4.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.4.3
-      memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0)
-      util-deprecate: 1.0.2
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
-    dev: true
-
   /@storybook/components@7.4.4(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-tFOSu3IoAab/0aY2TY66Go0Nba7AB/+ZB9GFet+dxWypIKGLcPjyX2POIumJU4swzK+4IA8GxgDQ2itS6EOISQ==}
     peerDependencies:
@@ -6473,12 +6433,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.4.3:
-    resolution: {integrity: sha512-FRfipCijMnVbGxL1ZjOLM836lyd/TGQcUFeVjTQWW/+pIGHELqDHiYeq68hqoGTKl0G0np59CJPWYTUZA4Dl9Q==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.4.4:
     resolution: {integrity: sha512-kOf4I/a1XC9CaGFwJG5WR2KnkwrOkWX68TLh7OlelKxdl/WjxA4zfzaFPC/8zyCSLdGFLPKNqr1w+ezkb+9Irw==}
     dependencies:
@@ -6711,7 +6665,7 @@ packages:
       react: 18.2.0
       react-docgen: 6.0.0-alpha.3
       react-dom: 18.2.0(react@18.2.0)
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -6813,20 +6767,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.4.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-u5wLwWmhGcTmkcs6f2wDGv+w8wzwbNJat0WaIIbwdJfX7arH6nO5HkBhNxvl6FUFxX0tovp/e9ULzxVPc356jw==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    dependencies:
-      '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
-      '@storybook/client-logger': 7.4.3
-      '@storybook/global': 5.0.0
-      memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
-
   /@storybook/theming@7.4.4(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-ABIwLRUj2IZKMGxKq+fCCFcY7w52P1a+q8j7qrlELaTe4M74K6rwTgRF0/AFgWeiGRkNuA7z8DjQ73xQLoLqUg==}
     peerDependencies:
@@ -6841,15 +6781,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.4.3:
-    resolution: {integrity: sha512-DrHC1hIiw9TqDILLokDnvbUPNxGz5iJaYFEv30uvYE0s9MvgEUPblCChEUjaHOps7zQTznMPf8ULfoXlgqxk2A==}
-    dependencies:
-      '@storybook/channels': 7.4.3
-      '@types/babel__core': 7.20.0
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.4.4:
     resolution: {integrity: sha512-B0VdgGb1XGEb9g3UuEd9xANCIhR3anvA3w0uYSG+7uMOflnEawwZksTSxvvoGM2hx9vC4pNT4Fci9sEC903UkA==}
     dependencies:
@@ -6874,7 +6805,7 @@ packages:
       magic-string: 0.30.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
       vue-docgen-api: 4.64.1(vue@3.3.4)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -7406,7 +7337,7 @@ packages:
   /@types/accepts@1.3.5:
     resolution: {integrity: sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/archiver@5.3.3:
@@ -7460,7 +7391,7 @@ packages:
     resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/braces@3.0.1:
@@ -7472,7 +7403,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       '@types/keyv': 3.1.4
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7505,7 +7436,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/content-disposition@0.5.6:
@@ -7519,7 +7450,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/debug@4.1.7:
@@ -7559,7 +7490,7 @@ packages:
   /@types/eslint@7.29.0:
     resolution: {integrity: sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==}
     dependencies:
-      '@types/estree': 1.0.1
+      '@types/estree': 1.0.2
       '@types/json-schema': 7.0.12
     dev: true
 
@@ -7567,13 +7498,13 @@ packages:
     resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
     dev: true
 
-  /@types/estree@1.0.1:
-    resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
+  /@types/estree@1.0.2:
+    resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==}
 
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7594,20 +7525,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.22:
     resolution: {integrity: sha512-ZZPDDrDOb2Ahp5fxZzuw64f0rCcviv+SDuCyJ1PIF/UFn9wNHtb/bY8Dj/2nrbQ7SNsGI7gaO2wJVkkU2HBcMg==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/hast@2.3.4:
@@ -7622,7 +7553,7 @@ packages:
   /@types/http-link-header@1.0.3:
     resolution: {integrity: sha512-y8HkoD/vyid+5MrJ3aas0FvU3/BVBGcyG9kgxL0Zn4JwstA8CglFPnrR0RuzOjRCXwqzL5uxWC2IO7Ub0rMU2A==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -7666,7 +7597,7 @@ packages:
   /@types/jsdom@21.1.3:
     resolution: {integrity: sha512-1zzqSP+iHJYV4lB3lZhNBa012pubABkj9yG/GuXuf6LZH1cSPIJBqFDrm5JX65HHt6VOnNYdTui/0ySerRbMgA==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -7690,7 +7621,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: false
 
   /@types/lodash@4.14.191:
@@ -7739,7 +7670,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       form-data: 3.0.1
 
   /@types/node-fetch@3.0.3:
@@ -7756,13 +7687,13 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.6.3:
-    resolution: {integrity: sha512-HksnYH4Ljr4VQgEy2lTStbCKv/P590tmPe5HqOnv9Gprffgv5WXAY+Y5Gqniu0GGqeTCUdBnzC3QSrzPkBkAMA==}
+  /@types/node@20.6.4:
+    resolution: {integrity: sha512-nU6d9MPY0NBUMiE/nXd2IIoC4OLvsLpwAjheoAeuzgvDZA1Cb10QYg+91AF6zQiKWRN5i1m07x6sMe0niBznoQ==}
 
-  /@types/nodemailer@6.4.10:
-    resolution: {integrity: sha512-oPW/IdhkU3FyZc1dzeqmS+MBjrjZNiiINnrEOrWALzccJlP5xTlbkNr2YnTnnyj9Eqm5ofjRoASEbrCYpA7BrA==}
+  /@types/nodemailer@6.4.11:
+    resolution: {integrity: sha512-Ld2c0frwpGT4VseuoeboCXQ7UJIkK3X7Lx/4YsZEiUHtHsthWAOCYtf6PAiLhMtfwV0cWJRabLBS3+LD8x6Nrw==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -7779,13 +7710,13 @@ packages:
     resolution: {integrity: sha512-U3L0c4eQA6lTSZRgW4LYfhKlR084Aw19akmYHrMdYzaqg9mQDfc2b/1iyqm9+1FJDEnVS5ONi5fxdDrB4/7CpQ==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/oauth@0.9.2:
     resolution: {integrity: sha512-Nu3/abQ6yR9VlsCdX3aiGsWFkj6OJvJqDvg/36t8Gwf2mFXdBZXPDN3K+2yfeA6Lo2m1Q12F8Qil9TZ48nWhOQ==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -7801,7 +7732,7 @@ packages:
   /@types/pg@8.10.2:
     resolution: {integrity: sha512-MKFs9P6nJ+LAeHLU3V0cODEOgyThJ3OAnmOlsZsxux6sfQs3HRXR5bBn7xG5DjckEFhTAxsXi7k7cd0pCMxpJw==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -7825,7 +7756,7 @@ packages:
   /@types/qrcode@1.5.2:
     resolution: {integrity: sha512-W4KDz75m7rJjFbyCctzCtRzZUj+PrUHV+YjqDp50sSRezTbrtEAIq2iTzC6lISARl3qw+8IlcCyljdcVJE0Wug==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/qs@6.9.7:
@@ -7855,7 +7786,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/rename@1.0.4:
@@ -7865,7 +7796,7 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: false
 
   /@types/sanitize-html@2.9.0:
@@ -7891,7 +7822,7 @@ packages:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -7901,7 +7832,7 @@ packages:
   /@types/set-cookie-parser@2.4.3:
     resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/sharp@0.32.0:
@@ -7964,13 +7895,13 @@ packages:
   /@types/vary@1.1.0:
     resolution: {integrity: sha512-LQWqrIa0dvEOOH37lGksMEXbypRLUFqu6Gx0pmX7zIUisD2I/qaVgEX/vJ/PSVSW0Hk6yz1BNkFpqg6dZm3Wug==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/web-push@3.6.0:
     resolution: {integrity: sha512-Kk23yDmYheAcQ0ALS9YE7MY7lqwaIfVQ67zVEFeqbLw+/g8jlYTg9o/zYJOk5YhebWrq2Cr/Lbh4RoYfzrn0ww==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -7981,13 +7912,13 @@ packages:
   /@types/websocket@1.0.6:
     resolution: {integrity: sha512-JXkliwz93B2cMWOI1ukElQBPN88vMg3CruvW4KVSKpflt3NyNCJImnhIuB/f97rG7kakqRJGFiwkA895Kn02Dg==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/ws@8.5.5:
     resolution: {integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg==}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -8010,7 +7941,7 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
     optional: true
 
@@ -8156,7 +8087,7 @@ packages:
       '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.11)
       magic-string: 0.27.0
       react-refresh: 0.14.0
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8168,7 +8099,7 @@ packages:
       vite: ^4.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
       vue: 3.3.4
 
   /@vitest/coverage-v8@0.34.5(vitest@0.34.5):
@@ -10023,7 +9954,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.6.3):
+  /create-jest@29.7.0(@types/node@20.6.4):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -10032,7 +9963,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.6.3)
+      jest-config: 29.7.0(@types/node@20.6.4)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -11262,7 +11193,7 @@ packages:
   /estree-walker@3.0.3:
     resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
     dependencies:
-      '@types/estree': 1.0.1
+      '@types/estree': 1.0.2
     dev: false
 
   /esutils@2.0.3:
@@ -13244,7 +13175,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13265,7 +13196,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.6.3):
+  /jest-cli@29.7.0(@types/node@20.6.4):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13279,10 +13210,10 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.6.3)
+      create-jest: 29.7.0(@types/node@20.6.4)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.6.3)
+      jest-config: 29.7.0(@types/node@20.6.4)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.6.2
@@ -13293,7 +13224,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.6.3):
+  /jest-config@29.7.0(@types/node@20.6.4):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13308,7 +13239,7 @@ packages:
       '@babel/core': 7.22.11
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       babel-jest: 29.7.0(@babel/core@7.22.11)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13388,7 +13319,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -13418,7 +13349,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13479,7 +13410,7 @@ packages:
     engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
     dependencies:
       '@jest/types': 27.5.1
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
     dev: true
 
   /jest-mock@29.7.0:
@@ -13487,7 +13418,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       jest-util: 29.7.0
     dev: true
 
@@ -13542,7 +13473,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -13573,7 +13504,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -13625,7 +13556,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -13650,7 +13581,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -13669,13 +13600,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.6.3):
+  /jest@29.7.0(@types/node@20.6.4):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13688,7 +13619,7 @@ packages:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.6.3)
+      jest-cli: 29.7.0(@types/node@20.6.4)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -18997,7 +18928,7 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
+  /vite-node@0.34.5(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0):
     resolution: {integrity: sha512-RNZ+DwbCvDoI5CbCSQSyRyzDTfFvFauvMs6Yq4ObJROKlIKuat1KgSX/Ako5rlDMfVCyMcpMRMTkJBxd6z8YRA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19007,7 +18938,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19023,7 +18954,7 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0):
+  /vite@4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0):
     resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
     engines: {node: ^14.18.0 || >=16.0.0}
     hasBin: true
@@ -19051,7 +18982,7 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       esbuild: 0.18.17
       postcss: 8.4.30
       rollup: 3.29.2
@@ -19105,7 +19036,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.6.3
+      '@types/node': 20.6.4
       '@vitest/expect': 0.34.5
       '@vitest/runner': 0.34.5
       '@vitest/snapshot': 0.34.5
@@ -19125,8 +19056,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 4.4.9(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
-      vite-node: 0.34.5(@types/node@20.6.3)(sass@1.68.0)(terser@5.20.0)
+      vite: 4.4.9(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
+      vite-node: 0.34.5(@types/node@20.6.4)(sass@1.68.0)(terser@5.20.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19669,7 +19600,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.3)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.4)(@storybook/components@7.4.4)(@storybook/core-events@7.4.4)(@storybook/manager-api@7.4.4)(@storybook/preview-api@7.4.4)(@storybook/theming@7.4.4)(@storybook/types@7.4.4)(react-dom@18.2.0)(react@18.2.0):
     resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640}
     id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640
     name: storybook-addon-misskey-theme
@@ -19691,7 +19622,7 @@ packages:
         optional: true
     dependencies:
       '@storybook/blocks': 7.4.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.4.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events': 7.4.4
       '@storybook/manager-api': 7.4.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api': 7.4.4

From 2b561d2648b17c1d568b0fb07f31f1a2ac85776b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:10:05 +0900
Subject: [PATCH 38/62] New Crowdin updates (#11875)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Greek)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Norwegian)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Turkish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Lao)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Czech)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Dutch)

* New translations ja-jp.yml (Polish)

* New translations ja-jp.yml (Portuguese)

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Slovak)

* New translations ja-jp.yml (Swedish)

* New translations ja-jp.yml (Turkish)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Vietnamese)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Uzbek)

* New translations ja-jp.yml (Japanese, Kansai)

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Italian)
---
 locales/ar-SA.yml |  3 +++
 locales/bn-BD.yml |  3 +++
 locales/ca-ES.yml |  3 +++
 locales/cs-CZ.yml |  3 +++
 locales/de-DE.yml | 27 ++++++++++++++++++----
 locales/el-GR.yml |  2 ++
 locales/en-US.yml | 17 ++++++++++++++
 locales/es-ES.yml |  3 +++
 locales/fr-FR.yml |  3 +++
 locales/id-ID.yml |  3 +++
 locales/it-IT.yml | 58 +++++++++++++++++++++++++++++++++--------------
 locales/ja-KS.yml |  3 +++
 locales/ko-KR.yml |  3 +++
 locales/lo-LA.yml |  2 ++
 locales/nl-NL.yml |  3 +++
 locales/no-NO.yml |  2 ++
 locales/pl-PL.yml |  3 +++
 locales/pt-PT.yml |  3 +++
 locales/ro-RO.yml |  3 +++
 locales/ru-RU.yml |  3 +++
 locales/sk-SK.yml |  3 +++
 locales/sv-SE.yml |  3 +++
 locales/th-TH.yml |  3 +++
 locales/tr-TR.yml |  3 +++
 locales/uk-UA.yml |  3 +++
 locales/uz-UZ.yml |  3 +++
 locales/vi-VN.yml |  3 +++
 locales/zh-CN.yml | 18 +++++++++++++++
 locales/zh-TW.yml | 19 +++++++++++++++-
 29 files changed, 185 insertions(+), 23 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index f36641128c..55b7cbb88c 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -1556,3 +1556,6 @@ _webhookSettings:
   active: "مُفعّل"
   _events:
     reaction: "عند التفاعل"
+_moderationLogTypes:
+  suspend: "علِق"
+  resetPassword: "أعد تعيين كلمتك السرية"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index e0e1e29433..64b32d176b 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -1333,3 +1333,6 @@ _deck:
 _webhookSettings:
   name: "নাম"
   active: "চালু"
+_moderationLogTypes:
+  suspend: "স্থগিত করা"
+  resetPassword: "পাসওয়ার্ড রিসেট করুন"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index ec52c57610..d1fd73b666 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -479,3 +479,6 @@ _deck:
     list: "Llistes"
     mentions: "Mencions"
     direct: "Publicacions directes"
+_moderationLogTypes:
+  suspend: "Suspèn"
+  resetPassword: "Restableix la contrasenya"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 36a104bdc3..751cc064ad 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -2036,3 +2036,6 @@ _webhookSettings:
     renote: "Při renotaci poznámky"
     reaction: "Při obdržení reakce"
     mention: "Při zmínce"
+_moderationLogTypes:
+  suspend: "Zmrazit"
+  resetPassword: "Resetovat heslo"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index b11cb7e5b9..11237d2e69 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -2,7 +2,7 @@
 _lang_: "Deutsch"
 headlineMisskey: "Ein durch Notizen verbundenes Netzwerk"
 introMisskey: "Willkommen! Misskey ist eine dezentralisierte Open-Source Microblogging-Platform.\nVerfasse „Notizen“ um mitzuteilen, was gerade passiert oder um Ereignisse mit anderen zu teilen. 📡\nMit „Reaktionen“ kannst du außerdem schnell deine Gefühle über Notizen anderer Benutzer zum Ausdruck bringen. 👍\nEine neue Welt wartet auf dich! 🚀"
-poweredByMisskeyDescription: "{name} ist einer der durch die Open-Source-Plattform <b>Misskey</b> betriebenen Dienste (meist als \"Misskey-Instanz\" bezeichnet)."
+poweredByMisskeyDescription: "{name} ist einer der durch die Open-Source-Plattform <b>Misskey</b> betriebenen Dienste."
 monthAndDay: "{day}.{month}."
 search: "Suchen"
 notifications: "Benachrichtigungen"
@@ -75,7 +75,7 @@ import: "Import"
 export: "Export"
 files: "Dateien"
 download: "Herunterladen"
-driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Sie wird in allen Inhalten, die sie verwenden, auch verschwinden."
+driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Einige Inhalte, die diese Datei verwenden, werden auch verschwinden."
 unfollowConfirm: "Möchtest du {name} wirklich nicht mehr folgen?"
 exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt."
 importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen."
@@ -418,6 +418,7 @@ moderator: "Moderator"
 moderation: "Moderation"
 moderationNote: "Moderationsnotiz"
 addModerationNote: "Moderationsnotiz hinzufügen"
+moderationLogs: "Moderationsprotokolle"
 nUsersMentioned: "Von {n} Benutzern erwähnt"
 securityKeyAndPasskey: "Hardware-Sicherheitsschlüssel und Passkeys"
 securityKey: "Hardware-Sicherheitsschlüssel"
@@ -639,7 +640,7 @@ display: "Anzeigeart"
 copy: "Kopieren"
 metrics: "Metriken"
 overview: "Übersicht"
-logs: "Logs"
+logs: "Protokolle"
 delayed: "Verzögert"
 database: "Datenbank"
 channel: "Kanäle"
@@ -1049,7 +1050,7 @@ vertical: "Vertikal"
 horizontal: "Horizontal"
 position: "Position"
 serverRules: "Serverregeln"
-pleaseConfirmBelowBeforeSignup: "Lies bitte Untenstehendes vor der Registration."
+pleaseConfirmBelowBeforeSignup: "Lies bitte diese Informationen und stimme ihnen vor der Registration zu."
 pleaseAgreeAllToContinue: "Zum Fortfahren muss allen obigen Feldern zugestimmt werden."
 continue: "Fortfahren"
 preservedUsernames: "Reservierte Benutzernamen"
@@ -1727,7 +1728,7 @@ _2fa:
   step2Click: "Durch Klicken dieses QR-Codes kannst du Verifikation mit deinem Security-Token oder einer App registrieren."
   step2Uri: "Nutzt du ein Desktopprogramm, gib folgende URI eingeben"
   step3Title: "Authentifizierungsscode eingeben"
-  step3: "Gib zum Abschluss den Token ein, der von deiner App angezeigt wird."
+  step3: "Gib zum Abschluss den Code (Token) ein, der von deiner App angezeigt wird."
   setupCompleted: "Einrichtung abgeschlossen"
   step4: "Alle folgenden Anmeldeversuche werden ab sofort die Eingabe eines solchen Tokens benötigen."
   securityKeyNotSupported: "Dein Browser unterstützt keine Hardware-Sicherheitsschlüssel."
@@ -2099,3 +2100,19 @@ _webhookSettings:
     renote: "Wenn du ein Renote erhältst"
     reaction: "Wenn du eine Reaktion erhältst"
     mention: "Wenn du erwähnt wirst"
+_moderationLogTypes:
+  assignRole: "Zu Rolle zugewiesen"
+  unassignRole: "Aus Rolle entfernt"
+  updateRole: "Rolle aktualisiert"
+  suspend: "Gesperrt"
+  unsuspend: "Entsperrt"
+  addCustomEmoji: "Benutzerdefiniertes Emoji hinzugefügt"
+  updateServerSettings: "Servereinstellungen aktualisiert"
+  updateUserNote: "Moderationsnotiz aktualisiert"
+  deleteDriveFile: "Datei gelöscht"
+  deleteNote: "Notiz gelöscht"
+  createGlobalAnnouncement: "Globale Ankündigung erstellt"
+  createUserAnnouncement: "Benutzerankündigung erstellt"
+  resetPassword: "Passwort zurückgesetzt"
+  suspendRemoteInstance: "Fremde Instanz gesperrt"
+  unsuspendRemoteInstance: "Fremde Instanz entsperrt"
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index e8ed6f118e..e46efcec1f 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -397,3 +397,5 @@ _deck:
     mentions: "Επισημάνσεις"
 _webhookSettings:
   name: "Όνομα"
+_moderationLogTypes:
+  suspend: "Αποβολή"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 5d9ec0370e..4cda0ff8c2 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -418,6 +418,7 @@ moderator: "Moderator"
 moderation: "Moderation"
 moderationNote: "Moderation note"
 addModerationNote: "Add moderation note"
+moderationLogs: "Moderation logs"
 nUsersMentioned: "Mentioned by {n} users"
 securityKeyAndPasskey: "Security- and passkeys"
 securityKey: "Security key"
@@ -2099,3 +2100,19 @@ _webhookSettings:
     renote: "When renoted"
     reaction: "When receiving a reaction"
     mention: "When being mentioned"
+_moderationLogTypes:
+  assignRole: "Assigned to role"
+  unassignRole: "Removed from role"
+  updateRole: "Role updated"
+  suspend: "Suspended"
+  unsuspend: "Unsuspended"
+  addCustomEmoji: "Custom emoji added"
+  updateServerSettings: "Server settings updated"
+  updateUserNote: "Moderation note updated"
+  deleteDriveFile: "File deleted"
+  deleteNote: "Note deleted"
+  createGlobalAnnouncement: "Global announcement created"
+  createUserAnnouncement: "User announcement created"
+  resetPassword: "Password reset"
+  suspendRemoteInstance: "Remote instance suspended"
+  unsuspendRemoteInstance: "Remote instance unsuspended"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index c6bb2c10de..1f84a0afb4 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -2079,3 +2079,6 @@ _webhookSettings:
     renote: "Cuando reciba un \"re-note\""
     reaction: "Cuando se recibe una reacción"
     mention: "Cuando hay una mención"
+_moderationLogTypes:
+  suspend: "Suspender"
+  resetPassword: "Resetear contraseña"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 71446667d6..db19b66880 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1691,3 +1691,6 @@ _deck:
 _webhookSettings:
   name: "Nom"
   active: "Activé"
+_moderationLogTypes:
+  suspend: "Suspendre"
+  resetPassword: "Réinitialiser le mot de passe"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 75baebaae4..56e7f9e4db 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -2041,3 +2041,6 @@ _webhookSettings:
     renote: "Ketika direnote"
     reaction: "Ketika menerima reaksi"
     mention: "Ketika sedang disebut"
+_moderationLogTypes:
+  suspend: "Tangguhkan"
+  resetPassword: "Atur ulang kata sandi"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index a69e75a374..b700fb8777 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -117,7 +117,7 @@ pinnedNote: "Nota fissata"
 pinned: "Fissa sul profilo"
 you: "Tu"
 clickToShow: "Clicca per visualizzare"
-sensitive: "Contenuto sensibile"
+sensitive: "Esplicito"
 add: "Aggiungi"
 reaction: "Reazioni"
 reactions: "Reazioni"
@@ -125,8 +125,8 @@ reactionSetting: "Reazioni visualizzate sul pannello"
 reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
 rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
 attachCancel: "Rimuovi allegato"
-markAsSensitive: "Segna come sensibile"
-unmarkAsSensitive: "Segna come non sensibile"
+markAsSensitive: "Segna come esplicito"
+unmarkAsSensitive: "Non segnare come esplicito "
 enterFileName: "Nome del file"
 mute: "Silenzia"
 unmute: "Riattiva l'audio"
@@ -148,7 +148,7 @@ editAntenna: "Modifica Antenna"
 selectWidget: "Seleziona il riquadro"
 editWidgets: "Modifica i riquadri"
 editWidgetsExit: "Conferma le modifiche"
-customEmojis: "Emoji personalizzati"
+customEmojis: "Emoji personalizzate"
 emoji: "Emoji"
 emojis: "Emoji"
 emojiName: "Nome dell'emoji"
@@ -158,8 +158,8 @@ settingGuide: "Configurazione suggerita"
 cacheRemoteFiles: "Memorizza i file remoti nella cache"
 cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno generate anteprime."
 youCanCleanRemoteFilesCache: "Puoi svuotare tutta la cache cliccando il bottone 🗑️ nella gestione file"
-cacheRemoteSensitiveFiles: "Memorizza nella cache i file sensibili remoti"
-cacheRemoteSensitiveFilesDescription: "Disattivando questa opzione, i file sensibili verranno caricati direttamente dall'istanza remota senza essere salvati dal server."
+cacheRemoteSensitiveFiles: "Copia nella cache locale i file espliciti remoti"
+cacheRemoteSensitiveFilesDescription: "Disattivando questa opzione, i file espliciti verranno richiesti direttamente all'istanza remota senza essere salvati nel server locale."
 flagAsBot: "Io sono un robot"
 flagAsBotDescription: "Attiva questo campo se il profilo esegue principalmente operazioni automatiche. L'attivazione segnala agli altri sviluppatori come comportarsi per evitare catene d’interazione infinite con altri bot. I sistemi interni di Misskey si adegueranno al fine di trattare questo profilo come bot."
 flagAsCat: "Sono un gatto"
@@ -321,7 +321,7 @@ copyUrl: "Copia URL"
 rename: "Modifica nome"
 avatar: "Foto del profilo"
 banner: "Intestazione"
-displayOfSensitiveMedia: "Visibilità dei media sensibili"
+displayOfSensitiveMedia: "Visibilità dei media espliciti"
 whenServerDisconnected: "Quando la connessione col server è persa"
 disconnectedFromServer: "Il server si è disconnesso"
 reload: "Ricarica"
@@ -418,6 +418,7 @@ moderator: "Moderatore"
 moderation: "moderazione"
 moderationNote: "Promemoria di moderazione"
 addModerationNote: "Aggiungi promemoria di moderazione"
+moderationLogs: "Cronologia di moderazione"
 nUsersMentioned: "{n} profili menzionati"
 securityKeyAndPasskey: "Chiave di sicurezza e accesso"
 securityKey: "Chiave di sicurezza"
@@ -707,9 +708,10 @@ driveUsage: "Utilizzazione del Drive"
 noCrawle: "Rifiuta l'indicizzazione dai robot."
 noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc."
 lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account per confermare manualmente le richieste di follow."
-alwaysMarkSensitive: "Segnare i media come sensibili per impostazione predefinita"
+alwaysMarkSensitive: "Segnare gli allegati come espliciti come opzione predefinita"
 loadRawImages: "Visualizza le intere immagini allegate invece delle miniature."
 disableShowingAnimatedImages: "Disabilita le immagini animate"
+highlightSensitiveMedia: "Evidenzia i media espliciti"
 verificationEmailSent: "Una mail di verifica è stata inviata. Si prega di accedere al collegamento per compiere la verifica."
 notSet: "Non impostato"
 emailVerified: "Il tuo indirizzo email è stato verificato"
@@ -926,7 +928,7 @@ type: "Tipo"
 speed: "Velocità"
 slow: "Lento"
 fast: "Veloce"
-sensitiveMediaDetection: "Rilevamento dei contenuti sensibili."
+sensitiveMediaDetection: "Rilevamento dei contenuti espliciti"
 localOnly: "Soltanto locale"
 remoteOnly: "Solo remoto"
 failedToUpload: "errore di caricamento"
@@ -1006,11 +1008,11 @@ cannotBeChangedLater: "Non sarà più modificabile"
 reactionAcceptance: "Reazioni consentite"
 likeOnly: "Solo i Like"
 likeOnlyForRemote: "Solo Like remoti"
-nonSensitiveOnly: "Solamente non sensibili"
-nonSensitiveOnlyForLocalLikeOnlyForRemote: "Solamente non sensibili (solo Mi piace remoti)"
+nonSensitiveOnly: "Soltanto non espliciti"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "Soltanto non espliciti (reazioni remote)"
 rolesAssignedToMe: "I miei ruoli"
 resetPasswordConfirm: "Vuoi davvero ripristinare la password?"
-sensitiveWords: "Parole sensibili"
+sensitiveWords: "Parole esplicite"
 sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga."
 sensitiveWordsDescription2: "Gli spazi creano la relazione \"E\" tra parole (questo E quello). Racchiudere una parola nelle slash \"/\" la trasforma in Espressione Regolare."
 notesSearchNotAvailable: "Non è possibile cercare tra le Note."
@@ -1116,6 +1118,8 @@ keepScreenOn: "Mantieni lo schermo acceso"
 verifiedLink: "Abbiamo confermato la validità di questo collegamento"
 notifyNotes: "Notifica nuove Note"
 unnotifyNotes: "Interrompi le notifiche di nuove Note"
+authentication: "Autenticazione"
+authenticationRequiredToContinue: "Per procedere, è richiesta l'autenticazione"
 _announcement:
   forExistingUsers: "Solo ai profili attuali"
   forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
@@ -1149,6 +1153,8 @@ _serverSettings:
   appIconStyleRecommendation: "Poiché l'icona potrebbe essere ritagliata in un quadrato o in un cerchio, si raccomanda che abbia un margine colorato."
   appIconResolutionMustBe: "La risoluzione minima è {resolution}"
   manifestJsonOverride: "Sostituire il file manifest.json"
+  shortName: "Abbreviazione"
+  shortNameDescription: "Un'abbreviazione o un nome comune che può essere visualizzato al posto del nome ufficiale lungo del server."
 _accountMigration:
   moveFrom: "Migra un altro profilo dentro a questo"
   moveFromSub: "Crea un alias verso un altro profilo remoto"
@@ -1478,9 +1484,9 @@ _role:
     or: "O"
     not: "NON"
 _sensitiveMediaDetection:
-  description: "L'apprendimento automatico può essere utilizzato per individuare automaticamente i media sensibili da moderare. Il carico del server aumenta leggermente."
-  sensitivity: "Sensibilità di rilevamento"
-  sensitivityDescription: "Una minore sensibilità riduce i falsi positivi (false positività). Una maggiore sensibilità riduce le omissioni (falsi negativi)."
+  description: "Utilizzare l'apprendimento automatico (machine learning) per riconoscere media espliciti e sottoporli alla moderazione. Aumenterà lievemente il carico del server."
+  sensitivity: "Sensibilità del rilevamento"
+  sensitivityDescription: "Abbassando la sensibilità si riducono i falsi positivi (rilevazioni errate). Aumentando la sensibilità si riduce il numero di rilevazioni mancate. (rilevazioni ignorate)."
   setSensitiveFlagAutomatically: "Impostare il flag NSFW."
   setSensitiveFlagAutomaticallyDescription: "Anche se questa impostazione è disattivata, il risultato della decisione viene conservato internamente."
   analyzeVideos: "Abilitazione dell'analisi video."
@@ -1564,8 +1570,8 @@ _aboutMisskey:
   morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰"
   patrons: "Sostenitori"
 _displayOfSensitiveMedia:
-  respect: "Nascondere i media sensibili"
-  ignore: "Non nascondere i media sensibili"
+  respect: "Nascondere i media espliciti"
+  ignore: "Non nascondere i media espliciti"
   force: "Nascondi tutti i media"
 _instanceTicker:
   none: "Nascondi"
@@ -1795,6 +1801,7 @@ _antennaSources:
   homeTimeline: "Note dagli utenti che segui"
   users: "Note dagli utenti selezionati"
   userList: "Note dagli utenti della lista selezionata"
+  userBlacklist: "Tutte le Note tranne quelle di uno o più profili specificati"
 _weekday:
   sunday: "Domenica"
   monday: "Lunedì"
@@ -2023,6 +2030,7 @@ _notification:
   notificationWillBeDisplayedLikeThis: "La notifica apparirà così"
   _types:
     all: "Tutto"
+    note: "Nuove Note"
     follow: "Novità follower"
     mention: "Menzioni"
     reply: "Risposte"
@@ -2092,3 +2100,19 @@ _webhookSettings:
     renote: "Quando la Nota è Rinotata"
     reaction: "Quando ricevo una reazione"
     mention: "Quando mi menzionano"
+_moderationLogTypes:
+  assignRole: "Assegna un ruolo"
+  unassignRole: "Disassegna un ruolo"
+  updateRole: "Aggiorna un ruolo"
+  suspend: "Sospensione"
+  unsuspend: "Toglie la sospensione"
+  addCustomEmoji: "Aggiunge una emoji personalizzata"
+  updateServerSettings: "Aggiorna le impostazioni del server"
+  updateUserNote: "Aggiorna il promemoria di moderazione"
+  deleteDriveFile: "Elimina file da Drive"
+  deleteNote: "Elimina la Nota"
+  createGlobalAnnouncement: "Crea un annuncio globale"
+  createUserAnnouncement: "Crea un annuncio ai profili iscritti"
+  resetPassword: "Ripristina la password"
+  suspendRemoteInstance: "Sospendi istanza remota"
+  unsuspendRemoteInstance: "Riattiva istanza remota"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index 2c1f718192..efbb0ff5f1 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -2058,3 +2058,6 @@ _webhookSettings:
     renote: "Renoteされるとき~!"
     reaction: "ツッコミがあるとき~!"
     mention: "メンションがあるとき~!"
+_moderationLogTypes:
+  suspend: "凍結"
+  resetPassword: "パスワードをリセット"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 48aaa2016b..0fcb39ebc7 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2073,3 +2073,6 @@ _webhookSettings:
     renote: "누군가 내 글을 Renote했을 때"
     reaction: "누군가 내 노트에 리액션했을 때"
     mention: "누군가 나를 멘션했을 때"
+_moderationLogTypes:
+  suspend: "정지"
+  resetPassword: "비밀번호 재설정"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 37251a95c8..22cb5857f9 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -463,3 +463,5 @@ _deck:
     mentions: "ກ່າວເຖິງ"
 _webhookSettings:
   name: "ຊື່"
+_moderationLogTypes:
+  suspend: "ລະງັບ"
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index d75f807316..fd9ffa33f2 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -494,3 +494,6 @@ _deck:
     mentions: "Vermeldingen"
 _webhookSettings:
   name: "Naam"
+_moderationLogTypes:
+  suspend: "Opschorten"
+  resetPassword: "Wachtwoord terugzetten"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 35a3866b95..00f22c0c4f 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -725,3 +725,5 @@ _deck:
     direct: "Direkte"
 _webhookSettings:
   name: "Navn"
+_moderationLogTypes:
+  suspend: "Suspender"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 1e9d61706b..1c7ebe8108 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -1402,3 +1402,6 @@ _webhookSettings:
     renote: "Po udostępnieniu wpisu"
     reaction: "Po otrzymaniu reakcji"
     mention: "Po zostaniu wspomnianym"
+_moderationLogTypes:
+  suspend: "Zawieś"
+  resetPassword: "Zresetuj hasło"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 737bab9adc..f9e777bc75 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -1497,3 +1497,6 @@ _webhookSettings:
     follow: "Quando seguindo um usuário"
     followed: "Quando sendo seguido"
     renote: "Quando repostado"
+_moderationLogTypes:
+  suspend: "Suspender"
+  resetPassword: "Redefinir senha"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 605fcc82f7..51c33085af 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -704,3 +704,6 @@ _deck:
     mentions: "Mențiuni"
 _webhookSettings:
   name: "Nume"
+_moderationLogTypes:
+  suspend: "Suspendă"
+  resetPassword: "Resetează parola"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index 0bd5db9268..937158978d 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -1951,3 +1951,6 @@ _webhookSettings:
   createWebhook: "Создать вебхук"
   name: "Название"
   active: "Вкл."
+_moderationLogTypes:
+  suspend: "Заморозить"
+  resetPassword: "Сброс пароля:"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 8d2af55db9..e44aaafc0a 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -1451,3 +1451,6 @@ _deck:
 _webhookSettings:
   name: "Názov"
   active: "Zapnuté"
+_moderationLogTypes:
+  suspend: "Zmraziť"
+  resetPassword: "Resetovať heslo"
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 507492d52c..62e7d412ab 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -573,3 +573,6 @@ _deck:
 _webhookSettings:
   name: "Namn"
   active: "Aktiverad"
+_moderationLogTypes:
+  suspend: "Suspendera"
+  resetPassword: "Återställ Lösenord"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index cf2f3b9a23..c252ce93f8 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -2065,3 +2065,6 @@ _webhookSettings:
     renote: "รีโน้ตแล้วเมื่อ"
     reaction: "เมื่อได้รับรีแอคชั่น"
     mention: "เมื่อกำลังถูกกล่าวถึง"
+_moderationLogTypes:
+  suspend: "ถูกระงับ"
+  resetPassword: "รีเซ็ตรหัสผ่าน"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index f8fb275eb9..1111c23091 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -448,3 +448,6 @@ _deck:
     tl: "Zaman çizelgesi"
     list: "Listeler"
     mentions: "Bahsetmeler"
+_moderationLogTypes:
+  suspend: "askıya al"
+  resetPassword: "Şifre sıfırlama"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 1516272c60..09b3eba745 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -1620,3 +1620,6 @@ _deck:
 _webhookSettings:
   name: "Ім'я"
   active: "Увімкнено"
+_moderationLogTypes:
+  suspend: "Призупинити"
+  resetPassword: "Скинути пароль"
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index 8dbcbd9d09..726333958b 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -1084,3 +1084,6 @@ _webhookSettings:
   _events:
     renote: "Qayta qayd qilinganda"
     mention: "Eslanganda"
+_moderationLogTypes:
+  suspend: "To'xtatish"
+  resetPassword: "Parolni tiklash"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 40688e98f3..3b34e4711c 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -1860,3 +1860,6 @@ _webhookSettings:
   _events:
     reaction: "Khi nhận được sự kiện"
     mention: "Khi có người nhắc tới bạn"
+_moderationLogTypes:
+  suspend: "Vô hiệu hóa"
+  resetPassword: "Đặt lại mật khẩu"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index f1512ac348..f9da50e68a 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -418,6 +418,7 @@ moderator: "监察员"
 moderation: "管理"
 moderationNote: "管理笔记"
 addModerationNote: "添加管理笔记"
+moderationLogs: "管理日志"
 nUsersMentioned: "{n} 被提到"
 securityKeyAndPasskey: "安全密钥或 Passkey"
 securityKey: "安全密钥"
@@ -1152,6 +1153,8 @@ _serverSettings:
   appIconStyleRecommendation: "因为有可能会被裁切为圆形或者圆角矩形,建议使用边缘带有留白背景的图标。"
   appIconResolutionMustBe: "分辨率必须为 {resolution}。"
   manifestJsonOverride: "覆盖 mainfest.json"
+  shortName: "简称"
+  shortNameDescription: "如果服务器的正式名称很长,可以用简称或者別名来替代。"
 _accountMigration:
   moveFrom: "从别的账号迁移到此账户"
   moveFromSub: "为另一个账户建立别名"
@@ -2027,6 +2030,7 @@ _notification:
   notificationWillBeDisplayedLikeThis: "通知将会这样表示"
   _types:
     all: "全部"
+    note: "用户的新帖子"
     follow: "关注中"
     mention: "提及"
     reply: "回复"
@@ -2096,3 +2100,17 @@ _webhookSettings:
     renote: "被转发时"
     reaction: "被回应时"
     mention: "被提及时"
+_moderationLogTypes:
+  assignRole: "分配角色"
+  unassignRole: "取消分配角色"
+  updateRole: "更新角色"
+  suspend: "冻结"
+  unsuspend: "解除冻结"
+  addCustomEmoji: "添加自定义表情符号"
+  updateServerSettings: "更新服务器设置"
+  updateUserNote: "更新管理笔记"
+  deleteDriveFile: "删除文件"
+  deleteNote: "删除帖子"
+  createGlobalAnnouncement: "创建全体通知"
+  createUserAnnouncement: "创建用户通知"
+  resetPassword: "重置密码"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index a031730a37..9a7840b53c 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -418,7 +418,8 @@ moderator: "審查員"
 moderation: "審查"
 moderationNote: "管理筆記"
 addModerationNote: "新增管理筆記"
-nUsersMentioned: "被提及到 {n} 次"
+moderationLogs: "管理日誌"
+nUsersMentioned: "被 {n} 個人提及"
 securityKeyAndPasskey: "安全金鑰、Passkey"
 securityKey: "安全金鑰"
 lastUsed: "上次使用"
@@ -2099,3 +2100,19 @@ _webhookSettings:
     renote: "當被轉發時"
     reaction: "當獲得反應時"
     mention: "當被提到時"
+_moderationLogTypes:
+  assignRole: "指派角色"
+  unassignRole: "撤銷角色"
+  updateRole: "更新角色設定"
+  suspend: "凍結"
+  unsuspend: "解除凍結"
+  addCustomEmoji: "新增自訂表情符號"
+  updateServerSettings: "更新伺服器設定"
+  updateUserNote: "更新管理筆記"
+  deleteDriveFile: "刪除檔案"
+  deleteNote: "刪除貼文"
+  createGlobalAnnouncement: "建立全網通知"
+  createUserAnnouncement: "建立使用者通知"
+  resetPassword: "重設密碼"
+  suspendRemoteInstance: "封鎖遠端伺服器"
+  unsuspendRemoteInstance: "解除封鎖遠端伺服器"

From 8d2fb99662e227a3e6bd64b8adb5da4a146c0dce Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:10:27 +0900
Subject: [PATCH 39/62] 2023.9.0-rc.3

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 8e5540f68d..4f42b5cec5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-rc.2",
+	"version": "2023.9.0-rc.3",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From 03c868b727835d1ac64abc3340d73b88162b9de0 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:20:08 +0900
Subject: [PATCH 40/62] Update CHANGELOG.md

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 46433bca35..f3e2053f2e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -96,6 +96,7 @@
 - Enhance: nodeinfo 2.1対応
 - Enhance: 自分へのメンション一覧を取得する際のパフォーマンスを向上
 - Enhance: Docker環境でjemallocを使用することでメモリ使用量を削減
+- Enhance: ID生成方式としてaidxを追加、かつデフォルトに(既存のサーバーで変更することはできません)
 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正
 - Fix: notes/reactionsのページネーションが機能しない問題を修正
 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように

From d05563c448c331b811181936d07f94566197f7f0 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 11:21:03 +0900
Subject: [PATCH 41/62] tweak ui

---
 packages/frontend/src/components/MkNotes.vue | 3 ++-
 packages/frontend/src/pages/note.vue         | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index e57698a824..89fd504dcc 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkPagination ref="pagingComponent" :pagination="pagination">
+<MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad">
 	<template #empty>
 		<div class="_fullinfo">
 			<img :src="infoImageUrl" class="_ghost"/>
@@ -42,6 +42,7 @@ import { infoImageUrl } from '@/instance.js';
 const props = defineProps<{
 	pagination: Paging;
 	noGap?: boolean;
+	disableAutoLoad?: boolean;
 }>();
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 33a49c3af7..066a3042ba 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
 				<div v-if="note">
 					<div v-if="showNext" class="_margin">
-						<MkNotes class="" :pagination="nextPagination" :noGap="true"/>
+						<MkNotes class="" :pagination="nextPagination" :noGap="true" :disableAutoLoad="true"/>
 					</div>
 
 					<div class="_margin">

From 30b231225c85abd269c005153167e6827bfc7921 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Sun, 24 Sep 2023 14:41:41 +0900
Subject: [PATCH 42/62] =?UTF-8?q?Mk:api=E3=81=A7=E5=A4=96=E9=83=A8?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=81=A8=E3=81=AE=E6=8E=A5?=
 =?UTF-8?q?=E7=B6=9A=E3=82=92=E7=A6=81=E6=AD=A2=20(#11883)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Mk:url(): no automatic token attaching when ep is url

* Limit requests to external servers by Mk:api

* remove unused import

* Update CHANGELOG.md

* Update packages/frontend/src/scripts/aiscript/api.ts

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  | 1 +
 packages/frontend/src/scripts/aiscript/api.ts | 5 ++++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f3e2053f2e..a0c5ea293d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -86,6 +86,7 @@
 - Fix: 複数の階層があるメニューで、短くタップすると正常に動かない場合がある問題を修正
 - Fix: アニメーションがオフのとき、スマホで子メニューの選択ができない問題を修正
 - Fix: ドロワーメニューで、親メニュー項目をマウスでホバーすると子メニューが表示されてしまう問題を修正
+- Fix: AiScriptでMk:apiが外部と通信できる問題を修正
 
 ### Server
 - Change: cacheRemoteFilesの初期値はfalseになりました
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index 0bb9185936..9f60e52cea 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -34,12 +34,15 @@ export function createAiScriptEnv(opts) {
 			return confirm.canceled ? values.FALSE : values.TRUE;
 		}),
 		'Mk:api': values.FN_NATIVE(async ([ep, param, token]) => {
+			utils.assertString(ep);
+			if (ep.value.includes('://')) throw new Error('invalid endpoint');
 			if (token) {
 				utils.assertString(token);
 				// バグがあればundefinedもあり得るため念のため
 				if (typeof token.value !== 'string') throw new Error('invalid token');
 			}
-			return os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token ?? null)).then(res => {
+			const actualToken: string|null = token?.value ?? opts.token ?? null;
+			return os.api(ep.value, utils.valToJs(param), actualToken).then(res => {
 				return utils.jsToVal(res);
 			}, err => {
 				return values.ERROR('request_failed', utils.jsToVal(err));

From 82a51d49a0d9a2758ce97b169d82147e5a26318d Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:10:56 +0900
Subject: [PATCH 43/62] improve moderation log

---
 packages/frontend/package.json                |  1 +
 .../src/pages/admin/modlog.ModLog.vue         | 22 ++++++++-
 packages/frontend/src/pages/admin/modlog.vue  |  2 +-
 pnpm-lock.yaml                                | 45 ++++++++++++++++++-
 4 files changed, 67 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 2e27f9e99c..f579da3cbb 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -70,6 +70,7 @@
 		"twemoji-parser": "14.0.0",
 		"typescript": "5.2.2",
 		"uuid": "9.0.1",
+		"v-code-diff": "^1.7.1",
 		"vanilla-tilt": "1.8.1",
 		"vite": "4.4.9",
 		"vue": "3.3.4",
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 3a474f73a8..ce7b89f8f7 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -16,7 +16,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div :class="$style.root">
 		<div>{{ i18n.ts.moderator }}: {{ log.userId }}</div>
 
-		<template v-if="log.type === 'suspend'">
+		<template v-if="log.type === 'updateServerSettings'">
+			<div :class="$style.diff">
+				<CodeDiff :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+			</div>
+		</template>
+		<template v-else-if="log.type === 'updateUserNote'">
+			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
+			<div :class="$style.diff">
+				<CodeDiff :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
+			</div>
+		</template>
+		<template v-else-if="log.type === 'suspend'">
 			<div>{{ i18n.ts.user }}: {{ log.info.targetId }}</div>
 		</template>
 		<template v-else-if="log.type === 'unsuspend'">
@@ -36,6 +47,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
+import { CodeDiff } from 'v-code-diff';
+import JSON5 from 'json5';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { dateString } from '@/filters/date.js';
@@ -54,4 +67,11 @@ const props = defineProps<{
 	width: 18px;
 	height: 18px;
 }
+
+.diff {
+	background: #fff;
+	color: #000;
+	border-radius: 6px;
+	overflow: clip;
+}
 </style>
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index da043f1b8f..4b5ef5f771 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSelect v-model="type" style="margin: 0; flex: 1;">
 					<template #label>{{ i18n.ts.type }}</template>
 					<option :value="null">{{ i18n.ts.all }}</option>
-					<option v-for="t in Misskey.moderationLogTypes" :key="t" :value="t">{{ t }}</option>
+					<option v-for="t in Misskey.moderationLogTypes" :key="t" :value="t">{{ i18n.ts._moderationLogTypes[t] ?? t }}</option>
 				</MkSelect>
 				<MkInput v-model="moderatorId" style="margin: 0; flex: 1;">
 					<template #label>{{ i18n.ts.moderator }}(ID)</template>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0297fdab6d..deb08b1027 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -802,6 +802,9 @@ importers:
       uuid:
         specifier: 9.0.1
         version: 9.0.1
+      v-code-diff:
+        specifier: ^1.7.1
+        version: 1.7.1(vue@3.3.4)
       vanilla-tilt:
         specifier: 1.8.1
         version: 1.8.1
@@ -10523,6 +10526,10 @@ packages:
       - supports-color
     dev: true
 
+  /diff-match-patch@1.0.5:
+    resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
+    dev: false
+
   /diff-sequences@28.1.1:
     resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==}
     engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0}
@@ -10536,7 +10543,6 @@ packages:
   /diff@5.1.0:
     resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
     engines: {node: '>=0.3.1'}
-    dev: true
 
   /dijkstrajs@1.0.2:
     resolution: {integrity: sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==}
@@ -12361,6 +12367,11 @@ packages:
   /highlight.js@10.7.3:
     resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
 
+  /highlight.js@11.8.0:
+    resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
+    engines: {node: '>=12.0.0'}
+    dev: false
+
   /hosted-git-info@2.8.9:
     resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
     dev: true
@@ -18891,6 +18902,23 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
+  /v-code-diff@1.7.1(vue@3.3.4):
+    resolution: {integrity: sha512-2O34z6DcVw3LygR9Xl07A28115nsps56dCH6zxFMLoW1jyEnWFPN7Kwh0GAYAeWzDiltbqsMWgvfqJYjBEZPgw==}
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.4.9
+      vue: ^2.6.0 || >=3.0.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      diff: 5.1.0
+      diff-match-patch: 1.0.5
+      highlight.js: 11.8.0
+      vue: 3.3.4
+      vue-demi: 0.13.11(vue@3.3.4)
+    dev: false
+
   /v8-to-istanbul@9.1.0:
     resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==}
     engines: {node: '>=10.12.0'}
@@ -19077,6 +19105,21 @@ packages:
     resolution: {integrity: sha512-zbCQviVRexZ7NF2kizQq5LicG5QGXPHPALKE3t59f5q2FwaG9GKtdhhIV4rw4LDUm9RkvGAP8TSXlXcBWY8rFQ==}
     dev: true
 
+  /vue-demi@0.13.11(vue@3.3.4):
+    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: 3.3.4
+    dev: false
+
   /vue-docgen-api@4.64.1(vue@3.3.4):
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:

From 841e6ff901a5a11769bd419ccf8b17c0085d938c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:40:38 +0900
Subject: [PATCH 44/62] improve moderation log

---
 locales/ja-JP.yml                             |  2 +
 packages/backend/src/core/DriveService.ts     | 51 ++++++++++++++++++
 .../api/endpoints/drive/files/update.ts       | 54 ++-----------------
 packages/backend/src/types.ts                 | 10 ++++
 packages/misskey-js/etc/misskey-js.api.md     |  8 ++-
 packages/misskey-js/src/consts.ts             | 10 ++++
 packages/misskey-js/src/entities.ts           |  6 +++
 7 files changed, 90 insertions(+), 51 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 13ab6755e6..69c48a5e64 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2184,3 +2184,5 @@ _moderationLogTypes:
   resetPassword: "パスワードをリセット"
   suspendRemoteInstance: "リモートサーバーを停止"
   unsuspendRemoteInstance: "リモートサーバーを再開"
+  markSensitiveDriveFile: "ファイルをセンシティブ付与"
+  unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 2ff062142c..0409a4f53b 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -649,6 +649,57 @@ export class DriveService {
 		return file;
 	}
 
+	@bindThis
+	public async update(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
+		const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
+
+		if (values.name && !this.driveFileEntityService.validateFileName(file.name)) {
+			throw new Error('invalid filename');
+		}
+
+		if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) {
+			throw new Error('cannot unmark nsfw');
+		}
+
+		if (values.folderId != null) {
+			const folder = await this.driveFoldersRepository.findOneBy({
+				id: values.folderId,
+				userId: file.userId!,
+			});
+
+			if (folder == null) {
+				throw new Error('folder-not-found');
+			}
+		}
+
+		await this.driveFilesRepository.update(file.id, values);
+
+		const fileObj = await this.driveFileEntityService.pack(file, { self: true });
+
+		// Publish fileUpdated event
+		if (file.userId) {
+			this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj);
+		}
+
+		if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) {
+			if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) {
+				if (values.isSensitive) {
+					this.moderationLogService.log(updater, 'markSensitiveDriveFile', {
+						fileId: file.id,
+						fileUserId: file.userId,
+					});
+				} else {
+					this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', {
+						fileId: file.id,
+						fileUserId: file.userId,
+					});
+				}
+			}
+		}
+
+		return fileObj;
+	}
+
 	@bindThis
 	public async deleteFile(file: MiDriveFile, isExpired = false, deleter?: MiUser) {
 		if (file.storedInternal) {
diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts
index d26ed63474..7db88e152c 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts
@@ -4,12 +4,11 @@
  */
 
 import { Inject, Injectable } from '@nestjs/common';
-import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/_.js';
+import type { DriveFilesRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { RoleService } from '@/core/RoleService.js';
+import { DriveService } from '@/core/DriveService.js';
 import { ApiError } from '../../../error.js';
 
 export const meta = {
@@ -77,16 +76,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.driveFilesRepository)
 		private driveFilesRepository: DriveFilesRepository,
 
-		@Inject(DI.driveFoldersRepository)
-		private driveFoldersRepository: DriveFoldersRepository,
-
-		private driveFileEntityService: DriveFileEntityService,
+		private driveService: DriveService,
 		private roleService: RoleService,
-		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
-			const alwaysMarkNsfw = (await this.roleService.getUserPolicies(me.id)).alwaysMarkNsfw;
 			if (file == null) {
 				throw new ApiError(meta.errors.noSuchFile);
 			}
@@ -95,47 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			if (ps.name) file.name = ps.name;
-			if (!this.driveFileEntityService.validateFileName(file.name)) {
-				throw new ApiError(meta.errors.invalidFileName);
-			}
-
-			if (ps.comment !== undefined) file.comment = ps.comment;
-
-			if (ps.isSensitive !== undefined && ps.isSensitive !== file.isSensitive && alwaysMarkNsfw && !ps.isSensitive) {
-				throw new ApiError(meta.errors.restrictedByRole);
-			}
-
-			if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive;
-
-			if (ps.folderId !== undefined) {
-				if (ps.folderId === null) {
-					file.folderId = null;
-				} else {
-					const folder = await this.driveFoldersRepository.findOneBy({
-						id: ps.folderId,
-						userId: me.id,
-					});
-
-					if (folder == null) {
-						throw new ApiError(meta.errors.noSuchFolder);
-					}
-
-					file.folderId = folder.id;
-				}
-			}
-
-			await this.driveFilesRepository.update(file.id, {
-				name: file.name,
-				comment: file.comment,
-				folderId: file.folderId,
-				isSensitive: file.isSensitive,
-			});
-
-			const fileObj = await this.driveFileEntityService.pack(file, { self: true });
-
-			// Publish fileUpdated event
-			this.globalEventService.publishDriveStream(me.id, 'fileUpdated', fileObj);
+			const fileObj = await this.driveService.update(file, ps, me);
 
 			return fileObj;
 		});
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index ea78bb919a..16654edd88 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -52,6 +52,8 @@ export const moderationLogTypes = [
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
+	'markSensitiveDriveFile',
+	'unmarkSensitiveDriveFile',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -152,4 +154,12 @@ export type ModerationLogPayloads = {
 		id: string;
 		host: string;
 	};
+	markSensitiveDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
+	unmarkSensitiveDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
 };
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index d8b6aa44d8..b3806754a8 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2595,10 +2595,16 @@ type ModerationLog = {
 } | {
     type: 'unsuspendRemoteInstance';
     info: ModerationLogPayloads['unsuspendRemoteInstance'];
+} | {
+    type: 'markSensitiveDriveFile';
+    info: ModerationLogPayloads['markSensitiveDriveFile'];
+} | {
+    type: 'unmarkSensitiveDriveFile';
+    info: ModerationLogPayloads['unmarkSensitiveDriveFile'];
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 462ad16cc8..63137dcc83 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -70,6 +70,8 @@ export const moderationLogTypes = [
 	'resetPassword',
 	'suspendRemoteInstance',
 	'unsuspendRemoteInstance',
+	'markSensitiveDriveFile',
+	'unmarkSensitiveDriveFile',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -170,4 +172,12 @@ export type ModerationLogPayloads = {
 		id: string;
 		host: string;
 	};
+	markSensitiveDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
+	unmarkSensitiveDriveFile: {
+		fileId: string;
+		fileUserId: string | null;
+	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index e6a97f0209..f377f1a5ed 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -646,4 +646,10 @@ export type ModerationLog = {
 } | {
 	type: 'unsuspendRemoteInstance';
 	info: ModerationLogPayloads['unsuspendRemoteInstance'];
+} | {
+	type: 'markSensitiveDriveFile';
+	info: ModerationLogPayloads['markSensitiveDriveFile'];
+} | {
+	type: 'unmarkSensitiveDriveFile';
+	info: ModerationLogPayloads['unmarkSensitiveDriveFile'];
 });

From 80d52f65ebea4ec35d1cf4f1a660dc4a6f0789ed Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:41:07 +0900
Subject: [PATCH 45/62] Update index.d.ts

---
 locales/index.d.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 0ca5919dd4..aa63c03856 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2271,6 +2271,8 @@ export interface Locale {
         "resetPassword": string;
         "suspendRemoteInstance": string;
         "unsuspendRemoteInstance": string;
+        "markSensitiveDriveFile": string;
+        "unmarkSensitiveDriveFile": string;
     };
 }
 declare const locales: {

From 51546ad1ce8fd65cfc75c915686095d11d9ec168 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:46:14 +0900
Subject: [PATCH 46/62] =?UTF-8?q?fix(backend):=20Play=E3=82=92=E9=9D=9E?=
 =?UTF-8?q?=E5=85=AC=E9=96=8B=E3=81=AB=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #11884
---
 packages/backend/src/server/api/endpoints/flash/update.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/src/server/api/endpoints/flash/update.ts b/packages/backend/src/server/api/endpoints/flash/update.ts
index cc2c926749..8b5e1f99e9 100644
--- a/packages/backend/src/server/api/endpoints/flash/update.ts
+++ b/packages/backend/src/server/api/endpoints/flash/update.ts
@@ -75,6 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				summary: ps.summary,
 				script: ps.script,
 				permissions: ps.permissions,
+				visibility: ps.visibility,
 			});
 		});
 	}

From 00659220a5c18c5f31661c7de620792cb9d14d68 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:51:04 +0900
Subject: [PATCH 47/62] Update update.ts

---
 .../backend/src/server/api/endpoints/drive/files/update.ts | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts
index 7db88e152c..c9e2d7464c 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts
@@ -89,7 +89,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			const fileObj = await this.driveService.update(file, ps, me);
+			const fileObj = await this.driveService.update(file, {
+				folderId: ps.folderId,
+				name: ps.name,
+				isSensitive: ps.isSensitive,
+				comment: ps.comment,
+			}, me);
 
 			return fileObj;
 		});

From 4a7f6e6de424ac818fb9b418dfc99179cf7307bf Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 15:53:26 +0900
Subject: [PATCH 48/62] 2023.9.0-rc.4

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 4f42b5cec5..166744853d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-rc.3",
+	"version": "2023.9.0-rc.4",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From cf573add270abd24768fb3acb345b8626fabde0b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 16:24:13 +0900
Subject: [PATCH 49/62] fix

---
 packages/backend/src/core/DriveService.ts     | 11 +++++---
 .../api/endpoints/drive/files/update.ts       | 28 ++++++++++++++-----
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 0409a4f53b..891be4611d 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -87,6 +87,9 @@ type UploadFromUrlArgs = {
 
 @Injectable()
 export class DriveService {
+	public static NoSuchFolderError = class extends Error {};
+	public static InvalidFileNameError = class extends Error {};
+	public static CannotUnmarkSensitiveError = class extends Error {};
 	private registerLogger: Logger;
 	private downloaderLogger: Logger;
 	private deleteLogger: Logger;
@@ -650,15 +653,15 @@ export class DriveService {
 	}
 
 	@bindThis
-	public async update(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
+	public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
 		const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
 
 		if (values.name && !this.driveFileEntityService.validateFileName(file.name)) {
-			throw new Error('invalid filename');
+			throw new DriveService.InvalidFileNameError();
 		}
 
 		if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) {
-			throw new Error('cannot unmark nsfw');
+			throw new DriveService.CannotUnmarkSensitiveError();
 		}
 
 		if (values.folderId != null) {
@@ -668,7 +671,7 @@ export class DriveService {
 			});
 
 			if (folder == null) {
-				throw new Error('folder-not-found');
+				throw new DriveService.NoSuchFolderError();
 			}
 		}
 
diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts
index c9e2d7464c..c01f3de03c 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/update.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts
@@ -89,14 +89,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.accessDenied);
 			}
 
-			const fileObj = await this.driveService.update(file, {
-				folderId: ps.folderId,
-				name: ps.name,
-				isSensitive: ps.isSensitive,
-				comment: ps.comment,
-			}, me);
+			let packedFile;
 
-			return fileObj;
+			try {
+				packedFile = await this.driveService.updateFile(file, {
+					folderId: ps.folderId,
+					name: ps.name,
+					isSensitive: ps.isSensitive,
+					comment: ps.comment,
+				}, me);
+			} catch (e) {
+				if (e instanceof DriveService.InvalidFileNameError) {
+					throw new ApiError(meta.errors.invalidFileName);
+				} else if (e instanceof DriveService.NoSuchFolderError) {
+					throw new ApiError(meta.errors.noSuchFolder);
+				} else if (e instanceof DriveService.CannotUnmarkSensitiveError) {
+					throw new ApiError(meta.errors.restrictedByRole);
+				} else {
+					throw e;
+				}
+			}
+
+			return packedFile;
 		});
 	}
 }

From fe570fe16be41b8413a9f0afc2a9cd20f3fe3481 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 16:45:36 +0900
Subject: [PATCH 50/62] Update DriveService.ts

---
 packages/backend/src/core/DriveService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 891be4611d..366205f586 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -677,7 +677,7 @@ export class DriveService {
 
 		await this.driveFilesRepository.update(file.id, values);
 
-		const fileObj = await this.driveFileEntityService.pack(file, { self: true });
+		const fileObj = await this.driveFileEntityService.pack(file.id, { self: true });
 
 		// Publish fileUpdated event
 		if (file.userId) {

From 48314a39e098a6350bfa892e8a127eb11beeb2ac Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 18:14:03 +0900
Subject: [PATCH 51/62] Update CHANGELOG.md

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0c5ea293d..6b696116b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -97,7 +97,7 @@
 - Enhance: nodeinfo 2.1対応
 - Enhance: 自分へのメンション一覧を取得する際のパフォーマンスを向上
 - Enhance: Docker環境でjemallocを使用することでメモリ使用量を削減
-- Enhance: ID生成方式としてaidxを追加、かつデフォルトに(既存のサーバーで変更することはできません)
+- Enhance: ID生成方式としてaidxを追加、かつデフォルトに
 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正
 - Fix: notes/reactionsのページネーションが機能しない問題を修正
 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように

From 65aef45050bcfff40f2798a56d1b2ffebd85f376 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 18:14:17 +0900
Subject: [PATCH 52/62] New Crowdin updates (#11885)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)
---
 locales/de-DE.yml | 10 +++++++++-
 locales/en-US.yml |  8 ++++++++
 locales/zh-CN.yml |  8 ++++++++
 locales/zh-TW.yml |  6 ++++++
 4 files changed, 31 insertions(+), 1 deletion(-)

diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 11237d2e69..98afd4db4c 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -2107,12 +2107,20 @@ _moderationLogTypes:
   suspend: "Gesperrt"
   unsuspend: "Entsperrt"
   addCustomEmoji: "Benutzerdefiniertes Emoji hinzugefügt"
+  updateCustomEmoji: "Benutzerdefiniertes Emoji aktualisiert"
+  deleteCustomEmoji: "Benutzerdefiniertes Emoji gelöscht"
   updateServerSettings: "Servereinstellungen aktualisiert"
   updateUserNote: "Moderationsnotiz aktualisiert"
   deleteDriveFile: "Datei gelöscht"
   deleteNote: "Notiz gelöscht"
   createGlobalAnnouncement: "Globale Ankündigung erstellt"
-  createUserAnnouncement: "Benutzerankündigung erstellt"
+  createUserAnnouncement: "Benutzerspezifische Ankündigung erstellt"
+  updateGlobalAnnouncement: "Globale Ankündigung aktualisiert"
+  updateUserAnnouncement: "Benutzerspezifische Ankündigung aktualisiert"
+  deleteGlobalAnnouncement: "Globale Ankündigung gelöscht"
+  deleteUserAnnouncement: "Benutzerspezifische Ankündigung gelöscht"
   resetPassword: "Passwort zurückgesetzt"
   suspendRemoteInstance: "Fremde Instanz gesperrt"
   unsuspendRemoteInstance: "Fremde Instanz entsperrt"
+  markSensitiveDriveFile: "Datei als sensitiv markiert"
+  unmarkSensitiveDriveFile: "Datei als nicht sensitiv markiert"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 4cda0ff8c2..75acf424be 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -2107,12 +2107,20 @@ _moderationLogTypes:
   suspend: "Suspended"
   unsuspend: "Unsuspended"
   addCustomEmoji: "Custom emoji added"
+  updateCustomEmoji: "Custom emoji updated"
+  deleteCustomEmoji: "Custom emoji deleted"
   updateServerSettings: "Server settings updated"
   updateUserNote: "Moderation note updated"
   deleteDriveFile: "File deleted"
   deleteNote: "Note deleted"
   createGlobalAnnouncement: "Global announcement created"
   createUserAnnouncement: "User announcement created"
+  updateGlobalAnnouncement: "Global announcement updated"
+  updateUserAnnouncement: "User announcement updated"
+  deleteGlobalAnnouncement: "Global announcement deleted"
+  deleteUserAnnouncement: "User announcement deleted"
   resetPassword: "Password reset"
   suspendRemoteInstance: "Remote instance suspended"
   unsuspendRemoteInstance: "Remote instance unsuspended"
+  markSensitiveDriveFile: "File marked as sensitive"
+  unmarkSensitiveDriveFile: "File unmarked as sensitive"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index f9da50e68a..bd03dba185 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -2107,10 +2107,18 @@ _moderationLogTypes:
   suspend: "冻结"
   unsuspend: "解除冻结"
   addCustomEmoji: "添加自定义表情符号"
+  updateCustomEmoji: "更新自定义表情符号"
+  deleteCustomEmoji: "删除自定义表情符号"
   updateServerSettings: "更新服务器设置"
   updateUserNote: "更新管理笔记"
   deleteDriveFile: "删除文件"
   deleteNote: "删除帖子"
   createGlobalAnnouncement: "创建全体通知"
   createUserAnnouncement: "创建用户通知"
+  updateGlobalAnnouncement: "更新全体通知"
+  updateUserAnnouncement: "更新用户通知"
+  deleteGlobalAnnouncement: "删除全体通知"
+  deleteUserAnnouncement: "删除用户通知"
   resetPassword: "重置密码"
+  markSensitiveDriveFile: "标记网盘文件为敏感媒体"
+  unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 9a7840b53c..cd6473aebd 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2107,12 +2107,18 @@ _moderationLogTypes:
   suspend: "凍結"
   unsuspend: "解除凍結"
   addCustomEmoji: "新增自訂表情符號"
+  updateCustomEmoji: "更新自訂表情符號"
+  deleteCustomEmoji: "刪除自訂表情符號"
   updateServerSettings: "更新伺服器設定"
   updateUserNote: "更新管理筆記"
   deleteDriveFile: "刪除檔案"
   deleteNote: "刪除貼文"
   createGlobalAnnouncement: "建立全網通知"
   createUserAnnouncement: "建立使用者通知"
+  updateGlobalAnnouncement: "更新全部的公告"
+  updateUserAnnouncement: "更新使用者的公告"
+  deleteGlobalAnnouncement: "刪除全部的公告"
+  deleteUserAnnouncement: "刪除使用者的公告"
   resetPassword: "重設密碼"
   suspendRemoteInstance: "封鎖遠端伺服器"
   unsuspendRemoteInstance: "解除封鎖遠端伺服器"

From 281369d8c51b85572e45f22a1fac643adeab6511 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Sep 2023 18:14:46 +0900
Subject: [PATCH 53/62] 2023.9.0

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 166744853d..67e41b6de6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0-rc.4",
+	"version": "2023.9.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From dc8ab01168873c4eafe79eb8002a0028b58758c0 Mon Sep 17 00:00:00 2001
From: taichan <40626578+taichanNE30@users.noreply.github.com>
Date: Sun, 24 Sep 2023 20:08:00 +0900
Subject: [PATCH 54/62] =?UTF-8?q?fix(backend):=20=E3=81=8A=E7=9F=A5?=
 =?UTF-8?q?=E3=82=89=E3=81=9B=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=8D?=
 =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=8C=E6=A9=9F=E8=83=BD?=
 =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=20#11800=20(#11890)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): お知らせのページネーションが機能しない #11800

* Update CHANGELOG
---
 CHANGELOG.md                                           | 10 ++++++++++
 .../backend/src/server/api/endpoints/announcements.ts  |  2 +-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b696116b7..5396962c07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,16 @@
 
 -->
 
+## (unreleased)
+### General
+-
+
+### Client
+-
+
+### Server
+- Fix: お知らせのページネーションが機能しない
+
 ## 2023.9.0 (unreleased)
 
 ### Note
diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts
index 498afe3448..7c242dbcd5 100644
--- a/packages/backend/src/server/api/endpoints/announcements.ts
+++ b/packages/backend/src/server/api/endpoints/announcements.ts
@@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId)
-				.where('announcement.isActive = :isActive', { isActive: ps.isActive })
+				.andWhere('announcement.isActive = :isActive', { isActive: ps.isActive })
 				.andWhere(new Brackets(qb => {
 					if (me) qb.orWhere('announcement.userId = :meId', { meId: me.id });
 					qb.orWhere('announcement.userId IS NULL');

From 646a8d1a54106cf0ec691a5ea08da9d2009762c0 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 24 Sep 2023 21:54:09 +0900
Subject: [PATCH 55/62] Add address bind config option (outgoingAddress) is
 feature of 2023.9 (#11894)

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5396962c07..37d5029575 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -108,6 +108,7 @@
 - Enhance: 自分へのメンション一覧を取得する際のパフォーマンスを向上
 - Enhance: Docker環境でjemallocを使用することでメモリ使用量を削減
 - Enhance: ID生成方式としてaidxを追加、かつデフォルトに
+- Enhance: Add address bind config option (outgoingAddress)
 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正
 - Fix: notes/reactionsのページネーションが機能しない問題を修正
 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように
@@ -130,7 +131,6 @@
 ### Server
 - Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
 - Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正
-- Enhance: Add address bind config option (outgoingAddress)
 
 ## 13.14.1
 

From 5318532a8d1208b4f286856e98ff199bfdeacb9c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Sep 2023 10:29:12 +0900
Subject: [PATCH 56/62] enhance: improve moderation log

---
 CHANGELOG.md                                  |  4 +-
 locales/index.d.ts                            |  5 +-
 locales/ja-JP.yml                             |  5 +-
 .../backend/src/core/AnnouncementService.ts   | 12 ++++-
 .../backend/src/core/CustomEmojiService.ts    |  7 +--
 packages/backend/src/core/DriveService.ts     |  8 ++++
 .../backend/src/core/NoteDeleteService.ts     |  3 ++
 packages/backend/src/core/RoleService.ts      | 46 +++++++++++++++++-
 .../api/endpoints/admin/reset-password.ts     |  4 +-
 .../api/endpoints/admin/roles/create.ts       | 35 ++------------
 .../api/endpoints/admin/roles/update.ts       |  2 -
 .../api/endpoints/admin/suspend-user.ts       |  4 +-
 .../api/endpoints/admin/unsuspend-user.ts     |  4 +-
 .../api/endpoints/admin/update-user-note.ts   |  2 +
 packages/backend/src/types.ts                 | 36 ++++++++++++--
 .../src/pages/admin/modlog.ModLog.vue         | 48 ++++++++++++++++---
 packages/misskey-js/etc/misskey-js.api.md     |  5 +-
 packages/misskey-js/src/consts.ts             | 36 ++++++++++++--
 packages/misskey-js/src/entities.ts           |  3 ++
 19 files changed, 209 insertions(+), 60 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37d5029575..ae5d90fff1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,7 +14,7 @@
 
 ## (unreleased)
 ### General
--
+- Enhance: モデレーションログ機能の強化
 
 ### Client
 -
@@ -22,7 +22,7 @@
 ### Server
 - Fix: お知らせのページネーションが機能しない
 
-## 2023.9.0 (unreleased)
+## 2023.9.0
 
 ### Note
 - meilisearchを使用する場合、v1.2以上が必要です
diff --git a/locales/index.d.ts b/locales/index.d.ts
index aa63c03856..5e24ecffa5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1123,6 +1123,7 @@ export interface Locale {
     "unnotifyNotes": string;
     "authentication": string;
     "authenticationRequiredToContinue": string;
+    "dateAndTime": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
@@ -2250,9 +2251,11 @@ export interface Locale {
         };
     };
     "_moderationLogTypes": {
+        "createRole": string;
+        "deleteRole": string;
+        "updateRole": string;
         "assignRole": string;
         "unassignRole": string;
-        "updateRole": string;
         "suspend": string;
         "unsuspend": string;
         "addCustomEmoji": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 69c48a5e64..1af73c6201 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1120,6 +1120,7 @@ notifyNotes: "投稿を通知"
 unnotifyNotes: "投稿の通知を解除"
 authentication: "認証"
 authenticationRequiredToContinue: "続けるには認証を行ってください"
+dateAndTime: "日時"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
@@ -2163,9 +2164,11 @@ _webhookSettings:
     mention: "メンションされたとき"
 
 _moderationLogTypes:
+  createRole: "ロールを作成"
+  deleteRole: "ロールを削除"
+  updateRole: "ロールを更新"
   assignRole: "ロールへアサイン"
   unassignRole: "ロールのアサイン解除"
-  updateRole: "ロール設定更新"
   suspend: "凍結"
   unsuspend: "凍結解除"
   addCustomEmoji: "カスタム絵文字追加"
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index 2b4877788a..ddacc0936f 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { Brackets } from 'typeorm';
 import { DI } from '@/di-symbols.js';
 import type { MiUser } from '@/models/User.js';
-import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead } from '@/models/_.js';
+import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js';
 import { bindThis } from '@/decorators.js';
 import { Packed } from '@/misc/json-schema.js';
 import { IdService } from '@/core/IdService.js';
@@ -23,6 +23,9 @@ export class AnnouncementService {
 		@Inject(DI.announcementReadsRepository)
 		private announcementReadsRepository: AnnouncementReadsRepository,
 
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+
 		private idService: IdService,
 		private globalEventService: GlobalEventService,
 		private moderationLogService: ModerationLogService,
@@ -83,10 +86,13 @@ export class AnnouncementService {
 			});
 
 			if (moderator) {
+				const user = await this.usersRepository.findOneByOrFail({ id: values.userId });
 				this.moderationLogService.log(moderator, 'createUserAnnouncement', {
 					announcementId: announcement.id,
 					announcement: announcement,
 					userId: values.userId,
+					userUsername: user.username,
+					userHost: user.host,
 				});
 			}
 		} else {
@@ -127,10 +133,14 @@ export class AnnouncementService {
 
 		if (moderator) {
 			if (announcement.userId) {
+				const user = await this.usersRepository.findOneByOrFail({ id: announcement.userId });
 				this.moderationLogService.log(moderator, 'updateUserAnnouncement', {
 					announcementId: announcement.id,
 					before: announcement,
 					after: after,
+					userId: announcement.userId,
+					userUsername: user.username,
+					userHost: user.host,
 				});
 			} else {
 				this.moderationLogService.log(moderator, 'updateGlobalAnnouncement', {
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index b14a8666e6..1b545a124e 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -134,11 +134,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
 
 		this.localEmojisCache.refresh();
 
-		const updated = await this.emojiEntityService.packDetailed(emoji.id);
+		const packed = await this.emojiEntityService.packDetailed(emoji.id);
 
 		if (emoji.name === data.name) {
 			this.globalEventService.publishBroadcastStream('emojiUpdated', {
-				emojis: [updated],
+				emojis: [packed],
 			});
 		} else {
 			this.globalEventService.publishBroadcastStream('emojiDeleted', {
@@ -146,11 +146,12 @@ export class CustomEmojiService implements OnApplicationShutdown {
 			});
 
 			this.globalEventService.publishBroadcastStream('emojiAdded', {
-				emoji: updated,
+				emoji: packed,
 			});
 		}
 
 		if (moderator) {
+			const updated = await this.emojisRepository.findOneByOrFail({ id: id });
 			this.moderationLogService.log(moderator, 'updateCustomEmoji', {
 				emojiId: emoji.id,
 				before: emoji,
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index 366205f586..cecbec9638 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -686,15 +686,20 @@ export class DriveService {
 
 		if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) {
 			if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) {
+				const user = file.userId ? await this.usersRepository.findOneByOrFail({ id: file.userId }) : null;
 				if (values.isSensitive) {
 					this.moderationLogService.log(updater, 'markSensitiveDriveFile', {
 						fileId: file.id,
 						fileUserId: file.userId,
+						fileUserUsername: user?.username ?? null,
+						fileUserHost: user?.host ?? null,
 					});
 				} else {
 					this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', {
 						fileId: file.id,
 						fileUserId: file.userId,
+						fileUserUsername: user?.username ?? null,
+						fileUserHost: user?.host ?? null,
 					});
 				}
 			}
@@ -795,9 +800,12 @@ export class DriveService {
 		}
 
 		if (deleter && await this.roleService.isModerator(deleter) && (file.userId !== deleter.id)) {
+			const user = file.userId ? await this.usersRepository.findOneByOrFail({ id: file.userId }) : null;
 			this.moderationLogService.log(deleter, 'deleteDriveFile', {
 				fileId: file.id,
 				fileUserId: file.userId,
+				fileUserUsername: user?.username ?? null,
+				fileUserHost: user?.host ?? null,
 			});
 		}
 	}
diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts
index c99f92b9cb..87979f22ac 100644
--- a/packages/backend/src/core/NoteDeleteService.ts
+++ b/packages/backend/src/core/NoteDeleteService.ts
@@ -135,9 +135,12 @@ export class NoteDeleteService {
 		});
 
 		if (deleter && (note.userId !== deleter.id)) {
+			const user = await this.usersRepository.findOneByOrFail({ id: note.userId });
 			this.moderationLogService.log(deleter, 'deleteNote', {
 				noteId: note.id,
 				noteUserId: note.userId,
+				noteUserUsername: user.username,
+				noteUserHost: user.host,
 				note: note,
 			});
 		}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index dea6dc68cd..934b7d676b 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -412,10 +412,13 @@ export class RoleService implements OnApplicationShutdown {
 		this.globalEventService.publishInternalEvent('userRoleAssigned', created);
 
 		if (moderator) {
+			const user = await this.usersRepository.findOneByOrFail({ id: userId });
 			this.moderationLogService.log(moderator, 'assignRole', {
 				roleId: roleId,
 				roleName: role.name,
 				userId: userId,
+				userUsername: user.username,
+				userHost: user.host,
 				expiresAt: expiresAt ? expiresAt.toISOString() : null,
 			});
 		}
@@ -445,11 +448,16 @@ export class RoleService implements OnApplicationShutdown {
 		this.globalEventService.publishInternalEvent('userRoleUnassigned', existing);
 
 		if (moderator) {
-			const role = await this.rolesRepository.findOneByOrFail({ id: roleId });
+			const [user, role] = await Promise.all([
+				this.usersRepository.findOneByOrFail({ id: userId }),
+				this.rolesRepository.findOneByOrFail({ id: roleId }),
+			]);
 			this.moderationLogService.log(moderator, 'unassignRole', {
 				roleId: roleId,
 				roleName: role.name,
 				userId: userId,
+				userUsername: user.username,
+				userHost: user.host,
 			});
 		}
 	}
@@ -473,6 +481,42 @@ export class RoleService implements OnApplicationShutdown {
 		redisPipeline.exec();
 	}
 
+	@bindThis
+	public async create(values: Partial<MiRole>, moderator?: MiUser): Promise<MiRole> {
+		const date = new Date();
+		const created = await this.rolesRepository.insert({
+			id: this.idService.genId(),
+			createdAt: date,
+			updatedAt: date,
+			lastUsedAt: date,
+			name: values.name,
+			description: values.description,
+			color: values.color,
+			iconUrl: values.iconUrl,
+			target: values.target,
+			condFormula: values.condFormula,
+			isPublic: values.isPublic,
+			isAdministrator: values.isAdministrator,
+			isModerator: values.isModerator,
+			isExplorable: values.isExplorable,
+			asBadge: values.asBadge,
+			canEditMembersByModerator: values.canEditMembersByModerator,
+			displayOrder: values.displayOrder,
+			policies: values.policies,
+		}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
+
+		this.globalEventService.publishInternalEvent('roleCreated', created);
+
+		if (moderator) {
+			this.moderationLogService.log(moderator, 'createRole', {
+				roleId: created.id,
+				role: created,
+			});
+		}
+
+		return created;
+	}
+
 	@bindThis
 	public async update(role: MiRole, params: Partial<MiRole>, moderator?: MiUser): Promise<void> {
 		const date = new Date();
diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
index 6ce7583276..13e9c30ed8 100644
--- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
@@ -73,7 +73,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			});
 
 			this.moderationLogService.log(me, 'resetPassword', {
-				targetId: user.id,
+				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
 			});
 
 			return {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
index f567b0d387..8451b1955f 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -5,11 +5,8 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { RolesRepository } from '@/models/_.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
-import { DI } from '@/di-symbols.js';
-import { IdService } from '@/core/IdService.js';
 import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
+import { RoleService } from '@/core/RoleService.js';
 
 export const meta = {
 	tags: ['admin', 'role'],
@@ -58,37 +55,11 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.rolesRepository)
-		private rolesRepository: RolesRepository,
-
-		private globalEventService: GlobalEventService,
-		private idService: IdService,
 		private roleEntityService: RoleEntityService,
+		private roleService: RoleService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const date = new Date();
-			const created = await this.rolesRepository.insert({
-				id: this.idService.genId(),
-				createdAt: date,
-				updatedAt: date,
-				lastUsedAt: date,
-				name: ps.name,
-				description: ps.description,
-				color: ps.color,
-				iconUrl: ps.iconUrl,
-				target: ps.target,
-				condFormula: ps.condFormula,
-				isPublic: ps.isPublic,
-				isAdministrator: ps.isAdministrator,
-				isModerator: ps.isModerator,
-				isExplorable: ps.isExplorable,
-				asBadge: ps.asBadge,
-				canEditMembersByModerator: ps.canEditMembersByModerator,
-				displayOrder: ps.displayOrder,
-				policies: ps.policies,
-			}).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0]));
-
-			this.globalEventService.publishInternalEvent('roleCreated', created);
+			const created = await this.roleService.create(ps, me);
 
 			return await this.roleEntityService.pack(created, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
index e4e59e487c..6031e2363e 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -79,9 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.noSuchRole);
 			}
 
-			const date = new Date();
 			await this.roleService.update(role, {
-				updatedAt: date,
 				name: ps.name,
 				description: ps.description,
 				color: ps.color,
diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
index 89199f8bff..9464f4b677 100644
--- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
@@ -61,7 +61,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			});
 
 			this.moderationLogService.log(me, 'suspend', {
-				targetId: user.id,
+				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
 			});
 
 			(async () => {
diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
index a2779148ed..5e523bbc31 100644
--- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -46,7 +46,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			});
 
 			this.moderationLogService.log(me, 'unsuspend', {
-				targetId: user.id,
+				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
 			});
 
 			this.userSuspendService.doPostUnsuspend(user);
diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
index 2e9fd5ad29..bfccc2a2a5 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts
@@ -51,6 +51,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			this.moderationLogService.log(me, 'updateUserNote', {
 				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
 				before: currentProfile.moderationNote,
 				after: ps.text,
 			});
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 16654edd88..b458c0fbcb 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -37,6 +37,7 @@ export const moderationLogTypes = [
 	'deleteCustomEmoji',
 	'assignRole',
 	'unassignRole',
+	'createRole',
 	'updateRole',
 	'deleteRole',
 	'clearQueue',
@@ -62,13 +63,19 @@ export type ModerationLogPayloads = {
 		after: any | null;
 	};
 	suspend: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	unsuspend: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	updateUserNote: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		before: string | null;
 		after: string | null;
 	};
@@ -87,15 +94,23 @@ export type ModerationLogPayloads = {
 	};
 	assignRole: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		roleId: string;
 		roleName: string;
 		expiresAt: string | null;
 	};
 	unassignRole: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		roleId: string;
 		roleName: string;
 	};
+	createRole: {
+		roleId: string;
+		role: any;
+	};
 	updateRole: {
 		roleId: string;
 		before: any;
@@ -110,10 +125,14 @@ export type ModerationLogPayloads = {
 	deleteDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 	deleteNote: {
 		noteId: string;
 		noteUserId: string;
+		noteUserUsername: string;
+		noteUserHost: string | null;
 		note: any;
 	};
 	createGlobalAnnouncement: {
@@ -124,6 +143,8 @@ export type ModerationLogPayloads = {
 		announcementId: string;
 		announcement: any;
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	updateGlobalAnnouncement: {
 		announcementId: string;
@@ -134,6 +155,9 @@ export type ModerationLogPayloads = {
 		announcementId: string;
 		before: any;
 		after: any;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	deleteGlobalAnnouncement: {
 		announcementId: string;
@@ -144,7 +168,9 @@ export type ModerationLogPayloads = {
 		announcement: any;
 	};
 	resetPassword: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	suspendRemoteInstance: {
 		id: string;
@@ -157,9 +183,13 @@ export type ModerationLogPayloads = {
 	markSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 	unmarkSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 };
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index ce7b89f8f7..f0de026ad8 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -5,33 +5,61 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkFolder>
-	<template #label>{{ i18n.ts._moderationLogTypes[log.type] }}</template>
+	<template #label>
+		<b>{{ i18n.ts._moderationLogTypes[log.type] }}</b>
+		<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'resetPassword'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'assignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'unassignRole'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'createRole'">: {{ log.info.role.name }}</span>
+		<span v-else-if="log.type === 'updateRole'">: {{ log.info.before.name }}</span>
+		<span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span>
+		<span v-else-if="log.type === 'updateCustomEmoji'">: {{ log.info.before.name }}</span>
+		<span v-else-if="log.type === 'markSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
+		<span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
+		<span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span>
+		<span v-else-if="log.type === 'unsuspendRemoteInstance'">: {{ log.info.host }}</span>
+		<span v-else-if="log.type === 'createUserAnnouncement'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'updateUserAnnouncement'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
+		<span v-else-if="log.type === 'deleteNote'">: @{{ log.info.noteUserUsername }}{{ log.info.noteUserHost ? '@' + log.info.noteUserHost : '' }}</span>
+		<span v-else-if="log.type === 'deleteDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
+	</template>
 	<template #icon>
 		<MkAvatar :user="log.user" :class="$style.avatar"/>
 	</template>
 	<template #suffix>
-		<MkTime :time="log.createdAt" mode="detail"/>
+		<MkTime :time="log.createdAt"/>
 	</template>
 
 	<div :class="$style.root">
-		<div>{{ i18n.ts.moderator }}: {{ log.userId }}</div>
+		<div style="display: flex; gap: var(--margin); flex-wrap: wrap;">
+			<div style="flex: 1;">{{ i18n.ts.moderator }}: <MkA :to="`/admin/user/${log.userId}`" class="_link">@{{ log.user?.username }}</MkA></div>
+			<div style="flex: 1;">{{ i18n.ts.dateAndTime }}: <MkTime :time="log.createdAt" mode="detail"/></div>
+		</div>
 
 		<template v-if="log.type === 'updateServerSettings'">
 			<div :class="$style.diff">
-				<CodeDiff :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
 			</div>
 		</template>
 		<template v-else-if="log.type === 'updateUserNote'">
 			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
 			<div :class="$style.diff">
-				<CodeDiff :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
+				<CodeDiff :context="5" :hideHeader="true" :oldString="log.info.before ?? ''" :newString="log.info.after ?? ''" maxHeight="300px"/>
 			</div>
 		</template>
 		<template v-else-if="log.type === 'suspend'">
-			<div>{{ i18n.ts.user }}: {{ log.info.targetId }}</div>
+			<div>{{ i18n.ts.user }}: <MkA :to="`/admin/user/${log.info.userId}`" class="_link">@{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</MkA></div>
 		</template>
 		<template v-else-if="log.type === 'unsuspend'">
-			<div>{{ i18n.ts.user }}: {{ log.info.targetId }}</div>
+			<div>{{ i18n.ts.user }}: <MkA :to="`/admin/user/${log.info.userId}`" class="_link">@{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</MkA></div>
+		</template>
+		<template v-else-if="log.type === 'updateRole'">
+			<div :class="$style.diff">
+				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+			</div>
 		</template>
 		<template v-else-if="log.type === 'assignRole'">
 			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
@@ -41,6 +69,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div>{{ i18n.ts.user }}: {{ log.info.userId }}</div>
 			<div>{{ i18n.ts.role }}: {{ log.info.roleName }} [{{ log.info.roleId }}]</div>
 		</template>
+		<template v-else-if="log.type === 'updateCustomEmoji'">
+			<div>{{ i18n.ts.emoji }}: {{ log.info.emojiId }}</div>
+			<div :class="$style.diff">
+				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+			</div>
+		</template>
 	</div>
 </MkFolder>
 </template>
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index b3806754a8..f72532f297 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2550,6 +2550,9 @@ type ModerationLog = {
 } | {
     type: 'unassignRole';
     info: ModerationLogPayloads['unassignRole'];
+} | {
+    type: 'createRole';
+    info: ModerationLogPayloads['createRole'];
 } | {
     type: 'updateRole';
     info: ModerationLogPayloads['updateRole'];
@@ -2604,7 +2607,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 63137dcc83..d2751c447e 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -55,6 +55,7 @@ export const moderationLogTypes = [
 	'deleteCustomEmoji',
 	'assignRole',
 	'unassignRole',
+	'createRole',
 	'updateRole',
 	'deleteRole',
 	'clearQueue',
@@ -80,13 +81,19 @@ export type ModerationLogPayloads = {
 		after: any | null;
 	};
 	suspend: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	unsuspend: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	updateUserNote: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		before: string | null;
 		after: string | null;
 	};
@@ -105,15 +112,23 @@ export type ModerationLogPayloads = {
 	};
 	assignRole: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		roleId: string;
 		roleName: string;
 		expiresAt: string | null;
 	};
 	unassignRole: {
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 		roleId: string;
 		roleName: string;
 	};
+	createRole: {
+		roleId: string;
+		role: any;
+	};
 	updateRole: {
 		roleId: string;
 		before: any;
@@ -128,10 +143,14 @@ export type ModerationLogPayloads = {
 	deleteDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 	deleteNote: {
 		noteId: string;
 		noteUserId: string;
+		noteUserUsername: string;
+		noteUserHost: string | null;
 		note: any;
 	};
 	createGlobalAnnouncement: {
@@ -142,6 +161,8 @@ export type ModerationLogPayloads = {
 		announcementId: string;
 		announcement: any;
 		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	updateGlobalAnnouncement: {
 		announcementId: string;
@@ -152,6 +173,9 @@ export type ModerationLogPayloads = {
 		announcementId: string;
 		before: any;
 		after: any;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	deleteGlobalAnnouncement: {
 		announcementId: string;
@@ -162,7 +186,9 @@ export type ModerationLogPayloads = {
 		announcement: any;
 	};
 	resetPassword: {
-		targetId: string;
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
 	};
 	suspendRemoteInstance: {
 		id: string;
@@ -175,9 +201,13 @@ export type ModerationLogPayloads = {
 	markSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 	unmarkSensitiveDriveFile: {
 		fileId: string;
 		fileUserId: string | null;
+		fileUserUsername: string | null;
+		fileUserHost: string | null;
 	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index f377f1a5ed..41c9bdef6e 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -601,6 +601,9 @@ export type ModerationLog = {
 } | {
 	type: 'unassignRole';
 	info: ModerationLogPayloads['unassignRole'];
+} | {
+	type: 'createRole';
+	info: ModerationLogPayloads['createRole'];
 } | {
 	type: 'updateRole';
 	info: ModerationLogPayloads['updateRole'];

From dcaea66dbfc642d9e3753561364081a38c4b659c Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Mon, 25 Sep 2023 10:30:00 +0900
Subject: [PATCH 57/62] =?UTF-8?q?fix(frontend):=20=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=82=A2=E3=82=A6=E3=83=88=E7=8A=B6=E6=85=8B=E3=81=A7=E3=81=AE?=
 =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=83=A1=E3=83=8B=E3=83=A5=E3=83=BC?=
 =?UTF-8?q?=E5=86=85=E3=81=AE=E8=A9=B3=E7=B4=B0=E3=83=9C=E3=82=BF=E3=83=B3?=
 =?UTF-8?q?=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=82=92=E3=83=AD=E3=82=B0=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E7=8A=B6=E6=85=8B=E3=81=A8=E5=90=8C=E3=81=98=E3=81=AB?=
 =?UTF-8?q?=20(#11896)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): ログアウト状態でのノートの詳細ボタンの表示をログイン状態と同じに

* Update CHANGELOG.md
---
 CHANGELOG.md                                   | 2 +-
 packages/frontend/src/scripts/get-note-menu.ts | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae5d90fff1..2703ef2aac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,7 @@
 - Enhance: モデレーションログ機能の強化
 
 ### Client
--
+- Fix: ノートのメニューにある「詳細」ボタンの表示がログイン/ログアウト状態で統一されていない問題を修正
 
 ### Server
 - Fix: お知らせのページネーションが機能しない
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 734a632039..0948741fc5 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -368,8 +368,8 @@ export function getNoteMenu(props: {
 			.filter(x => x !== undefined);
 	} else {
 		menu = [{
-			icon: 'ti ti-external-link',
-			text: i18n.ts.detailed,
+			icon: 'ti ti-info-circle',
+			text: i18n.ts.details,
 			action: openDetail,
 		}, {
 			icon: 'ti ti-copy',

From 576158e883aaed29c9aefa904c516c7ae7db02c3 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Sep 2023 10:34:07 +0900
Subject: [PATCH 58/62] =?UTF-8?q?fix(backend):=20=E3=80=8C=E3=83=A6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E6=96=B0=E8=A6=8F=E6=8A=95?=
 =?UTF-8?q?=E7=A8=BF=E3=80=8D=E3=81=AE=E9=80=9A=E7=9F=A5=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E3=82=92=E5=88=87=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8B=E3=81=A8?=
 =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E5=86=85=E9=83=A8=E3=82=A8?=
 =?UTF-8?q?=E3=83=A9=E3=83=BC=E3=81=8C=E5=87=BA=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #11892
---
 .../1695605508898-mutingNotificationTypes.js  | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 packages/backend/migration/1695605508898-mutingNotificationTypes.js

diff --git a/packages/backend/migration/1695605508898-mutingNotificationTypes.js b/packages/backend/migration/1695605508898-mutingNotificationTypes.js
new file mode 100644
index 0000000000..8c0e52a2f0
--- /dev/null
+++ b/packages/backend/migration/1695605508898-mutingNotificationTypes.js
@@ -0,0 +1,21 @@
+export class MutingNotificationTypes1695605508898 {
+    name = 'MutingNotificationTypes1695605508898'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" RENAME TO "user_profile_mutingnotificationtypes_enum_old"`);
+        await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test', 'pollVote', 'groupInvited')`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum"[]`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
+        await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum_old"`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`);
+        await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
+        await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`);
+        await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`);
+    }
+}

From 89edf8f81eb69aad50b2e4c202b06bbe3a36044c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Sep 2023 10:35:34 +0900
Subject: [PATCH 59/62] 2023.9.1

---
 CHANGELOG.md | 4 +++-
 package.json | 2 +-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2703ef2aac..26013c14df 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,7 +12,8 @@
 
 -->
 
-## (unreleased)
+## 2023.9.1
+
 ### General
 - Enhance: モデレーションログ機能の強化
 
@@ -21,6 +22,7 @@
 
 ### Server
 - Fix: お知らせのページネーションが機能しない
+- Fix: 「ユーザーの新規投稿」の通知設定を切り替えるとサーバー内部エラーが出る
 
 ## 2023.9.0
 
diff --git a/package.json b/package.json
index 67e41b6de6..eb82b503a2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.9.0",
+	"version": "2023.9.1",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From ee44f35feab7151630d7db5cd61286d3836506f3 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Sep 2023 13:57:09 +0900
Subject: [PATCH 60/62] [skip ci] New Crowdin updates (#11891)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (Chinese Simplified)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Chinese Traditional)

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/de-DE.yml |  2 +-
 locales/en-US.yml |  2 +-
 locales/it-IT.yml | 38 +++++++++++++++++++++++---------------
 locales/zh-CN.yml |  2 +-
 locales/zh-TW.yml | 31 ++++++++++++++++++-------------
 5 files changed, 44 insertions(+), 31 deletions(-)

diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 98afd4db4c..aae5f45046 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -2101,9 +2101,9 @@ _webhookSettings:
     reaction: "Wenn du eine Reaktion erhältst"
     mention: "Wenn du erwähnt wirst"
 _moderationLogTypes:
+  updateRole: "Rolle aktualisiert"
   assignRole: "Zu Rolle zugewiesen"
   unassignRole: "Aus Rolle entfernt"
-  updateRole: "Rolle aktualisiert"
   suspend: "Gesperrt"
   unsuspend: "Entsperrt"
   addCustomEmoji: "Benutzerdefiniertes Emoji hinzugefügt"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 75acf424be..920995a954 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -2101,9 +2101,9 @@ _webhookSettings:
     reaction: "When receiving a reaction"
     mention: "When being mentioned"
 _moderationLogTypes:
+  updateRole: "Role updated"
   assignRole: "Assigned to role"
   unassignRole: "Removed from role"
-  updateRole: "Role updated"
   suspend: "Suspended"
   unsuspend: "Unsuspended"
   addCustomEmoji: "Custom emoji added"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index b700fb8777..e99a41b75c 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -2031,7 +2031,7 @@ _notification:
   _types:
     all: "Tutto"
     note: "Nuove Note"
-    follow: "Novità follower"
+    follow: "Nuovi profili follower"
     mention: "Menzioni"
     reply: "Risposte"
     renote: "Rinota"
@@ -2101,18 +2101,26 @@ _webhookSettings:
     reaction: "Quando ricevo una reazione"
     mention: "Quando mi menzionano"
 _moderationLogTypes:
-  assignRole: "Assegna un ruolo"
-  unassignRole: "Disassegna un ruolo"
-  updateRole: "Aggiorna un ruolo"
+  updateRole: "Ruolo aggiornato"
+  assignRole: "Ruolo assegnato"
+  unassignRole: "Ruolo disassegnato"
   suspend: "Sospensione"
-  unsuspend: "Toglie la sospensione"
-  addCustomEmoji: "Aggiunge una emoji personalizzata"
-  updateServerSettings: "Aggiorna le impostazioni del server"
-  updateUserNote: "Aggiorna il promemoria di moderazione"
-  deleteDriveFile: "Elimina file da Drive"
-  deleteNote: "Elimina la Nota"
-  createGlobalAnnouncement: "Crea un annuncio globale"
-  createUserAnnouncement: "Crea un annuncio ai profili iscritti"
-  resetPassword: "Ripristina la password"
-  suspendRemoteInstance: "Sospendi istanza remota"
-  unsuspendRemoteInstance: "Riattiva istanza remota"
+  unsuspend: "Sospensione rimossa"
+  addCustomEmoji: "Emoji personalizzata aggiunta"
+  updateCustomEmoji: "Emoji personalizzata aggiornata"
+  deleteCustomEmoji: "Emoji personalizzata eliminata"
+  updateServerSettings: "Impostazioni del server aggiornate"
+  updateUserNote: "Promemoria di moderazione aggiornato"
+  deleteDriveFile: "File da Drive eliminato"
+  deleteNote: "Nota eliminata"
+  createGlobalAnnouncement: "Annuncio globale creato"
+  createUserAnnouncement: "Annuncio ai profili iscritti creato"
+  updateGlobalAnnouncement: "Annuncio globale aggiornato"
+  updateUserAnnouncement: "Annuncio ai profili iscritti aggiornato"
+  deleteGlobalAnnouncement: "Annuncio globale eliminato"
+  deleteUserAnnouncement: "Annuncio ai profili iscritti eliminato"
+  resetPassword: "Password azzerata"
+  suspendRemoteInstance: "Istanza remota sospesa"
+  unsuspendRemoteInstance: "Istanza remota riattivata"
+  markSensitiveDriveFile: "File nel Drive segnato come esplicito"
+  unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index bd03dba185..27f95eb377 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -2101,9 +2101,9 @@ _webhookSettings:
     reaction: "被回应时"
     mention: "被提及时"
 _moderationLogTypes:
+  updateRole: "更新角色"
   assignRole: "分配角色"
   unassignRole: "取消分配角色"
-  updateRole: "更新角色"
   suspend: "冻结"
   unsuspend: "解除冻结"
   addCustomEmoji: "添加自定义表情符号"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index cd6473aebd..7cea244098 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1,6 +1,6 @@
 ---
-_lang_: "繁體中文"
-headlineMisskey: "貼文連繫網絡"
+_lang_: "繁體中文(台灣)"
+headlineMisskey: "貼文連繫網路"
 introMisskey: "歡迎!Misskey 是一個開放原始碼且去中心化的社群網路服務。\n發布「貼文」向身邊的人分享您的想法!📡\n利用「反應」表達您對貼文的感覺!👍\n讓我們一起探索新的世界吧!🚀"
 poweredByMisskeyDescription: "{name}是開放原始碼平臺 <b>Misskey</b> 的伺服器之一。"
 monthAndDay: "{month} 月 {day} 日"
@@ -45,7 +45,7 @@ pin: "置頂"
 unpin: "取消置頂"
 copyContent: "複製內容"
 copyLink: "複製連結"
-copyLinkRenote: "複製轉貼連結"
+copyLinkRenote: "複製轉發的連結"
 delete: "刪除"
 deleteAndEdit: "刪除並編輯"
 deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有反應、轉發和回覆也將會消失。"
@@ -107,7 +107,7 @@ followRequestPending: "追隨許可待批准"
 enterEmoji: "輸入表情符號"
 renote: "轉發"
 unrenote: "取消轉發"
-renoted: "轉發成功"
+renoted: "轉發成功。"
 cantRenote: "無法轉發此貼文。"
 cantReRenote: "無法轉發之前已經轉發過的內容。"
 quote: "引用"
@@ -138,8 +138,8 @@ suspend: "凍結"
 unsuspend: "解除凍結"
 blockConfirm: "確定要封鎖此使用者嗎?"
 unblockConfirm: "確定要解除封鎖此使用者嗎?"
-suspendConfirm: "確定凍結此帳戶?"
-unsuspendConfirm: "確定解凍此帳戶?"
+suspendConfirm: "確定凍結此使用者?"
+unsuspendConfirm: "確定解凍此使用者?"
 selectList: "選擇清單"
 editList: "編輯清單"
 selectChannel: "選擇頻道"
@@ -152,12 +152,12 @@ customEmojis: "自訂表情符號"
 emoji: "表情符號"
 emojis: "表情符號"
 emojiName: "表情符號名稱"
-emojiUrl: "表情符號URL"
+emojiUrl: "表情符號 URL"
 addEmoji: "新增表情符號"
 settingGuide: "推薦設定"
 cacheRemoteFiles: "快取遠端檔案"
-cacheRemoteFilesDescription: "禁用此設定會停止建立遠端檔案快取,從而節省伺服器儲存空間,但會因從遠端讀取資料而增加網路數據用量。"
-youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,將快取全部刪除。"
+cacheRemoteFilesDescription: "啟用此設定後,遠端檔案會被快取在本伺服器的儲存空間中。雖然顯示圖片會變快,但會消耗較多伺服器的儲存空間。至於要快取遠端使用者到什麼程度,是依照角色的雲端硬碟容量而定。當超過這個限制時,從較舊的檔案開始自快取中刪除並改為連結。關閉這個設定時,遠端檔案從一開始就維持連結的方式,但為了產生圖片的縮圖並保護使用者的隱私,建議將 default.yml 的 proxyRemoteFiles 設為 true。"
+youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,可將快取全部刪除。"
 cacheRemoteSensitiveFiles: "快取遠端的敏感檔案"
 cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。"
 flagAsBot: "此使用者是機器人"
@@ -424,7 +424,7 @@ securityKeyAndPasskey: "安全金鑰、Passkey"
 securityKey: "安全金鑰"
 lastUsed: "上次使用"
 lastUsedAt: "上次使用:{t}"
-unregister: "註銷帳戶"
+unregister: "註銷"
 passwordLessLogin: "設置無密碼登入"
 passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入"
 resetPassword: "重設密碼"
@@ -509,8 +509,8 @@ promote: "推廣"
 numberOfDays: "有效天數"
 hideThisNote: "隱藏此貼文"
 showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
-objectStorage: "對象存儲"
-useObjectStorage: "使用對象存儲"
+objectStorage: "物件儲存"
+useObjectStorage: "使用物件儲存"
 objectStorageBaseUrl: "Base URL"
 objectStorageBaseUrlDesc: "用於引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL,例如 S3(https://<bucket>.s3.amazonaws.com)、GCS(https://storage.googleapis.com/<bucket>)。"
 objectStorageBucket: "儲存空間(Bucket)"
@@ -1120,6 +1120,7 @@ notifyNotes: "開啟貼文通知"
 unnotifyNotes: "關閉貼文通知"
 authentication: "驗證"
 authenticationRequiredToContinue: "請於繼續前完成驗證"
+dateAndTime: "日期與時間"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@@ -2101,9 +2102,11 @@ _webhookSettings:
     reaction: "當獲得反應時"
     mention: "當被提到時"
 _moderationLogTypes:
+  createRole: "新增角色"
+  deleteRole: "刪除角色 "
+  updateRole: "更新角色設定"
   assignRole: "指派角色"
   unassignRole: "撤銷角色"
-  updateRole: "更新角色設定"
   suspend: "凍結"
   unsuspend: "解除凍結"
   addCustomEmoji: "新增自訂表情符號"
@@ -2122,3 +2125,5 @@ _moderationLogTypes:
   resetPassword: "重設密碼"
   suspendRemoteInstance: "封鎖遠端伺服器"
   unsuspendRemoteInstance: "解除封鎖遠端伺服器"
+  markSensitiveDriveFile: "標記為敏感檔案"
+  unmarkSensitiveDriveFile: "撤銷標記為敏感檔案"

From bd19d75c9c49d601cbf29e2fdcd67f3864b74175 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 25 Sep 2023 16:03:43 +0900
Subject: [PATCH 61/62] enhance: improve moderation log

---
 locales/index.d.ts                                        | 1 +
 locales/ja-JP.yml                                         | 1 +
 .../api/endpoints/admin/resolve-abuse-user-report.ts      | 8 ++++++++
 packages/backend/src/types.ts                             | 6 ++++++
 packages/frontend/src/pages/admin/modlog.ModLog.vue       | 5 +++++
 packages/misskey-js/etc/misskey-js.api.md                 | 2 +-
 packages/misskey-js/src/consts.ts                         | 6 ++++++
 7 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 5e24ecffa5..4d8123eb5d 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2276,6 +2276,7 @@ export interface Locale {
         "unsuspendRemoteInstance": string;
         "markSensitiveDriveFile": string;
         "unmarkSensitiveDriveFile": string;
+        "resolveAbuseReport": string;
     };
 }
 declare const locales: {
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1af73c6201..647f5fb5f0 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2189,3 +2189,4 @@ _moderationLogTypes:
   unsuspendRemoteInstance: "リモートサーバーを再開"
   markSensitiveDriveFile: "ファイルをセンシティブ付与"
   unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
+  resolveAbuseReport: "通報を解決"
diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
index 8667640a67..fb5ac7a335 100644
--- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
+++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts
@@ -10,6 +10,7 @@ import { InstanceActorService } from '@/core/InstanceActorService.js';
 import { QueueService } from '@/core/QueueService.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
 import { DI } from '@/di-symbols.js';
+import { ModerationLogService } from '@/core/ModerationLogService.js';
 
 export const meta = {
 	tags: ['admin'],
@@ -41,6 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private queueService: QueueService,
 		private instanceActorService: InstanceActorService,
 		private apRendererService: ApRendererService,
+		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId });
@@ -61,6 +63,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				assigneeId: me.id,
 				forwarded: ps.forward && report.targetUserHost != null,
 			});
+
+			this.moderationLogService.log(me, 'resolveAbuseReport', {
+				reportId: report.id,
+				report: report,
+				forwarded: ps.forward && report.targetUserHost != null,
+			});
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index b458c0fbcb..35ea710f9e 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -55,6 +55,7 @@ export const moderationLogTypes = [
 	'unsuspendRemoteInstance',
 	'markSensitiveDriveFile',
 	'unmarkSensitiveDriveFile',
+	'resolveAbuseReport',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -192,4 +193,9 @@ export type ModerationLogPayloads = {
 		fileUserUsername: string | null;
 		fileUserHost: string | null;
 	};
+	resolveAbuseReport: {
+		reportId: string;
+		report: any;
+		forwarded: boolean;
+	};
 };
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index f0de026ad8..14f94479f1 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -75,6 +75,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
 			</div>
 		</template>
+
+		<details>
+			<summary>raw</summary>
+			<pre>{{ JSON5.stringify(log, null, '\t') }}</pre>
+		</details>
 	</div>
 </MkFolder>
 </template>
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index f72532f297..0686354ff4 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2607,7 +2607,7 @@ type ModerationLog = {
 });
 
 // @public (undocumented)
-export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile"];
+export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index d2751c447e..aedfb5570e 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -73,6 +73,7 @@ export const moderationLogTypes = [
 	'unsuspendRemoteInstance',
 	'markSensitiveDriveFile',
 	'unmarkSensitiveDriveFile',
+	'resolveAbuseReport',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -210,4 +211,9 @@ export type ModerationLogPayloads = {
 		fileUserUsername: string | null;
 		fileUserHost: string | null;
 	};
+	resolveAbuseReport: {
+		reportId: string;
+		report: any;
+		forwarded: boolean;
+	};
 };

From 2039e244c5c1ef4e68e16292e9d19e3d94629d5a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 25 Sep 2023 16:05:02 +0900
Subject: [PATCH 62/62] build(deps): bump actions/checkout from 4.0.0 to 4.1.0
 (#11900)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.0.0...v4.1.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/api-misskey-js.yml       | 2 +-
 .github/workflows/check_copyright_year.yml | 2 +-
 .github/workflows/docker-develop.yml       | 2 +-
 .github/workflows/docker.yml               | 2 +-
 .github/workflows/dockle.yml               | 2 +-
 .github/workflows/lint.yml                 | 6 +++---
 .github/workflows/pr-preview-deploy.yml    | 2 +-
 .github/workflows/test-backend.yml         | 2 +-
 .github/workflows/test-frontend.yml        | 4 ++--
 .github/workflows/test-misskey-js.yml      | 2 +-
 .github/workflows/test-production.yml      | 2 +-
 11 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index 4cf523a6b9..39f29bf773 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -9,7 +9,7 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4.0.0
+        uses: actions/checkout@v4.1.0
 
       - run: corepack enable
 
diff --git a/.github/workflows/check_copyright_year.yml b/.github/workflows/check_copyright_year.yml
index 313265f671..fb04cf1b00 100644
--- a/.github/workflows/check_copyright_year.yml
+++ b/.github/workflows/check_copyright_year.yml
@@ -10,7 +10,7 @@ jobs:
   check_copyright_year:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
     - run: |
         if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
           echo "Please change copyright year!"
diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml
index 05bb7f77f9..3e5bb17902 100644
--- a/.github/workflows/docker-develop.yml
+++ b/.github/workflows/docker-develop.yml
@@ -13,7 +13,7 @@ jobs:
     if: github.repository == 'misskey-dev/misskey'
     steps:
       - name: Check out the repo
-        uses: actions/checkout@v4.0.0
+        uses: actions/checkout@v4.1.0
       - name: Set up Docker Buildx
         id: buildx
         uses: docker/setup-buildx-action@v3.0.0
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 32a98a416d..33c85cbaf4 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -12,7 +12,7 @@ jobs:
 
     steps:
       - name: Check out the repo
-        uses: actions/checkout@v4.0.0
+        uses: actions/checkout@v4.1.0
       - name: Set up Docker Buildx
         id: buildx
         uses: docker/setup-buildx-action@v3.0.0
diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index d811944d61..2a1ac3a16c 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -14,7 +14,7 @@ jobs:
     env:
       DOCKER_CONTENT_TRUST: 1
     steps:
-      - uses: actions/checkout@v4.0.0
+      - uses: actions/checkout@v4.1.0
       - run: |
           curl -L -o dockle.deb "https://github.com/goodwithtech/dockle/releases/download/v0.4.10/dockle_0.4.10_Linux-64bit.deb"
           sudo dpkg -i dockle.deb
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 7c10c23e77..798e6f49a3 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -11,7 +11,7 @@ jobs:
   pnpm_install:
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         fetch-depth: 0
         submodules: true
@@ -38,7 +38,7 @@ jobs:
         - sw
         - misskey-js
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         fetch-depth: 0
         submodules: true
@@ -64,7 +64,7 @@ jobs:
         - backend
         - misskey-js
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         fetch-depth: 0
         submodules: true
diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml
index 702d8917e3..44f97645d0 100644
--- a/.github/workflows/pr-preview-deploy.yml
+++ b/.github/workflows/pr-preview-deploy.yml
@@ -53,7 +53,7 @@ jobs:
 
     # Check out merge commit
     - name: Fork based /deploy checkout
-      uses: actions/checkout@v4.0.0
+      uses: actions/checkout@v4.1.0
       with:
         ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
 
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 19496c8959..ac7d1afda1 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -29,7 +29,7 @@ jobs:
           - 56312:6379
 
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         submodules: true
     - name: Install pnpm
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 0618a0ef0a..e67b516546 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -16,7 +16,7 @@ jobs:
         node-version: [20.5.1]
 
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         submodules: true
     - name: Install pnpm
@@ -68,7 +68,7 @@ jobs:
           - 56312:6379
 
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         submodules: true
     # https://github.com/cypress-io/cypress-docker-images/issues/150
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index 7999c183b1..1846b628d3 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -21,7 +21,7 @@ jobs:
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v4.0.0
+        uses: actions/checkout@v4.1.0
 
       - run: corepack enable
 
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 0504f42d16..c570018962 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -19,7 +19,7 @@ jobs:
         node-version: [20.5.1]
 
     steps:
-    - uses: actions/checkout@v4.0.0
+    - uses: actions/checkout@v4.1.0
       with:
         submodules: true
     - name: Install pnpm